summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/acpi/acpica/Makefile27
-rw-r--r--drivers/acpi/acpica/aclocal.h7
-rw-r--r--drivers/acpi/acpica/rscalc.c7
-rw-r--r--drivers/acpi/bus.c2
-rw-r--r--drivers/acpi/pci_bind.c24
-rw-r--r--drivers/acpi/pci_irq.c5
-rw-r--r--drivers/acpi/processor_core.c2
-rw-r--r--drivers/acpi/processor_idle.c50
-rw-r--r--drivers/acpi/processor_perflib.c12
-rw-r--r--drivers/acpi/processor_throttling.c25
-rw-r--r--drivers/acpi/video.c57
-rw-r--r--drivers/ata/ahci.c159
-rw-r--r--drivers/ata/ata_piix.c38
-rw-r--r--drivers/ata/libata-core.c15
-rw-r--r--drivers/ata/libata-eh.c34
-rw-r--r--drivers/ata/libata-scsi.c38
-rw-r--r--drivers/ata/libata-sff.c20
-rw-r--r--drivers/ata/pata_ali.c17
-rw-r--r--drivers/ata/pata_efar.c17
-rw-r--r--drivers/ata/pata_legacy.c2
-rw-r--r--drivers/ata/pata_netcell.c13
-rw-r--r--drivers/ata/pata_pdc202xx_old.c6
-rw-r--r--drivers/ata/sata_fsl.c15
-rw-r--r--drivers/ata/sata_mv.c69
-rw-r--r--drivers/ata/sata_nv.c131
-rw-r--r--drivers/ata/sata_sil.c2
-rw-r--r--drivers/ata/sata_sx4.c191
-rw-r--r--drivers/base/bus.c4
-rw-r--r--drivers/base/core.c5
-rw-r--r--drivers/base/driver.c4
-rw-r--r--drivers/base/firmware_class.c129
-rw-r--r--drivers/base/iommu.c2
-rw-r--r--drivers/base/platform.c58
-rw-r--r--drivers/base/power/main.c98
-rw-r--r--drivers/base/sys.c16
-rw-r--r--drivers/block/Kconfig2
-rw-r--r--drivers/block/brd.c7
-rw-r--r--drivers/block/cciss.c888
-rw-r--r--drivers/block/cciss.h34
-rw-r--r--drivers/block/cciss_cmd.h2
-rw-r--r--drivers/block/cciss_scsi.c109
-rw-r--r--drivers/block/cpqarray.c4
-rw-r--r--drivers/block/floppy.c27
-rw-r--r--drivers/block/hd.c2
-rw-r--r--drivers/block/mg_disk.c2
-rw-r--r--drivers/block/pktcdvd.c8
-rw-r--r--drivers/block/ps3disk.c2
-rw-r--r--drivers/block/ub.c36
-rw-r--r--drivers/block/virtio_blk.c112
-rw-r--r--drivers/block/xen-blkfront.c14
-rw-r--r--drivers/block/xsysace.c2
-rw-r--r--drivers/bluetooth/hci_ldisc.c5
-rw-r--r--drivers/cdrom/cdrom.c4
-rw-r--r--drivers/cdrom/gdrom.c2
-rw-r--r--drivers/cdrom/viocd.c6
-rw-r--r--drivers/char/Kconfig15
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/agp/intel-agp.c16
-rw-r--r--drivers/char/bfin_jtag_comm.c365
-rw-r--r--drivers/char/cyclades.c290
-rw-r--r--drivers/char/epca.c17
-rw-r--r--drivers/char/hpet.c4
-rw-r--r--drivers/char/hw_random/virtio-rng.c30
-rw-r--r--drivers/char/ip2/i2lib.c4
-rw-r--r--drivers/char/ip2/ip2main.c4
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c13
-rw-r--r--drivers/char/isicom.c19
-rw-r--r--drivers/char/istallion.c8
-rw-r--r--drivers/char/mem.c2
-rw-r--r--drivers/char/moxa.c5
-rw-r--r--drivers/char/mxser.c14
-rw-r--r--drivers/char/n_hdlc.c4
-rw-r--r--drivers/char/n_tty.c29
-rw-r--r--drivers/char/pcmcia/synclink_cs.c11
-rw-r--r--drivers/char/pty.c11
-rw-r--r--drivers/char/random.c19
-rw-r--r--drivers/char/raw.c2
-rw-r--r--drivers/char/rocket.c19
-rw-r--r--drivers/char/selection.c2
-rw-r--r--drivers/char/stallion.c6
-rw-r--r--drivers/char/synclink.c9
-rw-r--r--drivers/char/synclink_gt.c86
-rw-r--r--drivers/char/synclinkmp.c9
-rw-r--r--drivers/char/sysrq.c6
-rw-r--r--drivers/char/tpm/tpm_bios.c3
-rw-r--r--drivers/char/tty_audit.c10
-rw-r--r--drivers/char/tty_io.c122
-rw-r--r--drivers/char/tty_ioctl.c88
-rw-r--r--drivers/char/tty_ldisc.c549
-rw-r--r--drivers/char/tty_port.c47
-rw-r--r--drivers/char/virtio_console.c26
-rw-r--r--drivers/char/vt.c8
-rw-r--r--drivers/char/vt_ioctl.c6
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/sh_cmt.c116
-rw-r--r--drivers/clocksource/sh_mtu2.c357
-rw-r--r--drivers/clocksource/sh_tmu.c461
-rw-r--r--drivers/cpufreq/cpufreq.c6
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c5
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c5
-rw-r--r--drivers/crypto/ixp4xx_crypto.c33
-rw-r--r--drivers/crypto/padlock-aes.c2
-rw-r--r--drivers/dma/dmaengine.c17
-rw-r--r--drivers/dma/dmatest.c4
-rw-r--r--drivers/dma/fsldma.c71
-rw-r--r--drivers/dma/ioat_dma.c47
-rw-r--r--drivers/dma/ipu/ipu_idmac.c7
-rw-r--r--drivers/edac/Kconfig34
-rw-r--r--drivers/edac/Makefile9
-rw-r--r--drivers/edac/amd64_edac.c3354
-rw-r--r--drivers/edac/amd64_edac.h644
-rw-r--r--drivers/edac/amd64_edac_dbg.c255
-rw-r--r--drivers/edac/amd64_edac_err_types.c161
-rw-r--r--drivers/edac/amd64_edac_inj.c185
-rw-r--r--drivers/edac/amd8111_edac.c4
-rw-r--r--drivers/edac/amd8131_edac.c2
-rw-r--r--drivers/edac/edac_core.h9
-rw-r--r--drivers/firmware/dmi_scan.c1
-rw-r--r--drivers/firmware/iscsi_ibft.c4
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpu/drm/Kconfig9
-rw-r--r--drivers/gpu/drm/drm_bufs.c11
-rw-r--r--drivers/gpu/drm/drm_crtc.c7
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c128
-rw-r--r--drivers/gpu/drm/drm_drv.c5
-rw-r--r--drivers/gpu/drm/drm_edid.c79
-rw-r--r--drivers/gpu/drm/drm_gem.c2
-rw-r--r--drivers/gpu/drm/drm_hashtab.c4
-rw-r--r--drivers/gpu/drm/drm_irq.c8
-rw-r--r--drivers/gpu/drm/drm_mm.c165
-rw-r--r--drivers/gpu/drm/drm_modes.c18
-rw-r--r--drivers/gpu/drm/drm_stub.c32
-rw-r--r--drivers/gpu/drm/drm_sysfs.c9
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c98
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h68
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c232
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c167
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c192
-rw-r--r--drivers/gpu/drm/i915/i915_opregion.c6
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h638
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c44
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c188
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h118
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c233
-rw-r--r--drivers/gpu/drm/i915/intel_display.c711
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h4
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c1
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c30
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c51
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c17
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c207
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c6
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c248
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c4
-rw-r--r--drivers/gpu/drm/r128/r128_cce.c6
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c42
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h9
-rw-r--r--drivers/gpu/drm/via/via_dmablit.c6
-rw-r--r--drivers/hid/hid-apple.c2
-rw-r--r--drivers/hid/hid-core.c4
-rw-r--r--drivers/hid/hid-ids.h6
-rw-r--r--drivers/hid/hid-lg.c2
-rw-r--r--drivers/hid/hidraw.c12
-rw-r--r--drivers/hid/usbhid/hid-core.c4
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hwmon/asus_atk0110.c2
-rw-r--r--drivers/hwmon/lm78.c2
-rw-r--r--drivers/hwmon/w83781d.c3
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c11
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-amd756.c4
-rw-r--r--drivers/i2c/busses/i2c-cpm.c16
-rw-r--r--drivers/i2c/busses/i2c-i801.c8
-rw-r--r--drivers/i2c/busses/i2c-isch.c2
-rw-r--r--drivers/i2c/busses/i2c-mpc.c34
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c2
-rw-r--r--drivers/i2c/busses/i2c-pxa.c6
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c2
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c2
-rw-r--r--drivers/ide/alim15x3.c10
-rw-r--r--drivers/ide/at91_ide.c7
-rw-r--r--drivers/ide/au1xxx-ide.c8
-rw-r--r--drivers/ide/buddha.c9
-rw-r--r--drivers/ide/cmd640.c7
-rw-r--r--drivers/ide/cs5520.c4
-rw-r--r--drivers/ide/delkin_cb.c6
-rw-r--r--drivers/ide/falconide.c9
-rw-r--r--drivers/ide/gayle.c9
-rw-r--r--drivers/ide/hpt366.c25
-rw-r--r--drivers/ide/icside.c77
-rw-r--r--drivers/ide/ide-4drives.c6
-rw-r--r--drivers/ide/ide-atapi.c8
-rw-r--r--drivers/ide/ide-cd.c25
-rw-r--r--drivers/ide/ide-cs.c6
-rw-r--r--drivers/ide/ide-disk.c77
-rw-r--r--drivers/ide/ide-dma.c1
-rw-r--r--drivers/ide/ide-eh.c14
-rw-r--r--drivers/ide/ide-gd.c14
-rw-r--r--drivers/ide/ide-generic.c7
-rw-r--r--drivers/ide/ide-h8300.c10
-rw-r--r--drivers/ide/ide-io.c81
-rw-r--r--drivers/ide/ide-iops.c47
-rw-r--r--drivers/ide/ide-legacy.c7
-rw-r--r--drivers/ide/ide-lib.c27
-rw-r--r--drivers/ide/ide-pci-generic.c11
-rw-r--r--drivers/ide/ide-pnp.c6
-rw-r--r--drivers/ide/ide-probe.c104
-rw-r--r--drivers/ide/ide-tape.c93
-rw-r--r--drivers/ide/ide-taskfile.c3
-rw-r--r--drivers/ide/ide.c10
-rw-r--r--drivers/ide/ide_platform.c9
-rw-r--r--drivers/ide/macide.c9
-rw-r--r--drivers/ide/palm_bk3710.c6
-rw-r--r--drivers/ide/pdc202xx_new.c26
-rw-r--r--drivers/ide/pdc202xx_old.c104
-rw-r--r--drivers/ide/piix.c1
-rw-r--r--drivers/ide/pmac.c13
-rw-r--r--drivers/ide/q40ide.c11
-rw-r--r--drivers/ide/rapide.c8
-rw-r--r--drivers/ide/scc_pata.c6
-rw-r--r--drivers/ide/setup-pci.c85
-rw-r--r--drivers/ide/sgiioc4.c7
-rw-r--r--drivers/ide/siimage.c4
-rw-r--r--drivers/ide/sl82c105.c9
-rw-r--r--drivers/ide/tx4938ide.c5
-rw-r--r--drivers/ide/tx4939ide.c5
-rw-r--r--drivers/ide/via82cxxx.c2
-rw-r--r--drivers/idle/i7300_idle.c6
-rw-r--r--drivers/ieee1394/dv1394.c5
-rw-r--r--drivers/ieee1394/ieee1394_core.h6
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_hal.c1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c8
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_qp.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_fs.c2
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h1
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c10
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c16
-rw-r--r--drivers/infiniband/hw/nes/nes.h4
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c84
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h1
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c30
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c67
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.h1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c6
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c5
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c11
-rw-r--r--drivers/input/ff-memless.c2
-rw-r--r--drivers/input/input.c37
-rw-r--r--drivers/input/joydev.c8
-rw-r--r--drivers/input/keyboard/atkbd.c16
-rw-r--r--drivers/input/keyboard/omap-keypad.c16
-rw-r--r--drivers/input/mouse/alps.c1
-rw-r--r--drivers/input/mouse/appletouch.c24
-rw-r--r--drivers/input/mouse/bcm5974.c108
-rw-r--r--drivers/input/mouse/elantech.c48
-rw-r--r--drivers/input/mouse/elantech.h5
-rw-r--r--drivers/input/mouse/lifebook.c6
-rw-r--r--drivers/input/mouse/maplemouse.c43
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/serio/ambakmi.c2
-rw-r--r--drivers/input/serio/libps2.c2
-rw-r--r--drivers/input/serio/sa1111ps2.c2
-rw-r--r--drivers/input/tablet/wacom.h5
-rw-r--r--drivers/input/tablet/wacom_sys.c13
-rw-r--r--drivers/input/touchscreen/tsc2007.c7
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c4
-rw-r--r--drivers/isdn/capi/capifs.c11
-rw-r--r--drivers/isdn/capi/kcapi.c171
-rw-r--r--drivers/isdn/gigaset/isocdata.c2
-rw-r--r--drivers/lguest/Kconfig2
-rw-r--r--drivers/lguest/core.c30
-rw-r--r--drivers/lguest/hypercalls.c14
-rw-r--r--drivers/lguest/interrupts_and_traps.c57
-rw-r--r--drivers/lguest/lg.h28
-rw-r--r--drivers/lguest/lguest_device.c41
-rw-r--r--drivers/lguest/lguest_user.c127
-rw-r--r--drivers/lguest/page_tables.c396
-rw-r--r--drivers/lguest/segments.c2
-rw-r--r--drivers/lguest/x86/core.c19
-rw-r--r--drivers/md/bitmap.c46
-rw-r--r--drivers/md/dm-exception-store.c2
-rw-r--r--drivers/md/dm-log.c3
-rw-r--r--drivers/md/dm-snap-persistent.c2
-rw-r--r--drivers/md/dm-table.c38
-rw-r--r--drivers/md/dm.c8
-rw-r--r--drivers/md/linear.c2
-rw-r--r--drivers/md/md.c103
-rw-r--r--drivers/md/multipath.c4
-rw-r--r--drivers/md/raid0.c2
-rw-r--r--drivers/md/raid1.c4
-rw-r--r--drivers/md/raid10.c20
-rw-r--r--drivers/md/raid5.c38
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c2
-rw-r--r--drivers/media/video/au0828/au0828-core.c6
-rw-r--r--drivers/media/video/cafe_ccic.c1
-rw-r--r--drivers/media/video/cx18/cx18-audio.c2
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c16
-rw-r--r--drivers/media/video/cx231xx/Kconfig44
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c4
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c3
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c9
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c5
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c3
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c3
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c3
-rw-r--r--drivers/media/video/mx3_camera.c4
-rw-r--r--drivers/media/video/s2255drv.c2
-rw-r--r--drivers/media/video/saa5246a.c3
-rw-r--r--drivers/media/video/saa5249.c4
-rw-r--r--drivers/media/video/uvc/uvc_driver.c9
-rw-r--r--drivers/media/video/uvc/uvc_video.c2
-rw-r--r--drivers/media/video/v4l2-ioctl.c45
-rw-r--r--drivers/media/video/zoran/zoran_driver.c28
-rw-r--r--drivers/memstick/core/mspro_block.c2
-rw-r--r--drivers/message/fusion/mptbase.c1571
-rw-r--r--drivers/message/fusion/mptbase.h180
-rw-r--r--drivers/message/fusion/mptctl.c692
-rw-r--r--drivers/message/fusion/mptdebug.h3
-rw-r--r--drivers/message/fusion/mptfc.c15
-rw-r--r--drivers/message/fusion/mptsas.c3117
-rw-r--r--drivers/message/fusion/mptsas.h41
-rw-r--r--drivers/message/fusion/mptscsih.c1329
-rw-r--r--drivers/message/fusion/mptscsih.h7
-rw-r--r--drivers/message/fusion/mptspi.c71
-rw-r--r--drivers/message/i2o/i2o_block.c5
-rw-r--r--drivers/mfd/pcf50633-core.c2
-rw-r--r--drivers/mfd/wm8350-core.c8
-rw-r--r--drivers/misc/enclosure.c6
-rw-r--r--drivers/misc/isl29003.c9
-rw-r--r--drivers/mmc/card/block.c10
-rw-r--r--drivers/mmc/core/core.c4
-rw-r--r--drivers/mmc/host/mmci.c4
-rw-r--r--drivers/mmc/host/mvsdio.c46
-rw-r--r--drivers/mmc/host/mxcmmc.c47
-rw-r--r--drivers/mmc/host/omap.c4
-rw-r--r--drivers/mmc/host/omap_hsmmc.c2
-rw-r--r--drivers/mmc/host/sdhci-of.c9
-rw-r--r--drivers/mmc/host/sdhci-pci.c8
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mtd/devices/m25p80.c26
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c2
-rw-r--r--drivers/mtd/mtdcore.c18
-rw-r--r--drivers/mtd/mtdsuper.c3
-rw-r--r--drivers/mtd/nand/davinci_nand.c7
-rw-r--r--drivers/mtd/nand/mxc_nand.c43
-rw-r--r--drivers/net/3c509.c4
-rw-r--r--drivers/net/Kconfig11
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/arm/ixp4xx_eth.c31
-rw-r--r--drivers/net/atl1e/atl1e_main.c1
-rw-r--r--drivers/net/atlx/atl1.c6
-rw-r--r--drivers/net/atlx/atlx.h6
-rw-r--r--drivers/net/benet/be.h14
-rw-r--r--drivers/net/benet/be_main.c10
-rw-r--r--drivers/net/bfin_mac.c29
-rw-r--r--drivers/net/bnx2.c199
-rw-r--r--drivers/net/bnx2.h18
-rw-r--r--drivers/net/bonding/bond_3ad.c11
-rw-r--r--drivers/net/bonding/bond_alb.c12
-rw-r--r--drivers/net/bonding/bond_main.c44
-rw-r--r--drivers/net/cnic.c2711
-rw-r--r--drivers/net/cnic.h299
-rw-r--r--drivers/net/cnic_defs.h580
-rw-r--r--drivers/net/cnic_if.h299
-rw-r--r--drivers/net/cxgb3/adapter.h4
-rw-r--r--drivers/net/cxgb3/cxgb3_main.c8
-rw-r--r--drivers/net/cxgb3/sge.c11
-rw-r--r--drivers/net/cxgb3/t3_hw.c7
-rw-r--r--drivers/net/e100.c30
-rw-r--r--drivers/net/e1000/e1000_main.c7
-rw-r--r--drivers/net/ehea/ehea.h2
-rw-r--r--drivers/net/ehea/ehea_main.c31
-rw-r--r--drivers/net/forcedeth.c46
-rw-r--r--drivers/net/gianfar.c11
-rw-r--r--drivers/net/gianfar.h2
-rw-r--r--drivers/net/igb/igb_main.c12
-rw-r--r--drivers/net/ixgbe/ixgbe_common.c51
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c10
-rw-r--r--drivers/net/mac8390.c12
-rw-r--r--drivers/net/meth.c27
-rw-r--r--drivers/net/mlx4/en_cq.c4
-rw-r--r--drivers/net/mlx4/en_netdev.c2
-rw-r--r--drivers/net/mlx4/en_rx.c5
-rw-r--r--drivers/net/mlx4/en_tx.c8
-rw-r--r--drivers/net/mlx4/main.c2
-rw-r--r--drivers/net/mv643xx_eth.c65
-rw-r--r--drivers/net/ne2k-pci.c7
-rw-r--r--drivers/net/netconsole.c11
-rw-r--r--drivers/net/r8169.c113
-rw-r--r--drivers/net/usb/Kconfig14
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc_eem.c381
-rw-r--r--drivers/net/usb/smsc95xx.c15
-rw-r--r--drivers/net/usb/smsc95xx.h3
-rw-r--r--drivers/net/veth.c41
-rw-r--r--drivers/net/virtio_net.c69
-rw-r--r--drivers/net/vxge/vxge-traffic.c2
-rw-r--r--drivers/net/wan/ixp4xx_hss.c4
-rw-r--r--drivers/net/wimax/i2400m/rx.c5
-rw-r--r--drivers/net/wimax/i2400m/usb.c35
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/airo.c21
-rw-r--r--drivers/net/wireless/at76c50x-usb.c12
-rw-r--r--drivers/net/wireless/ath5k/base.c22
-rw-r--r--drivers/net/wireless/ath5k/debug.c2
-rw-r--r--drivers/net/wireless/ath5k/phy.c49
-rw-r--r--drivers/net/wireless/ath5k/reset.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c11
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c21
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c11
-rw-r--r--drivers/net/wireless/rndis_wlan.c14
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c2
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187.h57
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c15
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_rtl8225.c8
-rw-r--r--drivers/of/Kconfig8
-rw-r--r--drivers/oprofile/cpu_buffer.c8
-rw-r--r--drivers/parisc/iosapic.c6
-rw-r--r--drivers/parport/parport_gsc.c4
-rw-r--r--drivers/parport/parport_pc.c1802
-rw-r--r--drivers/parport/share.c13
-rw-r--r--drivers/pci/access.c4
-rw-r--r--drivers/pci/hotplug/acpiphp.h1
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c63
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c54
-rw-r--r--drivers/pci/htirq.c5
-rw-r--r--drivers/pci/intel-iommu.c22
-rw-r--r--drivers/pci/intr_remapping.c54
-rw-r--r--drivers/pci/msi.c8
-rw-r--r--drivers/pci/pci-sysfs.c12
-rw-r--r--drivers/pci/pci.c73
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h3
-rw-r--r--drivers/pci/pcie/portdrv_pci.c2
-rw-r--r--drivers/pci/probe.c8
-rw-r--r--drivers/pci/quirks.c1
-rw-r--r--drivers/pci/setup-bus.c6
-rw-r--r--drivers/pci/slot.c4
-rw-r--r--drivers/platform/x86/asus-laptop.c6
-rw-r--r--drivers/platform/x86/eeepc-laptop.c47
-rw-r--r--drivers/pnp/pnpacpi/core.c8
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c2
-rw-r--r--drivers/pnp/resource.c18
-rw-r--r--drivers/regulator/bq24022.c3
-rw-r--r--drivers/regulator/core.c19
-rw-r--r--drivers/regulator/da903x.c2
-rw-r--r--drivers/regulator/virtual.c8
-rw-r--r--drivers/rtc/Kconfig2
-rw-r--r--drivers/rtc/rtc-pl030.c2
-rw-r--r--drivers/rtc/rtc-pl031.c2
-rw-r--r--drivers/rtc/rtc-twl4030.c4
-rw-r--r--drivers/s390/block/dasd.c36
-rw-r--r--drivers/s390/block/dasd_diag.c1
-rw-r--r--drivers/s390/block/dasd_eckd.c44
-rw-r--r--drivers/s390/block/dasd_fba.c22
-rw-r--r--drivers/s390/block/dasd_int.h3
-rw-r--r--drivers/s390/block/dcssblk.c6
-rw-r--r--drivers/s390/block/xpram.c2
-rw-r--r--drivers/s390/char/con3270.c38
-rw-r--r--drivers/s390/char/tape_block.c2
-rw-r--r--drivers/s390/char/tty3270.c57
-rw-r--r--drivers/s390/cio/cio.c6
-rw-r--r--drivers/s390/cio/device_ops.c24
-rw-r--r--drivers/s390/cio/qdio_main.c46
-rw-r--r--drivers/s390/cio/qdio_perf.c12
-rw-r--r--drivers/s390/cio/qdio_perf.h10
-rw-r--r--drivers/s390/kvm/kvm_virtio.c43
-rw-r--r--drivers/s390/scsi/zfcp_aux.c11
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c31
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c3
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c10
-rw-r--r--drivers/s390/scsi/zfcp_def.h9
-rw-r--r--drivers/s390/scsi/zfcp_erp.c68
-rw-r--r--drivers/s390/scsi/zfcp_ext.h3
-rw-r--r--drivers/s390/scsi/zfcp_fc.c40
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c88
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c57
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c21
-rw-r--r--drivers/scsi/3w-9xxx.c3
-rw-r--r--drivers/scsi/3w-xxxx.c5
-rw-r--r--drivers/scsi/3w-xxxx.h2
-rw-r--r--drivers/scsi/Kconfig42
-rw-r--r--drivers/scsi/Makefile4
-rw-r--r--drivers/scsi/NCR_D700.c2
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_constants.h155
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_hsi.h1509
-rw-r--r--drivers/scsi/bnx2i/Kconfig7
-rw-r--r--drivers/scsi/bnx2i/Makefile3
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h771
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c2405
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c438
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c2064
-rw-r--r--drivers/scsi/bnx2i/bnx2i_sysfs.c142
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i.h3
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_ddp.c32
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c31
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.c27
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.h3
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.c19
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c6
-rw-r--r--drivers/scsi/fcoe/fcoe.c154
-rw-r--r--drivers/scsi/fcoe/fcoe.h1
-rw-r--r--drivers/scsi/fcoe/libfcoe.c23
-rw-r--r--drivers/scsi/fnic/Makefile15
-rw-r--r--drivers/scsi/fnic/cq_desc.h78
-rw-r--r--drivers/scsi/fnic/cq_enet_desc.h167
-rw-r--r--drivers/scsi/fnic/cq_exch_desc.h182
-rw-r--r--drivers/scsi/fnic/fcpio.h780
-rw-r--r--drivers/scsi/fnic/fnic.h265
-rw-r--r--drivers/scsi/fnic/fnic_attrs.c56
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c742
-rw-r--r--drivers/scsi/fnic/fnic_io.h67
-rw-r--r--drivers/scsi/fnic/fnic_isr.c332
-rw-r--r--drivers/scsi/fnic/fnic_main.c943
-rw-r--r--drivers/scsi/fnic/fnic_res.c444
-rw-r--r--drivers/scsi/fnic/fnic_res.h197
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c1850
-rw-r--r--drivers/scsi/fnic/rq_enet_desc.h58
-rw-r--r--drivers/scsi/fnic/vnic_cq.c85
-rw-r--r--drivers/scsi/fnic/vnic_cq.h121
-rw-r--r--drivers/scsi/fnic/vnic_cq_copy.h62
-rw-r--r--drivers/scsi/fnic/vnic_dev.c690
-rw-r--r--drivers/scsi/fnic/vnic_dev.h161
-rw-r--r--drivers/scsi/fnic/vnic_devcmd.h281
-rw-r--r--drivers/scsi/fnic/vnic_intr.c60
-rw-r--r--drivers/scsi/fnic/vnic_intr.h118
-rw-r--r--drivers/scsi/fnic/vnic_nic.h69
-rw-r--r--drivers/scsi/fnic/vnic_resource.h61
-rw-r--r--drivers/scsi/fnic/vnic_rq.c196
-rw-r--r--drivers/scsi/fnic/vnic_rq.h235
-rw-r--r--drivers/scsi/fnic/vnic_scsi.h99
-rw-r--r--drivers/scsi/fnic/vnic_stats.h68
-rw-r--r--drivers/scsi/fnic/vnic_wq.c182
-rw-r--r--drivers/scsi/fnic/vnic_wq.h175
-rw-r--r--drivers/scsi/fnic/vnic_wq_copy.c117
-rw-r--r--drivers/scsi/fnic/vnic_wq_copy.h128
-rw-r--r--drivers/scsi/fnic/wq_enet_desc.h96
-rw-r--r--drivers/scsi/gdth_proc.c5
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c434
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h40
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c463
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.h4
-rw-r--r--drivers/scsi/ibmvscsi/viosrp.h68
-rw-r--r--drivers/scsi/ipr.c18
-rw-r--r--drivers/scsi/iscsi_tcp.c3
-rw-r--r--drivers/scsi/libfc/fc_disc.c54
-rw-r--r--drivers/scsi/libfc/fc_elsct.c2
-rw-r--r--drivers/scsi/libfc/fc_exch.c4
-rw-r--r--drivers/scsi/libfc/fc_fcp.c9
-rw-r--r--drivers/scsi/libfc/fc_lport.c74
-rw-r--r--drivers/scsi/libfc/fc_rport.c88
-rw-r--r--drivers/scsi/libiscsi.c478
-rw-r--r--drivers/scsi/libiscsi_tcp.c23
-rw-r--r--drivers/scsi/libsas/sas_expander.c4
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c3
-rw-r--r--drivers/scsi/lpfc/lpfc.h123
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c516
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h65
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c15
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c66
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c516
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c1453
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h142
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h2141
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c5795
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h54
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c735
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c219
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c69
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1042
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c7036
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h29
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h467
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h6
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c72
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c75
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h33
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c246
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.h7
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c392
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c40
-rw-r--r--drivers/scsi/mvsas.c3222
-rw-r--r--drivers/scsi/mvsas/Kconfig42
-rw-r--r--drivers/scsi/mvsas/Makefile32
-rw-r--r--drivers/scsi/mvsas/mv_64xx.c793
-rw-r--r--drivers/scsi/mvsas/mv_64xx.h151
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c672
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h222
-rw-r--r--drivers/scsi/mvsas/mv_chips.h280
-rw-r--r--drivers/scsi/mvsas/mv_defs.h502
-rw-r--r--drivers/scsi/mvsas/mv_init.c703
-rw-r--r--drivers/scsi/mvsas/mv_sas.c2154
-rw-r--r--drivers/scsi/mvsas/mv_sas.h406
-rw-r--r--drivers/scsi/osd/Kbuild25
-rwxr-xr-xdrivers/scsi/osd/Makefile37
-rw-r--r--drivers/scsi/osd/osd_initiator.c280
-rw-r--r--drivers/scsi/osd/osd_uld.c70
-rw-r--r--drivers/scsi/qla1280.c387
-rw-r--r--drivers/scsi/qla1280.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c227
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c13
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h45
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h6
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h43
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c5
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c206
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c55
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c240
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c244
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c118
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c294
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c47
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/scsi.c4
-rw-r--r--drivers/scsi/scsi_debug.c2
-rw-r--r--drivers/scsi/scsi_error.c21
-rw-r--r--drivers/scsi/scsi_lib.c20
-rw-r--r--drivers/scsi/scsi_scan.c5
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c175
-rw-r--r--drivers/scsi/sd.c62
-rw-r--r--drivers/scsi/sg.c29
-rw-r--r--drivers/scsi/sr.c2
-rw-r--r--drivers/scsi/sr_ioctl.c5
-rw-r--r--drivers/scsi/st.c6
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c66
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c49
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.h2
-rw-r--r--drivers/serial/8250.c22
-rw-r--r--drivers/serial/8250_gsc.c4
-rw-r--r--drivers/serial/8250_pci.c3
-rw-r--r--drivers/serial/Kconfig8
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/amba-pl011.c2
-rw-r--r--drivers/serial/bfin_5xx.c77
-rw-r--r--drivers/serial/bfin_sport_uart.c58
-rw-r--r--drivers/serial/crisv10.c173
-rw-r--r--drivers/serial/icom.c22
-rw-r--r--drivers/serial/imx.c296
-rw-r--r--drivers/serial/jsm/jsm.h3
-rw-r--r--drivers/serial/jsm/jsm_driver.c1
-rw-r--r--drivers/serial/jsm/jsm_tty.c14
-rw-r--r--drivers/serial/mpc52xx_uart.c2
-rw-r--r--drivers/serial/nwpserial.c4
-rw-r--r--drivers/serial/s3c6400.c1
-rw-r--r--drivers/serial/samsung.c61
-rw-r--r--drivers/serial/samsung.h4
-rw-r--r--drivers/serial/sh-sci.c388
-rw-r--r--drivers/serial/sh-sci.h42
-rw-r--r--drivers/serial/timbuart.c526
-rw-r--r--drivers/serial/timbuart.h58
-rw-r--r--drivers/sh/intc.c11
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/pxa2xx_spi.c3
-rw-r--r--drivers/ssb/embedded.c1
-rw-r--r--drivers/staging/comedi/TODO1
-rw-r--r--drivers/staging/rt2870/rt2870.h1
-rw-r--r--drivers/staging/rtl8187se/r8180.h2
-rw-r--r--drivers/staging/rtl8187se/r8180_core.c46
-rw-r--r--drivers/staging/winbond/wbusb.c9
-rw-r--r--drivers/thermal/thermal_sys.c8
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/Makefile3
-rw-r--r--drivers/usb/atm/cxacru.c10
-rw-r--r--drivers/usb/class/cdc-acm.c445
-rw-r--r--drivers/usb/class/cdc-acm.h5
-rw-r--r--drivers/usb/core/inode.c5
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c5
-rw-r--r--drivers/usb/gadget/usbstring.c6
-rw-r--r--drivers/usb/host/isp1760-hcd.c24
-rw-r--r--drivers/usb/serial/belkin_sa.c6
-rw-r--r--drivers/usb/serial/ch341.c46
-rw-r--r--drivers/usb/serial/console.c6
-rw-r--r--drivers/usb/serial/cp210x.c253
-rw-r--r--drivers/usb/serial/cyberjack.c6
-rw-r--r--drivers/usb/serial/cypress_m8.c81
-rw-r--r--drivers/usb/serial/digi_acceleport.c75
-rw-r--r--drivers/usb/serial/empeg.c6
-rw-r--r--drivers/usb/serial/ftdi_sio.c176
-rw-r--r--drivers/usb/serial/ftdi_sio.h6
-rw-r--r--drivers/usb/serial/garmin_gps.c3
-rw-r--r--drivers/usb/serial/generic.c3
-rw-r--r--drivers/usb/serial/io_edgeport.c10
-rw-r--r--drivers/usb/serial/io_ti.c3
-rw-r--r--drivers/usb/serial/ipaq.c6
-rw-r--r--drivers/usb/serial/ipw.c18
-rw-r--r--drivers/usb/serial/ir-usb.c6
-rw-r--r--drivers/usb/serial/iuu_phoenix.c102
-rw-r--r--drivers/usb/serial/keyspan.c13
-rw-r--r--drivers/usb/serial/keyspan.h8
-rw-r--r--drivers/usb/serial/keyspan_pda.c48
-rw-r--r--drivers/usb/serial/kl5kusb105.c6
-rw-r--r--drivers/usb/serial/kobil_sct.c9
-rw-r--r--drivers/usb/serial/mct_u232.c37
-rw-r--r--drivers/usb/serial/mos7720.c3
-rw-r--r--drivers/usb/serial/mos7840.c48
-rw-r--r--drivers/usb/serial/navman.c3
-rw-r--r--drivers/usb/serial/omninet.c6
-rw-r--r--drivers/usb/serial/opticon.c3
-rw-r--r--drivers/usb/serial/option.c68
-rw-r--r--drivers/usb/serial/oti6858.c57
-rw-r--r--drivers/usb/serial/pl2303.c79
-rw-r--r--drivers/usb/serial/sierra.c351
-rw-r--r--drivers/usb/serial/spcp8x5.c85
-rw-r--r--drivers/usb/serial/symbolserial.c3
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c6
-rw-r--r--drivers/usb/serial/usb-serial.c145
-rw-r--r--drivers/usb/serial/visor.c6
-rw-r--r--drivers/usb/serial/whiteheat.c33
-rw-r--r--drivers/usb/storage/scsiglue.c4
-rw-r--r--drivers/usb/storage/unusual_devs.h5
-rw-r--r--drivers/video/Kconfig25
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/amba-clcd.c2
-rw-r--r--drivers/video/atmel_lcdfb.c10
-rw-r--r--drivers/video/aty/aty128fb.c2
-rw-r--r--drivers/video/cfbfillrect.c36
-rw-r--r--drivers/video/console/vgacon.c7
-rw-r--r--drivers/video/cyber2000fb.c9
-rw-r--r--drivers/video/fb_draw.h21
-rw-r--r--drivers/video/gbefb.c14
-rw-r--r--drivers/video/hitfb.c4
-rw-r--r--drivers/video/logo/logo_linux_clut224.ppm4428
-rw-r--r--drivers/video/logo/logo_linux_vga16.ppm4339
-rw-r--r--drivers/video/mx3fb.c14
-rw-r--r--drivers/video/omap/dispc.c14
-rw-r--r--drivers/video/omap/rfbi.c8
-rw-r--r--drivers/video/s3c-fb.c12
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c7
-rw-r--r--drivers/video/sysfillrect.c30
-rw-r--r--drivers/video/uvesafb.c10
-rw-r--r--drivers/video/via/viafbdev.c6
-rw-r--r--drivers/virtio/virtio.c29
-rw-r--r--drivers/virtio/virtio_balloon.c27
-rw-r--r--drivers/virtio/virtio_pci.c307
-rw-r--r--drivers/virtio/virtio_ring.c102
-rw-r--r--drivers/xen/Kconfig20
-rw-r--r--drivers/xen/Makefile4
-rw-r--r--drivers/xen/balloon.c3
-rw-r--r--drivers/xen/events.c20
-rw-r--r--drivers/xen/evtchn.c507
-rw-r--r--drivers/xen/manage.c25
-rw-r--r--drivers/xen/sys-hypervisor.c445
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c61
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c2
-rw-r--r--drivers/xen/xenfs/super.c19
763 files changed, 77488 insertions, 22541 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 2618a6169a13..1266ead6ace0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -36,7 +36,7 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/
obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/
-obj-y += base/ block/ misc/ mfd/ media/
+obj-y += base/ block/ misc/ mfd/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
@@ -71,7 +71,7 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_INPUT) += input/
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_RTC_LIB) += rtc/
-obj-y += i2c/
+obj-y += i2c/ media/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_HWMON) += hwmon/
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index 17e50824a6f1..72ac28da14e3 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -5,40 +5,43 @@
ccflags-y := -Os
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
-obj-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \
+# use acpi.o to put all files here into acpi.o modparam namespace
+obj-y += acpi.o
+
+acpi-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \
dsmethod.o dsobject.o dsutils.o dswload.o dswstate.o \
dsinit.o
-obj-y += evevent.o evregion.o evsci.o evxfevnt.o \
+acpi-y += evevent.o evregion.o evsci.o evxfevnt.o \
evmisc.o evrgnini.o evxface.o evxfregn.o \
evgpe.o evgpeblk.o
-obj-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\
+acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\
exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\
excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \
exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o
-obj-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o
+acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o
-obj-$(ACPI_FUTURE_USAGE) += hwtimer.o
+acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
-obj-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \
+acpi-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \
nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \
nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \
nsparse.o nspredef.o
-obj-$(ACPI_FUTURE_USAGE) += nsdumpdv.o
+acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o
-obj-y += psargs.o psparse.o psloop.o pstree.o pswalk.o \
+acpi-y += psargs.o psparse.o psloop.o pstree.o pswalk.o \
psopcode.o psscope.o psutils.o psxface.o
-obj-y += rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \
+acpi-y += rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \
rscalc.o rsirq.o rsmemory.o rsutils.o
-obj-$(ACPI_FUTURE_USAGE) += rsdump.o
+acpi-$(ACPI_FUTURE_USAGE) += rsdump.o
-obj-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o
+acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o
-obj-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \
+acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \
utcopy.o utdelete.o utglobal.o utmath.o utobject.o \
utstate.o utmutex.o utobject.o utresrc.o utlock.o
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 772ee5c4ccca..2ec394a328e9 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -787,7 +787,12 @@ struct acpi_bit_register_info {
/* For control registers, both ignored and reserved bits must be preserved */
-#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0201 /* Bits 9, 0(SCI_EN) */
+/*
+ * The ACPI spec says to ignore PM1_CTL.SCI_EN (bit 0)
+ * but we need to be able to write ACPI_BITREG_SCI_ENABLE directly
+ * as a BIOS workaround on some machines.
+ */
+#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bits 9 */
#define ACPI_PM1_CONTROL_RESERVED_BITS 0xC1F8 /* Bits 14-15, 3-8 */
#define ACPI_PM1_CONTROL_PRESERVED_BITS \
(ACPI_PM1_CONTROL_IGNORED_BITS | ACPI_PM1_CONTROL_RESERVED_BITS)
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
index b6667ff059e5..88b5a2c4814d 100644
--- a/drivers/acpi/acpica/rscalc.c
+++ b/drivers/acpi/acpica/rscalc.c
@@ -543,6 +543,13 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
package_element = *top_object_list;
+ /* We must have a valid Package object */
+
+ if (!package_element ||
+ (package_element->common.type != ACPI_TYPE_PACKAGE)) {
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
+ }
+
/*
* The sub_object_list will now point to an array of the
* four IRQ elements: Address, Pin, Source and source_index
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index e8f7b64e92da..ae862f1798dc 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -312,7 +312,7 @@ int acpi_bus_set_power(acpi_handle handle, int state)
end:
if (result)
printk(KERN_WARNING PREFIX
- "Transitioning device [%s] to D%d\n",
+ "Device [%s] failed to transition to D%d\n",
device->pnp.bus_id, state);
else {
device->power.state = state;
diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c
index 95650f83ce2e..bc46de3d967f 100644
--- a/drivers/acpi/pci_bind.c
+++ b/drivers/acpi/pci_bind.c
@@ -116,9 +116,6 @@ int acpi_pci_bind(struct acpi_device *device)
struct acpi_pci_data *pdata;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_handle handle;
- struct pci_dev *dev;
- struct pci_bus *bus;
-
if (!device || !device->parent)
return -EINVAL;
@@ -176,20 +173,9 @@ int acpi_pci_bind(struct acpi_device *device)
* Locate matching device in PCI namespace. If it doesn't exist
* this typically means that the device isn't currently inserted
* (e.g. docking station, port replicator, etc.).
- * We cannot simply search the global pci device list, since
- * PCI devices are added to the global pci list when the root
- * bridge start ops are run, which may not have happened yet.
*/
- bus = pci_find_bus(data->id.segment, data->id.bus);
- if (bus) {
- list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->devfn == PCI_DEVFN(data->id.device,
- data->id.function)) {
- data->dev = dev;
- break;
- }
- }
- }
+ data->dev = pci_get_slot(pdata->bus,
+ PCI_DEVFN(data->id.device, data->id.function));
if (!data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device %04x:%02x:%02x.%d not present in PCI namespace\n",
@@ -259,9 +245,10 @@ int acpi_pci_bind(struct acpi_device *device)
end:
kfree(buffer.pointer);
- if (result)
+ if (result) {
+ pci_dev_put(data->dev);
kfree(data);
-
+ }
return result;
}
@@ -303,6 +290,7 @@ static int acpi_pci_unbind(struct acpi_device *device)
if (data->dev->subordinate) {
acpi_pci_irq_del_prt(data->id.segment, data->bus->number);
}
+ pci_dev_put(data->dev);
kfree(data);
end:
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 51b9f8280f88..2faa9e2ac893 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -401,7 +401,8 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
/* Interrupt Line values above 0xF are forbidden */
if (dev->irq > 0 && (dev->irq <= 0xF)) {
printk(" - using IRQ %d\n", dev->irq);
- acpi_register_gsi(dev->irq, ACPI_LEVEL_SENSITIVE,
+ acpi_register_gsi(&dev->dev, dev->irq,
+ ACPI_LEVEL_SENSITIVE,
ACPI_ACTIVE_LOW);
return 0;
} else {
@@ -410,7 +411,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
}
}
- rc = acpi_register_gsi(gsi, triggering, polarity);
+ rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
if (rc < 0) {
dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
pin_name(pin));
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 45ad3288c5ff..23f0fb84f1c1 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -844,7 +844,7 @@ static int acpi_processor_add(struct acpi_device *device)
if (!pr)
return -ENOMEM;
- if (!alloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) {
+ if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) {
kfree(pr);
return -ENOMEM;
}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index f7ca8c55956b..10a2d913635a 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -148,6 +148,9 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr,
if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT))
return;
+ if (boot_cpu_has(X86_FEATURE_AMDC1E))
+ type = ACPI_STATE_C1;
+
/*
* Check, if one of the previous states already marked the lapic
* unstable
@@ -202,21 +205,44 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr,
* Suspend / resume control
*/
static int acpi_idle_suspend;
+static u32 saved_bm_rld;
+
+static void acpi_idle_bm_rld_save(void)
+{
+ acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld);
+}
+static void acpi_idle_bm_rld_restore(void)
+{
+ u32 resumed_bm_rld;
+
+ acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &resumed_bm_rld);
+
+ if (resumed_bm_rld != saved_bm_rld)
+ acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld);
+}
int acpi_processor_suspend(struct acpi_device * device, pm_message_t state)
{
+ if (acpi_idle_suspend == 1)
+ return 0;
+
+ acpi_idle_bm_rld_save();
acpi_idle_suspend = 1;
return 0;
}
int acpi_processor_resume(struct acpi_device * device)
{
+ if (acpi_idle_suspend == 0)
+ return 0;
+
+ acpi_idle_bm_rld_restore();
acpi_idle_suspend = 0;
return 0;
}
#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86)
-static int tsc_halts_in_c(int state)
+static void tsc_check_state(int state)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
@@ -226,13 +252,17 @@ static int tsc_halts_in_c(int state)
* C/P/S0/S1 states when this bit is set.
*/
if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
- return 0;
+ return;
/*FALL THROUGH*/
default:
- return state > ACPI_STATE_C1;
+ /* TSC could halt in idle, so notify users */
+ if (state > ACPI_STATE_C1)
+ mark_tsc_unstable("TSC halts in idle");
}
}
+#else
+static void tsc_check_state(int state) { return; }
#endif
static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
@@ -578,17 +608,13 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
pr->power.timer_broadcast_on_state = INT_MAX;
- for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+ for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
struct acpi_processor_cx *cx = &pr->power.states[i];
-#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86)
- /* TSC could halt in idle, so notify users */
- if (tsc_halts_in_c(cx->type))
- mark_tsc_unstable("TSC halts in idle");;
-#endif
switch (cx->type) {
case ACPI_STATE_C1:
cx->valid = 1;
+ acpi_timer_check_state(i, pr, cx);
break;
case ACPI_STATE_C2:
@@ -603,6 +629,8 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
acpi_timer_check_state(i, pr, cx);
break;
}
+ if (cx->valid)
+ tsc_check_state(cx->type);
if (cx->valid)
working++;
@@ -806,11 +834,12 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
/* Do not access any ACPI IO ports in suspend path */
if (acpi_idle_suspend) {
- acpi_safe_halt();
local_irq_enable();
+ cpu_relax();
return 0;
}
+ acpi_state_timer_broadcast(pr, cx, 1);
kt1 = ktime_get_real();
acpi_idle_do_entry(cx);
kt2 = ktime_get_real();
@@ -818,6 +847,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
local_irq_enable();
cx->usage++;
+ acpi_state_timer_broadcast(pr, cx, 0);
return idle_time;
}
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index cafb41000f6b..60e543d3234e 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -309,9 +309,15 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
(u32) px->bus_master_latency,
(u32) px->control, (u32) px->status));
- if (!px->core_frequency) {
- printk(KERN_ERR PREFIX
- "Invalid _PSS data: freq is zero\n");
+ /*
+ * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq
+ */
+ if (!px->core_frequency ||
+ ((u32)(px->core_frequency * 1000) !=
+ (px->core_frequency * 1000))) {
+ printk(KERN_ERR FW_BUG PREFIX
+ "Invalid BIOS _PSS frequency: 0x%llx MHz\n",
+ px->core_frequency);
result = -EFAULT;
kfree(pr->performance->states);
goto end;
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index d0d1f4d50434..227543789ba9 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -45,6 +45,14 @@
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME("processor_throttling");
+/* ignore_tpc:
+ * 0 -> acpi processor driver doesn't ignore _TPC values
+ * 1 -> acpi processor driver ignores _TPC values
+ */
+static int ignore_tpc;
+module_param(ignore_tpc, int, 0644);
+MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support");
+
struct throttling_tstate {
unsigned int cpu; /* cpu nr */
int target_state; /* target T-state */
@@ -283,6 +291,10 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
if (!pr)
return -EINVAL;
+
+ if (ignore_tpc)
+ goto end;
+
status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
@@ -290,6 +302,8 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
}
return -ENODEV;
}
+
+end:
pr->throttling_platform_limit = (int)tpc;
return 0;
}
@@ -302,6 +316,9 @@ int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
struct acpi_processor_limit *limit;
int target_state;
+ if (ignore_tpc)
+ return 0;
+
result = acpi_processor_get_platform_limit(pr);
if (result) {
/* Throttling Limit is unsupported */
@@ -821,6 +838,14 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
ret = acpi_read_throttling_status(pr, &value);
if (ret >= 0) {
state = acpi_get_throttling_state(pr, value);
+ if (state == -1) {
+ ACPI_WARNING((AE_INFO,
+ "Invalid throttling state, reset"));
+ state = 0;
+ ret = acpi_processor_set_throttling(pr, state);
+ if (ret)
+ return ret;
+ }
pr->throttling.state = state;
}
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 1705d947ea09..1bdfb37377e3 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -538,6 +538,57 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
return -EINVAL;
}
+/*
+ * For some buggy _BQC methods, we need to add a constant value to
+ * the _BQC return value to get the actual current brightness level
+ */
+
+static int bqc_offset_aml_bug_workaround;
+static int __init video_set_bqc_offset(const struct dmi_system_id *d)
+{
+ bqc_offset_aml_bug_workaround = 9;
+ return 0;
+}
+
+static struct dmi_system_id video_dmi_table[] __initdata = {
+ /*
+ * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
+ */
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5720",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5710Z",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "eMachines E510",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5315",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
+ },
+ },
+ {}
+};
+
static int
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
unsigned long long *level)
@@ -557,6 +608,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
*level = device->brightness->levels[*level + 2];
}
+ *level += bqc_offset_aml_bug_workaround;
device->brightness->curr = *level;
return 0;
} else {
@@ -2290,13 +2342,15 @@ EXPORT_SYMBOL(acpi_video_register);
static int __init acpi_video_init(void)
{
+ dmi_check_system(video_dmi_table);
+
if (intel_opregion_present())
return 0;
return acpi_video_register();
}
-static void __exit acpi_video_exit(void)
+void acpi_video_exit(void)
{
acpi_bus_unregister_driver(&acpi_video_bus);
@@ -2305,6 +2359,7 @@ static void __exit acpi_video_exit(void)
return;
}
+EXPORT_SYMBOL(acpi_video_exit);
module_init(acpi_video_init);
module_exit(acpi_video_exit);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 08186ecbaf8d..15a23031833f 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -77,8 +77,6 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
size_t size);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
ssize_t size);
-#define MAX_SLOTS 8
-#define MAX_RETRY 15
enum {
AHCI_PCI_BAR = 5,
@@ -220,6 +218,7 @@ enum {
AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */
AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
+ AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
/* ap->flags bits */
@@ -230,6 +229,10 @@ enum {
ICH_MAP = 0x90, /* ICH MAP register */
+ /* em constants */
+ EM_MAX_SLOTS = 8,
+ EM_MAX_RETRY = 5,
+
/* em_ctl bits */
EM_CTL_RST = (1 << 9), /* Reset */
EM_CTL_TM = (1 << 8), /* Transmit Message */
@@ -281,8 +284,8 @@ struct ahci_port_priv {
unsigned int ncq_saw_dmas:1;
unsigned int ncq_saw_sdb:1;
u32 intr_mask; /* interrupts to enable */
- struct ahci_em_priv em_priv[MAX_SLOTS];/* enclosure management info
- * per PM slot */
+ /* enclosure management info per PM slot */
+ struct ahci_em_priv em_priv[EM_MAX_SLOTS];
};
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
@@ -312,7 +315,6 @@ static void ahci_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static int ahci_port_resume(struct ata_port *ap);
static void ahci_dev_config(struct ata_device *dev);
-static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl);
static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts);
#ifdef CONFIG_PM
@@ -403,14 +405,14 @@ static struct ata_port_operations ahci_sb600_ops = {
#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
static const struct ata_port_info ahci_port_info[] = {
- /* board_ahci */
+ [board_ahci] =
{
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- /* board_ahci_vt8251 */
+ [board_ahci_vt8251] =
{
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
@@ -418,7 +420,7 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_vt8251_ops,
},
- /* board_ahci_ign_iferr */
+ [board_ahci_ign_iferr] =
{
AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR),
.flags = AHCI_FLAG_COMMON,
@@ -426,17 +428,16 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- /* board_ahci_sb600 */
+ [board_ahci_sb600] =
{
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
- AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
- AHCI_HFLAG_SECT255),
+ AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_sb600_ops,
},
- /* board_ahci_mv */
+ [board_ahci_mv] =
{
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI |
AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP),
@@ -446,7 +447,7 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- /* board_ahci_sb700, for SB700 and SB800 */
+ [board_ahci_sb700] = /* for SB700 and SB800 */
{
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL),
.flags = AHCI_FLAG_COMMON,
@@ -454,7 +455,7 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_sb600_ops,
},
- /* board_ahci_mcp65 */
+ [board_ahci_mcp65] =
{
AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ),
.flags = AHCI_FLAG_COMMON,
@@ -462,7 +463,7 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- /* board_ahci_nopmp */
+ [board_ahci_nopmp] =
{
AHCI_HFLAGS (AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
@@ -1140,12 +1141,12 @@ static void ahci_start_port(struct ata_port *ap)
emp = &pp->em_priv[link->pmp];
/* EM Transmit bit maybe busy during init */
- for (i = 0; i < MAX_RETRY; i++) {
+ for (i = 0; i < EM_MAX_RETRY; i++) {
rc = ahci_transmit_led_message(ap,
emp->led_state,
4);
if (rc == -EBUSY)
- udelay(100);
+ msleep(1);
else
break;
}
@@ -1339,7 +1340,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
- if (pmp < MAX_SLOTS)
+ if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
@@ -1407,7 +1408,7 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
- if (pmp < MAX_SLOTS)
+ if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
@@ -2316,9 +2317,17 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
u32 ctl;
+ if (mesg.event & PM_EVENT_SUSPEND &&
+ hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "BIOS update required for suspend/resume\n");
+ return -EIO;
+ }
+
if (mesg.event & PM_EVENT_SLEEP) {
/* AHCI spec rev1.1 section 8.3.3:
* Software must disable interrupts prior to requesting a
@@ -2575,6 +2584,51 @@ static void ahci_p5wdh_workaround(struct ata_host *host)
}
}
+/*
+ * SB600 ahci controller on ASUS M2A-VM can't do 64bit DMA with older
+ * BIOS. The oldest version known to be broken is 0901 and working is
+ * 1501 which was released on 2007-10-26. Force 32bit DMA on anything
+ * older than 1501. Please read bko#9412 for more info.
+ */
+static bool ahci_asus_m2a_vm_32bit_only(struct pci_dev *pdev)
+{
+ static const struct dmi_system_id sysids[] = {
+ {
+ .ident = "ASUS M2A-VM",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "M2A-VM"),
+ },
+ },
+ { }
+ };
+ const char *cutoff_mmdd = "10/26";
+ const char *date;
+ int year;
+
+ if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) ||
+ !dmi_check_system(sysids))
+ return false;
+
+ /*
+ * Argh.... both version and date are free form strings.
+ * Let's hope they're using the same date format across
+ * different versions.
+ */
+ date = dmi_get_system_info(DMI_BIOS_DATE);
+ year = dmi_get_year(DMI_BIOS_DATE);
+ if (date && strlen(date) >= 10 && date[2] == '/' && date[5] == '/' &&
+ (year > 2007 ||
+ (year == 2007 && strncmp(date, cutoff_mmdd, 5) >= 0)))
+ return false;
+
+ dev_printk(KERN_WARNING, &pdev->dev, "ASUS M2A-VM: BIOS too old, "
+ "forcing 32bit DMA, update BIOS\n");
+
+ return true;
+}
+
static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
{
static const struct dmi_system_id broken_systems[] = {
@@ -2610,6 +2664,63 @@ static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
return false;
}
+static bool ahci_broken_suspend(struct pci_dev *pdev)
+{
+ static const struct dmi_system_id sysids[] = {
+ /*
+ * On HP dv[4-6] and HDX18 with earlier BIOSen, link
+ * to the harddisk doesn't become online after
+ * resuming from STR. Warn and fail suspend.
+ */
+ {
+ .ident = "dv4",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "HP Pavilion dv4 Notebook PC"),
+ },
+ .driver_data = "F.30", /* cutoff BIOS version */
+ },
+ {
+ .ident = "dv5",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "HP Pavilion dv5 Notebook PC"),
+ },
+ .driver_data = "F.16", /* cutoff BIOS version */
+ },
+ {
+ .ident = "dv6",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "HP Pavilion dv6 Notebook PC"),
+ },
+ .driver_data = "F.21", /* cutoff BIOS version */
+ },
+ {
+ .ident = "HDX18",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "HP HDX18 Notebook PC"),
+ },
+ .driver_data = "F.23", /* cutoff BIOS version */
+ },
+ { } /* terminate list */
+ };
+ const struct dmi_system_id *dmi = dmi_first_match(sysids);
+ const char *ver;
+
+ if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2))
+ return false;
+
+ ver = dmi_get_system_info(DMI_BIOS_VERSION);
+
+ return !ver || strcmp(ver, dmi->driver_data) < 0;
+}
+
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
@@ -2678,6 +2789,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (board_id == board_ahci_sb700 && pdev->revision >= 0x40)
hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL;
+ /* apply ASUS M2A_VM quirk */
+ if (ahci_asus_m2a_vm_32bit_only(pdev))
+ hpriv->flags |= AHCI_HFLAG_32BIT_ONLY;
+
if (!(hpriv->flags & AHCI_HFLAG_NO_MSI))
pci_enable_msi(pdev);
@@ -2715,6 +2830,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
"quirky BIOS, skipping spindown on poweroff\n");
}
+ if (ahci_broken_suspend(pdev)) {
+ hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "BIOS update required for suspend/resume\n");
+ }
+
/* CAP.NP sometimes indicate the index of the last enabled
* port, at other times, that of the last possible port, so
* determining the maximum port number requires looking at
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 942d14ac8792..d0a14cf2bd74 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -72,6 +72,7 @@
* ICH2 spec c #20 - IDE PRD must not cross a 64K boundary
* and must be dword aligned
* ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3
+ * ICH7 errata #16 - MWDMA1 timings are incorrect
*
* Should have been BIOS fixed:
* 450NX: errata #19 - DMA hangs on old 450NX
@@ -94,7 +95,7 @@
#include <linux/dmi.h>
#define DRV_NAME "ata_piix"
-#define DRV_VERSION "2.12"
+#define DRV_VERSION "2.13"
enum {
PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
@@ -136,6 +137,7 @@ enum piix_controller_ids {
ich_pata_33, /* ICH up to UDMA 33 only */
ich_pata_66, /* ICH up to 66 Mhz */
ich_pata_100, /* ICH up to UDMA 100 */
+ ich_pata_100_nomwdma1, /* ICH up to UDMA 100 but with no MWDMA1*/
ich5_sata,
ich6_sata,
ich6m_sata,
@@ -216,15 +218,13 @@ static const struct pci_device_id piix_pci_tbl[] = {
/* ICH6 (and 6) (i915) UDMA 100 */
{ 0x8086, 0x266F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
/* ICH7/7-R (i945, i975) UDMA 100*/
- { 0x8086, 0x27DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
- { 0x8086, 0x269E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
+ { 0x8086, 0x27DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100_nomwdma1 },
+ { 0x8086, 0x269E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100_nomwdma1 },
/* ICH8 Mobile PATA Controller */
{ 0x8086, 0x2850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
- /* NOTE: The following PCI ids must be kept in sync with the
- * list in drivers/pci/quirks.c.
- */
-
+ /* SATA ports */
+
/* 82801EB (ICH5) */
{ 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
/* 82801EB (ICH5) */
@@ -487,6 +487,15 @@ static struct ata_port_info piix_port_info[] = {
.port_ops = &ich_pata_ops,
},
+ [ich_pata_100_nomwdma1] =
+ {
+ .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2_ONLY,
+ .udma_mask = ATA_UDMA5,
+ .port_ops = &ich_pata_ops,
+ },
+
[ich5_sata] =
{
.flags = PIIX_SATA_FLAGS,
@@ -594,6 +603,7 @@ static const struct ich_laptop ich_laptop[] = {
{ 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */
{ 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */
{ 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
+ { 0x27df, 0x104d, 0x900e }, /* ICH7 on Sony TZ-90 */
/* end marker */
{ 0, }
};
@@ -1443,6 +1453,15 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev)
/* PCI slot number of the controller */
.driver_data = (void *)0x1FUL,
},
+ {
+ .ident = "HP Compaq nc6000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nc6000"),
+ },
+ /* PCI slot number of the controller */
+ .driver_data = (void *)0x1FUL,
+ },
{ } /* terminate list */
};
@@ -1488,8 +1507,8 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
dev_printk(KERN_DEBUG, &pdev->dev,
"version " DRV_VERSION "\n");
- /* no hotplugging support (FIXME) */
- if (!in_module_init)
+ /* no hotplugging support for later devices (FIXME) */
+ if (!in_module_init && ent->driver_data >= ich5_sata)
return -ENODEV;
if (piix_broken_system_poweroff(pdev)) {
@@ -1570,6 +1589,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
host->ports[1]->mwdma_mask = 0;
host->ports[1]->udma_mask = 0;
}
+ host->flags |= ATA_HOST_PARALLEL_SCAN;
pci_set_master(pdev);
return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 17c5d48a75d2..ca4d208ddf3b 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4091,7 +4091,9 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
/* fail early if !ATA && !ATAPI to avoid issuing [P]IDENTIFY to PMP */
if (ata_class_enabled(new_class) &&
- new_class != ATA_DEV_ATA && new_class != ATA_DEV_ATAPI) {
+ new_class != ATA_DEV_ATA &&
+ new_class != ATA_DEV_ATAPI &&
+ new_class != ATA_DEV_SEMB) {
ata_dev_printk(dev, KERN_INFO, "class mismatch %u != %u\n",
dev->class, new_class);
rc = -ENODEV;
@@ -5029,7 +5031,6 @@ int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active)
{
int nr_done = 0;
u32 done_mask;
- int i;
done_mask = ap->qc_active ^ qc_active;
@@ -5039,16 +5040,16 @@ int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active)
return -EINVAL;
}
- for (i = 0; i < ATA_MAX_QUEUE; i++) {
+ while (done_mask) {
struct ata_queued_cmd *qc;
+ unsigned int tag = __ffs(done_mask);
- if (!(done_mask & (1 << i)))
- continue;
-
- if ((qc = ata_qc_from_tag(ap, i))) {
+ qc = ata_qc_from_tag(ap, tag);
+ if (qc) {
ata_qc_complete(qc);
nr_done++;
}
+ done_mask &= ~(1 << tag);
}
return nr_done;
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 01831312c360..94919ad03df1 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2783,6 +2783,12 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
} else if (dev->class == ATA_DEV_UNKNOWN &&
ehc->tries[dev->devno] &&
ata_class_enabled(ehc->classes[dev->devno])) {
+ /* Temporarily set dev->class, it will be
+ * permanently set once all configurations are
+ * complete. This is necessary because new
+ * device configuration is done in two
+ * separate loops.
+ */
dev->class = ehc->classes[dev->devno];
if (dev->class == ATA_DEV_PMP)
@@ -2790,6 +2796,11 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
else
rc = ata_dev_read_id(dev, &dev->class,
readid_flags, dev->id);
+
+ /* read_id might have changed class, store and reset */
+ ehc->classes[dev->devno] = dev->class;
+ dev->class = ATA_DEV_UNKNOWN;
+
switch (rc) {
case 0:
/* clear error info accumulated during probe */
@@ -2799,13 +2810,11 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
case -ENOENT:
/* IDENTIFY was issued to non-existent
* device. No need to reset. Just
- * thaw and kill the device.
+ * thaw and ignore the device.
*/
ata_eh_thaw_port(ap);
- dev->class = ATA_DEV_UNKNOWN;
break;
default:
- dev->class = ATA_DEV_UNKNOWN;
goto err;
}
}
@@ -2826,11 +2835,15 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
dev->class == ATA_DEV_PMP)
continue;
+ dev->class = ehc->classes[dev->devno];
+
ehc->i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ehc->i.flags &= ~ATA_EHI_PRINTINFO;
- if (rc)
+ if (rc) {
+ dev->class = ATA_DEV_UNKNOWN;
goto err;
+ }
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
@@ -3494,6 +3507,8 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
*/
static void ata_eh_handle_port_resume(struct ata_port *ap)
{
+ struct ata_link *link;
+ struct ata_device *dev;
unsigned long flags;
int rc = 0;
@@ -3508,6 +3523,17 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED));
+ /*
+ * Error timestamps are in jiffies which doesn't run while
+ * suspended and PHY events during resume isn't too uncommon.
+ * When the two are combined, it can lead to unnecessary speed
+ * downs if the machine is suspended and resumed repeatedly.
+ * Clear error history.
+ */
+ ata_for_each_link(link, ap, HOST_FIRST)
+ ata_for_each_dev(dev, link, ALL)
+ ata_ering_clear(&dev->ering);
+
ata_acpi_set_state(ap, PMSG_ON);
if (ap->ops->port_resume)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 6e4c600f5a1c..d0dfeef55db5 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -313,7 +313,7 @@ ata_scsi_em_message_show(struct device *dev, struct device_attribute *attr,
return ap->ops->em_show(ap, buf);
return -EINVAL;
}
-DEVICE_ATTR(em_message, S_IRUGO | S_IWUGO,
+DEVICE_ATTR(em_message, S_IRUGO | S_IWUSR,
ata_scsi_em_message_show, ata_scsi_em_message_store);
EXPORT_SYMBOL_GPL(dev_attr_em_message);
@@ -366,7 +366,7 @@ ata_scsi_activity_store(struct device *dev, struct device_attribute *attr,
}
return -EINVAL;
}
-DEVICE_ATTR(sw_activity, S_IWUGO | S_IRUGO, ata_scsi_activity_show,
+DEVICE_ATTR(sw_activity, S_IWUSR | S_IRUGO, ata_scsi_activity_show,
ata_scsi_activity_store);
EXPORT_SYMBOL_GPL(dev_attr_sw_activity);
@@ -2142,13 +2142,14 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
{
+ int form_factor = ata_id_form_factor(args->id);
+ int media_rotation_rate = ata_id_rotation_rate(args->id);
+
rbuf[1] = 0xb1;
rbuf[3] = 0x3c;
- if (ata_id_major_version(args->id) > 7) {
- rbuf[4] = args->id[217] >> 8;
- rbuf[5] = args->id[217];
- rbuf[7] = args->id[168] & 0xf;
- }
+ rbuf[4] = media_rotation_rate >> 8;
+ rbuf[5] = media_rotation_rate;
+ rbuf[7] = form_factor;
return 0;
}
@@ -2376,7 +2377,23 @@ saving_not_supp:
*/
static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
{
- u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */
+ struct ata_device *dev = args->dev;
+ u64 last_lba = dev->n_sectors - 1; /* LBA of the last block */
+ u8 log_per_phys = 0;
+ u16 lowest_aligned = 0;
+ u16 word_106 = dev->id[106];
+ u16 word_209 = dev->id[209];
+
+ if ((word_106 & 0xc000) == 0x4000) {
+ /* Number and offset of logical sectors per physical sector */
+ if (word_106 & (1 << 13))
+ log_per_phys = word_106 & 0xf;
+ if ((word_209 & 0xc000) == 0x4000) {
+ u16 first = dev->id[209] & 0x3fff;
+ if (first > 0)
+ lowest_aligned = (1 << log_per_phys) - first;
+ }
+ }
VPRINTK("ENTER\n");
@@ -2407,6 +2424,11 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
/* sector size */
rbuf[10] = ATA_SECT_SIZE >> 8;
rbuf[11] = ATA_SECT_SIZE & 0xff;
+
+ rbuf[12] = 0;
+ rbuf[13] = log_per_phys;
+ rbuf[14] = (lowest_aligned >> 8) & 0x3f;
+ rbuf[15] = lowest_aligned;
}
return 0;
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index bb18415d3d63..bbbb1fab1755 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -727,17 +727,23 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
else
iowrite16_rep(data_addr, buf, words);
- /* Transfer trailing 1 byte, if any. */
+ /* Transfer trailing byte, if any. */
if (unlikely(buflen & 0x01)) {
- __le16 align_buf[1] = { 0 };
- unsigned char *trailing_buf = buf + buflen - 1;
+ unsigned char pad[2];
+ /* Point buf to the tail of buffer */
+ buf += buflen - 1;
+
+ /*
+ * Use io*16_rep() accessors here as well to avoid pointlessly
+ * swapping bytes to and fro on the big endian machines...
+ */
if (rw == READ) {
- align_buf[0] = cpu_to_le16(ioread16(data_addr));
- memcpy(trailing_buf, align_buf, 1);
+ ioread16_rep(data_addr, pad, 1);
+ *buf = pad[0];
} else {
- memcpy(align_buf, trailing_buf, 1);
- iowrite16(le16_to_cpu(align_buf[0]), data_addr);
+ pad[0] = *buf;
+ iowrite16_rep(data_addr, pad, 1);
}
words++;
}
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 751b7ea4816c..fc9c5d6d7d80 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -497,14 +497,16 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
/* Revision 0x20 added DMA */
static const struct ata_port_info info_20 = {
- .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48 |
+ ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.port_ops = &ali_20_port_ops
};
/* Revision 0x20 with support logic added UDMA */
static const struct ata_port_info info_20_udma = {
- .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48 |
+ ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
@@ -512,7 +514,8 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
/* Revision 0xC2 adds UDMA66 */
static const struct ata_port_info info_c2 = {
- .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48 |
+ ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
@@ -520,7 +523,8 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
/* Revision 0xC3 is UDMA66 for now */
static const struct ata_port_info info_c3 = {
- .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48 |
+ ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
@@ -528,7 +532,8 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
/* Revision 0xC4 is UDMA100 */
static const struct ata_port_info info_c4 = {
- .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_LBA48 |
+ ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -536,7 +541,7 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
/* Revision 0xC5 is UDMA133 with LBA48 DMA */
static const struct ata_port_info info_c5 = {
- .flags = ATA_FLAG_SLAVE_POSS,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_IGN_SIMPLEX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index 2085e0a3a05a..2a6412f5d117 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -22,7 +22,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_efar"
-#define DRV_VERSION "0.4.4"
+#define DRV_VERSION "0.4.5"
/**
* efar_pre_reset - Enable bits
@@ -98,18 +98,17 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
{ 2, 1 },
{ 2, 3 }, };
- if (pio > 2)
- control |= 1; /* TIME1 enable */
+ if (pio > 1)
+ control |= 1; /* TIME */
if (ata_pio_need_iordy(adev)) /* PIO 3/4 require IORDY */
- control |= 2; /* IE enable */
- /* Intel specifies that the PPE functionality is for disk only */
+ control |= 2; /* IE */
+ /* Intel specifies that the prefetch/posting is for disk only */
if (adev->class == ATA_DEV_ATA)
- control |= 4; /* PPE enable */
+ control |= 4; /* PPE */
pci_read_config_word(dev, idetm_port, &idetm_data);
- /* Enable PPE, IE and TIME as appropriate */
-
+ /* Set PPE, IE, and TIME as appropriate */
if (adev->devno == 0) {
idetm_data &= 0xCCF0;
idetm_data |= control;
@@ -129,7 +128,7 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
pci_write_config_byte(dev, 0x44, slave_data);
}
- idetm_data |= 0x4000; /* Ensure SITRE is enabled */
+ idetm_data |= 0x4000; /* Ensure SITRE is set */
pci_write_config_word(dev, idetm_port, idetm_data);
}
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index f72c6c5b820f..6932e56d179c 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -48,6 +48,7 @@
*
*/
+#include <linux/async.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -1028,6 +1029,7 @@ static __init int legacy_init_one(struct legacy_probe *probe)
&legacy_sht);
if (ret)
goto fail;
+ async_synchronize_full();
ld->platform_dev = pdev;
/* Nothing found means we drop the port as its probably not there */
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index bdb236957cb9..f0d52f72f5bb 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -20,13 +20,24 @@
/* No PIO or DMA methods needed for this device */
+static unsigned int netcell_read_id(struct ata_device *adev,
+ struct ata_taskfile *tf, u16 *id)
+{
+ unsigned int err_mask = ata_do_dev_read_id(adev, tf, id);
+ /* Firmware forgets to mark words 85-87 valid */
+ if (err_mask == 0)
+ id[ATA_ID_CSF_DEFAULT] |= 0x4000;
+ return err_mask;
+}
+
static struct scsi_host_template netcell_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
static struct ata_port_operations netcell_ops = {
.inherits = &ata_bmdma_port_ops,
- .cable_detect = ata_cable_80wire,
+ .cable_detect = ata_cable_80wire,
+ .read_id = netcell_read_id,
};
diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c
index 5fedb3d4032b..2f3c9bed63d9 100644
--- a/drivers/ata/pata_pdc202xx_old.c
+++ b/drivers/ata/pata_pdc202xx_old.c
@@ -2,7 +2,7 @@
* pata_pdc202xx_old.c - Promise PDC202xx PATA for new ATA layer
* (C) 2005 Red Hat Inc
* Alan Cox <alan@lxorguk.ukuu.org.uk>
- * (C) 2007 Bartlomiej Zolnierkiewicz
+ * (C) 2007,2009 Bartlomiej Zolnierkiewicz
*
* Based in part on linux/drivers/ide/pci/pdc202xx_old.c
*
@@ -158,7 +158,7 @@ static void pdc2026x_bmdma_start(struct ata_queued_cmd *qc)
u32 len;
/* Check we keep host level locking here */
- if (adev->dma_mode >= XFER_UDMA_2)
+ if (adev->dma_mode > XFER_UDMA_2)
iowrite8(ioread8(clock) | sel66, clock);
else
iowrite8(ioread8(clock) & ~sel66, clock);
@@ -212,7 +212,7 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc)
iowrite8(ioread8(clock) & ~sel66, clock);
}
/* Flip back to 33Mhz for PIO */
- if (adev->dma_mode >= XFER_UDMA_2)
+ if (adev->dma_mode > XFER_UDMA_2)
iowrite8(ioread8(clock) & ~sel66, clock);
ata_bmdma_stop(qc);
pdc202xx_set_piomode(ap, adev);
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index c2e90e1fece0..36b8629203be 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -205,6 +205,7 @@ struct cmdhdr_tbl_entry {
* Description information bitdefs
*/
enum {
+ CMD_DESC_RES = (1 << 11),
VENDOR_SPECIFIC_BIST = (1 << 10),
CMD_DESC_SNOOP_ENABLE = (1 << 9),
FPDMA_QUEUED_CMD = (1 << 8),
@@ -332,13 +333,14 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
dma_addr_t sg_addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
- VPRINTK("SATA FSL : fill_sg, sg_addr = 0x%x, sg_len = %d\n",
- sg_addr, sg_len);
+ VPRINTK("SATA FSL : fill_sg, sg_addr = 0x%llx, sg_len = %d\n",
+ (unsigned long long)sg_addr, sg_len);
/* warn if each s/g element is not dword aligned */
if (sg_addr & 0x03)
ata_port_printk(qc->ap, KERN_ERR,
- "s/g addr unaligned : 0x%x\n", sg_addr);
+ "s/g addr unaligned : 0x%llx\n",
+ (unsigned long long)sg_addr);
if (sg_len & 0x03)
ata_port_printk(qc->ap, KERN_ERR,
"s/g len unaligned : 0x%x\n", sg_len);
@@ -387,7 +389,7 @@ static void sata_fsl_qc_prep(struct ata_queued_cmd *qc)
void __iomem *hcr_base = host_priv->hcr_base;
unsigned int tag = sata_fsl_tag(qc->tag, hcr_base);
struct command_desc *cd;
- u32 desc_info = CMD_DESC_SNOOP_ENABLE;
+ u32 desc_info = CMD_DESC_RES | CMD_DESC_SNOOP_ENABLE;
u32 num_prde = 0;
u32 ttl_dwords = 0;
dma_addr_t cd_paddr;
@@ -840,7 +842,7 @@ issue_srst:
/* device reset/SRST is a control register update FIS, uses tag0 */
sata_fsl_setup_cmd_hdr_entry(pp, 0,
- SRST_CMD | CMD_DESC_SNOOP_ENABLE, 0, 0, 5);
+ SRST_CMD | CMD_DESC_RES | CMD_DESC_SNOOP_ENABLE, 0, 0, 5);
tf.ctl |= ATA_SRST; /* setup SRST bit in taskfile control reg */
ata_tf_to_fis(&tf, pmp, 0, cfis);
@@ -886,7 +888,8 @@ issue_srst:
* using ATA signature D2H register FIS to the host controller.
*/
- sata_fsl_setup_cmd_hdr_entry(pp, 0, CMD_DESC_SNOOP_ENABLE, 0, 0, 5);
+ sata_fsl_setup_cmd_hdr_entry(pp, 0, CMD_DESC_RES | CMD_DESC_SNOOP_ENABLE,
+ 0, 0, 5);
tf.ctl &= ~ATA_SRST; /* 2nd H2D Ctl. register FIS */
ata_tf_to_fis(&tf, pmp, 0, cfis);
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 870dcfd82357..23714aefb825 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -293,6 +293,10 @@ enum {
FISCFG_WAIT_DEV_ERR = (1 << 8), /* wait for host on DevErr */
FISCFG_SINGLE_SYNC = (1 << 16), /* SYNC on DMA activation */
+ PHY_MODE9_GEN2 = 0x398,
+ PHY_MODE9_GEN1 = 0x39c,
+ PHYCFG_OFS = 0x3a0, /* only in 65n devices */
+
MV5_PHY_MODE = 0x74,
MV5_LTMODE = 0x30,
MV5_PHY_CTL = 0x0C,
@@ -609,6 +613,8 @@ static int mv_soc_reset_hc(struct mv_host_priv *hpriv,
static void mv_soc_reset_flash(struct mv_host_priv *hpriv,
void __iomem *mmio);
static void mv_soc_reset_bus(struct ata_host *host, void __iomem *mmio);
+static void mv_soc_65n_phy_errata(struct mv_host_priv *hpriv,
+ void __iomem *mmio, unsigned int port);
static void mv_reset_pci_bus(struct ata_host *host, void __iomem *mmio);
static void mv_reset_channel(struct mv_host_priv *hpriv, void __iomem *mmio,
unsigned int port_no);
@@ -807,6 +813,14 @@ static const struct mv_hw_ops mv_soc_ops = {
.reset_bus = mv_soc_reset_bus,
};
+static const struct mv_hw_ops mv_soc_65n_ops = {
+ .phy_errata = mv_soc_65n_phy_errata,
+ .enable_leds = mv_soc_enable_leds,
+ .reset_hc = mv_soc_reset_hc,
+ .reset_flash = mv_soc_reset_flash,
+ .reset_bus = mv_soc_reset_bus,
+};
+
/*
* Functions
*/
@@ -3397,6 +3411,53 @@ static void mv_soc_reset_bus(struct ata_host *host, void __iomem *mmio)
return;
}
+static void mv_soc_65n_phy_errata(struct mv_host_priv *hpriv,
+ void __iomem *mmio, unsigned int port)
+{
+ void __iomem *port_mmio = mv_port_base(mmio, port);
+ u32 reg;
+
+ reg = readl(port_mmio + PHY_MODE3);
+ reg &= ~(0x3 << 27); /* SELMUPF (bits 28:27) to 1 */
+ reg |= (0x1 << 27);
+ reg &= ~(0x3 << 29); /* SELMUPI (bits 30:29) to 1 */
+ reg |= (0x1 << 29);
+ writel(reg, port_mmio + PHY_MODE3);
+
+ reg = readl(port_mmio + PHY_MODE4);
+ reg &= ~0x1; /* SATU_OD8 (bit 0) to 0, reserved bit 16 must be set */
+ reg |= (0x1 << 16);
+ writel(reg, port_mmio + PHY_MODE4);
+
+ reg = readl(port_mmio + PHY_MODE9_GEN2);
+ reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */
+ reg |= 0x8;
+ reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */
+ writel(reg, port_mmio + PHY_MODE9_GEN2);
+
+ reg = readl(port_mmio + PHY_MODE9_GEN1);
+ reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */
+ reg |= 0x8;
+ reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */
+ writel(reg, port_mmio + PHY_MODE9_GEN1);
+}
+
+/**
+ * soc_is_65 - check if the soc is 65 nano device
+ *
+ * Detect the type of the SoC, this is done by reading the PHYCFG_OFS
+ * register, this register should contain non-zero value and it exists only
+ * in the 65 nano devices, when reading it from older devices we get 0.
+ */
+static bool soc_is_65n(struct mv_host_priv *hpriv)
+{
+ void __iomem *port0_mmio = mv_port_base(hpriv->base, 0);
+
+ if (readl(port0_mmio + PHYCFG_OFS))
+ return true;
+ return false;
+}
+
static void mv_setup_ifcfg(void __iomem *port_mmio, int want_gen2i)
{
u32 ifcfg = readl(port_mmio + SATA_IFCFG);
@@ -3737,7 +3798,10 @@ static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
}
break;
case chip_soc:
- hpriv->ops = &mv_soc_ops;
+ if (soc_is_65n(hpriv))
+ hpriv->ops = &mv_soc_65n_ops;
+ else
+ hpriv->ops = &mv_soc_ops;
hp_flags |= MV_HP_FLAG_SOC | MV_HP_GEN_IIE |
MV_HP_ERRATA_60X1C0;
break;
@@ -3800,7 +3864,8 @@ static int mv_init_host(struct ata_host *host, unsigned int board_idx)
n_hc = mv_get_hc_count(host->ports[0]->flags);
for (port = 0; port < host->n_ports; port++)
- hpriv->ops->read_preamp(hpriv, port, mmio);
+ if (hpriv->ops->read_preamp)
+ hpriv->ops->read_preamp(hpriv, port, mmio);
rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc);
if (rc)
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 6cda12ba8122..b2d11f300c39 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -305,8 +305,8 @@ static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance);
static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
-static int nv_noclassify_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline);
+static int nv_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
static void nv_nf2_freeze(struct ata_port *ap);
static void nv_nf2_thaw(struct ata_port *ap);
static void nv_ck804_freeze(struct ata_port *ap);
@@ -406,49 +406,82 @@ static struct scsi_host_template nv_swncq_sht = {
.slave_configure = nv_swncq_slave_config,
};
-static struct ata_port_operations nv_common_ops = {
+/*
+ * NV SATA controllers have various different problems with hardreset
+ * protocol depending on the specific controller and device.
+ *
+ * GENERIC:
+ *
+ * bko11195 reports that link doesn't come online after hardreset on
+ * generic nv's and there have been several other similar reports on
+ * linux-ide.
+ *
+ * bko12351#c23 reports that warmplug on MCP61 doesn't work with
+ * softreset.
+ *
+ * NF2/3:
+ *
+ * bko3352 reports nf2/3 controllers can't determine device signature
+ * reliably after hardreset. The following thread reports detection
+ * failure on cold boot with the standard debouncing timing.
+ *
+ * http://thread.gmane.org/gmane.linux.ide/34098
+ *
+ * bko12176 reports that hardreset fails to bring up the link during
+ * boot on nf2.
+ *
+ * CK804:
+ *
+ * For initial probing after boot and hot plugging, hardreset mostly
+ * works fine on CK804 but curiously, reprobing on the initial port
+ * by rescanning or rmmod/insmod fails to acquire the initial D2H Reg
+ * FIS in somewhat undeterministic way.
+ *
+ * SWNCQ:
+ *
+ * bko12351 reports that when SWNCQ is enabled, for hotplug to work,
+ * hardreset should be used and hardreset can't report proper
+ * signature, which suggests that mcp5x is closer to nf2 as long as
+ * reset quirkiness is concerned.
+ *
+ * bko12703 reports that boot probing fails for intel SSD with
+ * hardreset. Link fails to come online. Softreset works fine.
+ *
+ * The failures are varied but the following patterns seem true for
+ * all flavors.
+ *
+ * - Softreset during boot always works.
+ *
+ * - Hardreset during boot sometimes fails to bring up the link on
+ * certain comibnations and device signature acquisition is
+ * unreliable.
+ *
+ * - Hardreset is often necessary after hotplug.
+ *
+ * So, preferring softreset for boot probing and error handling (as
+ * hardreset might bring down the link) but using hardreset for
+ * post-boot probing should work around the above issues in most
+ * cases. Define nv_hardreset() which only kicks in for post-boot
+ * probing and use it for all variants.
+ */
+static struct ata_port_operations nv_generic_ops = {
.inherits = &ata_bmdma_port_ops,
.lost_interrupt = ATA_OP_NULL,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
+ .hardreset = nv_hardreset,
};
-/* OSDL bz11195 reports that link doesn't come online after hardreset
- * on generic nv's and there have been several other similar reports
- * on linux-ide. Disable hardreset for generic nv's.
- */
-static struct ata_port_operations nv_generic_ops = {
- .inherits = &nv_common_ops,
- .hardreset = ATA_OP_NULL,
-};
-
-/* nf2 is ripe with hardreset related problems.
- *
- * kernel bz#3352 reports nf2/3 controllers can't determine device
- * signature reliably. The following thread reports detection failure
- * on cold boot with the standard debouncing timing.
- *
- * http://thread.gmane.org/gmane.linux.ide/34098
- *
- * And bz#12176 reports that hardreset simply doesn't work on nf2.
- * Give up on it and just don't do hardreset.
- */
static struct ata_port_operations nv_nf2_ops = {
.inherits = &nv_generic_ops,
.freeze = nv_nf2_freeze,
.thaw = nv_nf2_thaw,
};
-/* For initial probing after boot and hot plugging, hardreset mostly
- * works fine on CK804 but curiously, reprobing on the initial port by
- * rescanning or rmmod/insmod fails to acquire the initial D2H Reg FIS
- * in somewhat undeterministic way. Use noclassify hardreset.
- */
static struct ata_port_operations nv_ck804_ops = {
- .inherits = &nv_common_ops,
+ .inherits = &nv_generic_ops,
.freeze = nv_ck804_freeze,
.thaw = nv_ck804_thaw,
- .hardreset = nv_noclassify_hardreset,
.host_stop = nv_ck804_host_stop,
};
@@ -476,19 +509,8 @@ static struct ata_port_operations nv_adma_ops = {
.host_stop = nv_adma_host_stop,
};
-/* Kernel bz#12351 reports that when SWNCQ is enabled, for hotplug to
- * work, hardreset should be used and hardreset can't report proper
- * signature, which suggests that mcp5x is closer to nf2 as long as
- * reset quirkiness is concerned. Define separate ops for mcp5x with
- * nv_noclassify_hardreset().
- */
-static struct ata_port_operations nv_mcp5x_ops = {
- .inherits = &nv_common_ops,
- .hardreset = nv_noclassify_hardreset,
-};
-
static struct ata_port_operations nv_swncq_ops = {
- .inherits = &nv_mcp5x_ops,
+ .inherits = &nv_generic_ops,
.qc_defer = ata_std_qc_defer,
.qc_prep = nv_swncq_qc_prep,
@@ -557,7 +579,7 @@ static const struct ata_port_info nv_port_info[] = {
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
- .port_ops = &nv_mcp5x_ops,
+ .port_ops = &nv_generic_ops,
.private_data = NV_PI_PRIV(nv_generic_interrupt, &nv_sht),
},
/* SWNCQ */
@@ -1559,15 +1581,24 @@ static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
return 0;
}
-static int nv_noclassify_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
+static int nv_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
{
- bool online;
- int rc;
+ struct ata_eh_context *ehc = &link->eh_context;
- rc = sata_link_hardreset(link, sata_deb_timing_hotplug, deadline,
- &online, NULL);
- return online ? -EAGAIN : rc;
+ /* Do hardreset iff it's post-boot probing, please read the
+ * comment above port ops for details.
+ */
+ if (!(link->ap->pflags & ATA_PFLAG_LOADING) &&
+ !ata_dev_enabled(link->device))
+ sata_link_hardreset(link, sata_deb_timing_hotplug, deadline,
+ NULL, NULL);
+ else if (!(ehc->i.flags & ATA_EHI_QUIET))
+ ata_link_printk(link, KERN_INFO,
+ "nv: skipping hardreset on occupied port\n");
+
+ /* device signature acquisition is unreliable */
+ return -EAGAIN;
}
static void nv_nf2_freeze(struct ata_port *ap)
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index e67ce8e5caa5..030ec079b184 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -183,7 +183,7 @@ static struct scsi_host_template sil_sht = {
};
static struct ata_port_operations sil_ops = {
- .inherits = &ata_bmdma_port_ops,
+ .inherits = &ata_bmdma32_port_ops,
.dev_config = sil_dev_config,
.set_mode = sil_set_mode,
.bmdma_setup = sil_bmdma_setup,
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index dce3dccced3f..bbcf970068ad 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -193,6 +193,7 @@ enum {
PDC_TIMER_MASK_INT,
};
+#define ECC_ERASE_BUF_SZ (128 * 1024)
struct pdc_port_priv {
u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512];
@@ -213,8 +214,9 @@ struct pdc_host_priv {
static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
-static void pdc_eng_timeout(struct ata_port *ap);
-static void pdc_20621_phy_reset(struct ata_port *ap);
+static void pdc_error_handler(struct ata_port *ap);
+static void pdc_freeze(struct ata_port *ap);
+static void pdc_thaw(struct ata_port *ap);
static int pdc_port_start(struct ata_port *ap);
static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
@@ -233,6 +235,10 @@ static void pdc20621_put_to_dimm(struct ata_host *host,
void *psource, u32 offset, u32 size);
static void pdc20621_irq_clear(struct ata_port *ap);
static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc);
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
static struct scsi_host_template pdc_sata_sht = {
@@ -243,20 +249,24 @@ static struct scsi_host_template pdc_sata_sht = {
/* TODO: inherit from base port_ops after converting to new EH */
static struct ata_port_operations pdc_20621_ops = {
- .sff_tf_load = pdc_tf_load_mmio,
- .sff_tf_read = ata_sff_tf_read,
- .sff_check_status = ata_sff_check_status,
- .sff_exec_command = pdc_exec_command_mmio,
- .sff_dev_select = ata_sff_dev_select,
- .phy_reset = pdc_20621_phy_reset,
+ .inherits = &ata_sff_port_ops,
+
+ .check_atapi_dma = pdc_check_atapi_dma,
.qc_prep = pdc20621_qc_prep,
.qc_issue = pdc20621_qc_issue,
- .qc_fill_rtf = ata_sff_qc_fill_rtf,
- .sff_data_xfer = ata_sff_data_xfer,
- .eng_timeout = pdc_eng_timeout,
- .sff_irq_clear = pdc20621_irq_clear,
- .sff_irq_on = ata_sff_irq_on,
+
+ .freeze = pdc_freeze,
+ .thaw = pdc_thaw,
+ .softreset = pdc_softreset,
+ .error_handler = pdc_error_handler,
+ .lost_interrupt = ATA_OP_NULL,
+ .post_internal_cmd = pdc_post_internal_cmd,
+
.port_start = pdc_port_start,
+
+ .sff_tf_load = pdc_tf_load_mmio,
+ .sff_exec_command = pdc_exec_command_mmio,
+ .sff_irq_clear = pdc20621_irq_clear,
};
static const struct ata_port_info pdc_port_info[] = {
@@ -310,14 +320,6 @@ static int pdc_port_start(struct ata_port *ap)
return 0;
}
-static void pdc_20621_phy_reset(struct ata_port *ap)
-{
- VPRINTK("ENTER\n");
- ap->cbl = ATA_CBL_SATA;
- ata_port_probe(ap);
- ata_bus_reset(ap);
-}
-
static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
unsigned int portno,
unsigned int total_len)
@@ -686,8 +688,11 @@ static void pdc20621_packet_start(struct ata_queued_cmd *qc)
static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc)
{
switch (qc->tf.protocol) {
- case ATA_PROT_DMA:
case ATA_PROT_NODATA:
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ break;
+ /*FALLTHROUGH*/
+ case ATA_PROT_DMA:
pdc20621_packet_start(qc);
return 0;
@@ -786,12 +791,7 @@ static inline unsigned int pdc20621_host_intr(struct ata_port *ap,
static void pdc20621_irq_clear(struct ata_port *ap)
{
- struct ata_host *host = ap->host;
- void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
-
- mmio += PDC_CHIP0_OFS;
-
- readl(mmio + PDC_20621_SEQMASK);
+ ioread8(ap->ioaddr.status_addr);
}
static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
@@ -859,46 +859,119 @@ static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
return IRQ_RETVAL(handled);
}
-static void pdc_eng_timeout(struct ata_port *ap)
+static void pdc_freeze(struct ata_port *ap)
{
- u8 drv_stat;
- struct ata_host *host = ap->host;
- struct ata_queued_cmd *qc;
- unsigned long flags;
+ void __iomem *mmio = ap->ioaddr.cmd_addr;
+ u32 tmp;
- DPRINTK("ENTER\n");
+ /* FIXME: if all 4 ATA engines are stopped, also stop HDMA engine */
- spin_lock_irqsave(&host->lock, flags);
+ tmp = readl(mmio + PDC_CTLSTAT);
+ tmp |= PDC_MASK_INT;
+ tmp &= ~PDC_DMA_ENABLE;
+ writel(tmp, mmio + PDC_CTLSTAT);
+ readl(mmio + PDC_CTLSTAT); /* flush */
+}
- qc = ata_qc_from_tag(ap, ap->link.active_tag);
+static void pdc_thaw(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->ioaddr.cmd_addr;
+ u32 tmp;
- switch (qc->tf.protocol) {
- case ATA_PROT_DMA:
- case ATA_PROT_NODATA:
- ata_port_printk(ap, KERN_ERR, "command timeout\n");
- qc->err_mask |= __ac_err_mask(ata_wait_idle(ap));
- break;
+ /* FIXME: start HDMA engine, if zero ATA engines running */
- default:
- drv_stat = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+ /* clear IRQ */
+ ioread8(ap->ioaddr.status_addr);
- ata_port_printk(ap, KERN_ERR,
- "unknown timeout, cmd 0x%x stat 0x%x\n",
- qc->tf.command, drv_stat);
+ /* turn IRQ back on */
+ tmp = readl(mmio + PDC_CTLSTAT);
+ tmp &= ~PDC_MASK_INT;
+ writel(tmp, mmio + PDC_CTLSTAT);
+ readl(mmio + PDC_CTLSTAT); /* flush */
+}
- qc->err_mask |= ac_err_mask(drv_stat);
- break;
+static void pdc_reset_port(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT;
+ unsigned int i;
+ u32 tmp;
+
+ /* FIXME: handle HDMA copy engine */
+
+ for (i = 11; i > 0; i--) {
+ tmp = readl(mmio);
+ if (tmp & PDC_RESET)
+ break;
+
+ udelay(100);
+
+ tmp |= PDC_RESET;
+ writel(tmp, mmio);
}
- spin_unlock_irqrestore(&host->lock, flags);
- ata_eh_qc_complete(qc);
- DPRINTK("EXIT\n");
+ tmp &= ~PDC_RESET;
+ writel(tmp, mmio);
+ readl(mmio); /* flush */
+}
+
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ pdc_reset_port(link->ap);
+ return ata_sff_softreset(link, class, deadline);
+}
+
+static void pdc_error_handler(struct ata_port *ap)
+{
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ pdc_reset_port(ap);
+
+ ata_std_error_handler(ap);
+}
+
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ /* make DMA engine forget about the failed command */
+ if (qc->flags & ATA_QCFLAG_FAILED)
+ pdc_reset_port(ap);
+}
+
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ u8 *scsicmd = qc->scsicmd->cmnd;
+ int pio = 1; /* atapi dma off by default */
+
+ /* Whitelist commands that may use DMA. */
+ switch (scsicmd[0]) {
+ case WRITE_12:
+ case WRITE_10:
+ case WRITE_6:
+ case READ_12:
+ case READ_10:
+ case READ_6:
+ case 0xad: /* READ_DVD_STRUCTURE */
+ case 0xbe: /* READ_CD */
+ pio = 0;
+ }
+ /* -45150 (FFFF4FA2) to -1 (FFFFFFFF) shall use PIO mode */
+ if (scsicmd[0] == WRITE_10) {
+ unsigned int lba =
+ (scsicmd[2] << 24) |
+ (scsicmd[3] << 16) |
+ (scsicmd[4] << 8) |
+ scsicmd[5];
+ if (lba >= 0xFFFF4FA2)
+ pio = 1;
+ }
+ return pio;
}
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
{
WARN_ON(tf->protocol == ATA_PROT_DMA ||
- tf->protocol == ATA_PROT_NODATA);
+ tf->protocol == ATAPI_PROT_DMA);
ata_sff_tf_load(ap, tf);
}
@@ -906,7 +979,7 @@ static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
{
WARN_ON(tf->protocol == ATA_PROT_DMA ||
- tf->protocol == ATA_PROT_NODATA);
+ tf->protocol == ATAPI_PROT_DMA);
ata_sff_exec_command(ap, tf);
}
@@ -1208,7 +1281,6 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
{
int speed, size, length;
u32 addr, spd0, pci_status;
- u32 tmp = 0;
u32 time_period = 0;
u32 tcount = 0;
u32 ticks = 0;
@@ -1323,14 +1395,17 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
PDC_DIMM_SPD_TYPE, &spd0);
if (spd0 == 0x02) {
+ void *buf;
VPRINTK("Start ECC initialization\n");
addr = 0;
length = size * 1024 * 1024;
+ buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL);
while (addr < length) {
- pdc20621_put_to_dimm(host, (void *) &tmp, addr,
- sizeof(u32));
- addr += sizeof(u32);
+ pdc20621_put_to_dimm(host, buf, addr,
+ ECC_ERASE_BUF_SZ);
+ addr += ECC_ERASE_BUF_SZ;
}
+ kfree(buf);
VPRINTK("Finish ECC initialization\n");
}
return 0;
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index dc030f1f00f1..c6599618523e 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -700,8 +700,10 @@ int bus_add_driver(struct device_driver *drv)
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
- return error;
+ return 0;
out_unregister:
+ kfree(drv->p);
+ drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4aa527b8a913..1977d4beb89e 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -879,7 +879,7 @@ int device_add(struct device *dev)
}
if (!dev_name(dev))
- goto done;
+ goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -978,6 +978,9 @@ done:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
+name_error:
+ kfree(dev->p);
+ dev->p = NULL;
goto done;
}
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index c51f11bb29ae..8ae0f63602e0 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -257,6 +257,10 @@ EXPORT_SYMBOL_GPL(driver_register);
*/
void driver_unregister(struct device_driver *drv)
{
+ if (!drv || !drv->p) {
+ WARN(1, "Unexpected driver unregister!\n");
+ return;
+ }
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index d3a59c688fe4..8a267c427629 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -17,7 +17,7 @@
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
-
+#include <linux/highmem.h>
#include <linux/firmware.h>
#include "base.h"
@@ -45,7 +45,10 @@ struct firmware_priv {
struct bin_attribute attr_data;
struct firmware *fw;
unsigned long status;
- int alloc_size;
+ struct page **pages;
+ int nr_pages;
+ int page_array_size;
+ const char *vdata;
struct timer_list timeout;
};
@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
return sprintf(buf, "%d\n", loading);
}
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
/**
* firmware_loading_store - set value in the 'loading' control file
* @dev: device pointer
@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
{
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int loading = simple_strtol(buf, NULL, 10);
+ int i;
switch (loading) {
case 1:
@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
}
vfree(fw_priv->fw->data);
fw_priv->fw->data = NULL;
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ kfree(fw_priv->pages);
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
fw_priv->fw->size = 0;
- fw_priv->alloc_size = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status);
mutex_unlock(&fw_lock);
break;
case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+ vfree(fw_priv->fw->data);
+ fw_priv->fw->data = vmap(fw_priv->pages,
+ fw_priv->nr_pages,
+ 0, PAGE_KERNEL_RO);
+ if (!fw_priv->fw->data) {
+ dev_err(dev, "%s: vmap() failed\n", __func__);
+ goto err;
+ }
+ /* Pages will be freed by vfree() */
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
complete(&fw_priv->completion);
clear_bit(FW_STATUS_LOADING, &fw_priv->status);
break;
@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
+ err:
fw_load_abort(fw_priv);
break;
}
@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
ret_count = -ENODEV;
goto out;
}
- ret_count = memory_read_from_buffer(buffer, count, &offset,
- fw->data, fw->size);
+ if (offset > fw->size)
+ return 0;
+ if (count > fw->size - offset)
+ count = fw->size - offset;
+
+ ret_count = count;
+
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE-1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ memcpy(buffer, page_data + page_ofs, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
out:
mutex_unlock(&fw_lock);
return ret_count;
@@ -201,27 +247,39 @@ out:
static int
fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
- u8 *new_data;
- int new_size = fw_priv->alloc_size;
+ int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+
+ /* If the array of pages is too small, grow it... */
+ if (fw_priv->page_array_size < pages_needed) {
+ int new_array_size = max(pages_needed,
+ fw_priv->page_array_size * 2);
+ struct page **new_pages;
+
+ new_pages = kmalloc(new_array_size * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_pages) {
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ memcpy(new_pages, fw_priv->pages,
+ fw_priv->page_array_size * sizeof(void *));
+ memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+ (new_array_size - fw_priv->page_array_size));
+ kfree(fw_priv->pages);
+ fw_priv->pages = new_pages;
+ fw_priv->page_array_size = new_array_size;
+ }
- if (min_size <= fw_priv->alloc_size)
- return 0;
+ while (fw_priv->nr_pages < pages_needed) {
+ fw_priv->pages[fw_priv->nr_pages] =
+ alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- new_size = ALIGN(min_size, PAGE_SIZE);
- new_data = vmalloc(new_size);
- if (!new_data) {
- printk(KERN_ERR "%s: unable to alloc buffer\n", __func__);
- /* Make sure that we don't keep incomplete data */
- fw_load_abort(fw_priv);
- return -ENOMEM;
- }
- fw_priv->alloc_size = new_size;
- if (fw_priv->fw->data) {
- memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
- vfree(fw_priv->fw->data);
+ if (!fw_priv->pages[fw_priv->nr_pages]) {
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ fw_priv->nr_pages++;
}
- fw_priv->fw->data = new_data;
- BUG_ON(min_size > fw_priv->alloc_size);
return 0;
}
@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
if (retval)
goto out;
- memcpy((u8 *)fw->data + offset, buffer, count);
-
- fw->size = max_t(size_t, offset + count, fw->size);
retval = count;
+
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE - 1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ memcpy(page_data + page_ofs, buffer, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
+
+ fw->size = max_t(size_t, offset, fw->size);
out:
mutex_unlock(&fw_lock);
return retval;
@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = {
static void fw_dev_release(struct device *dev)
{
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+ int i;
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ kfree(fw_priv->pages);
kfree(fw_priv);
kfree(dev);
diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c
index 9f0e672f4be8..8ad4ffea6920 100644
--- a/drivers/base/iommu.c
+++ b/drivers/base/iommu.c
@@ -18,6 +18,8 @@
#include <linux/bug.h>
#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/iommu.h>
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index b5b6c973a2e0..ead3f64c41d0 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -217,7 +217,6 @@ int platform_device_add_data(struct platform_device *pdev, const void *data,
if (d) {
memcpy(d, data, size);
pdev->dev.platform_data = d;
- pdev->platform_data = d;
}
return d ? 0 : -ENOMEM;
}
@@ -247,21 +246,6 @@ int platform_device_add(struct platform_device *pdev)
else
dev_set_name(&pdev->dev, pdev->name);
- /* We will remove platform_data field from struct device
- * if all platform devices pass its platform specific data
- * from platform_device. The conversion is going to be a
- * long time, so we allow the two cases coexist to make
- * this kind of fix more easily*/
- if (pdev->platform_data && pdev->dev.platform_data) {
- printk(KERN_ERR
- "%s: use which platform_data?\n",
- dev_name(&pdev->dev));
- } else if (pdev->platform_data) {
- pdev->dev.platform_data = pdev->platform_data;
- } else if (pdev->dev.platform_data) {
- pdev->platform_data = pdev->dev.platform_data;
- }
-
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
@@ -485,22 +469,6 @@ static void platform_drv_shutdown(struct device *_dev)
drv->shutdown(dev);
}
-static int platform_drv_suspend(struct device *_dev, pm_message_t state)
-{
- struct platform_driver *drv = to_platform_driver(_dev->driver);
- struct platform_device *dev = to_platform_device(_dev);
-
- return drv->suspend(dev, state);
-}
-
-static int platform_drv_resume(struct device *_dev)
-{
- struct platform_driver *drv = to_platform_driver(_dev->driver);
- struct platform_device *dev = to_platform_device(_dev);
-
- return drv->resume(dev);
-}
-
/**
* platform_driver_register
* @drv: platform driver structure
@@ -514,10 +482,10 @@ int platform_driver_register(struct platform_driver *drv)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
- if (drv->suspend)
- drv->driver.suspend = platform_drv_suspend;
- if (drv->resume)
- drv->driver.resume = platform_drv_resume;
+ if (drv->suspend || drv->resume)
+ pr_warning("Platform driver '%s' needs updating - please use "
+ "dev_pm_ops\n", drv->driver.name);
+
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(platform_driver_register);
@@ -649,10 +617,12 @@ static int platform_match(struct device *dev, struct device_driver *drv)
static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
{
+ struct platform_driver *pdrv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = to_platform_device(dev);
int ret = 0;
- if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, mesg);
+ if (dev->driver && pdrv->suspend)
+ ret = pdrv->suspend(pdev, mesg);
return ret;
}
@@ -683,10 +653,12 @@ static int platform_legacy_resume_early(struct device *dev)
static int platform_legacy_resume(struct device *dev)
{
+ struct platform_driver *pdrv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = to_platform_device(dev);
int ret = 0;
- if (dev->driver && dev->driver->resume)
- ret = dev->driver->resume(dev);
+ if (dev->driver && pdrv->resume)
+ ret = pdrv->resume(pdev);
return ret;
}
@@ -1028,7 +1000,7 @@ static __initdata LIST_HEAD(early_platform_device_list);
/**
* early_platform_driver_register
- * @edrv: early_platform driver structure
+ * @epdrv: early_platform driver structure
* @buf: string passed from early_param()
*/
int __init early_platform_driver_register(struct early_platform_driver *epdrv,
@@ -1112,7 +1084,7 @@ void __init early_platform_driver_register_all(char *class_str)
/**
* early_platform_match
- * @edrv: early platform driver structure
+ * @epdrv: early platform driver structure
* @id: id to match against
*/
static __init struct platform_device *
@@ -1130,7 +1102,7 @@ early_platform_match(struct early_platform_driver *epdrv, int id)
/**
* early_platform_left
- * @edrv: early platform driver structure
+ * @epdrv: early platform driver structure
* @id: return true if id or above exists
*/
static __init int early_platform_left(struct early_platform_driver *epdrv,
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 69b4ddb7de3b..fae725458981 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -315,13 +315,13 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
/*------------------------- Resume routines -------------------------*/
/**
- * resume_device_noirq - Power on one device (early resume).
+ * device_resume_noirq - Power on one device (early resume).
* @dev: Device.
* @state: PM transition of the system being carried out.
*
* Must be called with interrupts disabled.
*/
-static int resume_device_noirq(struct device *dev, pm_message_t state)
+static int device_resume_noirq(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -334,9 +334,6 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
if (dev->bus->pm) {
pm_dev_dbg(dev, state, "EARLY ");
error = pm_noirq_op(dev, dev->bus->pm, state);
- } else if (dev->bus->resume_early) {
- pm_dev_dbg(dev, state, "legacy EARLY ");
- error = dev->bus->resume_early(dev);
}
End:
TRACE_RESUME(error);
@@ -344,50 +341,40 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
}
/**
- * dpm_power_up - Power on all regular (non-sysdev) devices.
+ * dpm_resume_noirq - Power on all regular (non-sysdev) devices.
* @state: PM transition of the system being carried out.
*
- * Execute the appropriate "noirq resume" callback for all devices marked
- * as DPM_OFF_IRQ.
+ * Call the "noirq" resume handlers for all devices marked as
+ * DPM_OFF_IRQ and enable device drivers to receive interrupts.
*
* Must be called under dpm_list_mtx. Device drivers should not receive
* interrupts while it's being executed.
*/
-static void dpm_power_up(pm_message_t state)
+void dpm_resume_noirq(pm_message_t state)
{
struct device *dev;
+ mutex_lock(&dpm_list_mtx);
list_for_each_entry(dev, &dpm_list, power.entry)
if (dev->power.status > DPM_OFF) {
int error;
dev->power.status = DPM_OFF;
- error = resume_device_noirq(dev, state);
+ error = device_resume_noirq(dev, state);
if (error)
pm_dev_err(dev, state, " early", error);
}
-}
-
-/**
- * device_power_up - Turn on all devices that need special attention.
- * @state: PM transition of the system being carried out.
- *
- * Call the "early" resume handlers and enable device drivers to receive
- * interrupts.
- */
-void device_power_up(pm_message_t state)
-{
- dpm_power_up(state);
+ mutex_unlock(&dpm_list_mtx);
resume_device_irqs();
}
-EXPORT_SYMBOL_GPL(device_power_up);
+EXPORT_SYMBOL_GPL(dpm_resume_noirq);
/**
- * resume_device - Restore state for one device.
+ * device_resume - Restore state for one device.
* @dev: Device.
* @state: PM transition of the system being carried out.
*/
-static int resume_device(struct device *dev, pm_message_t state)
+static int device_resume(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -412,9 +399,6 @@ static int resume_device(struct device *dev, pm_message_t state)
if (dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- } else if (dev->type->resume) {
- pm_dev_dbg(dev, state, "legacy type ");
- error = dev->type->resume(dev);
}
if (error)
goto End;
@@ -460,7 +444,7 @@ static void dpm_resume(pm_message_t state)
dev->power.status = DPM_RESUMING;
mutex_unlock(&dpm_list_mtx);
- error = resume_device(dev, state);
+ error = device_resume(dev, state);
mutex_lock(&dpm_list_mtx);
if (error)
@@ -478,11 +462,11 @@ static void dpm_resume(pm_message_t state)
}
/**
- * complete_device - Complete a PM transition for given device
+ * device_complete - Complete a PM transition for given device
* @dev: Device.
* @state: PM transition of the system being carried out.
*/
-static void complete_device(struct device *dev, pm_message_t state)
+static void device_complete(struct device *dev, pm_message_t state)
{
down(&dev->sem);
@@ -525,7 +509,7 @@ static void dpm_complete(pm_message_t state)
dev->power.status = DPM_ON;
mutex_unlock(&dpm_list_mtx);
- complete_device(dev, state);
+ device_complete(dev, state);
mutex_lock(&dpm_list_mtx);
}
@@ -538,19 +522,19 @@ static void dpm_complete(pm_message_t state)
}
/**
- * device_resume - Restore state of each device in system.
+ * dpm_resume_end - Restore state of each device in system.
* @state: PM transition of the system being carried out.
*
* Resume all the devices, unlock them all, and allow new
* devices to be registered once again.
*/
-void device_resume(pm_message_t state)
+void dpm_resume_end(pm_message_t state)
{
might_sleep();
dpm_resume(state);
dpm_complete(state);
}
-EXPORT_SYMBOL_GPL(device_resume);
+EXPORT_SYMBOL_GPL(dpm_resume_end);
/*------------------------- Suspend routines -------------------------*/
@@ -575,13 +559,13 @@ static pm_message_t resume_event(pm_message_t sleep_state)
}
/**
- * suspend_device_noirq - Shut down one device (late suspend).
+ * device_suspend_noirq - Shut down one device (late suspend).
* @dev: Device.
* @state: PM transition of the system being carried out.
*
* This is called with interrupts off and only a single CPU running.
*/
-static int suspend_device_noirq(struct device *dev, pm_message_t state)
+static int device_suspend_noirq(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -591,49 +575,47 @@ static int suspend_device_noirq(struct device *dev, pm_message_t state)
if (dev->bus->pm) {
pm_dev_dbg(dev, state, "LATE ");
error = pm_noirq_op(dev, dev->bus->pm, state);
- } else if (dev->bus->suspend_late) {
- pm_dev_dbg(dev, state, "legacy LATE ");
- error = dev->bus->suspend_late(dev, state);
- suspend_report_result(dev->bus->suspend_late, error);
}
return error;
}
/**
- * device_power_down - Shut down special devices.
+ * dpm_suspend_noirq - Power down all regular (non-sysdev) devices.
* @state: PM transition of the system being carried out.
*
- * Prevent device drivers from receiving interrupts and call the "late"
+ * Prevent device drivers from receiving interrupts and call the "noirq"
* suspend handlers.
*
* Must be called under dpm_list_mtx.
*/
-int device_power_down(pm_message_t state)
+int dpm_suspend_noirq(pm_message_t state)
{
struct device *dev;
int error = 0;
suspend_device_irqs();
+ mutex_lock(&dpm_list_mtx);
list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
- error = suspend_device_noirq(dev, state);
+ error = device_suspend_noirq(dev, state);
if (error) {
pm_dev_err(dev, state, " late", error);
break;
}
dev->power.status = DPM_OFF_IRQ;
}
+ mutex_unlock(&dpm_list_mtx);
if (error)
- device_power_up(resume_event(state));
+ dpm_resume_noirq(resume_event(state));
return error;
}
-EXPORT_SYMBOL_GPL(device_power_down);
+EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
/**
- * suspend_device - Save state of one device.
+ * device_suspend - Save state of one device.
* @dev: Device.
* @state: PM transition of the system being carried out.
*/
-static int suspend_device(struct device *dev, pm_message_t state)
+static int device_suspend(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -656,10 +638,6 @@ static int suspend_device(struct device *dev, pm_message_t state)
if (dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- } else if (dev->type->suspend) {
- pm_dev_dbg(dev, state, "legacy type ");
- error = dev->type->suspend(dev, state);
- suspend_report_result(dev->type->suspend, error);
}
if (error)
goto End;
@@ -700,7 +678,7 @@ static int dpm_suspend(pm_message_t state)
get_device(dev);
mutex_unlock(&dpm_list_mtx);
- error = suspend_device(dev, state);
+ error = device_suspend(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
@@ -719,11 +697,11 @@ static int dpm_suspend(pm_message_t state)
}
/**
- * prepare_device - Execute the ->prepare() callback(s) for given device.
+ * device_prepare - Execute the ->prepare() callback(s) for given device.
* @dev: Device.
* @state: PM transition of the system being carried out.
*/
-static int prepare_device(struct device *dev, pm_message_t state)
+static int device_prepare(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -777,7 +755,7 @@ static int dpm_prepare(pm_message_t state)
dev->power.status = DPM_PREPARING;
mutex_unlock(&dpm_list_mtx);
- error = prepare_device(dev, state);
+ error = device_prepare(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
@@ -803,12 +781,12 @@ static int dpm_prepare(pm_message_t state)
}
/**
- * device_suspend - Save state and stop all devices in system.
+ * dpm_suspend_start - Save state and stop all devices in system.
* @state: PM transition of the system being carried out.
*
* Prepare and suspend all devices.
*/
-int device_suspend(pm_message_t state)
+int dpm_suspend_start(pm_message_t state)
{
int error;
@@ -818,7 +796,7 @@ int device_suspend(pm_message_t state)
error = dpm_suspend(state);
return error;
}
-EXPORT_SYMBOL_GPL(device_suspend);
+EXPORT_SYMBOL_GPL(dpm_suspend_start);
void __suspend_report_result(const char *function, void *fn, int ret)
{
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index 3236b434b964..9742a78c9fe4 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -343,11 +343,15 @@ static void __sysdev_resume(struct sys_device *dev)
/* First, call the class-specific one */
if (cls->resume)
cls->resume(dev);
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled after %pF\n", cls->resume);
/* Call auxillary drivers next. */
list_for_each_entry(drv, &cls->drivers, entry) {
if (drv->resume)
drv->resume(dev);
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled after %pF\n", drv->resume);
}
}
@@ -377,6 +381,9 @@ int sysdev_suspend(pm_message_t state)
if (ret)
return ret;
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled while suspending system devices\n");
+
pr_debug("Suspending System Devices\n");
list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
@@ -393,6 +400,9 @@ int sysdev_suspend(pm_message_t state)
if (ret)
goto aux_driver;
}
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled after %pF\n",
+ drv->suspend);
}
/* Now call the generic one */
@@ -400,6 +410,9 @@ int sysdev_suspend(pm_message_t state)
ret = cls->suspend(sysdev, state);
if (ret)
goto cls_driver;
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled after %pF\n",
+ cls->suspend);
}
}
}
@@ -452,6 +465,9 @@ int sysdev_resume(void)
{
struct sysdev_class *cls;
+ WARN_ONCE(!irqs_disabled(),
+ "Interrupts enabled while resuming system devices\n");
+
pr_debug("Resuming System Devices\n");
list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) {
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index f42fa50d3550..ac5e05a98b2f 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -438,7 +438,7 @@ source "drivers/s390/block/Kconfig"
config XILINX_SYSACE
tristate "Xilinx SystemACE support"
- depends on 4xx
+ depends on 4xx || MICROBLAZE
help
Include support for the Xilinx SystemACE CompactFlash interface
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 5f7e64ba87e5..4bf8705b3ace 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -407,12 +407,7 @@ static int __init ramdisk_size(char *str)
rd_size = simple_strtol(str, NULL, 0);
return 1;
}
-static int __init ramdisk_size2(char *str)
-{
- return ramdisk_size(str);
-}
-__setup("ramdisk=", ramdisk_size);
-__setup("ramdisk_size=", ramdisk_size2);
+__setup("ramdisk_size=", ramdisk_size);
#endif
/*
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index e714e7cce6f2..b22cec97ea19 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -180,11 +180,13 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
__u32);
static void start_io(ctlr_info_t *h);
static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
- unsigned int use_unit_num, unsigned int log_unit,
__u8 page_code, unsigned char *scsi3addr, int cmd_type);
static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
- unsigned int use_unit_num, unsigned int log_unit,
- __u8 page_code, int cmd_type);
+ __u8 page_code, unsigned char scsi3addr[],
+ int cmd_type);
+static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
+ int attempt_retry);
+static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
static void fail_all_cmds(unsigned long ctlr);
static int scan_thread(void *data);
@@ -437,6 +439,194 @@ static void __devinit cciss_procinit(int i)
}
#endif /* CONFIG_PROC_FS */
+#define MAX_PRODUCT_NAME_LEN 19
+
+#define to_hba(n) container_of(n, struct ctlr_info, dev)
+#define to_drv(n) container_of(n, drive_info_struct, dev)
+
+static struct device_type cciss_host_type = {
+ .name = "cciss_host",
+};
+
+static ssize_t dev_show_unique_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
+ __u8 sn[16];
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring)
+ ret = -EBUSY;
+ else
+ memcpy(sn, drv->serial_no, sizeof(sn));
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ if (ret)
+ return ret;
+ else
+ return snprintf(buf, 16 * 2 + 2,
+ "%02X%02X%02X%02X%02X%02X%02X%02X"
+ "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ sn[0], sn[1], sn[2], sn[3],
+ sn[4], sn[5], sn[6], sn[7],
+ sn[8], sn[9], sn[10], sn[11],
+ sn[12], sn[13], sn[14], sn[15]);
+}
+DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL);
+
+static ssize_t dev_show_vendor(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
+ char vendor[VENDOR_LEN + 1];
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring)
+ ret = -EBUSY;
+ else
+ memcpy(vendor, drv->vendor, VENDOR_LEN + 1);
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ if (ret)
+ return ret;
+ else
+ return snprintf(buf, sizeof(vendor) + 1, "%s\n", drv->vendor);
+}
+DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL);
+
+static ssize_t dev_show_model(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
+ char model[MODEL_LEN + 1];
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring)
+ ret = -EBUSY;
+ else
+ memcpy(model, drv->model, MODEL_LEN + 1);
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ if (ret)
+ return ret;
+ else
+ return snprintf(buf, sizeof(model) + 1, "%s\n", drv->model);
+}
+DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL);
+
+static ssize_t dev_show_rev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
+ char rev[REV_LEN + 1];
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring)
+ ret = -EBUSY;
+ else
+ memcpy(rev, drv->rev, REV_LEN + 1);
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ if (ret)
+ return ret;
+ else
+ return snprintf(buf, sizeof(rev) + 1, "%s\n", drv->rev);
+}
+DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
+
+static struct attribute *cciss_dev_attrs[] = {
+ &dev_attr_unique_id.attr,
+ &dev_attr_model.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_rev.attr,
+ NULL
+};
+
+static struct attribute_group cciss_dev_attr_group = {
+ .attrs = cciss_dev_attrs,
+};
+
+static struct attribute_group *cciss_dev_attr_groups[] = {
+ &cciss_dev_attr_group,
+ NULL
+};
+
+static struct device_type cciss_dev_type = {
+ .name = "cciss_device",
+ .groups = cciss_dev_attr_groups,
+};
+
+static struct bus_type cciss_bus_type = {
+ .name = "cciss",
+};
+
+
+/*
+ * Initialize sysfs entry for each controller. This sets up and registers
+ * the 'cciss#' directory for each individual controller under
+ * /sys/bus/pci/devices/<dev>/.
+ */
+static int cciss_create_hba_sysfs_entry(struct ctlr_info *h)
+{
+ device_initialize(&h->dev);
+ h->dev.type = &cciss_host_type;
+ h->dev.bus = &cciss_bus_type;
+ dev_set_name(&h->dev, "%s", h->devname);
+ h->dev.parent = &h->pdev->dev;
+
+ return device_add(&h->dev);
+}
+
+/*
+ * Remove sysfs entries for an hba.
+ */
+static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
+{
+ device_del(&h->dev);
+}
+
+/*
+ * Initialize sysfs for each logical drive. This sets up and registers
+ * the 'c#d#' directory for each individual logical drive under
+ * /sys/bus/pci/devices/<dev/ccis#/. We also create a link from
+ * /sys/block/cciss!c#d# to this entry.
+ */
+static int cciss_create_ld_sysfs_entry(struct ctlr_info *h,
+ drive_info_struct *drv,
+ int drv_index)
+{
+ device_initialize(&drv->dev);
+ drv->dev.type = &cciss_dev_type;
+ drv->dev.bus = &cciss_bus_type;
+ dev_set_name(&drv->dev, "c%dd%d", h->ctlr, drv_index);
+ drv->dev.parent = &h->dev;
+ return device_add(&drv->dev);
+}
+
+/*
+ * Remove sysfs entries for a logical drive.
+ */
+static void cciss_destroy_ld_sysfs_entry(drive_info_struct *drv)
+{
+ device_del(&drv->dev);
+}
+
/*
* For operations that cannot sleep, a command block is allocated at init,
* and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
@@ -1332,6 +1522,56 @@ static void cciss_softirq_done(struct request *rq)
spin_unlock_irqrestore(&h->lock, flags);
}
+static void log_unit_to_scsi3addr(ctlr_info_t *h, unsigned char scsi3addr[],
+ uint32_t log_unit)
+{
+ log_unit = h->drv[log_unit].LunID & 0x03fff;
+ memset(&scsi3addr[4], 0, 4);
+ memcpy(&scsi3addr[0], &log_unit, 4);
+ scsi3addr[3] |= 0x40;
+}
+
+/* This function gets the SCSI vendor, model, and revision of a logical drive
+ * via the inquiry page 0. Model, vendor, and rev are set to empty strings if
+ * they cannot be read.
+ */
+static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
+ char *vendor, char *model, char *rev)
+{
+ int rc;
+ InquiryData_struct *inq_buf;
+ unsigned char scsi3addr[8];
+
+ *vendor = '\0';
+ *model = '\0';
+ *rev = '\0';
+
+ inq_buf = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+ if (!inq_buf)
+ return;
+
+ log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+ if (withirq)
+ rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf,
+ sizeof(InquiryData_struct), 0,
+ scsi3addr, TYPE_CMD);
+ else
+ rc = sendcmd(CISS_INQUIRY, ctlr, inq_buf,
+ sizeof(InquiryData_struct), 0,
+ scsi3addr, TYPE_CMD);
+ if (rc == IO_OK) {
+ memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
+ vendor[VENDOR_LEN] = '\0';
+ memcpy(model, &inq_buf->data_byte[16], MODEL_LEN);
+ model[MODEL_LEN] = '\0';
+ memcpy(rev, &inq_buf->data_byte[32], REV_LEN);
+ rev[REV_LEN] = '\0';
+ }
+
+ kfree(inq_buf);
+ return;
+}
+
/* This function gets the serial number of a logical drive via
* inquiry page 0x83. Serial no. is 16 bytes. If the serial
* number cannot be had, for whatever reason, 16 bytes of 0xff
@@ -1343,6 +1583,7 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
#define PAGE_83_INQ_BYTES 64
int rc;
unsigned char *buf;
+ unsigned char scsi3addr[8];
if (buflen > 16)
buflen = 16;
@@ -1351,12 +1592,13 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
if (!buf)
return;
memset(serial_no, 0, buflen);
+ log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
if (withirq)
rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
- PAGE_83_INQ_BYTES, 1, logvol, 0x83, TYPE_CMD);
+ PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
else
rc = sendcmd(CISS_INQUIRY, ctlr, buf,
- PAGE_83_INQ_BYTES, 1, logvol, 0x83, NULL, TYPE_CMD);
+ PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
if (rc == IO_OK)
memcpy(serial_no, &buf[8], buflen);
kfree(buf);
@@ -1372,7 +1614,7 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->first_minor = drv_index << NWD_SHIFT;
disk->fops = &cciss_fops;
disk->private_data = &h->drv[drv_index];
- disk->driverfs_dev = &h->pdev->dev;
+ disk->driverfs_dev = &h->drv[drv_index].dev;
/* Set up queue information */
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
@@ -1389,8 +1631,8 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->queue->queuedata = h;
- blk_queue_hardsect_size(disk->queue,
- h->drv[drv_index].block_size);
+ blk_queue_logical_block_size(disk->queue,
+ h->drv[drv_index].block_size);
/* Make sure all queue data is written out before */
/* setting h->drv[drv_index].queue, as setting this */
@@ -1463,6 +1705,8 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
drvinfo->block_size = block_size;
drvinfo->nr_blocks = total_size + 1;
+ cciss_get_device_descr(ctlr, drv_index, 1, drvinfo->vendor,
+ drvinfo->model, drvinfo->rev);
cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no,
sizeof(drvinfo->serial_no));
@@ -1512,6 +1756,9 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
h->drv[drv_index].cylinders = drvinfo->cylinders;
h->drv[drv_index].raid_level = drvinfo->raid_level;
memcpy(h->drv[drv_index].serial_no, drvinfo->serial_no, 16);
+ memcpy(h->drv[drv_index].vendor, drvinfo->vendor, VENDOR_LEN + 1);
+ memcpy(h->drv[drv_index].model, drvinfo->model, MODEL_LEN + 1);
+ memcpy(h->drv[drv_index].rev, drvinfo->rev, REV_LEN + 1);
++h->num_luns;
disk = h->gendisk[drv_index];
@@ -1586,6 +1833,8 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
}
}
h->drv[drv_index].LunID = lunid;
+ if (cciss_create_ld_sysfs_entry(h, &h->drv[drv_index], drv_index))
+ goto err_free_disk;
/* Don't need to mark this busy because nobody */
/* else knows about this disk yet to contend */
@@ -1593,6 +1842,11 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
h->drv[drv_index].busy_configuring = 0;
wmb();
return drv_index;
+
+err_free_disk:
+ put_disk(h->gendisk[drv_index]);
+ h->gendisk[drv_index] = NULL;
+ return -1;
}
/* This is for the special case of a controller which
@@ -1663,8 +1917,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
goto mem_msg;
return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
- sizeof(ReportLunData_struct), 0,
- 0, 0, TYPE_CMD);
+ sizeof(ReportLunData_struct),
+ 0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK)
listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
@@ -1713,6 +1967,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
h->drv[i].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return_code = deregister_disk(h, i, 1);
+ cciss_destroy_ld_sysfs_entry(&h->drv[i]);
h->drv[i].busy_configuring = 0;
}
}
@@ -1872,11 +2127,9 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
return 0;
}
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, /* 0: address the controller,
- 1: address logical volume log_unit,
- 2: periph device address is scsi3addr */
- unsigned int log_unit, __u8 page_code,
- unsigned char *scsi3addr, int cmd_type)
+static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+ size_t size, __u8 page_code, unsigned char *scsi3addr,
+ int cmd_type)
{
ctlr_info_t *h = hba[ctlr];
u64bit buff_dma_handle;
@@ -1892,27 +2145,12 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
c->Header.SGTotal = 0;
}
c->Header.Tag.lower = c->busaddr;
+ memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8);
c->Request.Type.Type = cmd_type;
if (cmd_type == TYPE_CMD) {
switch (cmd) {
case CISS_INQUIRY:
- /* If the logical unit number is 0 then, this is going
- to controller so It's a physical command
- mode = 0 target = 0. So we have nothing to write.
- otherwise, if use_unit_num == 1,
- mode = 1(volume set addressing) target = LUNID
- otherwise, if use_unit_num == 2,
- mode = 0(periph dev addr) target = scsi3addr */
- if (use_unit_num == 1) {
- c->Header.LUN.LogDev.VolId =
- h->drv[log_unit].LunID;
- c->Header.LUN.LogDev.Mode = 1;
- } else if (use_unit_num == 2) {
- memcpy(c->Header.LUN.LunAddrBytes, scsi3addr,
- 8);
- c->Header.LUN.LogDev.Mode = 0;
- }
/* are we trying to read a vital product page */
if (page_code != 0) {
c->Request.CDB[1] = 0x01;
@@ -1942,8 +2180,6 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
break;
case CCISS_READ_CAPACITY:
- c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
- c->Header.LUN.LogDev.Mode = 1;
c->Request.CDBLen = 10;
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction = XFER_READ;
@@ -1951,8 +2187,6 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
c->Request.CDB[0] = cmd;
break;
case CCISS_READ_CAPACITY_16:
- c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
- c->Header.LUN.LogDev.Mode = 1;
c->Request.CDBLen = 16;
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction = XFER_READ;
@@ -1974,6 +2208,12 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
c->Request.CDB[0] = BMIC_WRITE;
c->Request.CDB[6] = BMIC_CACHE_FLUSH;
break;
+ case TEST_UNIT_READY:
+ c->Request.CDBLen = 6;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = XFER_NONE;
+ c->Request.Timeout = 0;
+ break;
default:
printk(KERN_WARNING
"cciss%d: Unknown Command 0x%c\n", ctlr, cmd);
@@ -1992,13 +2232,13 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
memcpy(&c->Request.CDB[4], buff, 8);
break;
case 1: /* RESET message */
- c->Request.CDBLen = 12;
+ c->Request.CDBLen = 16;
c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.Type.Direction = XFER_NONE;
c->Request.Timeout = 0;
memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
c->Request.CDB[0] = cmd; /* reset */
- c->Request.CDB[1] = 0x04; /* reset a LUN */
+ c->Request.CDB[1] = 0x03; /* reset a target */
break;
case 3: /* No-Op message */
c->Request.CDBLen = 1;
@@ -2030,114 +2270,152 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
return status;
}
-static int sendcmd_withirq(__u8 cmd,
- int ctlr,
- void *buff,
- size_t size,
- unsigned int use_unit_num,
- unsigned int log_unit, __u8 page_code, int cmd_type)
+static int check_target_status(ctlr_info_t *h, CommandList_struct *c)
{
- ctlr_info_t *h = hba[ctlr];
- CommandList_struct *c;
+ switch (c->err_info->ScsiStatus) {
+ case SAM_STAT_GOOD:
+ return IO_OK;
+ case SAM_STAT_CHECK_CONDITION:
+ switch (0xf & c->err_info->SenseInfo[2]) {
+ case 0: return IO_OK; /* no sense */
+ case 1: return IO_OK; /* recovered error */
+ default:
+ printk(KERN_WARNING "cciss%d: cmd 0x%02x "
+ "check condition, sense key = 0x%02x\n",
+ h->ctlr, c->Request.CDB[0],
+ c->err_info->SenseInfo[2]);
+ }
+ break;
+ default:
+ printk(KERN_WARNING "cciss%d: cmd 0x%02x"
+ "scsi status = 0x%02x\n", h->ctlr,
+ c->Request.CDB[0], c->err_info->ScsiStatus);
+ break;
+ }
+ return IO_ERROR;
+}
+
+static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c)
+{
+ int return_status = IO_OK;
+
+ if (c->err_info->CommandStatus == CMD_SUCCESS)
+ return IO_OK;
+
+ switch (c->err_info->CommandStatus) {
+ case CMD_TARGET_STATUS:
+ return_status = check_target_status(h, c);
+ break;
+ case CMD_DATA_UNDERRUN:
+ case CMD_DATA_OVERRUN:
+ /* expected for inquiry and report lun commands */
+ break;
+ case CMD_INVALID:
+ printk(KERN_WARNING "cciss: cmd 0x%02x is "
+ "reported invalid\n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_PROTOCOL_ERR:
+ printk(KERN_WARNING "cciss: cmd 0x%02x has "
+ "protocol error \n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_HARDWARE_ERR:
+ printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ " hardware error\n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_CONNECTION_LOST:
+ printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ "connection lost\n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_ABORTED:
+ printk(KERN_WARNING "cciss: cmd 0x%02x was "
+ "aborted\n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_ABORT_FAILED:
+ printk(KERN_WARNING "cciss: cmd 0x%02x reports "
+ "abort failed\n", c->Request.CDB[0]);
+ return_status = IO_ERROR;
+ break;
+ case CMD_UNSOLICITED_ABORT:
+ printk(KERN_WARNING
+ "cciss%d: unsolicited abort 0x%02x\n", h->ctlr,
+ c->Request.CDB[0]);
+ return_status = IO_NEEDS_RETRY;
+ break;
+ default:
+ printk(KERN_WARNING "cciss: cmd 0x%02x returned "
+ "unknown status %x\n", c->Request.CDB[0],
+ c->err_info->CommandStatus);
+ return_status = IO_ERROR;
+ }
+ return return_status;
+}
+
+static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
+ int attempt_retry)
+{
+ DECLARE_COMPLETION_ONSTACK(wait);
u64bit buff_dma_handle;
unsigned long flags;
- int return_status;
- DECLARE_COMPLETION_ONSTACK(wait);
+ int return_status = IO_OK;
- if ((c = cmd_alloc(h, 0)) == NULL)
- return -ENOMEM;
- return_status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
- log_unit, page_code, NULL, cmd_type);
- if (return_status != IO_OK) {
- cmd_free(h, c, 0);
- return return_status;
- }
- resend_cmd2:
+resend_cmd2:
c->waiting = &wait;
-
/* Put the request on the tail of the queue and send it */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
addQ(&h->reqQ, c);
h->Qdepth++;
start_io(h);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
wait_for_completion(&wait);
- if (c->err_info->CommandStatus != 0) { /* an error has occurred */
- switch (c->err_info->CommandStatus) {
- case CMD_TARGET_STATUS:
- printk(KERN_WARNING "cciss: cmd %p has "
- " completed with errors\n", c);
- if (c->err_info->ScsiStatus) {
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- c, c->err_info->ScsiStatus);
- }
+ if (c->err_info->CommandStatus == 0 || !attempt_retry)
+ goto command_done;
- break;
- case CMD_DATA_UNDERRUN:
- case CMD_DATA_OVERRUN:
- /* expected for inquire and report lun commands */
- break;
- case CMD_INVALID:
- printk(KERN_WARNING "cciss: Cmd %p is "
- "reported invalid\n", c);
- return_status = IO_ERROR;
- break;
- case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd %p has "
- "protocol error \n", c);
- return_status = IO_ERROR;
- break;
- case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd %p had "
- " hardware error\n", c);
- return_status = IO_ERROR;
- break;
- case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd %p had "
- "connection lost\n", c);
- return_status = IO_ERROR;
- break;
- case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd %p was "
- "aborted\n", c);
- return_status = IO_ERROR;
- break;
- case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd %p reports "
- "abort failed\n", c);
- return_status = IO_ERROR;
- break;
- case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING
- "cciss%d: unsolicited abort %p\n", ctlr, c);
- if (c->retry_count < MAX_CMD_RETRIES) {
- printk(KERN_WARNING
- "cciss%d: retrying %p\n", ctlr, c);
- c->retry_count++;
- /* erase the old error information */
- memset(c->err_info, 0,
- sizeof(ErrorInfo_struct));
- return_status = IO_OK;
- INIT_COMPLETION(wait);
- goto resend_cmd2;
- }
- return_status = IO_ERROR;
- break;
- default:
- printk(KERN_WARNING "cciss: cmd %p returned "
- "unknown status %x\n", c,
- c->err_info->CommandStatus);
- return_status = IO_ERROR;
- }
+ return_status = process_sendcmd_error(h, c);
+
+ if (return_status == IO_NEEDS_RETRY &&
+ c->retry_count < MAX_CMD_RETRIES) {
+ printk(KERN_WARNING "cciss%d: retrying 0x%02x\n", h->ctlr,
+ c->Request.CDB[0]);
+ c->retry_count++;
+ /* erase the old error information */
+ memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+ return_status = IO_OK;
+ INIT_COMPLETION(wait);
+ goto resend_cmd2;
}
+
+command_done:
/* unlock the buffers from DMA */
buff_dma_handle.val32.lower = c->SG[0].Addr.lower;
buff_dma_handle.val32.upper = c->SG[0].Addr.upper;
pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val,
c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
+ return return_status;
+}
+
+static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+ __u8 page_code, unsigned char scsi3addr[],
+ int cmd_type)
+{
+ ctlr_info_t *h = hba[ctlr];
+ CommandList_struct *c;
+ int return_status;
+
+ c = cmd_alloc(h, 0);
+ if (!c)
+ return -ENOMEM;
+ return_status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
+ scsi3addr, cmd_type);
+ if (return_status == IO_OK)
+ return_status = sendcmd_withirq_core(h, c, 1);
+
cmd_free(h, c, 0);
return return_status;
}
@@ -2150,15 +2428,17 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
{
int return_code;
unsigned long t;
+ unsigned char scsi3addr[8];
memset(inq_buff, 0, sizeof(InquiryData_struct));
+ log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
if (withirq)
return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
- inq_buff, sizeof(*inq_buff), 1,
- logvol, 0xC1, TYPE_CMD);
+ inq_buff, sizeof(*inq_buff),
+ 0xC1, scsi3addr, TYPE_CMD);
else
return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff,
- sizeof(*inq_buff), 1, logvol, 0xC1, NULL,
+ sizeof(*inq_buff), 0xC1, scsi3addr,
TYPE_CMD);
if (return_code == IO_OK) {
if (inq_buff->data_byte[8] == 0xFF) {
@@ -2199,6 +2479,7 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
{
ReadCapdata_struct *buf;
int return_code;
+ unsigned char scsi3addr[8];
buf = kzalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
if (!buf) {
@@ -2206,14 +2487,15 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
return;
}
+ log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
if (withirq)
return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
ctlr, buf, sizeof(ReadCapdata_struct),
- 1, logvol, 0, TYPE_CMD);
+ 0, scsi3addr, TYPE_CMD);
else
return_code = sendcmd(CCISS_READ_CAPACITY,
ctlr, buf, sizeof(ReadCapdata_struct),
- 1, logvol, 0, NULL, TYPE_CMD);
+ 0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be32_to_cpu(*(__be32 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
@@ -2233,6 +2515,7 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
{
ReadCapdata_struct_16 *buf;
int return_code;
+ unsigned char scsi3addr[8];
buf = kzalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
if (!buf) {
@@ -2240,15 +2523,16 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
return;
}
+ log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
if (withirq) {
return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
ctlr, buf, sizeof(ReadCapdata_struct_16),
- 1, logvol, 0, TYPE_CMD);
+ 0, scsi3addr, TYPE_CMD);
}
else {
return_code = sendcmd(CCISS_READ_CAPACITY_16,
ctlr, buf, sizeof(ReadCapdata_struct_16),
- 1, logvol, 0, NULL, TYPE_CMD);
+ 0, scsi3addr, TYPE_CMD);
}
if (return_code == IO_OK) {
*total_size = be64_to_cpu(*(__be64 *) buf->total_size);
@@ -2298,7 +2582,7 @@ static int cciss_revalidate(struct gendisk *disk)
cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size,
inq_buff, drv);
- blk_queue_hardsect_size(drv->queue, drv->block_size);
+ blk_queue_logical_block_size(drv->queue, drv->block_size);
set_capacity(disk, drv->nr_blocks);
kfree(inq_buff);
@@ -2328,86 +2612,21 @@ static unsigned long pollcomplete(int ctlr)
return 1;
}
-static int add_sendcmd_reject(__u8 cmd, int ctlr, unsigned long complete)
-{
- /* We get in here if sendcmd() is polling for completions
- and gets some command back that it wasn't expecting --
- something other than that which it just sent down.
- Ordinarily, that shouldn't happen, but it can happen when
- the scsi tape stuff gets into error handling mode, and
- starts using sendcmd() to try to abort commands and
- reset tape drives. In that case, sendcmd may pick up
- completions of commands that were sent to logical drives
- through the block i/o system, or cciss ioctls completing, etc.
- In that case, we need to save those completions for later
- processing by the interrupt handler.
- */
-
-#ifdef CONFIG_CISS_SCSI_TAPE
- struct sendcmd_reject_list *srl = &hba[ctlr]->scsi_rejects;
-
- /* If it's not the scsi tape stuff doing error handling, (abort */
- /* or reset) then we don't expect anything weird. */
- if (cmd != CCISS_RESET_MSG && cmd != CCISS_ABORT_MSG) {
-#endif
- printk(KERN_WARNING "cciss cciss%d: SendCmd "
- "Invalid command list address returned! (%lx)\n",
- ctlr, complete);
- /* not much we can do. */
-#ifdef CONFIG_CISS_SCSI_TAPE
- return 1;
- }
-
- /* We've sent down an abort or reset, but something else
- has completed */
- if (srl->ncompletions >= (hba[ctlr]->nr_cmds + 2)) {
- /* Uh oh. No room to save it for later... */
- printk(KERN_WARNING "cciss%d: Sendcmd: Invalid command addr, "
- "reject list overflow, command lost!\n", ctlr);
- return 1;
- }
- /* Save it for later */
- srl->complete[srl->ncompletions] = complete;
- srl->ncompletions++;
-#endif
- return 0;
-}
-
-/*
- * Send a command to the controller, and wait for it to complete.
- * Only used at init time.
+/* Send command c to controller h and poll for it to complete.
+ * Turns interrupts off on the board. Used at driver init time
+ * and during SCSI error recovery.
*/
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, /* 0: address the controller,
- 1: address logical volume log_unit,
- 2: periph device address is scsi3addr */
- unsigned int log_unit,
- __u8 page_code, unsigned char *scsi3addr, int cmd_type)
+static int sendcmd_core(ctlr_info_t *h, CommandList_struct *c)
{
- CommandList_struct *c;
int i;
unsigned long complete;
- ctlr_info_t *info_p = hba[ctlr];
+ int status = IO_ERROR;
u64bit buff_dma_handle;
- int status, done = 0;
- if ((c = cmd_alloc(info_p, 1)) == NULL) {
- printk(KERN_WARNING "cciss: unable to get memory");
- return IO_ERROR;
- }
- status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
- log_unit, page_code, scsi3addr, cmd_type);
- if (status != IO_OK) {
- cmd_free(info_p, c, 1);
- return status;
- }
- resend_cmd1:
- /*
- * Disable interrupt
- */
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: turning intr off\n");
-#endif /* CCISS_DEBUG */
- info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF);
+resend_cmd1:
+
+ /* Disable interrupt on the board. */
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
/* Make sure there is room in the command FIFO */
/* Actually it should be completely empty at this time */
@@ -2415,21 +2634,15 @@ static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, unsigned int use
/* tape side of the driver. */
for (i = 200000; i > 0; i--) {
/* if fifo isn't full go */
- if (!(info_p->access.fifo_full(info_p))) {
-
+ if (!(h->access.fifo_full(h)))
break;
- }
udelay(10);
printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full,"
- " waiting!\n", ctlr);
+ " waiting!\n", h->ctlr);
}
- /*
- * Send the cmd
- */
- info_p->access.submit_command(info_p, c);
- done = 0;
+ h->access.submit_command(h, c); /* Send the cmd */
do {
- complete = pollcomplete(ctlr);
+ complete = pollcomplete(h->ctlr);
#ifdef CCISS_DEBUG
printk(KERN_DEBUG "cciss: command completed\n");
@@ -2438,97 +2651,102 @@ static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, unsigned int use
if (complete == 1) {
printk(KERN_WARNING
"cciss cciss%d: SendCmd Timeout out, "
- "No command list address returned!\n", ctlr);
+ "No command list address returned!\n", h->ctlr);
status = IO_ERROR;
- done = 1;
break;
}
- /* This will need to change for direct lookup completions */
- if ((complete & CISS_ERROR_BIT)
- && (complete & ~CISS_ERROR_BIT) == c->busaddr) {
- /* if data overrun or underun on Report command
- ignore it
- */
- if (((c->Request.CDB[0] == CISS_REPORT_LOG) ||
- (c->Request.CDB[0] == CISS_REPORT_PHYS) ||
- (c->Request.CDB[0] == CISS_INQUIRY)) &&
- ((c->err_info->CommandStatus ==
- CMD_DATA_OVERRUN) ||
- (c->err_info->CommandStatus == CMD_DATA_UNDERRUN)
- )) {
- complete = c->busaddr;
- } else {
- if (c->err_info->CommandStatus ==
- CMD_UNSOLICITED_ABORT) {
- printk(KERN_WARNING "cciss%d: "
- "unsolicited abort %p\n",
- ctlr, c);
- if (c->retry_count < MAX_CMD_RETRIES) {
- printk(KERN_WARNING
- "cciss%d: retrying %p\n",
- ctlr, c);
- c->retry_count++;
- /* erase the old error */
- /* information */
- memset(c->err_info, 0,
- sizeof
- (ErrorInfo_struct));
- goto resend_cmd1;
- } else {
- printk(KERN_WARNING
- "cciss%d: retried %p too "
- "many times\n", ctlr, c);
- status = IO_ERROR;
- goto cleanup1;
- }
- } else if (c->err_info->CommandStatus ==
- CMD_UNABORTABLE) {
- printk(KERN_WARNING
- "cciss%d: command could not be aborted.\n",
- ctlr);
- status = IO_ERROR;
- goto cleanup1;
- }
- printk(KERN_WARNING "ciss ciss%d: sendcmd"
- " Error %x \n", ctlr,
- c->err_info->CommandStatus);
- printk(KERN_WARNING "ciss ciss%d: sendcmd"
- " offensive info\n"
- " size %x\n num %x value %x\n",
- ctlr,
- c->err_info->MoreErrInfo.Invalid_Cmd.
- offense_size,
- c->err_info->MoreErrInfo.Invalid_Cmd.
- offense_num,
- c->err_info->MoreErrInfo.Invalid_Cmd.
- offense_value);
- status = IO_ERROR;
- goto cleanup1;
- }
+ /* Make sure it's the command we're expecting. */
+ if ((complete & ~CISS_ERROR_BIT) != c->busaddr) {
+ printk(KERN_WARNING "cciss%d: Unexpected command "
+ "completion.\n", h->ctlr);
+ continue;
+ }
+
+ /* It is our command. If no error, we're done. */
+ if (!(complete & CISS_ERROR_BIT)) {
+ status = IO_OK;
+ break;
}
- /* This will need changing for direct lookup completions */
- if (complete != c->busaddr) {
- if (add_sendcmd_reject(cmd, ctlr, complete) != 0) {
- BUG(); /* we are pretty much hosed if we get here. */
+
+ /* There is an error... */
+
+ /* if data overrun or underun on Report command ignore it */
+ if (((c->Request.CDB[0] == CISS_REPORT_LOG) ||
+ (c->Request.CDB[0] == CISS_REPORT_PHYS) ||
+ (c->Request.CDB[0] == CISS_INQUIRY)) &&
+ ((c->err_info->CommandStatus == CMD_DATA_OVERRUN) ||
+ (c->err_info->CommandStatus == CMD_DATA_UNDERRUN))) {
+ complete = c->busaddr;
+ status = IO_OK;
+ break;
+ }
+
+ if (c->err_info->CommandStatus == CMD_UNSOLICITED_ABORT) {
+ printk(KERN_WARNING "cciss%d: unsolicited abort %p\n",
+ h->ctlr, c);
+ if (c->retry_count < MAX_CMD_RETRIES) {
+ printk(KERN_WARNING "cciss%d: retrying %p\n",
+ h->ctlr, c);
+ c->retry_count++;
+ /* erase the old error information */
+ memset(c->err_info, 0, sizeof(c->err_info));
+ goto resend_cmd1;
}
- continue;
- } else
- done = 1;
- } while (!done);
+ printk(KERN_WARNING "cciss%d: retried %p too many "
+ "times\n", h->ctlr, c);
+ status = IO_ERROR;
+ break;
+ }
+
+ if (c->err_info->CommandStatus == CMD_UNABORTABLE) {
+ printk(KERN_WARNING "cciss%d: command could not be "
+ "aborted.\n", h->ctlr);
+ status = IO_ERROR;
+ break;
+ }
+
+ if (c->err_info->CommandStatus == CMD_TARGET_STATUS) {
+ status = check_target_status(h, c);
+ break;
+ }
+
+ printk(KERN_WARNING "cciss%d: sendcmd error\n", h->ctlr);
+ printk(KERN_WARNING "cmd = 0x%02x, CommandStatus = 0x%02x\n",
+ c->Request.CDB[0], c->err_info->CommandStatus);
+ status = IO_ERROR;
+ break;
+
+ } while (1);
- cleanup1:
/* unlock the data buffer from DMA */
buff_dma_handle.val32.lower = c->SG[0].Addr.lower;
buff_dma_handle.val32.upper = c->SG[0].Addr.upper;
- pci_unmap_single(info_p->pdev, (dma_addr_t) buff_dma_handle.val,
+ pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val,
c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
-#ifdef CONFIG_CISS_SCSI_TAPE
- /* if we saved some commands for later, process them now. */
- if (info_p->scsi_rejects.ncompletions > 0)
- do_cciss_intr(0, info_p);
-#endif
- cmd_free(info_p, c, 1);
+ return status;
+}
+
+/*
+ * Send a command to the controller, and wait for it to complete.
+ * Used at init time, and during SCSI error recovery.
+ */
+static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
+ __u8 page_code, unsigned char *scsi3addr, int cmd_type)
+{
+ CommandList_struct *c;
+ int status;
+
+ c = cmd_alloc(hba[ctlr], 1);
+ if (!c) {
+ printk(KERN_WARNING "cciss: unable to get memory");
+ return IO_ERROR;
+ }
+ status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
+ scsi3addr, cmd_type);
+ if (status == IO_OK)
+ status = sendcmd_core(hba[ctlr], c);
+ cmd_free(hba[ctlr], c, 1);
return status;
}
@@ -2926,44 +3144,18 @@ startio:
static inline unsigned long get_next_completion(ctlr_info_t *h)
{
-#ifdef CONFIG_CISS_SCSI_TAPE
- /* Any rejects from sendcmd() lying around? Process them first */
- if (h->scsi_rejects.ncompletions == 0)
- return h->access.command_completed(h);
- else {
- struct sendcmd_reject_list *srl;
- int n;
- srl = &h->scsi_rejects;
- n = --srl->ncompletions;
- /* printk("cciss%d: processing saved reject\n", h->ctlr); */
- printk("p");
- return srl->complete[n];
- }
-#else
return h->access.command_completed(h);
-#endif
}
static inline int interrupt_pending(ctlr_info_t *h)
{
-#ifdef CONFIG_CISS_SCSI_TAPE
- return (h->access.intr_pending(h)
- || (h->scsi_rejects.ncompletions > 0));
-#else
return h->access.intr_pending(h);
-#endif
}
static inline long interrupt_not_for_us(ctlr_info_t *h)
{
-#ifdef CONFIG_CISS_SCSI_TAPE
- return (((h->access.intr_pending(h) == 0) ||
- (h->interrupts_enabled == 0))
- && (h->scsi_rejects.ncompletions == 0));
-#else
return (((h->access.intr_pending(h) == 0) ||
(h->interrupts_enabled == 0)));
-#endif
}
static irqreturn_t do_cciss_intr(int irq, void *dev_id)
@@ -3718,12 +3910,15 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
INIT_HLIST_HEAD(&hba[i]->reqQ);
if (cciss_pci_init(hba[i], pdev) != 0)
- goto clean1;
+ goto clean0;
sprintf(hba[i]->devname, "cciss%d", i);
hba[i]->ctlr = i;
hba[i]->pdev = pdev;
+ if (cciss_create_hba_sysfs_entry(hba[i]))
+ goto clean0;
+
/* configure PCI DMA stuff */
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
dac = 1;
@@ -3782,15 +3977,6 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
printk(KERN_ERR "cciss: out of memory");
goto clean4;
}
-#ifdef CONFIG_CISS_SCSI_TAPE
- hba[i]->scsi_rejects.complete =
- kmalloc(sizeof(hba[i]->scsi_rejects.complete[0]) *
- (hba[i]->nr_cmds + 5), GFP_KERNEL);
- if (hba[i]->scsi_rejects.complete == NULL) {
- printk(KERN_ERR "cciss: out of memory");
- goto clean4;
- }
-#endif
spin_lock_init(&hba[i]->lock);
/* Initialize the pdev driver private data.
@@ -3823,7 +4009,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
}
return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
- sizeof(InquiryData_struct), 0, 0 , 0, TYPE_CMD);
+ sizeof(InquiryData_struct), 0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK) {
hba[i]->firm_ver[0] = inq_buff->data_byte[32];
hba[i]->firm_ver[1] = inq_buff->data_byte[33];
@@ -3850,9 +4036,6 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
clean4:
kfree(inq_buff);
-#ifdef CONFIG_CISS_SCSI_TAPE
- kfree(hba[i]->scsi_rejects.complete);
-#endif
kfree(hba[i]->cmd_pool_bits);
if (hba[i]->cmd_pool)
pci_free_consistent(hba[i]->pdev,
@@ -3867,6 +4050,8 @@ clean4:
clean2:
unregister_blkdev(hba[i]->major, hba[i]->devname);
clean1:
+ cciss_destroy_hba_sysfs_entry(hba[i]);
+clean0:
hba[i]->busy_initializing = 0;
/* cleanup any queues that may have been initialized */
for (j=0; j <= hba[i]->highest_lun; j++){
@@ -3902,8 +4087,8 @@ static void cciss_shutdown(struct pci_dev *pdev)
/* sendcmd will turn off interrupt, and send the flush...
* To write all data in the battery backed cache to disks */
memset(flush_buf, 0, 4);
- return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL,
- TYPE_CMD);
+ return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0,
+ CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK) {
printk(KERN_INFO "Completed flushing cache on controller %d\n", i);
} else {
@@ -3968,15 +4153,13 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
kfree(hba[i]->cmd_pool_bits);
-#ifdef CONFIG_CISS_SCSI_TAPE
- kfree(hba[i]->scsi_rejects.complete);
-#endif
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
+ cciss_destroy_hba_sysfs_entry(hba[i]);
free_hba(i);
}
@@ -3994,6 +4177,8 @@ static struct pci_driver cciss_pci_driver = {
*/
static int __init cciss_init(void)
{
+ int err;
+
/*
* The hardware requires that commands are aligned on a 64-bit
* boundary. Given that we use pci_alloc_consistent() to allocate an
@@ -4003,8 +4188,20 @@ static int __init cciss_init(void)
printk(KERN_INFO DRIVER_NAME "\n");
+ err = bus_register(&cciss_bus_type);
+ if (err)
+ return err;
+
/* Register for our PCI devices */
- return pci_register_driver(&cciss_pci_driver);
+ err = pci_register_driver(&cciss_pci_driver);
+ if (err)
+ goto err_bus_register;
+
+ return 0;
+
+err_bus_register:
+ bus_unregister(&cciss_bus_type);
+ return err;
}
static void __exit cciss_cleanup(void)
@@ -4021,6 +4218,7 @@ static void __exit cciss_cleanup(void)
}
}
remove_proc_entry("driver/cciss", NULL);
+ bus_unregister(&cciss_bus_type);
}
static void fail_all_cmds(unsigned long ctlr)
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 703e08038fb9..06a5db25b298 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -11,6 +11,11 @@
#define IO_OK 0
#define IO_ERROR 1
+#define IO_NEEDS_RETRY 3
+
+#define VENDOR_LEN 8
+#define MODEL_LEN 16
+#define REV_LEN 4
struct ctlr_info;
typedef struct ctlr_info ctlr_info_t;
@@ -34,23 +39,20 @@ typedef struct _drive_info_struct
int cylinders;
int raid_level; /* set to -1 to indicate that
* the drive is not in use/configured
- */
- int busy_configuring; /*This is set when the drive is being removed
- *to prevent it from being opened or it's queue
- *from being started.
- */
- __u8 serial_no[16]; /* from inquiry page 0x83, */
- /* not necc. null terminated. */
+ */
+ int busy_configuring; /* This is set when a drive is being removed
+ * to prevent it from being opened or it's
+ * queue from being started.
+ */
+ struct device dev;
+ __u8 serial_no[16]; /* from inquiry page 0x83,
+ * not necc. null terminated.
+ */
+ char vendor[VENDOR_LEN + 1]; /* SCSI vendor string */
+ char model[MODEL_LEN + 1]; /* SCSI model string */
+ char rev[REV_LEN + 1]; /* SCSI revision string */
} drive_info_struct;
-#ifdef CONFIG_CISS_SCSI_TAPE
-
-struct sendcmd_reject_list {
- int ncompletions;
- unsigned long *complete; /* array of NR_CMDS tags */
-};
-
-#endif
struct ctlr_info
{
int ctlr;
@@ -118,11 +120,11 @@ struct ctlr_info
void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
/* list of block side commands the scsi error handling sucked up */
/* and saved for later processing */
- struct sendcmd_reject_list scsi_rejects;
#endif
unsigned char alive;
struct completion *rescan_wait;
struct task_struct *cciss_scan_thread;
+ struct device dev;
};
/* Defining the diffent access_menthods */
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index 40b1b92dae7f..cd665b00c7c5 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -217,6 +217,8 @@ typedef union _LUNAddr_struct {
LogDevAddr_struct LogDev;
} LUNAddr_struct;
+#define CTLR_LUNID "\0\0\0\0\0\0\0\0"
+
typedef struct _CommandListHeader_struct {
BYTE ReplyQueue;
BYTE SGList;
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index a3fd87b41444..3315268b4ec7 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -44,20 +44,13 @@
#define CCISS_ABORT_MSG 0x00
#define CCISS_RESET_MSG 0x01
-/* some prototypes... */
-static int sendcmd(
- __u8 cmd,
- int ctlr,
- void *buff,
- size_t size,
- unsigned int use_unit_num, /* 0: address the controller,
- 1: address logical volume log_unit,
- 2: address is in scsi3addr */
- unsigned int log_unit,
- __u8 page_code,
- unsigned char *scsi3addr,
+static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+ size_t size,
+ __u8 page_code, unsigned char *scsi3addr,
int cmd_type);
+static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool);
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool);
static int cciss_scsi_proc_info(
struct Scsi_Host *sh,
@@ -1575,6 +1568,75 @@ cciss_seq_tape_report(struct seq_file *seq, int ctlr)
CPQ_TAPE_UNLOCK(ctlr, flags);
}
+static int wait_for_device_to_become_ready(ctlr_info_t *h,
+ unsigned char lunaddr[])
+{
+ int rc;
+ int count = 0;
+ int waittime = HZ;
+ CommandList_struct *c;
+
+ c = cmd_alloc(h, 1);
+ if (!c) {
+ printk(KERN_WARNING "cciss%d: out of memory in "
+ "wait_for_device_to_become_ready.\n", h->ctlr);
+ return IO_ERROR;
+ }
+
+ /* Send test unit ready until device ready, or give up. */
+ while (count < 20) {
+
+ /* Wait for a bit. do this first, because if we send
+ * the TUR right away, the reset will just abort it.
+ */
+ schedule_timeout_uninterruptible(waittime);
+ count++;
+
+ /* Increase wait time with each try, up to a point. */
+ if (waittime < (HZ * 30))
+ waittime = waittime * 2;
+
+ /* Send the Test Unit Ready */
+ rc = fill_cmd(c, TEST_UNIT_READY, h->ctlr, NULL, 0, 0,
+ lunaddr, TYPE_CMD);
+ if (rc == 0)
+ rc = sendcmd_withirq_core(h, c, 0);
+
+ (void) process_sendcmd_error(h, c);
+
+ if (rc != 0)
+ goto retry_tur;
+
+ if (c->err_info->CommandStatus == CMD_SUCCESS)
+ break;
+
+ if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+ c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION) {
+ if (c->err_info->SenseInfo[2] == NO_SENSE)
+ break;
+ if (c->err_info->SenseInfo[2] == UNIT_ATTENTION) {
+ unsigned char asc;
+ asc = c->err_info->SenseInfo[12];
+ check_for_unit_attention(h, c);
+ if (asc == POWER_OR_RESET)
+ break;
+ }
+ }
+retry_tur:
+ printk(KERN_WARNING "cciss%d: Waiting %d secs "
+ "for device to become ready.\n",
+ h->ctlr, waittime / HZ);
+ rc = 1; /* device not ready. */
+ }
+
+ if (rc)
+ printk("cciss%d: giving up on device.\n", h->ctlr);
+ else
+ printk(KERN_WARNING "cciss%d: device is ready.\n", h->ctlr);
+
+ cmd_free(h, c, 1);
+ return rc;
+}
/* Need at least one of these error handlers to keep ../scsi/hosts.c from
* complaining. Doing a host- or bus-reset can't do anything good here.
@@ -1591,6 +1653,7 @@ static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
{
int rc;
CommandList_struct *cmd_in_trouble;
+ unsigned char lunaddr[8];
ctlr_info_t **c;
int ctlr;
@@ -1600,19 +1663,15 @@ static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
return FAILED;
ctlr = (*c)->ctlr;
printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr);
-
/* find the command that's giving us trouble */
cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble;
- if (cmd_in_trouble == NULL) { /* paranoia */
+ if (cmd_in_trouble == NULL) /* paranoia */
return FAILED;
- }
+ memcpy(lunaddr, &cmd_in_trouble->Header.LUN.LunAddrBytes[0], 8);
/* send a reset to the SCSI LUN which the command was sent to */
- rc = sendcmd(CCISS_RESET_MSG, ctlr, NULL, 0, 2, 0, 0,
- (unsigned char *) &cmd_in_trouble->Header.LUN.LunAddrBytes[0],
+ rc = sendcmd_withirq(CCISS_RESET_MSG, ctlr, NULL, 0, 0, lunaddr,
TYPE_MSG);
- /* sendcmd turned off interrupts on the board, turn 'em back on. */
- (*c)->access.set_intr_mask(*c, CCISS_INTR_ON);
- if (rc == 0)
+ if (rc == 0 && wait_for_device_to_become_ready(*c, lunaddr) == 0)
return SUCCESS;
printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr);
return FAILED;
@@ -1622,6 +1681,7 @@ static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
{
int rc;
CommandList_struct *cmd_to_abort;
+ unsigned char lunaddr[8];
ctlr_info_t **c;
int ctlr;
@@ -1636,12 +1696,9 @@ static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble;
if (cmd_to_abort == NULL) /* paranoia */
return FAILED;
- rc = sendcmd(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag,
- 0, 2, 0, 0,
- (unsigned char *) &cmd_to_abort->Header.LUN.LunAddrBytes[0],
- TYPE_MSG);
- /* sendcmd turned off interrupts on the board, turn 'em back on. */
- (*c)->access.set_intr_mask(*c, CCISS_INTR_ON);
+ memcpy(lunaddr, &cmd_to_abort->Header.LUN.LunAddrBytes[0], 8);
+ rc = sendcmd_withirq(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag,
+ 0, 0, lunaddr, TYPE_MSG);
if (rc == 0)
return SUCCESS;
return FAILED;
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index a02dcfc00f13..44fa2018f6b0 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -474,7 +474,7 @@ static int __init cpqarray_register_ctlr( int i, struct pci_dev *pdev)
disk->fops = &ida_fops;
if (j && !drv->nr_blks)
continue;
- blk_queue_hardsect_size(hba[i]->queue, drv->blk_size);
+ blk_queue_logical_block_size(hba[i]->queue, drv->blk_size);
set_capacity(disk, drv->nr_blks);
disk->queue = hba[i]->queue;
disk->private_data = drv;
@@ -1546,7 +1546,7 @@ static int revalidate_allvol(ctlr_info_t *host)
drv_info_t *drv = &host->drv[i];
if (i && !drv->nr_blks)
continue;
- blk_queue_hardsect_size(host->queue, drv->blk_size);
+ blk_queue_logical_block_size(host->queue, drv->blk_size);
set_capacity(disk, drv->nr_blks);
disk->queue = host->queue;
disk->private_data = drv;
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 90877fee0ee0..862b40c90181 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -4148,6 +4148,24 @@ static void floppy_device_release(struct device *dev)
{
}
+static int floppy_resume(struct platform_device *dev)
+{
+ int fdc;
+
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1)
+ user_reset_fdc(-1, FD_RESET_ALWAYS, 0);
+
+ return 0;
+}
+
+static struct platform_driver floppy_driver = {
+ .resume = floppy_resume,
+ .driver = {
+ .name = "floppy",
+ },
+};
+
static struct platform_device floppy_device[N_DRIVE];
static struct kobject *floppy_find(dev_t dev, int *part, void *data)
@@ -4196,10 +4214,14 @@ static int __init floppy_init(void)
if (err)
goto out_put_disk;
+ err = platform_driver_register(&floppy_driver);
+ if (err)
+ goto out_unreg_blkdev;
+
floppy_queue = blk_init_queue(do_fd_request, &floppy_lock);
if (!floppy_queue) {
err = -ENOMEM;
- goto out_unreg_blkdev;
+ goto out_unreg_driver;
}
blk_queue_max_sectors(floppy_queue, 64);
@@ -4346,6 +4368,8 @@ out_flush_work:
out_unreg_region:
blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
blk_cleanup_queue(floppy_queue);
+out_unreg_driver:
+ platform_driver_unregister(&floppy_driver);
out_unreg_blkdev:
unregister_blkdev(FLOPPY_MAJOR, "fd");
out_put_disk:
@@ -4566,6 +4590,7 @@ static void __exit floppy_module_exit(void)
blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
unregister_blkdev(FLOPPY_MAJOR, "fd");
+ platform_driver_unregister(&floppy_driver);
for (drive = 0; drive < N_DRIVE; drive++) {
del_timer_sync(&motor_off_timer[drive]);
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 961de56d00a9..f65b3f369eb0 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -724,7 +724,7 @@ static int __init hd_init(void)
blk_queue_max_sectors(hd_queue, 255);
init_timer(&device_timer);
device_timer.function = hd_times_out;
- blk_queue_hardsect_size(hd_queue, 512);
+ blk_queue_logical_block_size(hd_queue, 512);
if (!NR_HD) {
/*
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index c0cd0a03f698..60de5a01e71e 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -996,7 +996,7 @@ static int mg_probe(struct platform_device *plat_dev)
goto probe_err_6;
}
blk_queue_max_sectors(host->breq, MG_MAX_SECTS);
- blk_queue_hardsect_size(host->breq, MG_SECTOR_SIZE);
+ blk_queue_logical_block_size(host->breq, MG_SECTOR_SIZE);
init_timer(&host->timer);
host->timer.function = mg_times_out;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index dc7a8c352da2..d57f11759480 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -991,13 +991,15 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
*/
static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q)
{
- if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) {
+ if ((pd->settings.size << 9) / CD_FRAMESIZE
+ <= queue_max_phys_segments(q)) {
/*
* The cdrom device can handle one segment/frame
*/
clear_bit(PACKET_MERGE_SEGS, &pd->flags);
return 0;
- } else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) {
+ } else if ((pd->settings.size << 9) / PAGE_SIZE
+ <= queue_max_phys_segments(q)) {
/*
* We can handle this case at the expense of some extra memory
* copies during write operations
@@ -2657,7 +2659,7 @@ static void pkt_init_queue(struct pktcdvd_device *pd)
struct request_queue *q = pd->disk->queue;
blk_queue_make_request(q, pkt_make_request);
- blk_queue_hardsect_size(q, CD_FRAMESIZE);
+ blk_queue_logical_block_size(q, CD_FRAMESIZE);
blk_queue_max_sectors(q, PACKET_MAX_SECTORS);
blk_queue_merge_bvec(q, pkt_merge_bvec);
q->queuedata = pd;
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 338cee4cc0ba..aaeeb544228a 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -477,7 +477,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
blk_queue_max_sectors(queue, dev->bounce_size >> 9);
blk_queue_segment_boundary(queue, -1UL);
blk_queue_dma_alignment(queue, dev->blk_size-1);
- blk_queue_hardsect_size(queue, dev->blk_size);
+ blk_queue_logical_block_size(queue, dev->blk_size);
blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
ps3disk_prepare_flush);
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 178f459a50ed..cc54473b8e77 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -360,8 +360,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
struct ub_scsi_cmd *cmd, struct ub_request *urq);
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, unsigned int status,
- unsigned int cmd_len);
+static void ub_end_rq(struct request *rq, unsigned int status);
static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
struct ub_request *urq, struct ub_scsi_cmd *cmd);
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
@@ -644,13 +643,13 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
if (atomic_read(&sc->poison)) {
blk_start_request(rq);
- ub_end_rq(rq, DID_NO_CONNECT << 16, blk_rq_bytes(rq));
+ ub_end_rq(rq, DID_NO_CONNECT << 16);
return 0;
}
if (lun->changed && !blk_pc_request(rq)) {
blk_start_request(rq);
- ub_end_rq(rq, SAM_STAT_CHECK_CONDITION, blk_rq_bytes(rq));
+ ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
}
@@ -702,7 +701,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
drop:
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, DID_ERROR << 16, blk_rq_bytes(rq));
+ ub_end_rq(rq, DID_ERROR << 16);
return 0;
}
@@ -723,7 +722,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
/*
* build the command
*
- * The call to blk_queue_hardsect_size() guarantees that request
+ * The call to blk_queue_logical_block_size() guarantees that request
* is aligned, but it is given in terms of 512 byte units, always.
*/
block = blk_rq_pos(rq) >> lun->capacity.bshift;
@@ -777,14 +776,15 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
struct ub_request *urq = cmd->back;
struct request *rq;
unsigned int scsi_status;
- unsigned int cmd_len;
rq = urq->rq;
if (cmd->error == 0) {
if (blk_pc_request(rq)) {
- if (cmd->act_len < blk_rq_bytes(rq))
- rq->resid_len = blk_rq_bytes(rq) - cmd->act_len;
+ if (cmd->act_len >= rq->resid_len)
+ rq->resid_len = 0;
+ else
+ rq->resid_len -= cmd->act_len;
scsi_status = 0;
} else {
if (cmd->act_len != cmd->len) {
@@ -816,17 +816,14 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
urq->rq = NULL;
- cmd_len = cmd->len;
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, scsi_status, cmd_len);
+ ub_end_rq(rq, scsi_status);
blk_start_queue(lun->disk->queue);
}
-static void ub_end_rq(struct request *rq, unsigned int scsi_status,
- unsigned int cmd_len)
+static void ub_end_rq(struct request *rq, unsigned int scsi_status)
{
int error;
- long rqlen;
if (scsi_status == 0) {
error = 0;
@@ -834,12 +831,7 @@ static void ub_end_rq(struct request *rq, unsigned int scsi_status,
error = -EIO;
rq->errors = scsi_status;
}
- rqlen = blk_rq_bytes(rq); /* Oddly enough, this is the residue. */
- if (__blk_end_request(rq, error, cmd_len)) {
- printk(KERN_WARNING DRV_NAME
- ": __blk_end_request blew, %s-cmd total %u rqlen %ld\n",
- blk_pc_request(rq)? "pc": "fs", cmd_len, rqlen);
- }
+ __blk_end_request_all(rq, error);
}
static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
@@ -1757,7 +1749,7 @@ static int ub_bd_revalidate(struct gendisk *disk)
ub_revalidate(lun->udev, lun);
/* XXX Support sector size switching like in sr.c */
- blk_queue_hardsect_size(disk->queue, lun->capacity.bsize);
+ blk_queue_logical_block_size(disk->queue, lun->capacity.bsize);
set_capacity(disk, lun->capacity.nsec);
// set_disk_ro(sdkp->disk, lun->readonly);
@@ -2332,7 +2324,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
blk_queue_max_phys_segments(q, UB_MAX_REQ_SG);
blk_queue_segment_boundary(q, 0xffffffff); /* Dubious. */
blk_queue_max_sectors(q, UB_MAX_SECTORS);
- blk_queue_hardsect_size(q, lun->capacity.bsize);
+ blk_queue_logical_block_size(q, lun->capacity.bsize);
lun->disk = disk;
q->queuedata = lun;
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 29a9daf48621..43db3ea15b54 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -37,6 +37,7 @@ struct virtblk_req
struct list_head list;
struct request *req;
struct virtio_blk_outhdr out_hdr;
+ struct virtio_scsi_inhdr in_hdr;
u8 status;
};
@@ -50,6 +51,7 @@ static void blk_done(struct virtqueue *vq)
spin_lock_irqsave(&vblk->lock, flags);
while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) {
int error;
+
switch (vbr->status) {
case VIRTIO_BLK_S_OK:
error = 0;
@@ -62,6 +64,12 @@ static void blk_done(struct virtqueue *vq)
break;
}
+ if (blk_pc_request(vbr->req)) {
+ vbr->req->resid_len = vbr->in_hdr.residual;
+ vbr->req->sense_len = vbr->in_hdr.sense_len;
+ vbr->req->errors = vbr->in_hdr.errors;
+ }
+
__blk_end_request_all(vbr->req, error);
list_del(&vbr->list);
mempool_free(vbr, vblk->pool);
@@ -74,7 +82,7 @@ static void blk_done(struct virtqueue *vq)
static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
struct request *req)
{
- unsigned long num, out, in;
+ unsigned long num, out = 0, in = 0;
struct virtblk_req *vbr;
vbr = mempool_alloc(vblk->pool, GFP_ATOMIC);
@@ -99,18 +107,36 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
if (blk_barrier_rq(vbr->req))
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
- sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
- num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
- sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status));
+ sg_set_buf(&vblk->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
- if (rq_data_dir(vbr->req) == WRITE) {
- vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
- out = 1 + num;
- in = 1;
- } else {
- vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
- out = 1;
- in = 1 + num;
+ /*
+ * If this is a packet command we need a couple of additional headers.
+ * Behind the normal outhdr we put a segment with the scsi command
+ * block, and before the normal inhdr we put the sense data and the
+ * inhdr with additional status information before the normal inhdr.
+ */
+ if (blk_pc_request(vbr->req))
+ sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len);
+
+ num = blk_rq_map_sg(q, vbr->req, vblk->sg + out);
+
+ if (blk_pc_request(vbr->req)) {
+ sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, 96);
+ sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr,
+ sizeof(vbr->in_hdr));
+ }
+
+ sg_set_buf(&vblk->sg[num + out + in++], &vbr->status,
+ sizeof(vbr->status));
+
+ if (num) {
+ if (rq_data_dir(vbr->req) == WRITE) {
+ vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
+ out += num;
+ } else {
+ vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
+ in += num;
+ }
}
if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) {
@@ -124,12 +150,11 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
static void do_virtblk_request(struct request_queue *q)
{
- struct virtio_blk *vblk = NULL;
+ struct virtio_blk *vblk = q->queuedata;
struct request *req;
unsigned int issued = 0;
while ((req = blk_peek_request(q)) != NULL) {
- vblk = req->rq_disk->private_data;
BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
/* If this request fails, stop queue and wait for something to
@@ -146,12 +171,51 @@ static void do_virtblk_request(struct request_queue *q)
vblk->vq->vq_ops->kick(vblk->vq);
}
+/* return ATA identify data
+ */
+static int virtblk_identify(struct gendisk *disk, void *argp)
+{
+ struct virtio_blk *vblk = disk->private_data;
+ void *opaque;
+ int err = -ENOMEM;
+
+ opaque = kmalloc(VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
+ if (!opaque)
+ goto out;
+
+ err = virtio_config_buf(vblk->vdev, VIRTIO_BLK_F_IDENTIFY,
+ offsetof(struct virtio_blk_config, identify), opaque,
+ VIRTIO_BLK_ID_BYTES);
+
+ if (err)
+ goto out_kfree;
+
+ if (copy_to_user(argp, opaque, VIRTIO_BLK_ID_BYTES))
+ err = -EFAULT;
+
+out_kfree:
+ kfree(opaque);
+out:
+ return err;
+}
+
static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long data)
{
- return scsi_cmd_ioctl(bdev->bd_disk->queue,
- bdev->bd_disk, mode, cmd,
- (void __user *)data);
+ struct gendisk *disk = bdev->bd_disk;
+ struct virtio_blk *vblk = disk->private_data;
+ void __user *argp = (void __user *)data;
+
+ if (cmd == HDIO_GET_IDENTITY)
+ return virtblk_identify(disk, argp);
+
+ /*
+ * Only allow the generic SCSI ioctls if the host can support it.
+ */
+ if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI))
+ return -ENOIOCTLCMD;
+
+ return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp);
}
/* We provide getgeo only to please some old bootloader/partitioning tools */
@@ -190,7 +254,7 @@ static int index_to_minor(int index)
return index << PART_BITS;
}
-static int virtblk_probe(struct virtio_device *vdev)
+static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
int err;
@@ -224,7 +288,7 @@ static int virtblk_probe(struct virtio_device *vdev)
sg_init_table(vblk->sg, vblk->sg_elems);
/* We expect one virtqueue, for output. */
- vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
+ vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
if (IS_ERR(vblk->vq)) {
err = PTR_ERR(vblk->vq);
goto out_free_vblk;
@@ -249,6 +313,7 @@ static int virtblk_probe(struct virtio_device *vdev)
goto out_put_disk;
}
+ vblk->disk->queue->queuedata = vblk;
queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue);
if (index < 26) {
@@ -313,7 +378,7 @@ static int virtblk_probe(struct virtio_device *vdev)
offsetof(struct virtio_blk_config, blk_size),
&blk_size);
if (!err)
- blk_queue_hardsect_size(vblk->disk->queue, blk_size);
+ blk_queue_logical_block_size(vblk->disk->queue, blk_size);
add_disk(vblk->disk);
return 0;
@@ -323,14 +388,14 @@ out_put_disk:
out_mempool:
mempool_destroy(vblk->pool);
out_free_vq:
- vdev->config->del_vq(vblk->vq);
+ vdev->config->del_vqs(vdev);
out_free_vblk:
kfree(vblk);
out:
return err;
}
-static void virtblk_remove(struct virtio_device *vdev)
+static void __devexit virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
@@ -344,7 +409,7 @@ static void virtblk_remove(struct virtio_device *vdev)
blk_cleanup_queue(vblk->disk->queue);
put_disk(vblk->disk);
mempool_destroy(vblk->pool);
- vdev->config->del_vq(vblk->vq);
+ vdev->config->del_vqs(vdev);
kfree(vblk);
}
@@ -356,6 +421,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX,
VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
+ VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY
};
static struct virtio_driver virtio_blk = {
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 6d4ac76c2806..c1996829d5ec 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(blkif_io_lock);
static int get_id_from_freelist(struct blkfront_info *info)
{
unsigned long free = info->shadow_free;
- BUG_ON(free > BLK_RING_SIZE);
+ BUG_ON(free >= BLK_RING_SIZE);
info->shadow_free = info->shadow[free].req.id;
info->shadow[free].req.id = 0x0fffffee; /* debug */
return free;
@@ -344,7 +344,7 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
/* Hard sector size and max sectors impersonate the equiv. hardware. */
- blk_queue_hardsect_size(rq, sector_size);
+ blk_queue_logical_block_size(rq, sector_size);
blk_queue_max_sectors(rq, 512);
/* Each segment in a request is up to an aligned page in size. */
@@ -932,8 +932,6 @@ static void blkfront_closing(struct xenbus_device *dev)
spin_lock_irqsave(&blkif_io_lock, flags);
- del_gendisk(info->gd);
-
/* No more blkif_request(). */
blk_stop_queue(info->rq);
@@ -947,6 +945,8 @@ static void blkfront_closing(struct xenbus_device *dev)
blk_cleanup_queue(info->rq);
info->rq = NULL;
+ del_gendisk(info->gd);
+
out:
xenbus_frontend_closed(dev);
}
@@ -975,8 +975,10 @@ static void backend_changed(struct xenbus_device *dev,
break;
case XenbusStateClosing:
- if (info->gd == NULL)
- xenbus_dev_fatal(dev, -ENODEV, "gd is NULL");
+ if (info->gd == NULL) {
+ xenbus_frontend_closed(dev);
+ break;
+ }
bd = bdget_disk(info->gd, 0);
if (bd == NULL)
xenbus_dev_fatal(dev, -ENODEV, "bdget failed");
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index 3a4397edab71..f08491a3a813 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -984,7 +984,7 @@ static int __devinit ace_setup(struct ace_device *ace)
ace->queue = blk_init_queue(ace_request, &ace->lock);
if (ace->queue == NULL)
goto err_blk_initq;
- blk_queue_hardsect_size(ace->queue, 512);
+ blk_queue_logical_block_size(ace->queue, 512);
/*
* Allocate and initialize GD structure
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index af761dc434f6..4895f0e05322 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -277,8 +277,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
/* FIXME: why is this needed. Note don't use ldisc_ref here as the
open path is before the ldisc is referencable */
- if (tty->ldisc.ops->flush_buffer)
- tty->ldisc.ops->flush_buffer(tty);
+ if (tty->ldisc->ops->flush_buffer)
+ tty->ldisc->ops->flush_buffer(tty);
tty_driver_flush_buffer(tty);
return 0;
@@ -463,7 +463,6 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
return err;
}
- tty->low_latency = 1;
} else
return -EBUSY;
break;
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index cceace61ef28..71d1b9bab70b 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -2101,8 +2101,8 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
nr = nframes;
if (cdi->cdda_method == CDDA_BPC_SINGLE)
nr = 1;
- if (nr * CD_FRAMESIZE_RAW > (q->max_sectors << 9))
- nr = (q->max_sectors << 9) / CD_FRAMESIZE_RAW;
+ if (nr * CD_FRAMESIZE_RAW > (queue_max_sectors(q) << 9))
+ nr = (queue_max_sectors(q) << 9) / CD_FRAMESIZE_RAW;
len = nr * CD_FRAMESIZE_RAW;
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index 1e366ad8f680..b5621f27c4be 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -739,7 +739,7 @@ static void __devinit probe_gdrom_setupdisk(void)
static int __devinit probe_gdrom_setupqueue(void)
{
- blk_queue_hardsect_size(gd.gdrom_rq, GDROM_HARD_SECTOR);
+ blk_queue_logical_block_size(gd.gdrom_rq, GDROM_HARD_SECTOR);
/* using DMA so memory will need to be contiguous */
blk_queue_max_hw_segments(gd.gdrom_rq, 1);
/* set a large max size to get most from DMA */
diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c
index ca741c21e4aa..0fff646cc2f0 100644
--- a/drivers/cdrom/viocd.c
+++ b/drivers/cdrom/viocd.c
@@ -469,8 +469,8 @@ static void vio_handle_cd_event(struct HvLpEvent *event)
case viocdopen:
if (event->xRc == 0) {
di = &viocd_diskinfo[bevent->disk];
- blk_queue_hardsect_size(di->viocd_disk->queue,
- bevent->block_size);
+ blk_queue_logical_block_size(di->viocd_disk->queue,
+ bevent->block_size);
set_capacity(di->viocd_disk,
bevent->media_size *
bevent->block_size / 512);
@@ -570,7 +570,7 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
struct device_node *node = vdev->dev.archdata.of_node;
deviceno = vdev->unit_address;
- if (deviceno > VIOCD_MAX_CD)
+ if (deviceno >= VIOCD_MAX_CD)
return -ENODEV;
if (!node)
return -ENODEV;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 735bbe2be51a..b1e9652c0d9d 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -97,6 +97,19 @@ config DEVKMEM
kind of kernel debugging operations.
When in doubt, say "N".
+config BFIN_JTAG_COMM
+ tristate "Blackfin JTAG Communication"
+ depends on BLACKFIN
+ help
+ Add support for emulating a TTY device over the Blackfin JTAG.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_jtag_comm.
+
+config BFIN_JTAG_COMM_CONSOLE
+ bool "Console on Blackfin JTAG"
+ depends on BFIN_JTAG_COMM=y
+
config SERIAL_NONSTANDARD
bool "Non-standard serial port support"
depends on HAS_IOMEM
@@ -893,7 +906,7 @@ config DTLK
config XILINX_HWICAP
tristate "Xilinx HWICAP Support"
- depends on XILINX_VIRTEX
+ depends on XILINX_VIRTEX || MICROBLAZE
help
This option enables support for Xilinx Internal Configuration
Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 9caf5b5ad1c0..189efcff08ce 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
+obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_AUDIT) += tty_audit.o
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 3686912427ba..7a748fa0dfce 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -46,6 +46,10 @@
#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22
#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30
#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32
+#define PCI_DEVICE_ID_INTEL_IGDNG_D_HB 0x0040
+#define PCI_DEVICE_ID_INTEL_IGDNG_D_IG 0x0042
+#define PCI_DEVICE_ID_INTEL_IGDNG_M_HB 0x0044
+#define PCI_DEVICE_ID_INTEL_IGDNG_M_IG 0x0046
/* cover 915 and 945 variants */
#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
@@ -75,7 +79,9 @@
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB)
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB)
extern int agp_memory_reserved;
@@ -1211,6 +1217,8 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
case PCI_DEVICE_ID_INTEL_Q45_HB:
case PCI_DEVICE_ID_INTEL_G45_HB:
case PCI_DEVICE_ID_INTEL_G41_HB:
+ case PCI_DEVICE_ID_INTEL_IGDNG_D_HB:
+ case PCI_DEVICE_ID_INTEL_IGDNG_M_HB:
*gtt_offset = *gtt_size = MB(2);
break;
default:
@@ -2186,6 +2194,10 @@ static const struct intel_driver_description {
"G45/G43", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0,
"G41", NULL, &intel_i965_driver },
+ { PCI_DEVICE_ID_INTEL_IGDNG_D_HB, PCI_DEVICE_ID_INTEL_IGDNG_D_IG, 0,
+ "IGDNG/D", NULL, &intel_i965_driver },
+ { PCI_DEVICE_ID_INTEL_IGDNG_M_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
+ "IGDNG/M", NULL, &intel_i965_driver },
{ 0, 0, 0, NULL, NULL, NULL }
};
@@ -2387,6 +2399,8 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_Q45_HB),
ID(PCI_DEVICE_ID_INTEL_G45_HB),
ID(PCI_DEVICE_ID_INTEL_G41_HB),
+ ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB),
+ ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB),
{ }
};
diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c
new file mode 100644
index 000000000000..44c113d56045
--- /dev/null
+++ b/drivers/char/bfin_jtag_comm.c
@@ -0,0 +1,365 @@
+/*
+ * TTY over Blackfin JTAG Communication
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <asm/atomic.h>
+
+/* See the Debug/Emulation chapter in the HRM */
+#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */
+#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */
+#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */
+#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */
+
+#define DRV_NAME "bfin-jtag-comm"
+#define DEV_NAME "ttyBFJC"
+
+#define pr_init(fmt, args...) ({ static const __initdata char __fmt[] = fmt; printk(__fmt, ## args); })
+#define debug(fmt, args...) pr_debug(DRV_NAME ": " fmt, ## args)
+
+static inline uint32_t bfin_write_emudat(uint32_t emudat)
+{
+ __asm__ __volatile__("emudat = %0;" : : "d"(emudat));
+ return emudat;
+}
+
+static inline uint32_t bfin_read_emudat(void)
+{
+ uint32_t emudat;
+ __asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
+ return emudat;
+}
+
+static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d)
+{
+ return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24));
+}
+
+#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */
+#define CIRC_MASK (CIRC_SIZE - 1)
+#define circ_empty(circ) ((circ)->head == (circ)->tail)
+#define circ_free(circ) CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE)
+#define circ_cnt(circ) CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE)
+#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK])
+
+static struct tty_driver *bfin_jc_driver;
+static struct task_struct *bfin_jc_kthread;
+static struct tty_struct * volatile bfin_jc_tty;
+static unsigned long bfin_jc_count;
+static DEFINE_MUTEX(bfin_jc_tty_mutex);
+static volatile struct circ_buf bfin_jc_write_buf;
+
+static int
+bfin_jc_emudat_manager(void *arg)
+{
+ uint32_t inbound_len = 0, outbound_len = 0;
+
+ while (!kthread_should_stop()) {
+ /* no one left to give data to, so sleep */
+ if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) {
+ debug("waiting for readers\n");
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ }
+
+ /* no data available, so just chill */
+ if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) {
+ debug("waiting for data (in_len = %i) (circ: %i %i)\n",
+ inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head);
+ if (inbound_len)
+ schedule();
+ else
+ schedule_timeout_interruptible(HZ);
+ continue;
+ }
+
+ /* if incoming data is ready, eat it */
+ if (bfin_read_DBGSTAT() & EMUDIF) {
+ struct tty_struct *tty;
+ mutex_lock(&bfin_jc_tty_mutex);
+ tty = (struct tty_struct *)bfin_jc_tty;
+ if (tty != NULL) {
+ uint32_t emudat = bfin_read_emudat();
+ if (inbound_len == 0) {
+ debug("incoming length: 0x%08x\n", emudat);
+ inbound_len = emudat;
+ } else {
+ size_t num_chars = (4 <= inbound_len ? 4 : inbound_len);
+ debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars);
+ inbound_len -= num_chars;
+ tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars);
+ tty_flip_buffer_push(tty);
+ }
+ }
+ mutex_unlock(&bfin_jc_tty_mutex);
+ }
+
+ /* if outgoing data is ready, post it */
+ if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) {
+ if (outbound_len == 0) {
+ outbound_len = circ_cnt(&bfin_jc_write_buf);
+ bfin_write_emudat(outbound_len);
+ debug("outgoing length: 0x%08x\n", outbound_len);
+ } else {
+ struct tty_struct *tty;
+ int tail = bfin_jc_write_buf.tail;
+ size_t ate = (4 <= outbound_len ? 4 : outbound_len);
+ uint32_t emudat =
+ bfin_write_emudat_chars(
+ circ_byte(&bfin_jc_write_buf, tail + 0),
+ circ_byte(&bfin_jc_write_buf, tail + 1),
+ circ_byte(&bfin_jc_write_buf, tail + 2),
+ circ_byte(&bfin_jc_write_buf, tail + 3)
+ );
+ bfin_jc_write_buf.tail += ate;
+ outbound_len -= ate;
+ mutex_lock(&bfin_jc_tty_mutex);
+ tty = (struct tty_struct *)bfin_jc_tty;
+ if (tty)
+ tty_wakeup(tty);
+ mutex_unlock(&bfin_jc_tty_mutex);
+ debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate);
+ }
+ }
+ }
+
+ __set_current_state(TASK_RUNNING);
+ return 0;
+}
+
+static int
+bfin_jc_open(struct tty_struct *tty, struct file *filp)
+{
+ mutex_lock(&bfin_jc_tty_mutex);
+ debug("open %lu\n", bfin_jc_count);
+ ++bfin_jc_count;
+ bfin_jc_tty = tty;
+ wake_up_process(bfin_jc_kthread);
+ mutex_unlock(&bfin_jc_tty_mutex);
+ return 0;
+}
+
+static void
+bfin_jc_close(struct tty_struct *tty, struct file *filp)
+{
+ mutex_lock(&bfin_jc_tty_mutex);
+ debug("close %lu\n", bfin_jc_count);
+ if (--bfin_jc_count == 0)
+ bfin_jc_tty = NULL;
+ wake_up_process(bfin_jc_kthread);
+ mutex_unlock(&bfin_jc_tty_mutex);
+}
+
+/* XXX: we dont handle the put_char() case where we must handle count = 1 */
+static int
+bfin_jc_circ_write(const unsigned char *buf, int count)
+{
+ int i;
+ count = min(count, circ_free(&bfin_jc_write_buf));
+ debug("going to write chunk of %i bytes\n", count);
+ for (i = 0; i < count; ++i)
+ circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i];
+ bfin_jc_write_buf.head += i;
+ return i;
+}
+
+#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE
+# define acquire_console_sem()
+# define release_console_sem()
+#endif
+static int
+bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ int i;
+ acquire_console_sem();
+ i = bfin_jc_circ_write(buf, count);
+ release_console_sem();
+ wake_up_process(bfin_jc_kthread);
+ return i;
+}
+
+static void
+bfin_jc_flush_chars(struct tty_struct *tty)
+{
+ wake_up_process(bfin_jc_kthread);
+}
+
+static int
+bfin_jc_write_room(struct tty_struct *tty)
+{
+ return circ_free(&bfin_jc_write_buf);
+}
+
+static int
+bfin_jc_chars_in_buffer(struct tty_struct *tty)
+{
+ return circ_cnt(&bfin_jc_write_buf);
+}
+
+static void
+bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ unsigned long expire = jiffies + timeout;
+ while (!circ_empty(&bfin_jc_write_buf)) {
+ if (signal_pending(current))
+ break;
+ if (time_after(jiffies, expire))
+ break;
+ }
+}
+
+static struct tty_operations bfin_jc_ops = {
+ .open = bfin_jc_open,
+ .close = bfin_jc_close,
+ .write = bfin_jc_write,
+ /*.put_char = bfin_jc_put_char,*/
+ .flush_chars = bfin_jc_flush_chars,
+ .write_room = bfin_jc_write_room,
+ .chars_in_buffer = bfin_jc_chars_in_buffer,
+ .wait_until_sent = bfin_jc_wait_until_sent,
+};
+
+static int __init bfin_jc_init(void)
+{
+ int ret;
+
+ bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME);
+ if (IS_ERR(bfin_jc_kthread))
+ return PTR_ERR(bfin_jc_kthread);
+
+ ret = -ENOMEM;
+
+ bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0;
+ bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
+ if (!bfin_jc_write_buf.buf)
+ goto err;
+
+ bfin_jc_driver = alloc_tty_driver(1);
+ if (!bfin_jc_driver)
+ goto err;
+
+ bfin_jc_driver->owner = THIS_MODULE;
+ bfin_jc_driver->driver_name = DRV_NAME;
+ bfin_jc_driver->name = DEV_NAME;
+ bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL;
+ bfin_jc_driver->init_termios = tty_std_termios;
+ tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
+
+ ret = tty_register_driver(bfin_jc_driver);
+ if (ret)
+ goto err;
+
+ pr_init(KERN_INFO DRV_NAME ": initialized\n");
+
+ return 0;
+
+ err:
+ put_tty_driver(bfin_jc_driver);
+ kfree(bfin_jc_write_buf.buf);
+ kthread_stop(bfin_jc_kthread);
+ return ret;
+}
+module_init(bfin_jc_init);
+
+static void __exit bfin_jc_exit(void)
+{
+ kthread_stop(bfin_jc_kthread);
+ kfree(bfin_jc_write_buf.buf);
+ tty_unregister_driver(bfin_jc_driver);
+ put_tty_driver(bfin_jc_driver);
+}
+module_exit(bfin_jc_exit);
+
+#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
+static void
+bfin_jc_straight_buffer_write(const char *buf, unsigned count)
+{
+ unsigned ate = 0;
+ while (bfin_read_DBGSTAT() & EMUDOF)
+ continue;
+ bfin_write_emudat(count);
+ while (ate < count) {
+ while (bfin_read_DBGSTAT() & EMUDOF)
+ continue;
+ bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]);
+ ate += 4;
+ }
+}
+#endif
+
+#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE
+static void
+bfin_jc_console_write(struct console *co, const char *buf, unsigned count)
+{
+ if (bfin_jc_kthread == NULL)
+ bfin_jc_straight_buffer_write(buf, count);
+ else
+ bfin_jc_circ_write(buf, count);
+}
+
+static struct tty_driver *
+bfin_jc_console_device(struct console *co, int *index)
+{
+ *index = co->index;
+ return bfin_jc_driver;
+}
+
+static struct console bfin_jc_console = {
+ .name = DEV_NAME,
+ .write = bfin_jc_console_write,
+ .device = bfin_jc_console_device,
+ .flags = CON_ANYTIME | CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static int __init bfin_jc_console_init(void)
+{
+ register_console(&bfin_jc_console);
+ return 0;
+}
+console_initcall(bfin_jc_console_init);
+#endif
+
+#ifdef CONFIG_EARLY_PRINTK
+static void __init
+bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
+{
+ bfin_jc_straight_buffer_write(buf, count);
+}
+
+static struct __initdata console bfin_jc_early_console = {
+ .name = "early_BFJC",
+ .write = bfin_jc_early_write,
+ .flags = CON_ANYTIME | CON_PRINTBUFFER,
+ .index = -1,
+};
+
+struct console * __init
+bfin_jc_early_init(unsigned int port, unsigned int cflag)
+{
+ return &bfin_jc_early_console;
+}
+#endif
+
+MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
+MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index 1fdb9f657d8f..f3366d3f06cf 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -604,7 +604,6 @@
#define NR_PORTS 256
-#define ZE_V1_NPORTS 64
#define ZO_V1 0
#define ZO_V2 1
#define ZE_V1 2
@@ -663,18 +662,6 @@
static void cy_throttle(struct tty_struct *tty);
static void cy_send_xchar(struct tty_struct *tty, char ch);
-#define IS_CYC_Z(card) ((card).num_chips == (unsigned int)-1)
-
-#define Z_FPGA_CHECK(card) \
- ((readl(&((struct RUNTIME_9060 __iomem *) \
- ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
-
-#define ISZLOADED(card) (((ZO_V1 == readl(&((struct RUNTIME_9060 __iomem *) \
- ((card).ctl_addr))->mail_box_0)) || \
- Z_FPGA_CHECK(card)) && \
- (ZFIRM_ID == readl(&((struct FIRM_ID __iomem *) \
- ((card).base_addr+ID_ADDRESS))->signature)))
-
#ifndef SERIAL_XMIT_SIZE
#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096))
#endif
@@ -687,8 +674,6 @@ static void cy_send_xchar(struct tty_struct *tty, char ch);
#define DRIVER_VERSION 0x02010203
#define RAM_SIZE 0x80000
-#define Z_FPGA_LOADED(X) ((readl(&(X)->init_ctrl) & (1<<17)) != 0)
-
enum zblock_type {
ZBLOCK_PRG = 0,
ZBLOCK_FPGA = 1
@@ -883,6 +868,29 @@ static void cyz_rx_restart(unsigned long);
static struct timer_list cyz_rx_full_timer[NR_PORTS];
#endif /* CONFIG_CYZ_INTR */
+static inline bool cy_is_Z(struct cyclades_card *card)
+{
+ return card->num_chips == (unsigned int)-1;
+}
+
+static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
+{
+ return readl(&ctl_addr->init_ctrl) & (1 << 17);
+}
+
+static inline bool cyz_fpga_loaded(struct cyclades_card *card)
+{
+ return __cyz_fpga_loaded(card->ctl_addr.p9060);
+}
+
+static inline bool cyz_is_loaded(struct cyclades_card *card)
+{
+ struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
+
+ return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
+ readl(&fw_id->signature) == ZFIRM_ID;
+}
+
static inline int serial_paranoia_check(struct cyclades_port *info,
char *name, const char *routine)
{
@@ -1395,19 +1403,15 @@ cyz_fetch_msg(struct cyclades_card *cinfo,
unsigned long loc_doorbell;
firm_id = cinfo->base_addr + ID_ADDRESS;
- if (!ISZLOADED(*cinfo))
- return -1;
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
board_ctrl = &zfw_ctrl->board_ctrl;
- loc_doorbell = readl(&((struct RUNTIME_9060 __iomem *)
- (cinfo->ctl_addr))->loc_doorbell);
+ loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
if (loc_doorbell) {
*cmd = (char)(0xff & loc_doorbell);
*channel = readl(&board_ctrl->fwcmd_channel);
*param = (__u32) readl(&board_ctrl->fwcmd_param);
- cy_writel(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->
- loc_doorbell, 0xffffffff);
+ cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
return 1;
}
return 0;
@@ -1424,15 +1428,14 @@ cyz_issue_cmd(struct cyclades_card *cinfo,
unsigned int index;
firm_id = cinfo->base_addr + ID_ADDRESS;
- if (!ISZLOADED(*cinfo))
+ if (!cyz_is_loaded(cinfo))
return -1;
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
board_ctrl = &zfw_ctrl->board_ctrl;
index = 0;
- pci_doorbell =
- &((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->pci_doorbell;
+ pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
while ((readl(pci_doorbell) & 0xff) != 0) {
if (index++ == 1000)
return (int)(readl(pci_doorbell) & 0xff);
@@ -1624,10 +1627,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
static struct BOARD_CTRL __iomem *board_ctrl;
static struct CH_CTRL __iomem *ch_ctrl;
static struct BUF_CTRL __iomem *buf_ctrl;
- __u32 channel;
+ __u32 channel, param, fw_ver;
__u8 cmd;
- __u32 param;
- __u32 hw_ver, fw_ver;
int special_count;
int delta_count;
@@ -1635,8 +1636,6 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
board_ctrl = &zfw_ctrl->board_ctrl;
fw_ver = readl(&board_ctrl->fw_version);
- hw_ver = readl(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->
- mail_box_0);
while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
special_count = 0;
@@ -1737,15 +1736,7 @@ static irqreturn_t cyz_interrupt(int irq, void *dev_id)
{
struct cyclades_card *cinfo = dev_id;
- if (unlikely(cinfo == NULL)) {
-#ifdef CY_DEBUG_INTERRUPTS
- printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n",
- irq);
-#endif
- return IRQ_NONE; /* spurious interrupt */
- }
-
- if (unlikely(!ISZLOADED(*cinfo))) {
+ if (unlikely(!cyz_is_loaded(cinfo))) {
#ifdef CY_DEBUG_INTERRUPTS
printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
"(IRQ%d).\n", irq);
@@ -1785,7 +1776,6 @@ static void cyz_poll(unsigned long arg)
struct tty_struct *tty;
struct FIRM_ID __iomem *firm_id;
struct ZFW_CTRL __iomem *zfw_ctrl;
- struct BOARD_CTRL __iomem *board_ctrl;
struct BUF_CTRL __iomem *buf_ctrl;
unsigned long expires = jiffies + HZ;
unsigned int port, card;
@@ -1793,19 +1783,17 @@ static void cyz_poll(unsigned long arg)
for (card = 0; card < NR_CARDS; card++) {
cinfo = &cy_card[card];
- if (!IS_CYC_Z(*cinfo))
+ if (!cy_is_Z(cinfo))
continue;
- if (!ISZLOADED(*cinfo))
+ if (!cyz_is_loaded(cinfo))
continue;
firm_id = cinfo->base_addr + ID_ADDRESS;
zfw_ctrl = cinfo->base_addr +
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
- board_ctrl = &(zfw_ctrl->board_ctrl);
/* Skip first polling cycle to avoid racing conditions with the FW */
if (!cinfo->intr_enabled) {
- cinfo->nports = (int)readl(&board_ctrl->n_channel);
cinfo->intr_enabled = 1;
continue;
}
@@ -1874,7 +1862,7 @@ static int startup(struct cyclades_port *info)
set_line_char(info);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -1931,7 +1919,7 @@ static int startup(struct cyclades_port *info)
base_addr = card->base_addr;
firm_id = base_addr + ID_ADDRESS;
- if (!ISZLOADED(*card))
+ if (!cyz_is_loaded(card))
return -ENODEV;
zfw_ctrl = card->base_addr +
@@ -2026,7 +2014,7 @@ static void start_xmit(struct cyclades_port *info)
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -2070,7 +2058,7 @@ static void shutdown(struct cyclades_port *info)
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -2126,7 +2114,7 @@ static void shutdown(struct cyclades_port *info)
#endif
firm_id = base_addr + ID_ADDRESS;
- if (!ISZLOADED(*card))
+ if (!cyz_is_loaded(card))
return;
zfw_ctrl = card->base_addr +
@@ -2233,7 +2221,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
#endif
info->port.blocked_open++;
- if (!IS_CYC_Z(*cinfo)) {
+ if (!cy_is_Z(cinfo)) {
chip = channel >> 2;
channel &= 0x03;
index = cinfo->bus_index;
@@ -2296,7 +2284,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
base_addr = cinfo->base_addr;
firm_id = base_addr + ID_ADDRESS;
- if (!ISZLOADED(*cinfo)) {
+ if (!cyz_is_loaded(cinfo)) {
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->port.open_wait, &wait);
return -EINVAL;
@@ -2397,16 +2385,14 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
treat it as absent from the system. This
will make the user pay attention.
*/
- if (IS_CYC_Z(*info->card)) {
+ if (cy_is_Z(info->card)) {
struct cyclades_card *cinfo = info->card;
struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS;
- if (!ISZLOADED(*cinfo)) {
- if (((ZE_V1 == readl(&((struct RUNTIME_9060 __iomem *)
- (cinfo->ctl_addr))->mail_box_0)) &&
- Z_FPGA_CHECK(*cinfo)) &&
- (ZFIRM_HLT == readl(
- &firm_id->signature))) {
+ if (!cyz_is_loaded(cinfo)) {
+ if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) &&
+ readl(&firm_id->signature) ==
+ ZFIRM_HLT) {
printk(KERN_ERR "cyc:Cyclades-Z Error: you "
"need an external power supply for "
"this number of ports.\nFirmware "
@@ -2423,18 +2409,13 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
interrupts should be enabled as soon as the first open
happens to one of its ports. */
if (!cinfo->intr_enabled) {
- struct ZFW_CTRL __iomem *zfw_ctrl;
- struct BOARD_CTRL __iomem *board_ctrl;
-
- zfw_ctrl = cinfo->base_addr +
- (readl(&firm_id->zfwctrl_addr) &
- 0xfffff);
-
- board_ctrl = &zfw_ctrl->board_ctrl;
+ u16 intr;
/* Enable interrupts on the PLX chip */
- cy_writew(cinfo->ctl_addr + 0x68,
- readw(cinfo->ctl_addr + 0x68) | 0x0900);
+ intr = readw(&cinfo->ctl_addr.p9060->
+ intr_ctrl_stat) | 0x0900;
+ cy_writew(&cinfo->ctl_addr.p9060->
+ intr_ctrl_stat, intr);
/* Enable interrupts on the FW */
retval = cyz_issue_cmd(cinfo, 0,
C_CM_IRQ_ENBL, 0L);
@@ -2442,8 +2423,6 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
printk(KERN_ERR "cyc:IRQ enable retval "
"was %x\n", retval);
}
- cinfo->nports =
- (int)readl(&board_ctrl->n_channel);
cinfo->intr_enabled = 1;
}
}
@@ -2556,7 +2535,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
#endif
card = info->card;
channel = (info->line) - (card->first_line);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -2601,7 +2580,7 @@ static void cy_flush_buffer(struct tty_struct *tty)
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
spin_unlock_irqrestore(&card->card_lock, flags);
- if (IS_CYC_Z(*card)) { /* If it is a Z card, flush the on-board
+ if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board
buffers as well */
spin_lock_irqsave(&card->card_lock, flags);
retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L);
@@ -2682,7 +2661,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp)
spin_lock_irqsave(&card->card_lock, flags);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
int channel = info->line - card->first_line;
int index = card->bus_index;
void __iomem *base_addr = card->base_addr +
@@ -2902,7 +2881,7 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
channel = (info->line) - (card->first_line);
#ifdef Z_EXT_CHARS_IN_BUFFER
- if (!IS_CYC_Z(cy_card[card])) {
+ if (!cy_is_Z(card)) {
#endif /* Z_EXT_CHARS_IN_BUFFER */
#ifdef CY_DEBUG_IO
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
@@ -2984,7 +2963,6 @@ static void set_line_char(struct cyclades_port *info)
void __iomem *base_addr;
int chip, channel, index;
unsigned cflag, iflag;
- unsigned short chip_number;
int baud, baud_rate = 0;
int i;
@@ -3013,9 +2991,8 @@ static void set_line_char(struct cyclades_port *info)
card = info->card;
channel = info->line - card->first_line;
- chip_number = channel / 4;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
index = card->bus_index;
@@ -3233,21 +3210,17 @@ static void set_line_char(struct cyclades_port *info)
} else {
struct FIRM_ID __iomem *firm_id;
struct ZFW_CTRL __iomem *zfw_ctrl;
- struct BOARD_CTRL __iomem *board_ctrl;
struct CH_CTRL __iomem *ch_ctrl;
- struct BUF_CTRL __iomem *buf_ctrl;
__u32 sw_flow;
int retval;
firm_id = card->base_addr + ID_ADDRESS;
- if (!ISZLOADED(*card))
+ if (!cyz_is_loaded(card))
return;
zfw_ctrl = card->base_addr +
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
- board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
- buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
/* baud rate */
baud = tty_get_baud_rate(info->port.tty);
@@ -3457,7 +3430,7 @@ static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value)
card = info->card;
channel = (info->line) - (card->first_line);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3497,7 +3470,7 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3523,7 +3496,7 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
} else {
base_addr = card->base_addr;
firm_id = card->base_addr + ID_ADDRESS;
- if (ISZLOADED(*card)) {
+ if (cyz_is_loaded(card)) {
zfw_ctrl = card->base_addr +
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
board_ctrl = &zfw_ctrl->board_ctrl;
@@ -3566,7 +3539,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file,
card = info->card;
channel = (info->line) - (card->first_line);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3641,7 +3614,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file,
base_addr = card->base_addr;
firm_id = card->base_addr + ID_ADDRESS;
- if (ISZLOADED(*card)) {
+ if (cyz_is_loaded(card)) {
zfw_ctrl = card->base_addr +
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
board_ctrl = &zfw_ctrl->board_ctrl;
@@ -3713,7 +3686,7 @@ static int cy_break(struct tty_struct *tty, int break_state)
card = info->card;
spin_lock_irqsave(&card->card_lock, flags);
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
/* Let the transmit ISR take care of this (since it
requires stuffing characters into the output stream).
*/
@@ -3782,7 +3755,7 @@ static int set_threshold(struct cyclades_port *info, unsigned long value)
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3810,7 +3783,7 @@ static int get_threshold(struct cyclades_port *info,
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3844,7 +3817,7 @@ static int set_timeout(struct cyclades_port *info, unsigned long value)
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -3867,7 +3840,7 @@ static int get_timeout(struct cyclades_port *info,
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -4121,7 +4094,7 @@ static void cy_send_xchar(struct tty_struct *tty, char ch)
card = info->card;
channel = info->line - card->first_line;
- if (IS_CYC_Z(*card)) {
+ if (cy_is_Z(card)) {
if (ch == STOP_CHAR(tty))
cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L);
else if (ch == START_CHAR(tty))
@@ -4154,7 +4127,7 @@ static void cy_throttle(struct tty_struct *tty)
card = info->card;
if (I_IXOFF(tty)) {
- if (!IS_CYC_Z(*card))
+ if (!cy_is_Z(card))
cy_send_xchar(tty, STOP_CHAR(tty));
else
info->throttle = 1;
@@ -4162,7 +4135,7 @@ static void cy_throttle(struct tty_struct *tty)
if (tty->termios->c_cflag & CRTSCTS) {
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -4219,7 +4192,7 @@ static void cy_unthrottle(struct tty_struct *tty)
if (tty->termios->c_cflag & CRTSCTS) {
card = info->card;
channel = info->line - card->first_line;
- if (!IS_CYC_Z(*card)) {
+ if (!cy_is_Z(card)) {
chip = channel >> 2;
channel &= 0x03;
index = card->bus_index;
@@ -4263,7 +4236,7 @@ static void cy_stop(struct tty_struct *tty)
cinfo = info->card;
channel = info->line - cinfo->first_line;
- if (!IS_CYC_Z(*cinfo)) {
+ if (!cy_is_Z(cinfo)) {
index = cinfo->bus_index;
chip = channel >> 2;
channel &= 0x03;
@@ -4296,7 +4269,7 @@ static void cy_start(struct tty_struct *tty)
cinfo = info->card;
channel = info->line - cinfo->first_line;
index = cinfo->bus_index;
- if (!IS_CYC_Z(*cinfo)) {
+ if (!cy_is_Z(cinfo)) {
chip = channel >> 2;
channel &= 0x03;
base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index);
@@ -4347,33 +4320,20 @@ static void cy_hangup(struct tty_struct *tty)
static int __devinit cy_init_card(struct cyclades_card *cinfo)
{
struct cyclades_port *info;
- u32 uninitialized_var(mailbox);
- unsigned int nports, port;
+ unsigned int port;
unsigned short chip_number;
- int uninitialized_var(index);
spin_lock_init(&cinfo->card_lock);
+ cinfo->intr_enabled = 0;
- if (IS_CYC_Z(*cinfo)) { /* Cyclades-Z */
- mailbox = readl(&((struct RUNTIME_9060 __iomem *)
- cinfo->ctl_addr)->mail_box_0);
- nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
- cinfo->intr_enabled = 0;
- cinfo->nports = 0; /* Will be correctly set later, after
- Z FW is loaded */
- } else {
- index = cinfo->bus_index;
- nports = cinfo->nports = CyPORTS_PER_CHIP * cinfo->num_chips;
- }
-
- cinfo->ports = kzalloc(sizeof(*cinfo->ports) * nports, GFP_KERNEL);
+ cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports),
+ GFP_KERNEL);
if (cinfo->ports == NULL) {
printk(KERN_ERR "Cyclades: cannot allocate ports\n");
- cinfo->nports = 0;
return -ENOMEM;
}
- for (port = cinfo->first_line; port < cinfo->first_line + nports;
+ for (port = cinfo->first_line; port < cinfo->first_line + cinfo->nports;
port++) {
info = &cinfo->ports[port - cinfo->first_line];
tty_port_init(&info->port);
@@ -4387,9 +4347,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
init_completion(&info->shutdown_wait);
init_waitqueue_head(&info->delta_msr_wait);
- if (IS_CYC_Z(*cinfo)) {
+ if (cy_is_Z(cinfo)) {
info->type = PORT_STARTECH;
- if (mailbox == ZO_V1)
+ if (cinfo->hw_ver == ZO_V1)
info->xmit_fifo_size = CYZ_FIFO_SIZE;
else
info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
@@ -4398,6 +4358,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
cyz_rx_restart, (unsigned long)info);
#endif
} else {
+ int index = cinfo->bus_index;
info->type = PORT_CIRRUS;
info->xmit_fifo_size = CyMAX_CHAR_FIFO;
info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS;
@@ -4430,7 +4391,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
}
#ifndef CONFIG_CYZ_INTR
- if (IS_CYC_Z(*cinfo) && !timer_pending(&cyz_timerlist)) {
+ if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) {
mod_timer(&cyz_timerlist, jiffies + 1);
#ifdef CY_PCI_DEBUG
printk(KERN_DEBUG "Cyclades-Z polling initialized\n");
@@ -4621,11 +4582,12 @@ static int __init cy_detect_isa(void)
/* set cy_card */
cy_card[j].base_addr = cy_isa_address;
- cy_card[j].ctl_addr = NULL;
+ cy_card[j].ctl_addr.p9050 = NULL;
cy_card[j].irq = (int)cy_isa_irq;
cy_card[j].bus_index = 0;
cy_card[j].first_line = cy_next_channel;
- cy_card[j].num_chips = cy_isa_nchan / 4;
+ cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
+ cy_card[j].nports = cy_isa_nchan;
if (cy_init_card(&cy_card[j])) {
cy_card[j].base_addr = NULL;
free_irq(cy_isa_irq, &cy_card[j]);
@@ -4781,7 +4743,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
struct CUSTOM_REG __iomem *cust = base_addr;
struct ZFW_CTRL __iomem *pt_zfwctrl;
void __iomem *tmp;
- u32 mailbox, status;
+ u32 mailbox, status, nchan;
unsigned int i;
int retval;
@@ -4793,7 +4755,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
/* Check whether the firmware is already loaded and running. If
positive, skip this board */
- if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
+ if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
u32 cntval = readl(base_addr + 0x190);
udelay(100);
@@ -4812,7 +4774,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
mailbox = readl(&ctl_addr->mail_box_0);
- if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) {
+ if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) {
/* stops CPU and set window to beginning of RAM */
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
cy_writel(&cust->cpu_stop, 0);
@@ -4828,7 +4790,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
base_addr);
if (retval)
goto err_rel;
- if (!Z_FPGA_LOADED(ctl_addr)) {
+ if (!__cyz_fpga_loaded(ctl_addr)) {
dev_err(&pdev->dev, "fw upload successful, but fw is "
"not loaded\n");
goto err_rel;
@@ -4887,7 +4849,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
"system before loading the new FW to the "
"Cyclades-Z.\n");
- if (Z_FPGA_LOADED(ctl_addr))
+ if (__cyz_fpga_loaded(ctl_addr))
plx_init(pdev, irq, ctl_addr);
retval = -EIO;
@@ -4902,16 +4864,16 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
base_addr + readl(&fid->zfwctrl_addr));
+ nchan = readl(&pt_zfwctrl->board_ctrl.n_channel);
dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
- readl(&pt_zfwctrl->board_ctrl.fw_version),
- readl(&pt_zfwctrl->board_ctrl.n_channel));
+ readl(&pt_zfwctrl->board_ctrl.fw_version), nchan);
- if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) {
+ if (nchan == 0) {
dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
"check the connection between the Z host card and the "
"serial expanders.\n");
- if (Z_FPGA_LOADED(ctl_addr))
+ if (__cyz_fpga_loaded(ctl_addr))
plx_init(pdev, irq, ctl_addr);
dev_info(&pdev->dev, "Null number of ports detected. Board "
@@ -4932,9 +4894,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
0x00030800UL);
- plx_init(pdev, irq, ctl_addr);
-
- return 0;
+ return nchan;
err_rel:
release_firmware(fw);
err:
@@ -4946,7 +4906,7 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
{
void __iomem *addr0 = NULL, *addr2 = NULL;
char *card_name = NULL;
- u32 mailbox;
+ u32 uninitialized_var(mailbox);
unsigned int device_id, nchan = 0, card_no, i;
unsigned char plx_ver;
int retval, irq;
@@ -5023,11 +4983,12 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
}
/* Disable interrupts on the PLX before resetting it */
- cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900);
+ cy_writew(&ctl_addr->intr_ctrl_stat,
+ readw(&ctl_addr->intr_ctrl_stat) & ~0x0900);
plx_init(pdev, irq, addr0);
- mailbox = (u32)readl(&ctl_addr->mail_box_0);
+ mailbox = readl(&ctl_addr->mail_box_0);
addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
@@ -5038,12 +4999,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
if (mailbox == ZE_V1) {
card_name = "Cyclades-Ze";
-
- readl(&ctl_addr->mail_box_0);
- nchan = ZE_V1_NPORTS;
} else {
card_name = "Cyclades-8Zo";
-
#ifdef CY_PCI_DEBUG
if (mailbox == ZO_V1) {
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
@@ -5065,15 +5022,12 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
*/
if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
cy_writel(addr2 + ID_ADDRESS, 0L);
-
- retval = cyz_load_fw(pdev, addr2, addr0, irq);
- if (retval)
- goto err_unmap;
- /* This must be a Cyclades-8Zo/PCI. The extendable
- version will have a different device_id and will
- be allocated its maximum number of ports. */
- nchan = 8;
}
+
+ retval = cyz_load_fw(pdev, addr2, addr0, irq);
+ if (retval <= 0)
+ goto err_unmap;
+ nchan = retval;
}
if ((cy_next_channel + nchan) > NR_PORTS) {
@@ -5103,8 +5057,10 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
dev_err(&pdev->dev, "could not allocate IRQ\n");
goto err_unmap;
}
- cy_card[card_no].num_chips = nchan / 4;
+ cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
} else {
+ cy_card[card_no].hw_ver = mailbox;
+ cy_card[card_no].num_chips = (unsigned int)-1;
#ifdef CONFIG_CYZ_INTR
/* allocate IRQ only if board has an IRQ */
if (irq != 0 && irq != 255) {
@@ -5117,15 +5073,15 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
}
}
#endif /* CONFIG_CYZ_INTR */
- cy_card[card_no].num_chips = (unsigned int)-1;
}
/* set cy_card */
cy_card[card_no].base_addr = addr2;
- cy_card[card_no].ctl_addr = addr0;
+ cy_card[card_no].ctl_addr.p9050 = addr0;
cy_card[card_no].irq = irq;
cy_card[card_no].bus_index = 1;
cy_card[card_no].first_line = cy_next_channel;
+ cy_card[card_no].nports = nchan;
retval = cy_init_card(&cy_card[card_no]);
if (retval)
goto err_null;
@@ -5138,17 +5094,20 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
switch (plx_ver) {
case PLX_9050:
-
cy_writeb(addr0 + 0x4c, 0x43);
break;
case PLX_9060:
case PLX_9080:
default: /* Old boards, use PLX_9060 */
- plx_init(pdev, irq, addr0);
- cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900);
+ {
+ struct RUNTIME_9060 __iomem *ctl_addr = addr0;
+ plx_init(pdev, irq, ctl_addr);
+ cy_writew(&ctl_addr->intr_ctrl_stat,
+ readw(&ctl_addr->intr_ctrl_stat) | 0x0900);
break;
}
+ }
}
dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
@@ -5179,22 +5138,23 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev)
unsigned int i;
/* non-Z with old PLX */
- if (!IS_CYC_Z(*cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
+ if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
PLX_9050)
- cy_writeb(cinfo->ctl_addr + 0x4c, 0);
+ cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0);
else
#ifndef CONFIG_CYZ_INTR
- if (!IS_CYC_Z(*cinfo))
+ if (!cy_is_Z(cinfo))
#endif
- cy_writew(cinfo->ctl_addr + 0x68,
- readw(cinfo->ctl_addr + 0x68) & ~0x0900);
+ cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat,
+ readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) &
+ ~0x0900);
iounmap(cinfo->base_addr);
- if (cinfo->ctl_addr)
- iounmap(cinfo->ctl_addr);
+ if (cinfo->ctl_addr.p9050)
+ iounmap(cinfo->ctl_addr.p9050);
if (cinfo->irq
#ifndef CONFIG_CYZ_INTR
- && !IS_CYC_Z(*cinfo)
+ && !cy_is_Z(cinfo)
#endif /* CONFIG_CYZ_INTR */
)
free_irq(cinfo->irq, cinfo);
@@ -5240,7 +5200,7 @@ static int cyclades_proc_show(struct seq_file *m, void *v)
(cur_jifs - info->idle_stats.recv_idle)/
HZ, info->idle_stats.overruns,
/* FIXME: double check locking */
- (long)info->port.tty->ldisc.ops->num);
+ (long)info->port.tty->ldisc->ops->num);
else
seq_printf(m, "%3d %8lu %10lu %8lu "
"%10lu %8lu %9lu %6ld\n",
@@ -5386,11 +5346,11 @@ static void __exit cy_cleanup_module(void)
/* clear interrupt */
cy_writeb(card->base_addr + Cy_ClrIntr, 0);
iounmap(card->base_addr);
- if (card->ctl_addr)
- iounmap(card->ctl_addr);
+ if (card->ctl_addr.p9050)
+ iounmap(card->ctl_addr.p9050);
if (card->irq
#ifndef CONFIG_CYZ_INTR
- && !IS_CYC_Z(*card)
+ && !cy_is_Z(card)
#endif /* CONFIG_CYZ_INTR */
)
free_irq(card->irq, card);
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index af7c13ca9493..abef1f7d84fe 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -745,7 +745,7 @@ static int epca_carrier_raised(struct tty_port *port)
return 0;
}
-static void epca_raise_dtr_rts(struct tty_port *port)
+static void epca_dtr_rts(struct tty_port *port, int onoff)
{
}
@@ -925,7 +925,7 @@ static const struct tty_operations pc_ops = {
static const struct tty_port_operations epca_port_ops = {
.carrier_raised = epca_carrier_raised,
- .raise_dtr_rts = epca_raise_dtr_rts,
+ .dtr_rts = epca_dtr_rts,
};
static int info_open(struct tty_struct *tty, struct file *filp)
@@ -1518,7 +1518,7 @@ static void doevent(int crd)
if (event & MODEMCHG_IND) {
/* A modem signal change has been indicated */
ch->imodem = mstat;
- if (test_bit(ASYNC_CHECK_CD, &ch->port.flags)) {
+ if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) {
/* We are now receiving dcd */
if (mstat & ch->dcd)
wake_up_interruptible(&ch->port.open_wait);
@@ -1765,9 +1765,9 @@ static void epcaparam(struct tty_struct *tty, struct channel *ch)
* that the driver will wait on carrier detect.
*/
if (ts->c_cflag & CLOCAL)
- clear_bit(ASYNC_CHECK_CD, &ch->port.flags);
+ clear_bit(ASYNCB_CHECK_CD, &ch->port.flags);
else
- set_bit(ASYNC_CHECK_CD, &ch->port.flags);
+ set_bit(ASYNCB_CHECK_CD, &ch->port.flags);
mval = ch->m_dtr | ch->m_rts;
} /* End CBAUD not detected */
iflag = termios2digi_i(ch, ts->c_iflag);
@@ -2114,8 +2114,8 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
tty_wait_until_sent(tty, 0);
} else {
/* ldisc lock already held in ioctl */
- if (tty->ldisc.ops->flush_buffer)
- tty->ldisc.ops->flush_buffer(tty);
+ if (tty->ldisc->ops->flush_buffer)
+ tty->ldisc->ops->flush_buffer(tty);
}
unlock_kernel();
/* Fall Thru */
@@ -2244,7 +2244,8 @@ static void do_softint(struct work_struct *work)
if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) {
tty_hangup(tty);
wake_up_interruptible(&ch->port.open_wait);
- clear_bit(ASYNC_NORMAL_ACTIVE, &ch->port.flags);
+ clear_bit(ASYNCB_NORMAL_ACTIVE,
+ &ch->port.flags);
}
}
tty_kref_put(tty);
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 340ba4f9dc54..4a9f3492b921 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -224,7 +224,7 @@ static void hpet_timer_set_irq(struct hpet_dev *devp)
break;
}
- gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE,
+ gsi = acpi_register_gsi(NULL, irq, ACPI_LEVEL_SENSITIVE,
ACPI_ACTIVE_LOW);
if (gsi > 0)
break;
@@ -939,7 +939,7 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
irqp = &res->data.extended_irq;
for (i = 0; i < irqp->interrupt_count; i++) {
- irq = acpi_register_gsi(irqp->interrupts[i],
+ irq = acpi_register_gsi(NULL, irqp->interrupts[i],
irqp->triggering, irqp->polarity);
if (irq < 0)
return AE_ERROR;
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 86e83f883139..32216b623248 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -35,13 +35,13 @@ static DECLARE_COMPLETION(have_data);
static void random_recv_done(struct virtqueue *vq)
{
- int len;
+ unsigned int len;
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
if (!vq->vq_ops->get_buf(vq, &len))
return;
- data_left = len / sizeof(random_data[0]);
+ data_left += len;
complete(&have_data);
}
@@ -49,7 +49,7 @@ static void register_buffer(void)
{
struct scatterlist sg;
- sg_init_one(&sg, random_data, RANDOM_DATA_SIZE);
+ sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left);
/* There should always be room for one buffer. */
if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0)
BUG();
@@ -59,24 +59,32 @@ static void register_buffer(void)
/* At least we don't udelay() in a loop like some other drivers. */
static int virtio_data_present(struct hwrng *rng, int wait)
{
- if (data_left)
+ if (data_left >= sizeof(u32))
return 1;
+again:
if (!wait)
return 0;
wait_for_completion(&have_data);
+
+ /* Not enough? Re-register. */
+ if (unlikely(data_left < sizeof(u32))) {
+ register_buffer();
+ goto again;
+ }
+
return 1;
}
/* virtio_data_present() must have succeeded before this is called. */
static int virtio_data_read(struct hwrng *rng, u32 *data)
{
- BUG_ON(!data_left);
-
- *data = random_data[--data_left];
+ BUG_ON(data_left < sizeof(u32));
+ data_left -= sizeof(u32);
+ *data = random_data[data_left / 4];
- if (!data_left) {
+ if (data_left < sizeof(u32)) {
init_completion(&have_data);
register_buffer();
}
@@ -94,13 +102,13 @@ static int virtrng_probe(struct virtio_device *vdev)
int err;
/* We expect a single virtqueue. */
- vq = vdev->config->find_vq(vdev, 0, random_recv_done);
+ vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vq))
return PTR_ERR(vq);
err = hwrng_register(&virtio_hwrng);
if (err) {
- vdev->config->del_vq(vq);
+ vdev->config->del_vqs(vdev);
return err;
}
@@ -112,7 +120,7 @@ static void virtrng_remove(struct virtio_device *vdev)
{
vdev->config->reset(vdev);
hwrng_unregister(&virtio_hwrng);
- vdev->config->del_vq(vq);
+ vdev->config->del_vqs(vdev);
}
static struct virtio_device_id id_table[] = {
diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c
index 0061e18aff60..0d10b89218ed 100644
--- a/drivers/char/ip2/i2lib.c
+++ b/drivers/char/ip2/i2lib.c
@@ -868,11 +868,11 @@ i2Input(i2ChanStrPtr pCh)
amountToMove = count;
}
// Move the first block
- pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY,
+ pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
&(pCh->Ibuf[stripIndex]), NULL, amountToMove );
// If we needed to wrap, do the second data move
if (count > amountToMove) {
- pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY,
+ pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
pCh->Ibuf, NULL, count - amountToMove );
}
// Bump and wrap the stripIndex all at once by the amount of data read. This
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index afd9247cf082..517271c762e6 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -1315,8 +1315,8 @@ static inline void isig(int sig, struct tty_struct *tty, int flush)
if (tty->pgrp)
kill_pgrp(tty->pgrp, sig, 1);
if (flush || !L_NOFLSH(tty)) {
- if ( tty->ldisc.ops->flush_buffer )
- tty->ldisc.ops->flush_buffer(tty);
+ if ( tty->ldisc->ops->flush_buffer )
+ tty->ldisc->ops->flush_buffer(tty);
i2InputFlush( tty->driver_data );
}
}
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index aa83a0865ec1..09050797c76a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -2856,6 +2856,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
/* Assume a single IPMB channel at zero. */
intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
+ intf->curr_channel = IPMI_MAX_CHANNELS;
}
if (rv == 0)
@@ -3648,13 +3649,13 @@ static int handle_new_recv_msg(ipmi_smi_t intf,
}
/*
- ** We need to make sure the channels have been initialized.
- ** The channel_handler routine will set the "curr_channel"
- ** equal to or greater than IPMI_MAX_CHANNELS when all the
- ** channels for this interface have been initialized.
- */
+ * We need to make sure the channels have been initialized.
+ * The channel_handler routine will set the "curr_channel"
+ * equal to or greater than IPMI_MAX_CHANNELS when all the
+ * channels for this interface have been initialized.
+ */
if (intf->curr_channel < IPMI_MAX_CHANNELS) {
- requeue = 1; /* Just put the message back for now */
+ requeue = 0; /* Throw the message away */
goto out;
}
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index a59eac584d16..4d745a89504f 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -329,7 +329,7 @@ static inline void drop_rts(struct isi_port *port)
/* card->lock MUST NOT be held */
-static void isicom_raise_dtr_rts(struct tty_port *port)
+static void isicom_dtr_rts(struct tty_port *port, int on)
{
struct isi_port *ip = container_of(port, struct isi_port, port);
struct isi_board *card = ip->card;
@@ -339,10 +339,17 @@ static void isicom_raise_dtr_rts(struct tty_port *port)
if (!lock_card(card))
return;
- outw(0x8000 | (channel << card->shift_count) | 0x02, base);
- outw(0x0f04, base);
- InterruptTheCard(base);
- ip->status |= (ISI_DTR | ISI_RTS);
+ if (on) {
+ outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+ outw(0x0f04, base);
+ InterruptTheCard(base);
+ ip->status |= (ISI_DTR | ISI_RTS);
+ } else {
+ outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+ outw(0x0C04, base);
+ InterruptTheCard(base);
+ ip->status &= ~(ISI_DTR | ISI_RTS);
+ }
unlock_card(card);
}
@@ -1339,7 +1346,7 @@ static const struct tty_operations isicom_ops = {
static const struct tty_port_operations isicom_port_ops = {
.carrier_raised = isicom_carrier_raised,
- .raise_dtr_rts = isicom_raise_dtr_rts,
+ .dtr_rts = isicom_dtr_rts,
};
static int __devinit reset_card(struct pci_dev *pdev,
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index fff19f7e29d2..e18800c400b1 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -1140,14 +1140,14 @@ static int stli_carrier_raised(struct tty_port *port)
return (portp->sigs & TIOCM_CD) ? 1 : 0;
}
-static void stli_raise_dtr_rts(struct tty_port *port)
+static void stli_dtr_rts(struct tty_port *port, int on)
{
struct stliport *portp = container_of(port, struct stliport, port);
struct stlibrd *brdp = stli_brds[portp->brdnr];
- stli_mkasysigs(&portp->asig, 1, 1);
+ stli_mkasysigs(&portp->asig, on, on);
if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
sizeof(asysigs_t), 0) < 0)
- printk(KERN_WARNING "istallion: dtr raise failed.\n");
+ printk(KERN_WARNING "istallion: dtr set failed.\n");
}
@@ -4417,7 +4417,7 @@ static const struct tty_operations stli_ops = {
static const struct tty_port_operations stli_port_ops = {
.carrier_raised = stli_carrier_raised,
- .raise_dtr_rts = stli_raise_dtr_rts,
+ .dtr_rts = stli_dtr_rts,
};
/*****************************************************************************/
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 8f05c38c2f06..f96d0bef855e 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -694,6 +694,8 @@ static ssize_t read_zero(struct file * file, char __user * buf,
written += chunk - unwritten;
if (unwritten)
break;
+ if (signal_pending(current))
+ return written ? written : -ERESTARTSYS;
buf += chunk;
count -= chunk;
cond_resched();
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index 4a4cab73d0be..65b6ff2442c6 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -1184,6 +1184,11 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
return -ENODEV;
}
+ if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
+ mutex_unlock(&moxa_openlock);
+ return -ENODEV;
+ }
+
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
ch->port.count++;
tty->driver_data = ch;
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index a420e8d437dd..9533f43a30bb 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -547,14 +547,18 @@ static int mxser_carrier_raised(struct tty_port *port)
return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0;
}
-static void mxser_raise_dtr_rts(struct tty_port *port)
+static void mxser_dtr_rts(struct tty_port *port, int on)
{
struct mxser_port *mp = container_of(port, struct mxser_port, port);
unsigned long flags;
spin_lock_irqsave(&mp->slock, flags);
- outb(inb(mp->ioaddr + UART_MCR) |
- UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
+ if (on)
+ outb(inb(mp->ioaddr + UART_MCR) |
+ UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
+ else
+ outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS),
+ mp->ioaddr + UART_MCR);
spin_unlock_irqrestore(&mp->slock, flags);
}
@@ -2356,7 +2360,7 @@ static const struct tty_operations mxser_ops = {
struct tty_port_operations mxser_port_ops = {
.carrier_raised = mxser_carrier_raised,
- .raise_dtr_rts = mxser_raise_dtr_rts,
+ .dtr_rts = mxser_dtr_rts,
};
/*
@@ -2711,7 +2715,7 @@ static int __init mxser_module_init(void)
continue;
brd = &mxser_boards[m];
- retval = mxser_get_ISA_conf(!ioaddr[b], brd);
+ retval = mxser_get_ISA_conf(ioaddr[b], brd);
if (retval <= 0) {
brd->info = NULL;
continue;
diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
index bacb3e2872ae..461ece591a5b 100644
--- a/drivers/char/n_hdlc.c
+++ b/drivers/char/n_hdlc.c
@@ -342,8 +342,8 @@ static int n_hdlc_tty_open (struct tty_struct *tty)
#endif
/* Flush any pending characters in the driver and discipline. */
- if (tty->ldisc.ops->flush_buffer)
- tty->ldisc.ops->flush_buffer(tty);
+ if (tty->ldisc->ops->flush_buffer)
+ tty->ldisc->ops->flush_buffer(tty);
tty_driver_flush_buffer(tty);
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index f6f0e4ec2b51..94a5d5020abc 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -73,24 +73,6 @@
#define ECHO_OP_SET_CANON_COL 0x81
#define ECHO_OP_ERASE_TAB 0x82
-static inline unsigned char *alloc_buf(void)
-{
- gfp_t prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
-
- if (PAGE_SIZE != N_TTY_BUF_SIZE)
- return kmalloc(N_TTY_BUF_SIZE, prio);
- else
- return (unsigned char *)__get_free_page(prio);
-}
-
-static inline void free_buf(unsigned char *buf)
-{
- if (PAGE_SIZE != N_TTY_BUF_SIZE)
- kfree(buf);
- else
- free_page((unsigned long) buf);
-}
-
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
unsigned char __user *ptr)
{
@@ -1558,11 +1540,11 @@ static void n_tty_close(struct tty_struct *tty)
{
n_tty_flush_buffer(tty);
if (tty->read_buf) {
- free_buf(tty->read_buf);
+ kfree(tty->read_buf);
tty->read_buf = NULL;
}
if (tty->echo_buf) {
- free_buf(tty->echo_buf);
+ kfree(tty->echo_buf);
tty->echo_buf = NULL;
}
}
@@ -1584,17 +1566,16 @@ static int n_tty_open(struct tty_struct *tty)
/* These are ugly. Currently a malloc failure here can panic */
if (!tty->read_buf) {
- tty->read_buf = alloc_buf();
+ tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
if (!tty->read_buf)
return -ENOMEM;
}
if (!tty->echo_buf) {
- tty->echo_buf = alloc_buf();
+ tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
+
if (!tty->echo_buf)
return -ENOMEM;
}
- memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
- memset(tty->echo_buf, 0, N_TTY_BUF_SIZE);
reset_buffer_flags(tty);
tty->column = 0;
n_tty_set_termios(tty, NULL);
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 19d79fc54461..77b364889224 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -383,7 +383,7 @@ static void async_mode(MGSLPC_INFO *info);
static void tx_timeout(unsigned long context);
static int carrier_raised(struct tty_port *port);
-static void raise_dtr_rts(struct tty_port *port);
+static void dtr_rts(struct tty_port *port, int onoff);
#if SYNCLINK_GENERIC_HDLC
#define dev_to_port(D) (dev_to_hdlc(D)->priv)
@@ -513,7 +513,7 @@ static void ldisc_receive_buf(struct tty_struct *tty,
static const struct tty_port_operations mgslpc_port_ops = {
.carrier_raised = carrier_raised,
- .raise_dtr_rts = raise_dtr_rts
+ .dtr_rts = dtr_rts
};
static int mgslpc_probe(struct pcmcia_device *link)
@@ -2528,13 +2528,16 @@ static int carrier_raised(struct tty_port *port)
return 0;
}
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int onoff)
{
MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
unsigned long flags;
spin_lock_irqsave(&info->lock,flags);
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ if (onoff)
+ info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ else
+ info->serial_signals &= ~SerialSignal_RTS + SerialSignal_DTR;
set_signals(info);
spin_unlock_irqrestore(&info->lock,flags);
}
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 31038a0052a2..5acd29e6e043 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -30,7 +30,6 @@
#include <asm/system.h>
-/* These are global because they are accessed in tty_io.c */
#ifdef CONFIG_UNIX98_PTYS
static struct tty_driver *ptm_driver;
static struct tty_driver *pts_driver;
@@ -111,7 +110,7 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
c = to->receive_room;
if (c > count)
c = count;
- to->ldisc.ops->receive_buf(to, buf, NULL, c);
+ to->ldisc->ops->receive_buf(to, buf, NULL, c);
return c;
}
@@ -149,11 +148,11 @@ static int pty_chars_in_buffer(struct tty_struct *tty)
int count;
/* We should get the line discipline lock for "tty->link" */
- if (!to || !to->ldisc.ops->chars_in_buffer)
+ if (!to || !to->ldisc->ops->chars_in_buffer)
return 0;
/* The ldisc must report 0 if no characters available to be read */
- count = to->ldisc.ops->chars_in_buffer(to);
+ count = to->ldisc->ops->chars_in_buffer(to);
if (tty->driver->subtype == PTY_TYPE_SLAVE)
return count;
@@ -187,8 +186,8 @@ static void pty_flush_buffer(struct tty_struct *tty)
if (!to)
return;
- if (to->ldisc.ops->flush_buffer)
- to->ldisc.ops->flush_buffer(to);
+ if (to->ldisc->ops->flush_buffer)
+ to->ldisc->ops->flush_buffer(to);
if (to->packet) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index f824ef8a9273..8c7444857a4b 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1665,15 +1665,20 @@ EXPORT_SYMBOL(secure_dccp_sequence_number);
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
+DEFINE_PER_CPU(__u32 [4], get_random_int_hash);
unsigned int get_random_int(void)
{
- /*
- * Use IP's RNG. It suits our purpose perfectly: it re-keys itself
- * every second, from the entropy pool (and thus creates a limited
- * drain on it), and uses halfMD4Transform within the second. We
- * also mix it with jiffies and the PID:
- */
- return secure_ip_id((__force __be32)(current->pid + jiffies));
+ struct keydata *keyptr;
+ __u32 *hash = get_cpu_var(get_random_int_hash);
+ int ret;
+
+ keyptr = get_keyptr();
+ hash[0] += current->pid + jiffies + get_cycles();
+
+ ret = half_md4_transform(hash, keyptr->secret);
+ put_cpu_var(get_random_int_hash);
+
+ return ret;
}
/*
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 20d90e6a6e50..db32f0e4c7dd 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -71,7 +71,7 @@ static int raw_open(struct inode *inode, struct file *filp)
err = bd_claim(bdev, raw_open);
if (err)
goto out1;
- err = set_blocksize(bdev, bdev_hardsect_size(bdev));
+ err = set_blocksize(bdev, bdev_logical_block_size(bdev));
if (err)
goto out2;
filp->f_flags |= O_DIRECT;
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index f59fc5cea067..63d5b628477a 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -872,11 +872,16 @@ static int carrier_raised(struct tty_port *port)
return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0;
}
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int on)
{
struct r_port *info = container_of(port, struct r_port, port);
- sSetDTR(&info->channel);
- sSetRTS(&info->channel);
+ if (on) {
+ sSetDTR(&info->channel);
+ sSetRTS(&info->channel);
+ } else {
+ sClrDTR(&info->channel);
+ sClrRTS(&info->channel);
+ }
}
/*
@@ -934,7 +939,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
/*
* Info->count is now 1; so it's safe to sleep now.
*/
- if (!test_bit(ASYNC_INITIALIZED, &port->flags)) {
+ if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
cp = &info->channel;
sSetRxTrigger(cp, TRIG_1);
if (sGetChanStatus(cp) & CD_ACT)
@@ -958,7 +963,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
sEnRxFIFO(cp);
sEnTransmit(cp);
- set_bit(ASYNC_INITIALIZED, &info->port.flags);
+ set_bit(ASYNCB_INITIALIZED, &info->port.flags);
/*
* Set up the tty->alt_speed kludge
@@ -1641,7 +1646,7 @@ static int rp_write(struct tty_struct *tty,
/* Write remaining data into the port's xmit_buf */
while (1) {
/* Hung up ? */
- if (!test_bit(ASYNC_NORMAL_ACTIVE, &info->port.flags))
+ if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags))
goto end;
c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1);
c = min(c, XMIT_BUF_SIZE - info->xmit_head);
@@ -2250,7 +2255,7 @@ static const struct tty_operations rocket_ops = {
static const struct tty_port_operations rocket_port_ops = {
.carrier_raised = carrier_raised,
- .raise_dtr_rts = raise_dtr_rts,
+ .dtr_rts = dtr_rts,
};
/*
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
index cb8ca5698963..f97b9e848064 100644
--- a/drivers/char/selection.c
+++ b/drivers/char/selection.c
@@ -327,7 +327,7 @@ int paste_selection(struct tty_struct *tty)
}
count = sel_buffer_lth - pasted;
count = min(count, tty->receive_room);
- tty->ldisc.ops->receive_buf(tty, sel_buffer + pasted,
+ tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
NULL, count);
pasted += count;
}
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 2ad813a801dc..53e504f41b20 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -772,11 +772,11 @@ static int stl_carrier_raised(struct tty_port *port)
return (portp->sigs & TIOCM_CD) ? 1 : 0;
}
-static void stl_raise_dtr_rts(struct tty_port *port)
+static void stl_dtr_rts(struct tty_port *port, int on)
{
struct stlport *portp = container_of(port, struct stlport, port);
/* Takes brd_lock internally */
- stl_setsignals(portp, 1, 1);
+ stl_setsignals(portp, on, on);
}
/*****************************************************************************/
@@ -2547,7 +2547,7 @@ static const struct tty_operations stl_ops = {
static const struct tty_port_operations stl_port_ops = {
.carrier_raised = stl_carrier_raised,
- .raise_dtr_rts = stl_raise_dtr_rts,
+ .dtr_rts = stl_dtr_rts,
};
/*****************************************************************************/
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index afd0b26ca056..afded3a2379c 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -3247,13 +3247,16 @@ static int carrier_raised(struct tty_port *port)
return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
}
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int on)
{
struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
unsigned long flags;
spin_lock_irqsave(&info->irq_spinlock,flags);
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ if (on)
+ info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ else
+ info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
usc_set_serial_signals(info);
spin_unlock_irqrestore(&info->irq_spinlock,flags);
}
@@ -4258,7 +4261,7 @@ static void mgsl_add_device( struct mgsl_struct *info )
static const struct tty_port_operations mgsl_port_ops = {
.carrier_raised = carrier_raised,
- .raise_dtr_rts = raise_dtr_rts,
+ .dtr_rts = dtr_rts,
};
diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c
index 5e256494686a..1386625fc4ca 100644
--- a/drivers/char/synclink_gt.c
+++ b/drivers/char/synclink_gt.c
@@ -214,6 +214,7 @@ struct slgt_desc
#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b))
#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b))
#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
+#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
#define desc_count(a) (le16_to_cpu((a).count))
#define desc_status(a) (le16_to_cpu((a).status))
#define desc_complete(a) (le16_to_cpu((a).status) & BIT15)
@@ -297,6 +298,7 @@ struct slgt_info {
u32 max_frame_size; /* as set by device config */
unsigned int rbuf_fill_level;
+ unsigned int rx_pio;
unsigned int if_mode;
unsigned int base_clock;
@@ -331,6 +333,8 @@ struct slgt_info {
struct slgt_desc *rbufs;
unsigned int rbuf_current;
unsigned int rbuf_index;
+ unsigned int rbuf_fill_index;
+ unsigned short rbuf_fill_count;
unsigned int tbuf_count;
struct slgt_desc *tbufs;
@@ -2110,6 +2114,40 @@ static void ri_change(struct slgt_info *info, unsigned short status)
info->pending_bh |= BH_STATUS;
}
+static void isr_rxdata(struct slgt_info *info)
+{
+ unsigned int count = info->rbuf_fill_count;
+ unsigned int i = info->rbuf_fill_index;
+ unsigned short reg;
+
+ while (rd_reg16(info, SSR) & IRQ_RXDATA) {
+ reg = rd_reg16(info, RDR);
+ DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
+ if (desc_complete(info->rbufs[i])) {
+ /* all buffers full */
+ rx_stop(info);
+ info->rx_restart = 1;
+ continue;
+ }
+ info->rbufs[i].buf[count++] = (unsigned char)reg;
+ /* async mode saves status byte to buffer for each data byte */
+ if (info->params.mode == MGSL_MODE_ASYNC)
+ info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
+ if (count == info->rbuf_fill_level || (reg & BIT10)) {
+ /* buffer full or end of frame */
+ set_desc_count(info->rbufs[i], count);
+ set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
+ info->rbuf_fill_count = count = 0;
+ if (++i == info->rbuf_count)
+ i = 0;
+ info->pending_bh |= BH_RECEIVE;
+ }
+ }
+
+ info->rbuf_fill_index = i;
+ info->rbuf_fill_count = count;
+}
+
static void isr_serial(struct slgt_info *info)
{
unsigned short status = rd_reg16(info, SSR);
@@ -2125,6 +2163,8 @@ static void isr_serial(struct slgt_info *info)
if (info->tx_count)
isr_txeom(info, status);
}
+ if (info->rx_pio && (status & IRQ_RXDATA))
+ isr_rxdata(info);
if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
info->icount.brk++;
/* process break detection if tty control allows */
@@ -2141,7 +2181,8 @@ static void isr_serial(struct slgt_info *info)
} else {
if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
isr_txeom(info, status);
-
+ if (info->rx_pio && (status & IRQ_RXDATA))
+ isr_rxdata(info);
if (status & IRQ_RXIDLE) {
if (status & RXIDLE)
info->icount.rxidle++;
@@ -2642,6 +2683,10 @@ static int rx_enable(struct slgt_info *info, int enable)
return -EINVAL;
}
info->rbuf_fill_level = rbuf_fill_level;
+ if (rbuf_fill_level < 128)
+ info->rx_pio = 1; /* PIO mode */
+ else
+ info->rx_pio = 0; /* DMA mode */
rx_stop(info); /* restart receiver to use new fill level */
}
@@ -3099,13 +3144,16 @@ static int carrier_raised(struct tty_port *port)
return (info->signals & SerialSignal_DCD) ? 1 : 0;
}
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int on)
{
unsigned long flags;
struct slgt_info *info = container_of(port, struct slgt_info, port);
spin_lock_irqsave(&info->lock,flags);
- info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+ if (on)
+ info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+ else
+ info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
set_signals(info);
spin_unlock_irqrestore(&info->lock,flags);
}
@@ -3419,7 +3467,7 @@ static void add_device(struct slgt_info *info)
static const struct tty_port_operations slgt_port_ops = {
.carrier_raised = carrier_raised,
- .raise_dtr_rts = raise_dtr_rts,
+ .dtr_rts = dtr_rts,
};
/*
@@ -3841,15 +3889,27 @@ static void rx_start(struct slgt_info *info)
rdma_reset(info);
reset_rbufs(info);
- /* set 1st descriptor address */
- wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
-
- if (info->params.mode != MGSL_MODE_ASYNC) {
- /* enable rx DMA and DMA interrupt */
- wr_reg32(info, RDCSR, (BIT2 + BIT0));
+ if (info->rx_pio) {
+ /* rx request when rx FIFO not empty */
+ wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
+ slgt_irq_on(info, IRQ_RXDATA);
+ if (info->params.mode == MGSL_MODE_ASYNC) {
+ /* enable saving of rx status */
+ wr_reg32(info, RDCSR, BIT6);
+ }
} else {
- /* enable saving of rx status, rx DMA and DMA interrupt */
- wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
+ /* rx request when rx FIFO half full */
+ wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
+ /* set 1st descriptor address */
+ wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
+
+ if (info->params.mode != MGSL_MODE_ASYNC) {
+ /* enable rx DMA and DMA interrupt */
+ wr_reg32(info, RDCSR, (BIT2 + BIT0));
+ } else {
+ /* enable saving of rx status, rx DMA and DMA interrupt */
+ wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
+ }
}
slgt_irq_on(info, IRQ_RXOVER);
@@ -4467,6 +4527,8 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last
static void reset_rbufs(struct slgt_info *info)
{
free_rbufs(info, 0, info->rbuf_count - 1);
+ info->rbuf_fill_index = 0;
+ info->rbuf_fill_count = 0;
}
/*
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
index 26de60efe4b2..6f727e3c53ad 100644
--- a/drivers/char/synclinkmp.c
+++ b/drivers/char/synclinkmp.c
@@ -3277,13 +3277,16 @@ static int carrier_raised(struct tty_port *port)
return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
}
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int on)
{
SLMP_INFO *info = container_of(port, SLMP_INFO, port);
unsigned long flags;
spin_lock_irqsave(&info->lock,flags);
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ if (on)
+ info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ else
+ info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
set_signals(info);
spin_unlock_irqrestore(&info->lock,flags);
}
@@ -3746,7 +3749,7 @@ static void add_device(SLMP_INFO *info)
static const struct tty_port_operations port_ops = {
.carrier_raised = carrier_raised,
- .raise_dtr_rts = raise_dtr_rts,
+ .dtr_rts = dtr_rts,
};
/* Allocate and initialize a device instance structure
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index b0a6a3e51924..39a05b5fa9cb 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -25,6 +25,7 @@
#include <linux/kbd_kern.h>
#include <linux/proc_fs.h>
#include <linux/quotaops.h>
+#include <linux/perf_counter.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/suspend.h>
@@ -243,6 +244,7 @@ static void sysrq_handle_showregs(int key, struct tty_struct *tty)
struct pt_regs *regs = get_irq_regs();
if (regs)
show_regs(regs);
+ perf_counter_print_debug();
}
static struct sysrq_key_op sysrq_showregs_op = {
.handler = sysrq_handle_showregs,
@@ -406,7 +408,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
&sysrq_showlocks_op, /* d */
&sysrq_term_op, /* e */
&sysrq_moom_op, /* f */
- /* g: May be registered by ppc for kgdb */
+ /* g: May be registered for the kernel debugger */
NULL, /* g */
NULL, /* h - reserved for help */
&sysrq_kill_op, /* i */
@@ -431,7 +433,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
&sysrq_sync_op, /* s */
&sysrq_showstate_op, /* t */
&sysrq_mountro_op, /* u */
- /* v: May be registered at init time by SMP VOYAGER */
+ /* v: May be registered for frame buffer console restore */
NULL, /* v */
&sysrq_showstate_blocked_op, /* w */
/* x: May be registered on ppc/powerpc for xmon */
diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c
index ed306eb1057f..0c2f55a38b95 100644
--- a/drivers/char/tpm/tpm_bios.c
+++ b/drivers/char/tpm/tpm_bios.c
@@ -212,7 +212,8 @@ static int get_event_name(char *dest, struct tcpa_event *event,
unsigned char * event_entry)
{
const char *name = "";
- char data[40] = "";
+ /* 41 so there is room for 40 data and 1 nul */
+ char data[41] = "";
int i, n_len = 0, d_len = 0;
struct tcpa_pc_event *pc_event;
diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c
index 55ba6f142883..ac16fbec72d0 100644
--- a/drivers/char/tty_audit.c
+++ b/drivers/char/tty_audit.c
@@ -29,10 +29,7 @@ static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
goto err;
- if (PAGE_SIZE != N_TTY_BUF_SIZE)
- buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
- else
- buf->data = (unsigned char *)__get_free_page(GFP_KERNEL);
+ buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
if (!buf->data)
goto err_buf;
atomic_set(&buf->count, 1);
@@ -52,10 +49,7 @@ err:
static void tty_audit_buf_free(struct tty_audit_buf *buf)
{
WARN_ON(buf->valid != 0);
- if (PAGE_SIZE != N_TTY_BUF_SIZE)
- kfree(buf->data);
- else
- free_page((unsigned long)buf->data);
+ kfree(buf->data);
kfree(buf);
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 66b99a2049e3..939e198d7670 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -295,7 +295,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
struct tty_driver *p, *res = NULL;
int tty_line = 0;
int len;
- char *str;
+ char *str, *stp;
for (str = name; *str; str++)
if ((*str >= '0' && *str <= '9') || *str == ',')
@@ -311,13 +311,14 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
list_for_each_entry(p, &tty_drivers, tty_drivers) {
if (strncmp(name, p->name, len) != 0)
continue;
- if (*str == ',')
- str++;
- if (*str == '\0')
- str = NULL;
+ stp = str;
+ if (*stp == ',')
+ stp++;
+ if (*stp == '\0')
+ stp = NULL;
if (tty_line >= 0 && tty_line <= p->num && p->ops &&
- p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
+ p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
res = tty_driver_kref_get(p);
*line = tty_line;
break;
@@ -470,43 +471,6 @@ void tty_wakeup(struct tty_struct *tty)
EXPORT_SYMBOL_GPL(tty_wakeup);
/**
- * tty_ldisc_flush - flush line discipline queue
- * @tty: tty
- *
- * Flush the line discipline queue (if any) for this tty. If there
- * is no line discipline active this is a no-op.
- */
-
-void tty_ldisc_flush(struct tty_struct *tty)
-{
- struct tty_ldisc *ld = tty_ldisc_ref(tty);
- if (ld) {
- if (ld->ops->flush_buffer)
- ld->ops->flush_buffer(tty);
- tty_ldisc_deref(ld);
- }
- tty_buffer_flush(tty);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_flush);
-
-/**
- * tty_reset_termios - reset terminal state
- * @tty: tty to reset
- *
- * Restore a terminal to the driver default state
- */
-
-static void tty_reset_termios(struct tty_struct *tty)
-{
- mutex_lock(&tty->termios_mutex);
- *tty->termios = tty->driver->init_termios;
- tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
- tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
- mutex_unlock(&tty->termios_mutex);
-}
-
-/**
* do_tty_hangup - actual handler for hangup events
* @work: tty device
*
@@ -535,7 +499,6 @@ static void do_tty_hangup(struct work_struct *work)
struct file *cons_filp = NULL;
struct file *filp, *f = NULL;
struct task_struct *p;
- struct tty_ldisc *ld;
int closecount = 0, n;
unsigned long flags;
int refs = 0;
@@ -566,40 +529,8 @@ static void do_tty_hangup(struct work_struct *work)
filp->f_op = &hung_up_tty_fops;
}
file_list_unlock();
- /*
- * FIXME! What are the locking issues here? This may me overdoing
- * things... This question is especially important now that we've
- * removed the irqlock.
- */
- ld = tty_ldisc_ref(tty);
- if (ld != NULL) {
- /* We may have no line discipline at this point */
- if (ld->ops->flush_buffer)
- ld->ops->flush_buffer(tty);
- tty_driver_flush_buffer(tty);
- if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
- ld->ops->write_wakeup)
- ld->ops->write_wakeup(tty);
- if (ld->ops->hangup)
- ld->ops->hangup(tty);
- }
- /*
- * FIXME: Once we trust the LDISC code better we can wait here for
- * ldisc completion and fix the driver call race
- */
- wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
- wake_up_interruptible_poll(&tty->read_wait, POLLIN);
- /*
- * Shutdown the current line discipline, and reset it to
- * N_TTY.
- */
- if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
- tty_reset_termios(tty);
- /* Defer ldisc switch */
- /* tty_deferred_ldisc_switch(N_TTY);
- This should get done automatically when the port closes and
- tty_release is called */
+ tty_ldisc_hangup(tty);
read_lock(&tasklist_lock);
if (tty->session) {
@@ -628,12 +559,15 @@ static void do_tty_hangup(struct work_struct *work)
read_unlock(&tasklist_lock);
spin_lock_irqsave(&tty->ctrl_lock, flags);
- tty->flags = 0;
+ clear_bit(TTY_THROTTLED, &tty->flags);
+ clear_bit(TTY_PUSH, &tty->flags);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
put_pid(tty->session);
put_pid(tty->pgrp);
tty->session = NULL;
tty->pgrp = NULL;
tty->ctrl_status = 0;
+ set_bit(TTY_HUPPED, &tty->flags);
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
/* Account for the p->signal references we killed */
@@ -659,10 +593,7 @@ static void do_tty_hangup(struct work_struct *work)
* can't yet guarantee all that.
*/
set_bit(TTY_HUPPED, &tty->flags);
- if (ld) {
- tty_ldisc_enable(tty);
- tty_ldisc_deref(ld);
- }
+ tty_ldisc_enable(tty);
unlock_kernel();
if (f)
fput(f);
@@ -2480,6 +2411,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
return tty->ops->tiocmset(tty, file, set, clear);
}
+struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+{
+ if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver->subtype == PTY_TYPE_MASTER)
+ tty = tty->link;
+ return tty;
+}
+EXPORT_SYMBOL(tty_pair_get_tty);
+
+struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+{
+ if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver->subtype == PTY_TYPE_MASTER)
+ return tty;
+ return tty->link;
+}
+EXPORT_SYMBOL(tty_pair_get_pty);
+
/*
* Split this up, as gcc can choke on it otherwise..
*/
@@ -2495,11 +2444,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL;
- real_tty = tty;
- if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
- tty->driver->subtype == PTY_TYPE_MASTER)
- real_tty = tty->link;
-
+ real_tty = tty_pair_get_tty(tty);
/*
* Factor out some common prep work
@@ -2555,7 +2500,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case TIOCGSID:
return tiocgsid(tty, real_tty, p);
case TIOCGETD:
- return put_user(tty->ldisc.ops->num, (int __user *)p);
+ return put_user(tty->ldisc->ops->num, (int __user *)p);
case TIOCSETD:
return tiocsetd(tty, p);
/*
@@ -2770,6 +2715,7 @@ void initialize_tty_struct(struct tty_struct *tty,
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty);
mutex_init(&tty->termios_mutex);
+ mutex_init(&tty->ldisc_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 6f4c7d0a53bf..8116bb1c8f80 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -97,14 +97,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);
* @tty: terminal
*
* Indicate that a tty should stop transmitting data down the stack.
+ * Takes the termios mutex to protect against parallel throttle/unthrottle
+ * and also to ensure the driver can consistently reference its own
+ * termios data at this point when implementing software flow control.
*/
void tty_throttle(struct tty_struct *tty)
{
+ mutex_lock(&tty->termios_mutex);
/* check TTY_THROTTLED first so it indicates our state */
if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->throttle)
tty->ops->throttle(tty);
+ mutex_unlock(&tty->termios_mutex);
}
EXPORT_SYMBOL(tty_throttle);
@@ -113,13 +118,21 @@ EXPORT_SYMBOL(tty_throttle);
* @tty: terminal
*
* Indicate that a tty may continue transmitting data down the stack.
+ * Takes the termios mutex to protect against parallel throttle/unthrottle
+ * and also to ensure the driver can consistently reference its own
+ * termios data at this point when implementing software flow control.
+ *
+ * Drivers should however remember that the stack can issue a throttle,
+ * then change flow control method, then unthrottle.
*/
void tty_unthrottle(struct tty_struct *tty)
{
+ mutex_lock(&tty->termios_mutex);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
+ mutex_unlock(&tty->termios_mutex);
}
EXPORT_SYMBOL(tty_unthrottle);
@@ -613,9 +626,25 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
return 0;
}
+static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
+{
+ mutex_lock(&tty->termios_mutex);
+ memcpy(kterm, tty->termios, sizeof(struct ktermios));
+ mutex_unlock(&tty->termios_mutex);
+}
+
+static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
+{
+ mutex_lock(&tty->termios_mutex);
+ memcpy(kterm, tty->termios_locked, sizeof(struct ktermios));
+ mutex_unlock(&tty->termios_mutex);
+}
+
static int get_termio(struct tty_struct *tty, struct termio __user *termio)
{
- if (kernel_termios_to_user_termio(termio, tty->termios))
+ struct ktermios kterm;
+ copy_termios(tty, &kterm);
+ if (kernel_termios_to_user_termio(termio, &kterm))
return -EFAULT;
return 0;
}
@@ -917,6 +946,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
struct tty_struct *real_tty;
void __user *p = (void __user *)arg;
int ret = 0;
+ struct ktermios kterm;
+ struct termiox ktermx;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
@@ -952,23 +983,20 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
return set_termios(real_tty, p, TERMIOS_OLD);
#ifndef TCGETS2
case TCGETS:
- mutex_lock(&real_tty->termios_mutex);
- if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios))
+ copy_termios(real_tty, &kterm);
+ if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
ret = -EFAULT;
- mutex_unlock(&real_tty->termios_mutex);
return ret;
#else
case TCGETS:
- mutex_lock(&real_tty->termios_mutex);
- if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios))
+ copy_termios(real_tty, &kterm);
+ if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
ret = -EFAULT;
- mutex_unlock(&real_tty->termios_mutex);
return ret;
case TCGETS2:
- mutex_lock(&real_tty->termios_mutex);
- if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios))
+ copy_termios(real_tty, &kterm);
+ if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
ret = -EFAULT;
- mutex_unlock(&real_tty->termios_mutex);
return ret;
case TCSETSF2:
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT);
@@ -987,34 +1015,36 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
return set_termios(real_tty, p, TERMIOS_TERMIO);
#ifndef TCGETS2
case TIOCGLCKTRMIOS:
- mutex_lock(&real_tty->termios_mutex);
- if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked))
+ copy_termios_locked(real_tty, &kterm);
+ if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
ret = -EFAULT;
- mutex_unlock(&real_tty->termios_mutex);
return ret;
case TIOCSLCKTRMIOS:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock(&real_tty->termios_mutex);
- if (user_termios_to_kernel_termios(real_tty->termios_locked,
+ copy_termios_locked(real_tty, &kterm);
+ if (user_termios_to_kernel_termios(&kterm,
(struct termios __user *) arg))
- ret = -EFAULT;
+ return -EFAULT;
+ mutex_lock(&real_tty->termios_mutex);
+ memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
mutex_unlock(&real_tty->termios_mutex);
- return ret;
+ return 0;
#else
case TIOCGLCKTRMIOS:
- mutex_lock(&real_tty->termios_mutex);
- if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked))
+ copy_termios_locked(real_tty, &kterm);
+ if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
ret = -EFAULT;
- mutex_unlock(&real_tty->termios_mutex);
return ret;
case TIOCSLCKTRMIOS:
if (!capable(CAP_SYS_ADMIN))
- ret = -EPERM;
- mutex_lock(&real_tty->termios_mutex);
- if (user_termios_to_kernel_termios_1(real_tty->termios_locked,
+ return -EPERM;
+ copy_termios_locked(real_tty, &kterm);
+ if (user_termios_to_kernel_termios_1(&kterm,
(struct termios __user *) arg))
- ret = -EFAULT;
+ return -EFAULT;
+ mutex_lock(&real_tty->termios_mutex);
+ memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
mutex_unlock(&real_tty->termios_mutex);
return ret;
#endif
@@ -1023,9 +1053,10 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
if (real_tty->termiox == NULL)
return -EINVAL;
mutex_lock(&real_tty->termios_mutex);
- if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox)))
- ret = -EFAULT;
+ memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox));
mutex_unlock(&real_tty->termios_mutex);
+ if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
+ ret = -EFAULT;
return ret;
case TCSETX:
return set_termiox(real_tty, p, 0);
@@ -1035,10 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
return set_termiox(real_tty, p, TERMIOS_FLUSH);
#endif
case TIOCGSOFTCAR:
- mutex_lock(&real_tty->termios_mutex);
- ret = put_user(C_CLOCAL(real_tty) ? 1 : 0,
+ copy_termios(real_tty, &kterm);
+ ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
(int __user *)arg);
- mutex_unlock(&real_tty->termios_mutex);
return ret;
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int __user *) arg))
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index f78f5b0127a8..39c8f86dedd4 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -115,19 +115,22 @@ EXPORT_SYMBOL(tty_unregister_ldisc);
/**
* tty_ldisc_try_get - try and reference an ldisc
* @disc: ldisc number
- * @ld: tty ldisc structure to complete
*
* Attempt to open and lock a line discipline into place. Return
- * the line discipline refcounted and assigned in ld. On an error
- * report the error code back
+ * the line discipline refcounted or an error.
*/
-static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
+static struct tty_ldisc *tty_ldisc_try_get(int disc)
{
unsigned long flags;
+ struct tty_ldisc *ld;
struct tty_ldisc_ops *ldops;
int err = -EINVAL;
-
+
+ ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
+ if (ld == NULL)
+ return ERR_PTR(-ENOMEM);
+
spin_lock_irqsave(&tty_ldisc_lock, flags);
ld->ops = NULL;
ldops = tty_ldiscs[disc];
@@ -140,17 +143,19 @@ static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
/* lock it */
ldops->refcount++;
ld->ops = ldops;
+ ld->refcount = 0;
err = 0;
}
}
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- return err;
+ if (err)
+ return ERR_PTR(err);
+ return ld;
}
/**
* tty_ldisc_get - take a reference to an ldisc
* @disc: ldisc number
- * @ld: tty line discipline structure to use
*
* Takes a reference to a line discipline. Deals with refcounts and
* module locking counts. Returns NULL if the discipline is not available.
@@ -161,52 +166,54 @@ static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
* takes tty_ldisc_lock to guard against ldisc races
*/
-static int tty_ldisc_get(int disc, struct tty_ldisc *ld)
+static struct tty_ldisc *tty_ldisc_get(int disc)
{
- int err;
+ struct tty_ldisc *ld;
if (disc < N_TTY || disc >= NR_LDISCS)
- return -EINVAL;
- err = tty_ldisc_try_get(disc, ld);
- if (err < 0) {
+ return ERR_PTR(-EINVAL);
+ ld = tty_ldisc_try_get(disc);
+ if (IS_ERR(ld)) {
request_module("tty-ldisc-%d", disc);
- err = tty_ldisc_try_get(disc, ld);
+ ld = tty_ldisc_try_get(disc);
}
- return err;
+ return ld;
}
/**
* tty_ldisc_put - drop ldisc reference
- * @disc: ldisc number
+ * @ld: ldisc
*
* Drop a reference to a line discipline. Manage refcounts and
- * module usage counts
+ * module usage counts. Free the ldisc once the recount hits zero.
*
* Locking:
* takes tty_ldisc_lock to guard against ldisc races
*/
-static void tty_ldisc_put(struct tty_ldisc_ops *ld)
+static void tty_ldisc_put(struct tty_ldisc *ld)
{
unsigned long flags;
- int disc = ld->num;
+ int disc = ld->ops->num;
+ struct tty_ldisc_ops *ldo;
BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
spin_lock_irqsave(&tty_ldisc_lock, flags);
- ld = tty_ldiscs[disc];
- BUG_ON(ld->refcount == 0);
- ld->refcount--;
- module_put(ld->owner);
+ ldo = tty_ldiscs[disc];
+ BUG_ON(ldo->refcount == 0);
+ ldo->refcount--;
+ module_put(ldo->owner);
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ kfree(ld);
}
-static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
+static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
{
return (*pos < NR_LDISCS) ? pos : NULL;
}
-static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
+static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
(*pos)++;
return (*pos < NR_LDISCS) ? pos : NULL;
@@ -219,12 +226,13 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v)
static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
{
int i = *(loff_t *)v;
- struct tty_ldisc ld;
-
- if (tty_ldisc_get(i, &ld) < 0)
+ struct tty_ldisc *ld;
+
+ ld = tty_ldisc_try_get(i);
+ if (IS_ERR(ld))
return 0;
- seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i);
- tty_ldisc_put(ld.ops);
+ seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i);
+ tty_ldisc_put(ld);
return 0;
}
@@ -263,8 +271,7 @@ const struct file_operations tty_ldiscs_proc_fops = {
static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
{
- ld->refcount = 0;
- tty->ldisc = *ld;
+ tty->ldisc = ld;
}
/**
@@ -286,7 +293,7 @@ static int tty_ldisc_try(struct tty_struct *tty)
int ret = 0;
spin_lock_irqsave(&tty_ldisc_lock, flags);
- ld = &tty->ldisc;
+ ld = tty->ldisc;
if (test_bit(TTY_LDISC, &tty->flags)) {
ld->refcount++;
ret = 1;
@@ -315,10 +322,9 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{
/* wait_event is a macro */
wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
- WARN_ON(tty->ldisc.refcount == 0);
- return &tty->ldisc;
+ WARN_ON(tty->ldisc->refcount == 0);
+ return tty->ldisc;
}
-
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
/**
@@ -335,10 +341,9 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
{
if (tty_ldisc_try(tty))
- return &tty->ldisc;
+ return tty->ldisc;
return NULL;
}
-
EXPORT_SYMBOL_GPL(tty_ldisc_ref);
/**
@@ -366,7 +371,6 @@ void tty_ldisc_deref(struct tty_ldisc *ld)
wake_up(&tty_ldisc_wait);
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
}
-
EXPORT_SYMBOL_GPL(tty_ldisc_deref);
/**
@@ -389,6 +393,26 @@ void tty_ldisc_enable(struct tty_struct *tty)
}
/**
+ * tty_ldisc_flush - flush line discipline queue
+ * @tty: tty
+ *
+ * Flush the line discipline queue (if any) for this tty. If there
+ * is no line discipline active this is a no-op.
+ */
+
+void tty_ldisc_flush(struct tty_struct *tty)
+{
+ struct tty_ldisc *ld = tty_ldisc_ref(tty);
+ if (ld) {
+ if (ld->ops->flush_buffer)
+ ld->ops->flush_buffer(tty);
+ tty_ldisc_deref(ld);
+ }
+ tty_buffer_flush(tty);
+}
+EXPORT_SYMBOL_GPL(tty_ldisc_flush);
+
+/**
* tty_set_termios_ldisc - set ldisc field
* @tty: tty structure
* @num: line discipline number
@@ -407,6 +431,39 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
mutex_unlock(&tty->termios_mutex);
}
+/**
+ * tty_ldisc_open - open a line discipline
+ * @tty: tty we are opening the ldisc on
+ * @ld: discipline to open
+ *
+ * A helper opening method. Also a convenient debugging and check
+ * point.
+ */
+
+static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
+{
+ WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
+ if (ld->ops->open)
+ return ld->ops->open(tty);
+ return 0;
+}
+
+/**
+ * tty_ldisc_close - close a line discipline
+ * @tty: tty we are opening the ldisc on
+ * @ld: discipline to close
+ *
+ * A helper close method. Also a convenient debugging and check
+ * point.
+ */
+
+static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
+{
+ WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
+ clear_bit(TTY_LDISC_OPEN, &tty->flags);
+ if (ld->ops->close)
+ ld->ops->close(tty);
+}
/**
* tty_ldisc_restore - helper for tty ldisc change
@@ -420,66 +477,136 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
{
char buf[64];
- struct tty_ldisc new_ldisc;
+ struct tty_ldisc *new_ldisc;
+ int r;
/* There is an outstanding reference here so this is safe */
- tty_ldisc_get(old->ops->num, old);
+ old = tty_ldisc_get(old->ops->num);
+ WARN_ON(IS_ERR(old));
tty_ldisc_assign(tty, old);
tty_set_termios_ldisc(tty, old->ops->num);
- if (old->ops->open && (old->ops->open(tty) < 0)) {
- tty_ldisc_put(old->ops);
+ if (tty_ldisc_open(tty, old) < 0) {
+ tty_ldisc_put(old);
/* This driver is always present */
- if (tty_ldisc_get(N_TTY, &new_ldisc) < 0)
+ new_ldisc = tty_ldisc_get(N_TTY);
+ if (IS_ERR(new_ldisc))
panic("n_tty: get");
- tty_ldisc_assign(tty, &new_ldisc);
+ tty_ldisc_assign(tty, new_ldisc);
tty_set_termios_ldisc(tty, N_TTY);
- if (new_ldisc.ops->open) {
- int r = new_ldisc.ops->open(tty);
- if (r < 0)
- panic("Couldn't open N_TTY ldisc for "
- "%s --- error %d.",
- tty_name(tty, buf), r);
- }
+ r = tty_ldisc_open(tty, new_ldisc);
+ if (r < 0)
+ panic("Couldn't open N_TTY ldisc for "
+ "%s --- error %d.",
+ tty_name(tty, buf), r);
}
}
/**
+ * tty_ldisc_halt - shut down the line discipline
+ * @tty: tty device
+ *
+ * Shut down the line discipline and work queue for this tty device.
+ * The TTY_LDISC flag being cleared ensures no further references can
+ * be obtained while the delayed work queue halt ensures that no more
+ * data is fed to the ldisc.
+ *
+ * In order to wait for any existing references to complete see
+ * tty_ldisc_wait_idle.
+ */
+
+static int tty_ldisc_halt(struct tty_struct *tty)
+{
+ clear_bit(TTY_LDISC, &tty->flags);
+ return cancel_delayed_work(&tty->buf.work);
+}
+
+/**
+ * tty_ldisc_wait_idle - wait for the ldisc to become idle
+ * @tty: tty to wait for
+ *
+ * Wait for the line discipline to become idle. The discipline must
+ * have been halted for this to guarantee it remains idle.
+ *
+ * tty_ldisc_lock protects the ref counts currently.
+ */
+
+static int tty_ldisc_wait_idle(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
+ while (tty->ldisc->refcount) {
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ if (wait_event_timeout(tty_ldisc_wait,
+ tty->ldisc->refcount == 0, 5 * HZ) == 0)
+ return -EBUSY;
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
+ }
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ return 0;
+}
+
+/**
* tty_set_ldisc - set line discipline
* @tty: the terminal to set
* @ldisc: the line discipline
*
* Set the discipline of a tty line. Must be called from a process
- * context.
+ * context. The ldisc change logic has to protect itself against any
+ * overlapping ldisc change (including on the other end of pty pairs),
+ * the close of one side of a tty/pty pair, and eventually hangup.
*
- * Locking: takes tty_ldisc_lock.
- * called functions take termios_mutex
+ * Locking: takes tty_ldisc_lock, termios_mutex
*/
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
int retval;
- struct tty_ldisc o_ldisc, new_ldisc;
- int work;
- unsigned long flags;
+ struct tty_ldisc *o_ldisc, *new_ldisc;
+ int work, o_work = 0;
struct tty_struct *o_tty;
-restart:
- /* This is a bit ugly for now but means we can break the 'ldisc
- is part of the tty struct' assumption later */
- retval = tty_ldisc_get(ldisc, &new_ldisc);
- if (retval)
- return retval;
+ new_ldisc = tty_ldisc_get(ldisc);
+ if (IS_ERR(new_ldisc))
+ return PTR_ERR(new_ldisc);
+
+ /*
+ * We need to look at the tty locking here for pty/tty pairs
+ * when both sides try to change in parallel.
+ */
+
+ o_tty = tty->link; /* o_tty is the pty side or NULL */
+
+
+ /*
+ * Check the no-op case
+ */
+
+ if (tty->ldisc->ops->num == ldisc) {
+ tty_ldisc_put(new_ldisc);
+ return 0;
+ }
/*
* Problem: What do we do if this blocks ?
+ * We could deadlock here
*/
tty_wait_until_sent(tty, 0);
- if (tty->ldisc.ops->num == ldisc) {
- tty_ldisc_put(new_ldisc.ops);
- return 0;
+ mutex_lock(&tty->ldisc_mutex);
+
+ /*
+ * We could be midstream of another ldisc change which has
+ * dropped the lock during processing. If so we need to wait.
+ */
+
+ while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
+ mutex_unlock(&tty->ldisc_mutex);
+ wait_event(tty_ldisc_wait,
+ test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
+ mutex_lock(&tty->ldisc_mutex);
}
+ set_bit(TTY_LDISC_CHANGING, &tty->flags);
/*
* No more input please, we are switching. The new ldisc
@@ -489,8 +616,6 @@ restart:
tty->receive_room = 0;
o_ldisc = tty->ldisc;
- o_tty = tty->link;
-
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
@@ -501,108 +626,181 @@ restart:
* with a userspace app continually trying to use the tty in
* parallel to the change and re-referencing the tty.
*/
- clear_bit(TTY_LDISC, &tty->flags);
- if (o_tty)
- clear_bit(TTY_LDISC, &o_tty->flags);
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
- if (tty->ldisc.refcount) {
- /* Free the new ldisc we grabbed. Must drop the lock
- first. */
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(o_ldisc.ops);
- /*
- * There are several reasons we may be busy, including
- * random momentary I/O traffic. We must therefore
- * retry. We could distinguish between blocking ops
- * and retries if we made tty_ldisc_wait() smarter.
- * That is up for discussion.
- */
- if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
- return -ERESTARTSYS;
- goto restart;
- }
- if (o_tty && o_tty->ldisc.refcount) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(o_tty->ldisc.ops);
- if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
- return -ERESTARTSYS;
- goto restart;
- }
- }
- /*
- * If the TTY_LDISC bit is set, then we are racing against
- * another ldisc change
- */
- if (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
- struct tty_ldisc *ld;
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(new_ldisc.ops);
- ld = tty_ldisc_ref_wait(tty);
- tty_ldisc_deref(ld);
- goto restart;
- }
- /*
- * This flag is used to avoid two parallel ldisc changes. Once
- * open and close are fine grained locked this may work better
- * as a mutex shared with the open/close/hup paths
- */
- set_bit(TTY_LDISC_CHANGING, &tty->flags);
+ work = tty_ldisc_halt(tty);
if (o_tty)
- set_bit(TTY_LDISC_CHANGING, &o_tty->flags);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
- /*
- * From this point on we know nobody has an ldisc
- * usage reference, nor can they obtain one until
- * we say so later on.
- */
+ o_work = tty_ldisc_halt(o_tty);
- work = cancel_delayed_work(&tty->buf.work);
/*
- * Wait for ->hangup_work and ->buf.work handlers to terminate
- * MUST NOT hold locks here.
+ * Wait for ->hangup_work and ->buf.work handlers to terminate.
+ * We must drop the mutex here in case a hangup is also in process.
*/
+
+ mutex_unlock(&tty->ldisc_mutex);
+
flush_scheduled_work();
+
+ /* Let any existing reference holders finish */
+ retval = tty_ldisc_wait_idle(tty);
+ if (retval < 0) {
+ clear_bit(TTY_LDISC_CHANGING, &tty->flags);
+ tty_ldisc_put(new_ldisc);
+ return retval;
+ }
+
+ mutex_lock(&tty->ldisc_mutex);
+ if (test_bit(TTY_HUPPED, &tty->flags)) {
+ /* We were raced by the hangup method. It will have stomped
+ the ldisc data and closed the ldisc down */
+ clear_bit(TTY_LDISC_CHANGING, &tty->flags);
+ mutex_unlock(&tty->ldisc_mutex);
+ tty_ldisc_put(new_ldisc);
+ return -EIO;
+ }
+
/* Shutdown the current discipline. */
- if (o_ldisc.ops->close)
- (o_ldisc.ops->close)(tty);
+ tty_ldisc_close(tty, o_ldisc);
/* Now set up the new line discipline. */
- tty_ldisc_assign(tty, &new_ldisc);
+ tty_ldisc_assign(tty, new_ldisc);
tty_set_termios_ldisc(tty, ldisc);
- if (new_ldisc.ops->open)
- retval = (new_ldisc.ops->open)(tty);
+
+ retval = tty_ldisc_open(tty, new_ldisc);
if (retval < 0) {
- tty_ldisc_put(new_ldisc.ops);
- tty_ldisc_restore(tty, &o_ldisc);
+ /* Back to the old one or N_TTY if we can't */
+ tty_ldisc_put(new_ldisc);
+ tty_ldisc_restore(tty, o_ldisc);
}
+
/* At this point we hold a reference to the new ldisc and a
a reference to the old ldisc. If we ended up flipping back
to the existing ldisc we have two references to it */
- if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc)
+ if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc)
tty->ops->set_ldisc(tty);
- tty_ldisc_put(o_ldisc.ops);
+ tty_ldisc_put(o_ldisc);
/*
- * Allow ldisc referencing to occur as soon as the driver
- * ldisc callback completes.
+ * Allow ldisc referencing to occur again
*/
tty_ldisc_enable(tty);
if (o_tty)
tty_ldisc_enable(o_tty);
- /* Restart it in case no characters kick it off. Safe if
+ /* Restart the work queue in case no characters kick it off. Safe if
already running */
if (work)
schedule_delayed_work(&tty->buf.work, 1);
+ if (o_work)
+ schedule_delayed_work(&o_tty->buf.work, 1);
+ mutex_unlock(&tty->ldisc_mutex);
return retval;
}
+/**
+ * tty_reset_termios - reset terminal state
+ * @tty: tty to reset
+ *
+ * Restore a terminal to the driver default state.
+ */
+
+static void tty_reset_termios(struct tty_struct *tty)
+{
+ mutex_lock(&tty->termios_mutex);
+ *tty->termios = tty->driver->init_termios;
+ tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+ tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+ mutex_unlock(&tty->termios_mutex);
+}
+
+
+/**
+ * tty_ldisc_reinit - reinitialise the tty ldisc
+ * @tty: tty to reinit
+ *
+ * Switch the tty back to N_TTY line discipline and leave the
+ * ldisc state closed
+ */
+
+static void tty_ldisc_reinit(struct tty_struct *tty)
+{
+ struct tty_ldisc *ld;
+
+ tty_ldisc_close(tty, tty->ldisc);
+ tty_ldisc_put(tty->ldisc);
+ tty->ldisc = NULL;
+ /*
+ * Switch the line discipline back
+ */
+ ld = tty_ldisc_get(N_TTY);
+ BUG_ON(IS_ERR(ld));
+ tty_ldisc_assign(tty, ld);
+ tty_set_termios_ldisc(tty, N_TTY);
+}
+
+/**
+ * tty_ldisc_hangup - hangup ldisc reset
+ * @tty: tty being hung up
+ *
+ * Some tty devices reset their termios when they receive a hangup
+ * event. In that situation we must also switch back to N_TTY properly
+ * before we reset the termios data.
+ *
+ * Locking: We can take the ldisc mutex as the rest of the code is
+ * careful to allow for this.
+ *
+ * In the pty pair case this occurs in the close() path of the
+ * tty itself so we must be careful about locking rules.
+ */
+
+void tty_ldisc_hangup(struct tty_struct *tty)
+{
+ struct tty_ldisc *ld;
+
+ /*
+ * FIXME! What are the locking issues here? This may me overdoing
+ * things... This question is especially important now that we've
+ * removed the irqlock.
+ */
+ ld = tty_ldisc_ref(tty);
+ if (ld != NULL) {
+ /* We may have no line discipline at this point */
+ if (ld->ops->flush_buffer)
+ ld->ops->flush_buffer(tty);
+ tty_driver_flush_buffer(tty);
+ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
+ ld->ops->write_wakeup)
+ ld->ops->write_wakeup(tty);
+ if (ld->ops->hangup)
+ ld->ops->hangup(tty);
+ tty_ldisc_deref(ld);
+ }
+ /*
+ * FIXME: Once we trust the LDISC code better we can wait here for
+ * ldisc completion and fix the driver call race
+ */
+ wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+ wake_up_interruptible_poll(&tty->read_wait, POLLIN);
+ /*
+ * Shutdown the current line discipline, and reset it to
+ * N_TTY.
+ */
+ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+ /* Avoid racing set_ldisc */
+ mutex_lock(&tty->ldisc_mutex);
+ /* Switch back to N_TTY */
+ tty_ldisc_reinit(tty);
+ /* At this point we have a closed ldisc and we want to
+ reopen it. We could defer this to the next open but
+ it means auditing a lot of other paths so this is a FIXME */
+ WARN_ON(tty_ldisc_open(tty, tty->ldisc));
+ tty_ldisc_enable(tty);
+ mutex_unlock(&tty->ldisc_mutex);
+ tty_reset_termios(tty);
+ }
+}
/**
* tty_ldisc_setup - open line discipline
@@ -610,24 +808,23 @@ restart:
* @o_tty: pair tty for pty/tty pairs
*
* Called during the initial open of a tty/pty pair in order to set up the
- * line discplines and bind them to the tty.
+ * line disciplines and bind them to the tty. This has no locking issues
+ * as the device isn't yet active.
*/
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
- struct tty_ldisc *ld = &tty->ldisc;
+ struct tty_ldisc *ld = tty->ldisc;
int retval;
- if (ld->ops->open) {
- retval = (ld->ops->open)(tty);
- if (retval)
- return retval;
- }
- if (o_tty && o_tty->ldisc.ops->open) {
- retval = (o_tty->ldisc.ops->open)(o_tty);
+ retval = tty_ldisc_open(tty, ld);
+ if (retval)
+ return retval;
+
+ if (o_tty) {
+ retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
- if (ld->ops->close)
- (ld->ops->close)(tty);
+ tty_ldisc_close(tty, ld);
return retval;
}
tty_ldisc_enable(o_tty);
@@ -635,32 +832,25 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
tty_ldisc_enable(tty);
return 0;
}
-
/**
* tty_ldisc_release - release line discipline
* @tty: tty being shut down
* @o_tty: pair tty for pty/tty pairs
*
- * Called during the final close of a tty/pty pair in order to shut down the
- * line discpline layer.
+ * Called during the final close of a tty/pty pair in order to shut down
+ * the line discpline layer. On exit the ldisc assigned is N_TTY and the
+ * ldisc has not been opened.
*/
void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
{
- unsigned long flags;
- struct tty_ldisc ld;
/*
* Prevent flush_to_ldisc() from rescheduling the work for later. Then
* kill any delayed work. As this is the final close it does not
* race with the set_ldisc code path.
*/
- clear_bit(TTY_LDISC, &tty->flags);
- cancel_delayed_work(&tty->buf.work);
-
- /*
- * Wait for ->hangup_work and ->buf.work handlers to terminate
- */
+ tty_ldisc_halt(tty);
flush_scheduled_work();
/*
@@ -668,38 +858,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
* side waiters as the file is closing so user count on the file
* side is zero.
*/
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- while (tty->ldisc.refcount) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- }
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+ tty_ldisc_wait_idle(tty);
+
/*
* Shutdown the current line discipline, and reset it to N_TTY.
*
* FIXME: this MUST get fixed for the new reflocking
*/
- if (tty->ldisc.ops->close)
- (tty->ldisc.ops->close)(tty);
- tty_ldisc_put(tty->ldisc.ops);
- /*
- * Switch the line discipline back
- */
- WARN_ON(tty_ldisc_get(N_TTY, &ld));
- tty_ldisc_assign(tty, &ld);
- tty_set_termios_ldisc(tty, N_TTY);
- if (o_tty) {
- /* FIXME: could o_tty be in setldisc here ? */
- clear_bit(TTY_LDISC, &o_tty->flags);
- if (o_tty->ldisc.ops->close)
- (o_tty->ldisc.ops->close)(o_tty);
- tty_ldisc_put(o_tty->ldisc.ops);
- WARN_ON(tty_ldisc_get(N_TTY, &ld));
- tty_ldisc_assign(o_tty, &ld);
- tty_set_termios_ldisc(o_tty, N_TTY);
- }
+ tty_ldisc_reinit(tty);
+ /* This will need doing differently if we need to lock */
+ if (o_tty)
+ tty_ldisc_release(o_tty, NULL);
}
/**
@@ -712,10 +883,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
void tty_ldisc_init(struct tty_struct *tty)
{
- struct tty_ldisc ld;
- if (tty_ldisc_get(N_TTY, &ld) < 0)
+ struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
+ if (IS_ERR(ld))
panic("n_tty: init_tty");
- tty_ldisc_assign(tty, &ld);
+ tty_ldisc_assign(tty, ld);
}
void tty_ldisc_begin(void)
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index 9b8004c72686..62dadfc95e34 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -137,7 +137,7 @@ int tty_port_carrier_raised(struct tty_port *port)
EXPORT_SYMBOL(tty_port_carrier_raised);
/**
- * tty_port_raise_dtr_rts - Riase DTR/RTS
+ * tty_port_raise_dtr_rts - Raise DTR/RTS
* @port: tty port
*
* Wrapper for the DTR/RTS raise logic. For the moment this is used
@@ -147,12 +147,28 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
void tty_port_raise_dtr_rts(struct tty_port *port)
{
- if (port->ops->raise_dtr_rts)
- port->ops->raise_dtr_rts(port);
+ if (port->ops->dtr_rts)
+ port->ops->dtr_rts(port, 1);
}
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
/**
+ * tty_port_lower_dtr_rts - Lower DTR/RTS
+ * @port: tty port
+ *
+ * Wrapper for the DTR/RTS raise logic. For the moment this is used
+ * to hide some internal details. This will eventually become entirely
+ * internal to the tty port.
+ */
+
+void tty_port_lower_dtr_rts(struct tty_port *port)
+{
+ if (port->ops->dtr_rts)
+ port->ops->dtr_rts(port, 0);
+}
+EXPORT_SYMBOL(tty_port_lower_dtr_rts);
+
+/**
* tty_port_block_til_ready - Waiting logic for tty open
* @port: the tty port being opened
* @tty: the tty device being bound
@@ -167,7 +183,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
* - port flags and counts
*
* The passed tty_port must implement the carrier_raised method if it can
- * do carrier detect and the raise_dtr_rts method if it supports software
+ * do carrier detect and the dtr_rts method if it supports software
* management of these lines. Note that the dtr/rts raise is done each
* iteration as a hangup may have previously dropped them while we wait.
*/
@@ -182,7 +198,8 @@ int tty_port_block_til_ready(struct tty_port *port,
/* block if port is in the process of being closed */
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
- interruptible_sleep_on(&port->close_wait);
+ wait_event_interruptible(port->close_wait,
+ !(port->flags & ASYNC_CLOSING));
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
@@ -205,7 +222,6 @@ int tty_port_block_til_ready(struct tty_port *port,
before the next open may complete */
retval = 0;
- add_wait_queue(&port->open_wait, &wait);
/* The port lock protects the port counts */
spin_lock_irqsave(&port->lock, flags);
@@ -219,7 +235,7 @@ int tty_port_block_til_ready(struct tty_port *port,
if (tty->termios->c_cflag & CBAUD)
tty_port_raise_dtr_rts(port);
- set_current_state(TASK_INTERRUPTIBLE);
+ prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
/* Check for a hangup or uninitialised port. Return accordingly */
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
@@ -240,8 +256,7 @@ int tty_port_block_til_ready(struct tty_port *port,
}
schedule();
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
+ finish_wait(&port->open_wait, &wait);
/* Update counts. A parallel hangup will have set count to zero and
we must not mess that up further */
@@ -292,6 +307,17 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
if (port->flags & ASYNC_INITIALIZED &&
port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, port->closing_wait);
+ if (port->drain_delay) {
+ unsigned int bps = tty_get_baud_rate(tty);
+ long timeout;
+
+ if (bps > 1200)
+ timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps,
+ HZ / 10);
+ else
+ timeout = 2 * HZ;
+ schedule_timeout_interruptible(timeout);
+ }
return 1;
}
EXPORT_SYMBOL(tty_port_close_start);
@@ -302,6 +328,9 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
tty_ldisc_flush(tty);
+ if (tty->termios->c_cflag & HUPCL)
+ tty_port_lower_dtr_rts(port);
+
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index ff6f5a4b58fb..c74dacfa6795 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -188,6 +188,9 @@ static void hvc_handle_input(struct virtqueue *vq)
* Finally we put our input buffer in the input queue, ready to receive. */
static int __devinit virtcons_probe(struct virtio_device *dev)
{
+ vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
+ const char *names[] = { "input", "output" };
+ struct virtqueue *vqs[2];
int err;
vdev = dev;
@@ -199,20 +202,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
goto fail;
}
- /* Find the input queue. */
+ /* Find the queues. */
/* FIXME: This is why we want to wean off hvc: we do nothing
* when input comes in. */
- in_vq = vdev->config->find_vq(vdev, 0, hvc_handle_input);
- if (IS_ERR(in_vq)) {
- err = PTR_ERR(in_vq);
+ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+ if (err)
goto free;
- }
- out_vq = vdev->config->find_vq(vdev, 1, NULL);
- if (IS_ERR(out_vq)) {
- err = PTR_ERR(out_vq);
- goto free_in_vq;
- }
+ in_vq = vqs[0];
+ out_vq = vqs[1];
/* Start using the new console output. */
virtio_cons.get_chars = get_chars;
@@ -233,17 +231,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
if (IS_ERR(hvc)) {
err = PTR_ERR(hvc);
- goto free_out_vq;
+ goto free_vqs;
}
/* Register the input buffer the first time. */
add_inbuf();
return 0;
-free_out_vq:
- vdev->config->del_vq(out_vq);
-free_in_vq:
- vdev->config->del_vq(in_vq);
+free_vqs:
+ vdev->config->del_vqs(vdev);
free:
kfree(inbuf);
fail:
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 08151d4de489..c796a86ab7f3 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -95,7 +95,6 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
-#include <linux/bootmem.h>
#include <linux/pm.h>
#include <linux/font.h>
#include <linux/bitops.h>
@@ -2875,14 +2874,11 @@ static int __init con_init(void)
mod_timer(&console_timer, jiffies + blankinterval);
}
- /*
- * kmalloc is not running yet - we use the bootmem allocator.
- */
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
- vc_cons[currcons].d = vc = alloc_bootmem(sizeof(struct vc_data));
+ vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
visual_init(vc, currcons, 1);
- vc->vc_screenbuf = (unsigned short *)alloc_bootmem(vc->vc_screenbuf_size);
+ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc->vc_kmalloced = 0;
vc_init(vc, vc->vc_rows, vc->vc_cols,
currcons || !vc->vc_sw->con_save_screen);
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index a2dee0eb6dad..e6ce632a393e 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -400,6 +400,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
case KIOCSOUND:
if (!perm)
goto eperm;
+ /* FIXME: This is an old broken API but we need to keep it
+ supported and somehow separate the historic advertised
+ tick rate from any real one */
if (arg)
arg = CLOCK_TICK_RATE / arg;
kd_mksound(arg, 0);
@@ -417,6 +420,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
*/
ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
count = ticks ? (arg & 0xffff) : 0;
+ /* FIXME: This is an old broken API but we need to keep it
+ supported and somehow separate the historic advertised
+ tick rate from any real one */
if (count)
count = CLOCK_TICK_RATE / count;
kd_mksound(count, ticks);
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 1efb2879a94f..eef216f7f61d 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -3,3 +3,5 @@ obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
+obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
+obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 1c92c39a53aa..cf56a2af5fe1 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -18,7 +18,6 @@
*/
#include <linux/init.h>
-#include <linux/bootmem.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
@@ -29,7 +28,7 @@
#include <linux/err.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
-#include <linux/sh_cmt.h>
+#include <linux/sh_timer.h>
struct sh_cmt_priv {
void __iomem *mapbase;
@@ -47,6 +46,7 @@ struct sh_cmt_priv {
unsigned long rate;
spinlock_t lock;
struct clock_event_device ced;
+ struct clocksource cs;
unsigned long total_cycles;
};
@@ -59,7 +59,7 @@ static DEFINE_SPINLOCK(sh_cmt_lock);
static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr)
{
- struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
@@ -83,7 +83,7 @@ static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr)
static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr,
unsigned long value)
{
- struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs;
@@ -110,23 +110,28 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
int *has_wrapped)
{
unsigned long v1, v2, v3;
+ int o1, o2;
+
+ o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
+ o2 = o1;
v1 = sh_cmt_read(p, CMCNT);
v2 = sh_cmt_read(p, CMCNT);
v3 = sh_cmt_read(p, CMCNT);
- } while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
- || (v3 > v1 && v3 < v2)));
+ o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit;
+ } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
+ || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
- *has_wrapped = sh_cmt_read(p, CMCSR) & p->overflow_bit;
+ *has_wrapped = o1;
return v2;
}
static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
{
- struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
@@ -144,7 +149,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
{
- struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
int ret;
/* enable clock */
@@ -153,16 +158,18 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk);
return ret;
}
- *rate = clk_get_rate(p->clk) / 8;
/* make sure channel is disabled */
sh_cmt_start_stop_ch(p, 0);
/* configure channel, periodic mode and maximum timeout */
- if (p->width == 16)
- sh_cmt_write(p, CMCSR, 0);
- else
+ if (p->width == 16) {
+ *rate = clk_get_rate(p->clk) / 512;
+ sh_cmt_write(p, CMCSR, 0x43);
+ } else {
+ *rate = clk_get_rate(p->clk) / 8;
sh_cmt_write(p, CMCSR, 0x01a4);
+ }
sh_cmt_write(p, CMCOR, 0xffffffff);
sh_cmt_write(p, CMCNT, 0);
@@ -376,6 +383,68 @@ static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag)
spin_unlock_irqrestore(&p->lock, flags);
}
+static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs)
+{
+ return container_of(cs, struct sh_cmt_priv, cs);
+}
+
+static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
+{
+ struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ unsigned long flags, raw;
+ unsigned long value;
+ int has_wrapped;
+
+ spin_lock_irqsave(&p->lock, flags);
+ value = p->total_cycles;
+ raw = sh_cmt_get_counter(p, &has_wrapped);
+
+ if (unlikely(has_wrapped))
+ raw += p->match_value;
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return value + raw;
+}
+
+static int sh_cmt_clocksource_enable(struct clocksource *cs)
+{
+ struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ int ret;
+
+ p->total_cycles = 0;
+
+ ret = sh_cmt_start(p, FLAG_CLOCKSOURCE);
+ if (ret)
+ return ret;
+
+ /* TODO: calculate good shift from rate and counter bit width */
+ cs->shift = 0;
+ cs->mult = clocksource_hz2mult(p->rate, cs->shift);
+ return 0;
+}
+
+static void sh_cmt_clocksource_disable(struct clocksource *cs)
+{
+ sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
+}
+
+static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
+ char *name, unsigned long rating)
+{
+ struct clocksource *cs = &p->cs;
+
+ cs->name = name;
+ cs->rating = rating;
+ cs->read = sh_cmt_clocksource_read;
+ cs->enable = sh_cmt_clocksource_enable;
+ cs->disable = sh_cmt_clocksource_disable;
+ cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
+ cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ pr_info("sh_cmt: %s used as clock source\n", cs->name);
+ clocksource_register(cs);
+ return 0;
+}
+
static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced)
{
return container_of(ced, struct sh_cmt_priv, ced);
@@ -468,9 +537,9 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
clockevents_register_device(ced);
}
-int sh_cmt_register(struct sh_cmt_priv *p, char *name,
- unsigned long clockevent_rating,
- unsigned long clocksource_rating)
+static int sh_cmt_register(struct sh_cmt_priv *p, char *name,
+ unsigned long clockevent_rating,
+ unsigned long clocksource_rating)
{
if (p->width == (sizeof(p->max_match_value) * 8))
p->max_match_value = ~0;
@@ -483,12 +552,15 @@ int sh_cmt_register(struct sh_cmt_priv *p, char *name,
if (clockevent_rating)
sh_cmt_register_clockevent(p, name, clockevent_rating);
+ if (clocksource_rating)
+ sh_cmt_register_clocksource(p, name, clocksource_rating);
+
return 0;
}
static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
{
- struct sh_cmt_config *cfg = pdev->dev.platform_data;
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
int irq, ret;
ret = -ENXIO;
@@ -545,7 +617,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
if (resource_size(res) == 6) {
p->width = 16;
p->overflow_bit = 0x80;
- p->clear_bits = ~0xc0;
+ p->clear_bits = ~0x80;
} else {
p->width = 32;
p->overflow_bit = 0x8000;
@@ -566,8 +638,14 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
static int __devinit sh_cmt_probe(struct platform_device *pdev)
{
struct sh_cmt_priv *p = platform_get_drvdata(pdev);
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
+ if (p) {
+ pr_info("sh_cmt: %s kept as earlytimer\n", cfg->name);
+ return 0;
+ }
+
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
@@ -577,7 +655,6 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev)
ret = sh_cmt_setup(p, pdev);
if (ret) {
kfree(p);
-
platform_set_drvdata(pdev, NULL);
}
return ret;
@@ -606,6 +683,7 @@ static void __exit sh_cmt_exit(void)
platform_driver_unregister(&sh_cmt_device_driver);
}
+early_platform_init("earlytimer", &sh_cmt_device_driver);
module_init(sh_cmt_init);
module_exit(sh_cmt_exit);
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
new file mode 100644
index 000000000000..d1ae75454d10
--- /dev/null
+++ b/drivers/clocksource/sh_mtu2.c
@@ -0,0 +1,357 @@
+/*
+ * SuperH Timer Support - MTU2
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clockchips.h>
+#include <linux/sh_timer.h>
+
+struct sh_mtu2_priv {
+ void __iomem *mapbase;
+ struct clk *clk;
+ struct irqaction irqaction;
+ struct platform_device *pdev;
+ unsigned long rate;
+ unsigned long periodic;
+ struct clock_event_device ced;
+};
+
+static DEFINE_SPINLOCK(sh_mtu2_lock);
+
+#define TSTR -1 /* shared register */
+#define TCR 0 /* channel register */
+#define TMDR 1 /* channel register */
+#define TIOR 2 /* channel register */
+#define TIER 3 /* channel register */
+#define TSR 4 /* channel register */
+#define TCNT 5 /* channel register */
+#define TGR 6 /* channel register */
+
+static unsigned long mtu2_reg_offs[] = {
+ [TCR] = 0,
+ [TMDR] = 1,
+ [TIOR] = 2,
+ [TIER] = 4,
+ [TSR] = 5,
+ [TCNT] = 6,
+ [TGR] = 8,
+};
+
+static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == TSTR)
+ return ioread8(base + cfg->channel_offset);
+
+ offs = mtu2_reg_offs[reg_nr];
+
+ if ((reg_nr == TCNT) || (reg_nr == TGR))
+ return ioread16(base + offs);
+ else
+ return ioread8(base + offs);
+}
+
+static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr,
+ unsigned long value)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == TSTR) {
+ iowrite8(value, base + cfg->channel_offset);
+ return;
+ }
+
+ offs = mtu2_reg_offs[reg_nr];
+
+ if ((reg_nr == TCNT) || (reg_nr == TGR))
+ iowrite16(value, base + offs);
+ else
+ iowrite8(value, base + offs);
+}
+
+static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ unsigned long flags, value;
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&sh_mtu2_lock, flags);
+ value = sh_mtu2_read(p, TSTR);
+
+ if (start)
+ value |= 1 << cfg->timer_bit;
+ else
+ value &= ~(1 << cfg->timer_bit);
+
+ sh_mtu2_write(p, TSTR, value);
+ spin_unlock_irqrestore(&sh_mtu2_lock, flags);
+}
+
+static int sh_mtu2_enable(struct sh_mtu2_priv *p)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ int ret;
+
+ /* enable clock */
+ ret = clk_enable(p->clk);
+ if (ret) {
+ pr_err("sh_mtu2: cannot enable clock \"%s\"\n", cfg->clk);
+ return ret;
+ }
+
+ /* make sure channel is disabled */
+ sh_mtu2_start_stop_ch(p, 0);
+
+ p->rate = clk_get_rate(p->clk) / 64;
+ p->periodic = (p->rate + HZ/2) / HZ;
+
+ /* "Periodic Counter Operation" */
+ sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */
+ sh_mtu2_write(p, TIOR, 0);
+ sh_mtu2_write(p, TGR, p->periodic);
+ sh_mtu2_write(p, TCNT, 0);
+ sh_mtu2_write(p, TMDR, 0);
+ sh_mtu2_write(p, TIER, 0x01);
+
+ /* enable channel */
+ sh_mtu2_start_stop_ch(p, 1);
+
+ return 0;
+}
+
+static void sh_mtu2_disable(struct sh_mtu2_priv *p)
+{
+ /* disable channel */
+ sh_mtu2_start_stop_ch(p, 0);
+
+ /* stop clock */
+ clk_disable(p->clk);
+}
+
+static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
+{
+ struct sh_mtu2_priv *p = dev_id;
+
+ /* acknowledge interrupt */
+ sh_mtu2_read(p, TSR);
+ sh_mtu2_write(p, TSR, 0xfe);
+
+ /* notify clockevent layer */
+ p->ced.event_handler(&p->ced);
+ return IRQ_HANDLED;
+}
+
+static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced)
+{
+ return container_of(ced, struct sh_mtu2_priv, ced);
+}
+
+static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
+ struct clock_event_device *ced)
+{
+ struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced);
+ int disabled = 0;
+
+ /* deal with old setting first */
+ switch (ced->mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ sh_mtu2_disable(p);
+ disabled = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ pr_info("sh_mtu2: %s used for periodic clock events\n",
+ ced->name);
+ sh_mtu2_enable(p);
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ if (!disabled)
+ sh_mtu2_disable(p);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ break;
+ }
+}
+
+static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
+ char *name, unsigned long rating)
+{
+ struct clock_event_device *ced = &p->ced;
+ int ret;
+
+ memset(ced, 0, sizeof(*ced));
+
+ ced->name = name;
+ ced->features = CLOCK_EVT_FEAT_PERIODIC;
+ ced->rating = rating;
+ ced->cpumask = cpumask_of(0);
+ ced->set_mode = sh_mtu2_clock_event_mode;
+
+ ret = setup_irq(p->irqaction.irq, &p->irqaction);
+ if (ret) {
+ pr_err("sh_mtu2: failed to request irq %d\n",
+ p->irqaction.irq);
+ return;
+ }
+
+ pr_info("sh_mtu2: %s used for clock events\n", ced->name);
+ clockevents_register_device(ced);
+}
+
+static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
+ unsigned long clockevent_rating)
+{
+ if (clockevent_rating)
+ sh_mtu2_register_clockevent(p, name, clockevent_rating);
+
+ return 0;
+}
+
+static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ struct resource *res;
+ int irq, ret;
+ ret = -ENXIO;
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ if (!cfg) {
+ dev_err(&p->pdev->dev, "missing platform data\n");
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, p);
+
+ res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ goto err0;
+ }
+
+ irq = platform_get_irq(p->pdev, 0);
+ if (irq < 0) {
+ dev_err(&p->pdev->dev, "failed to get irq\n");
+ goto err0;
+ }
+
+ /* map memory, let mapbase point to our channel */
+ p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (p->mapbase == NULL) {
+ pr_err("sh_mtu2: failed to remap I/O memory\n");
+ goto err0;
+ }
+
+ /* setup data for setup_irq() (too early for request_irq()) */
+ p->irqaction.name = cfg->name;
+ p->irqaction.handler = sh_mtu2_interrupt;
+ p->irqaction.dev_id = p;
+ p->irqaction.irq = irq;
+ p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
+ p->irqaction.mask = CPU_MASK_NONE;
+
+ /* get hold of clock */
+ p->clk = clk_get(&p->pdev->dev, cfg->clk);
+ if (IS_ERR(p->clk)) {
+ pr_err("sh_mtu2: cannot get clock \"%s\"\n", cfg->clk);
+ ret = PTR_ERR(p->clk);
+ goto err1;
+ }
+
+ return sh_mtu2_register(p, cfg->name, cfg->clockevent_rating);
+ err1:
+ iounmap(p->mapbase);
+ err0:
+ return ret;
+}
+
+static int __devinit sh_mtu2_probe(struct platform_device *pdev)
+{
+ struct sh_mtu2_priv *p = platform_get_drvdata(pdev);
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ int ret;
+
+ if (p) {
+ pr_info("sh_mtu2: %s kept as earlytimer\n", cfg->name);
+ return 0;
+ }
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_mtu2_setup(p, pdev);
+ if (ret) {
+ kfree(p);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return ret;
+}
+
+static int __devexit sh_mtu2_remove(struct platform_device *pdev)
+{
+ return -EBUSY; /* cannot unregister clockevent */
+}
+
+static struct platform_driver sh_mtu2_device_driver = {
+ .probe = sh_mtu2_probe,
+ .remove = __devexit_p(sh_mtu2_remove),
+ .driver = {
+ .name = "sh_mtu2",
+ }
+};
+
+static int __init sh_mtu2_init(void)
+{
+ return platform_driver_register(&sh_mtu2_device_driver);
+}
+
+static void __exit sh_mtu2_exit(void)
+{
+ platform_driver_unregister(&sh_mtu2_device_driver);
+}
+
+early_platform_init("earlytimer", &sh_mtu2_device_driver);
+module_init(sh_mtu2_init);
+module_exit(sh_mtu2_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH MTU2 Timer Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
new file mode 100644
index 000000000000..d6ea4398bf62
--- /dev/null
+++ b/drivers/clocksource/sh_tmu.c
@@ -0,0 +1,461 @@
+/*
+ * SuperH Timer Support - TMU
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/sh_timer.h>
+
+struct sh_tmu_priv {
+ void __iomem *mapbase;
+ struct clk *clk;
+ struct irqaction irqaction;
+ struct platform_device *pdev;
+ unsigned long rate;
+ unsigned long periodic;
+ struct clock_event_device ced;
+ struct clocksource cs;
+};
+
+static DEFINE_SPINLOCK(sh_tmu_lock);
+
+#define TSTR -1 /* shared register */
+#define TCOR 0 /* channel register */
+#define TCNT 1 /* channel register */
+#define TCR 2 /* channel register */
+
+static inline unsigned long sh_tmu_read(struct sh_tmu_priv *p, int reg_nr)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == TSTR)
+ return ioread8(base - cfg->channel_offset);
+
+ offs = reg_nr << 2;
+
+ if (reg_nr == TCR)
+ return ioread16(base + offs);
+ else
+ return ioread32(base + offs);
+}
+
+static inline void sh_tmu_write(struct sh_tmu_priv *p, int reg_nr,
+ unsigned long value)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == TSTR) {
+ iowrite8(value, base - cfg->channel_offset);
+ return;
+ }
+
+ offs = reg_nr << 2;
+
+ if (reg_nr == TCR)
+ iowrite16(value, base + offs);
+ else
+ iowrite32(value, base + offs);
+}
+
+static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ unsigned long flags, value;
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&sh_tmu_lock, flags);
+ value = sh_tmu_read(p, TSTR);
+
+ if (start)
+ value |= 1 << cfg->timer_bit;
+ else
+ value &= ~(1 << cfg->timer_bit);
+
+ sh_tmu_write(p, TSTR, value);
+ spin_unlock_irqrestore(&sh_tmu_lock, flags);
+}
+
+static int sh_tmu_enable(struct sh_tmu_priv *p)
+{
+ struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ int ret;
+
+ /* enable clock */
+ ret = clk_enable(p->clk);
+ if (ret) {
+ pr_err("sh_tmu: cannot enable clock \"%s\"\n", cfg->clk);
+ return ret;
+ }
+
+ /* make sure channel is disabled */
+ sh_tmu_start_stop_ch(p, 0);
+
+ /* maximum timeout */
+ sh_tmu_write(p, TCOR, 0xffffffff);
+ sh_tmu_write(p, TCNT, 0xffffffff);
+
+ /* configure channel to parent clock / 4, irq off */
+ p->rate = clk_get_rate(p->clk) / 4;
+ sh_tmu_write(p, TCR, 0x0000);
+
+ /* enable channel */
+ sh_tmu_start_stop_ch(p, 1);
+
+ return 0;
+}
+
+static void sh_tmu_disable(struct sh_tmu_priv *p)
+{
+ /* disable channel */
+ sh_tmu_start_stop_ch(p, 0);
+
+ /* stop clock */
+ clk_disable(p->clk);
+}
+
+static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta,
+ int periodic)
+{
+ /* stop timer */
+ sh_tmu_start_stop_ch(p, 0);
+
+ /* acknowledge interrupt */
+ sh_tmu_read(p, TCR);
+
+ /* enable interrupt */
+ sh_tmu_write(p, TCR, 0x0020);
+
+ /* reload delta value in case of periodic timer */
+ if (periodic)
+ sh_tmu_write(p, TCOR, delta);
+ else
+ sh_tmu_write(p, TCOR, 0);
+
+ sh_tmu_write(p, TCNT, delta);
+
+ /* start timer */
+ sh_tmu_start_stop_ch(p, 1);
+}
+
+static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
+{
+ struct sh_tmu_priv *p = dev_id;
+
+ /* disable or acknowledge interrupt */
+ if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT)
+ sh_tmu_write(p, TCR, 0x0000);
+ else
+ sh_tmu_write(p, TCR, 0x0020);
+
+ /* notify clockevent layer */
+ p->ced.event_handler(&p->ced);
+ return IRQ_HANDLED;
+}
+
+static struct sh_tmu_priv *cs_to_sh_tmu(struct clocksource *cs)
+{
+ return container_of(cs, struct sh_tmu_priv, cs);
+}
+
+static cycle_t sh_tmu_clocksource_read(struct clocksource *cs)
+{
+ struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
+
+ return sh_tmu_read(p, TCNT) ^ 0xffffffff;
+}
+
+static int sh_tmu_clocksource_enable(struct clocksource *cs)
+{
+ struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
+ int ret;
+
+ ret = sh_tmu_enable(p);
+ if (ret)
+ return ret;
+
+ /* TODO: calculate good shift from rate and counter bit width */
+ cs->shift = 10;
+ cs->mult = clocksource_hz2mult(p->rate, cs->shift);
+ return 0;
+}
+
+static void sh_tmu_clocksource_disable(struct clocksource *cs)
+{
+ sh_tmu_disable(cs_to_sh_tmu(cs));
+}
+
+static int sh_tmu_register_clocksource(struct sh_tmu_priv *p,
+ char *name, unsigned long rating)
+{
+ struct clocksource *cs = &p->cs;
+
+ cs->name = name;
+ cs->rating = rating;
+ cs->read = sh_tmu_clocksource_read;
+ cs->enable = sh_tmu_clocksource_enable;
+ cs->disable = sh_tmu_clocksource_disable;
+ cs->mask = CLOCKSOURCE_MASK(32);
+ cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ pr_info("sh_tmu: %s used as clock source\n", cs->name);
+ clocksource_register(cs);
+ return 0;
+}
+
+static struct sh_tmu_priv *ced_to_sh_tmu(struct clock_event_device *ced)
+{
+ return container_of(ced, struct sh_tmu_priv, ced);
+}
+
+static void sh_tmu_clock_event_start(struct sh_tmu_priv *p, int periodic)
+{
+ struct clock_event_device *ced = &p->ced;
+
+ sh_tmu_enable(p);
+
+ /* TODO: calculate good shift from rate and counter bit width */
+
+ ced->shift = 32;
+ ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
+ ced->max_delta_ns = clockevent_delta2ns(0xffffffff, ced);
+ ced->min_delta_ns = 5000;
+
+ if (periodic) {
+ p->periodic = (p->rate + HZ/2) / HZ;
+ sh_tmu_set_next(p, p->periodic, 1);
+ }
+}
+
+static void sh_tmu_clock_event_mode(enum clock_event_mode mode,
+ struct clock_event_device *ced)
+{
+ struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
+ int disabled = 0;
+
+ /* deal with old setting first */
+ switch (ced->mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ case CLOCK_EVT_MODE_ONESHOT:
+ sh_tmu_disable(p);
+ disabled = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ pr_info("sh_tmu: %s used for periodic clock events\n",
+ ced->name);
+ sh_tmu_clock_event_start(p, 1);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ pr_info("sh_tmu: %s used for oneshot clock events\n",
+ ced->name);
+ sh_tmu_clock_event_start(p, 0);
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ if (!disabled)
+ sh_tmu_disable(p);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ break;
+ }
+}
+
+static int sh_tmu_clock_event_next(unsigned long delta,
+ struct clock_event_device *ced)
+{
+ struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
+
+ BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
+
+ /* program new delta value */
+ sh_tmu_set_next(p, delta, 0);
+ return 0;
+}
+
+static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
+ char *name, unsigned long rating)
+{
+ struct clock_event_device *ced = &p->ced;
+ int ret;
+
+ memset(ced, 0, sizeof(*ced));
+
+ ced->name = name;
+ ced->features = CLOCK_EVT_FEAT_PERIODIC;
+ ced->features |= CLOCK_EVT_FEAT_ONESHOT;
+ ced->rating = rating;
+ ced->cpumask = cpumask_of(0);
+ ced->set_next_event = sh_tmu_clock_event_next;
+ ced->set_mode = sh_tmu_clock_event_mode;
+
+ ret = setup_irq(p->irqaction.irq, &p->irqaction);
+ if (ret) {
+ pr_err("sh_tmu: failed to request irq %d\n",
+ p->irqaction.irq);
+ return;
+ }
+
+ pr_info("sh_tmu: %s used for clock events\n", ced->name);
+ clockevents_register_device(ced);
+}
+
+static int sh_tmu_register(struct sh_tmu_priv *p, char *name,
+ unsigned long clockevent_rating,
+ unsigned long clocksource_rating)
+{
+ if (clockevent_rating)
+ sh_tmu_register_clockevent(p, name, clockevent_rating);
+ else if (clocksource_rating)
+ sh_tmu_register_clocksource(p, name, clocksource_rating);
+
+ return 0;
+}
+
+static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ struct resource *res;
+ int irq, ret;
+ ret = -ENXIO;
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ if (!cfg) {
+ dev_err(&p->pdev->dev, "missing platform data\n");
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, p);
+
+ res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ goto err0;
+ }
+
+ irq = platform_get_irq(p->pdev, 0);
+ if (irq < 0) {
+ dev_err(&p->pdev->dev, "failed to get irq\n");
+ goto err0;
+ }
+
+ /* map memory, let mapbase point to our channel */
+ p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (p->mapbase == NULL) {
+ pr_err("sh_tmu: failed to remap I/O memory\n");
+ goto err0;
+ }
+
+ /* setup data for setup_irq() (too early for request_irq()) */
+ p->irqaction.name = cfg->name;
+ p->irqaction.handler = sh_tmu_interrupt;
+ p->irqaction.dev_id = p;
+ p->irqaction.irq = irq;
+ p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
+ p->irqaction.mask = CPU_MASK_NONE;
+
+ /* get hold of clock */
+ p->clk = clk_get(&p->pdev->dev, cfg->clk);
+ if (IS_ERR(p->clk)) {
+ pr_err("sh_tmu: cannot get clock \"%s\"\n", cfg->clk);
+ ret = PTR_ERR(p->clk);
+ goto err1;
+ }
+
+ return sh_tmu_register(p, cfg->name,
+ cfg->clockevent_rating,
+ cfg->clocksource_rating);
+ err1:
+ iounmap(p->mapbase);
+ err0:
+ return ret;
+}
+
+static int __devinit sh_tmu_probe(struct platform_device *pdev)
+{
+ struct sh_tmu_priv *p = platform_get_drvdata(pdev);
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ int ret;
+
+ if (p) {
+ pr_info("sh_tmu: %s kept as earlytimer\n", cfg->name);
+ return 0;
+ }
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_tmu_setup(p, pdev);
+ if (ret) {
+ kfree(p);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return ret;
+}
+
+static int __devexit sh_tmu_remove(struct platform_device *pdev)
+{
+ return -EBUSY; /* cannot unregister clockevent and clocksource */
+}
+
+static struct platform_driver sh_tmu_device_driver = {
+ .probe = sh_tmu_probe,
+ .remove = __devexit_p(sh_tmu_remove),
+ .driver = {
+ .name = "sh_tmu",
+ }
+};
+
+static int __init sh_tmu_init(void)
+{
+ return platform_driver_register(&sh_tmu_device_driver);
+}
+
+static void __exit sh_tmu_exit(void)
+{
+ platform_driver_unregister(&sh_tmu_device_driver);
+}
+
+early_platform_init("earlytimer", &sh_tmu_device_driver);
+module_init(sh_tmu_init);
+module_exit(sh_tmu_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH TMU Timer Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d270e8eb3e67..6e2ec0b18948 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -808,7 +808,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
ret = -ENOMEM;
goto nomem_out;
}
- if (!alloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) {
+ if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) {
free_cpumask_var(policy->cpus);
kfree(policy);
ret = -ENOMEM;
@@ -1070,11 +1070,11 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
#endif
+ unlock_policy_rwsem_write(cpu);
+
if (cpufreq_driver->target)
__cpufreq_governor(data, CPUFREQ_GOV_STOP);
- unlock_policy_rwsem_write(cpu);
-
kobject_put(&data->kobj);
/* we need to make sure that the underlying kobj is actually
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 2ecd95e4ab1a..7a74d175287b 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -91,6 +91,9 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */
* (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then
* cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock
* is recursive for the same process. -Venki
+ * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it
+ * would deadlock with cancel_delayed_work_sync(), which is needed for proper
+ * raceless workqueue teardown.
*/
static DEFINE_MUTEX(dbs_mutex);
@@ -542,7 +545,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
{
dbs_info->enable = 0;
- cancel_delayed_work(&dbs_info->work);
+ cancel_delayed_work_sync(&dbs_info->work);
}
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 338f428a15b7..e741c339df76 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -98,6 +98,9 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */
* (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then
* cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock
* is recursive for the same process. -Venki
+ * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it
+ * would deadlock with cancel_delayed_work_sync(), which is needed for proper
+ * raceless workqueue teardown.
*/
static DEFINE_MUTEX(dbs_mutex);
@@ -562,7 +565,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
{
dbs_info->enable = 0;
- cancel_delayed_work(&dbs_info->work);
+ cancel_delayed_work_sync(&dbs_info->work);
}
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index f9f05d7a707d..6c6656d3b1e2 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -415,6 +415,7 @@ static void crypto_done_action(unsigned long arg)
static int init_ixp_crypto(void)
{
int ret = -ENODEV;
+ u32 msg[2] = { 0, 0 };
if (! ( ~(*IXP4XX_EXP_CFG2) & (IXP4XX_FEATURE_HASH |
IXP4XX_FEATURE_AES | IXP4XX_FEATURE_DES))) {
@@ -426,9 +427,35 @@ static int init_ixp_crypto(void)
return ret;
if (!npe_running(npe_c)) {
- npe_load_firmware(npe_c, npe_name(npe_c), dev);
+ ret = npe_load_firmware(npe_c, npe_name(npe_c), dev);
+ if (ret) {
+ return ret;
+ }
+ if (npe_recv_message(npe_c, msg, "STATUS_MSG"))
+ goto npe_error;
+ } else {
+ if (npe_send_message(npe_c, msg, "STATUS_MSG"))
+ goto npe_error;
+
+ if (npe_recv_message(npe_c, msg, "STATUS_MSG"))
+ goto npe_error;
}
+ switch ((msg[1]>>16) & 0xff) {
+ case 3:
+ printk(KERN_WARNING "Firmware of %s lacks AES support\n",
+ npe_name(npe_c));
+ support_aes = 0;
+ break;
+ case 4:
+ case 5:
+ support_aes = 1;
+ break;
+ default:
+ printk(KERN_ERR "Firmware of %s lacks crypto support\n",
+ npe_name(npe_c));
+ return -ENODEV;
+ }
/* buffer_pool will also be used to sometimes store the hmac,
* so assure it is large enough
*/
@@ -459,6 +486,10 @@ static int init_ixp_crypto(void)
qmgr_enable_irq(RECV_QID);
return 0;
+
+npe_error:
+ printk(KERN_ERR "%s not responding\n", npe_name(npe_c));
+ ret = -EIO;
err:
if (ctx_pool)
dma_pool_destroy(ctx_pool);
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 3f0fdd18255d..856b3cc25583 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -489,4 +489,4 @@ MODULE_DESCRIPTION("VIA PadLock AES algorithm support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Ludvig");
-MODULE_ALIAS("aes-all");
+MODULE_ALIAS("aes");
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 92438e9dacc3..5a87384ea4ff 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -804,11 +804,14 @@ dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest,
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
int cpu;
+ unsigned long flags;
dma_src = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE);
dma_dest = dma_map_single(dev->dev, dest, len, DMA_FROM_DEVICE);
- tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len,
- DMA_CTRL_ACK);
+ flags = DMA_CTRL_ACK |
+ DMA_COMPL_SRC_UNMAP_SINGLE |
+ DMA_COMPL_DEST_UNMAP_SINGLE;
+ tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
if (!tx) {
dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
@@ -850,11 +853,12 @@ dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page,
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
int cpu;
+ unsigned long flags;
dma_src = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE);
dma_dest = dma_map_page(dev->dev, page, offset, len, DMA_FROM_DEVICE);
- tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len,
- DMA_CTRL_ACK);
+ flags = DMA_CTRL_ACK | DMA_COMPL_SRC_UNMAP_SINGLE;
+ tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
if (!tx) {
dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
@@ -898,12 +902,13 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg,
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
int cpu;
+ unsigned long flags;
dma_src = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE);
dma_dest = dma_map_page(dev->dev, dest_pg, dest_off, len,
DMA_FROM_DEVICE);
- tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len,
- DMA_CTRL_ACK);
+ flags = DMA_CTRL_ACK;
+ tx = dev->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, flags);
if (!tx) {
dma_unmap_page(dev->dev, dma_src, len, DMA_TO_DEVICE);
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index a27c0fb1bc11..fb7da5141e96 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -531,9 +531,7 @@ static int __init dmatest_init(void)
chan = dma_request_channel(mask, filter, NULL);
if (chan) {
err = dmatest_add_channel(chan);
- if (err == 0)
- continue;
- else {
+ if (err) {
dma_release_channel(chan);
break; /* add_channel failed, punt */
}
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index da8a8ed9e411..f18d1bde0439 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -179,9 +179,14 @@ static void dma_halt(struct fsl_dma_chan *fsl_chan)
static void set_ld_eol(struct fsl_dma_chan *fsl_chan,
struct fsl_desc_sw *desc)
{
+ u64 snoop_bits;
+
+ snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
+ ? FSL_DMA_SNEN : 0;
+
desc->hw.next_ln_addr = CPU_TO_DMA(fsl_chan,
- DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL,
- 64);
+ DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
+ | snoop_bits, 64);
}
static void append_ld_queue(struct fsl_dma_chan *fsl_chan,
@@ -313,8 +318,8 @@ static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable)
static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
{
- struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan);
+ struct fsl_desc_sw *desc;
unsigned long flags;
dma_cookie_t cookie;
@@ -322,14 +327,17 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
spin_lock_irqsave(&fsl_chan->desc_lock, flags);
cookie = fsl_chan->common.cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
- desc->async_tx.cookie = cookie;
- fsl_chan->common.cookie = desc->async_tx.cookie;
+ list_for_each_entry(desc, &tx->tx_list, node) {
+ cookie++;
+ if (cookie < 0)
+ cookie = 1;
- append_ld_queue(fsl_chan, desc);
- list_splice_init(&desc->async_tx.tx_list, fsl_chan->ld_queue.prev);
+ desc->async_tx.cookie = cookie;
+ }
+
+ fsl_chan->common.cookie = cookie;
+ append_ld_queue(fsl_chan, tx_to_fsl_desc(tx));
+ list_splice_init(&tx->tx_list, fsl_chan->ld_queue.prev);
spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
@@ -454,8 +462,8 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
{
struct fsl_dma_chan *fsl_chan;
struct fsl_desc_sw *first = NULL, *prev = NULL, *new;
+ struct list_head *list;
size_t copy;
- LIST_HEAD(link_chain);
if (!chan)
return NULL;
@@ -472,7 +480,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
if (!new) {
dev_err(fsl_chan->dev,
"No free memory for link descriptor\n");
- return NULL;
+ goto fail;
}
#ifdef FSL_DMA_LD_DEBUG
dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new);
@@ -507,7 +515,19 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
/* Set End-of-link to the last link descriptor of new list*/
set_ld_eol(fsl_chan, new);
- return first ? &first->async_tx : NULL;
+ return &first->async_tx;
+
+fail:
+ if (!first)
+ return NULL;
+
+ list = &first->async_tx.tx_list;
+ list_for_each_entry_safe_reverse(new, prev, list, node) {
+ list_del(&new->node);
+ dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
+ }
+
+ return NULL;
}
/**
@@ -598,15 +618,16 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan)
dma_addr_t next_dest_addr;
unsigned long flags;
+ spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+
if (!dma_is_idle(fsl_chan))
- return;
+ goto out_unlock;
dma_halt(fsl_chan);
/* If there are some link descriptors
* not transfered in queue. We need to start it.
*/
- spin_lock_irqsave(&fsl_chan->desc_lock, flags);
/* Find the first un-transfer desciptor */
for (ld_node = fsl_chan->ld_queue.next;
@@ -617,19 +638,20 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan)
fsl_chan->common.cookie) == DMA_SUCCESS);
ld_node = ld_node->next);
- spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-
if (ld_node != &fsl_chan->ld_queue) {
/* Get the ld start address from ld_queue */
next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys;
- dev_dbg(fsl_chan->dev, "xfer LDs staring from %p\n",
- (void *)next_dest_addr);
+ dev_dbg(fsl_chan->dev, "xfer LDs staring from 0x%llx\n",
+ (unsigned long long)next_dest_addr);
set_cdar(fsl_chan, next_dest_addr);
dma_start(fsl_chan);
} else {
set_cdar(fsl_chan, 0);
set_ndar(fsl_chan, 0);
}
+
+out_unlock:
+ spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
}
/**
@@ -734,8 +756,9 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data)
*/
if (stat & FSL_DMA_SR_EOSI) {
dev_dbg(fsl_chan->dev, "event: End-of-segments INT\n");
- dev_dbg(fsl_chan->dev, "event: clndar %p, nlndar %p\n",
- (void *)get_cdar(fsl_chan), (void *)get_ndar(fsl_chan));
+ dev_dbg(fsl_chan->dev, "event: clndar 0x%llx, nlndar 0x%llx\n",
+ (unsigned long long)get_cdar(fsl_chan),
+ (unsigned long long)get_ndar(fsl_chan));
stat &= ~FSL_DMA_SR_EOSI;
update_cookie = 1;
}
@@ -830,7 +853,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
- if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) {
+ if (new_fsl_chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
dev_err(fdev->dev, "There is no %d channel!\n",
new_fsl_chan->id);
err = -EINVAL;
@@ -925,8 +948,8 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
}
dev_info(&dev->dev, "Probe the Freescale DMA driver for %s "
- "controller at %p...\n",
- match->compatible, (void *)fdev->reg.start);
+ "controller at 0x%llx...\n",
+ match->compatible, (unsigned long long)fdev->reg.start);
fdev->reg_base = ioremap(fdev->reg.start, fdev->reg.end
- fdev->reg.start + 1);
diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c
index e4fc33c1c32f..a600fc0f7962 100644
--- a/drivers/dma/ioat_dma.c
+++ b/drivers/dma/ioat_dma.c
@@ -173,7 +173,7 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device)
xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale));
#ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL
- if (i7300_idle_platform_probe(NULL, NULL) == 0) {
+ if (i7300_idle_platform_probe(NULL, NULL, 1) == 0) {
device->common.chancnt--;
}
#endif
@@ -1063,22 +1063,31 @@ static void ioat_dma_cleanup_tasklet(unsigned long data)
static void
ioat_dma_unmap(struct ioat_dma_chan *ioat_chan, struct ioat_desc_sw *desc)
{
- /*
- * yes we are unmapping both _page and _single
- * alloc'd regions with unmap_page. Is this
- * *really* that bad?
- */
- if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP))
- pci_unmap_page(ioat_chan->device->pdev,
- pci_unmap_addr(desc, dst),
- pci_unmap_len(desc, len),
- PCI_DMA_FROMDEVICE);
-
- if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP))
- pci_unmap_page(ioat_chan->device->pdev,
- pci_unmap_addr(desc, src),
- pci_unmap_len(desc, len),
- PCI_DMA_TODEVICE);
+ if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ if (desc->async_tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+ pci_unmap_single(ioat_chan->device->pdev,
+ pci_unmap_addr(desc, dst),
+ pci_unmap_len(desc, len),
+ PCI_DMA_FROMDEVICE);
+ else
+ pci_unmap_page(ioat_chan->device->pdev,
+ pci_unmap_addr(desc, dst),
+ pci_unmap_len(desc, len),
+ PCI_DMA_FROMDEVICE);
+ }
+
+ if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ if (desc->async_tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+ pci_unmap_single(ioat_chan->device->pdev,
+ pci_unmap_addr(desc, src),
+ pci_unmap_len(desc, len),
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_page(ioat_chan->device->pdev,
+ pci_unmap_addr(desc, src),
+ pci_unmap_len(desc, len),
+ PCI_DMA_TODEVICE);
+ }
}
/**
@@ -1363,6 +1372,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
int err = 0;
struct completion cmp;
unsigned long tmo;
+ unsigned long flags;
src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL);
if (!src)
@@ -1392,8 +1402,9 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
DMA_TO_DEVICE);
dma_dest = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE,
DMA_FROM_DEVICE);
+ flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE;
tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src,
- IOAT_TEST_SIZE, 0);
+ IOAT_TEST_SIZE, flags);
if (!tx) {
dev_err(&device->pdev->dev,
"Self-test prep failed, disabling\n");
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index e202a6ce5573..9a5bc1a7389e 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -1272,7 +1272,8 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
/* Other interrupts do not interfere with this channel */
spin_lock(&ichan->lock);
if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 &&
- ((curbuf >> chan_id) & 1) == ichan->active_buffer)) {
+ ((curbuf >> chan_id) & 1) == ichan->active_buffer &&
+ !list_is_last(ichan->queue.next, &ichan->queue))) {
int i = 100;
/* This doesn't help. See comment in ipu_disable_channel() */
@@ -1547,7 +1548,7 @@ static irqreturn_t ic_sof_irq(int irq, void *dev_id)
struct idmac_channel *ichan = dev_id;
printk(KERN_DEBUG "Got SOF IRQ %d on Channel %d\n",
irq, ichan->dma_chan.chan_id);
- disable_irq(irq);
+ disable_irq_nosync(irq);
return IRQ_HANDLED;
}
@@ -1556,7 +1557,7 @@ static irqreturn_t ic_eof_irq(int irq, void *dev_id)
struct idmac_channel *ichan = dev_id;
printk(KERN_DEBUG "Got EOF IRQ %d on Channel %d\n",
irq, ichan->dma_chan.chan_id);
- disable_irq(irq);
+ disable_irq_nosync(irq);
return IRQ_HANDLED;
}
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index e5f5c5a8ba6c..ab4f3592a11c 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -49,7 +49,6 @@ config EDAC_DEBUG_VERBOSE
config EDAC_MM_EDAC
tristate "Main Memory EDAC (Error Detection And Correction) reporting"
- default y
help
Some systems are able to detect and correct errors in main
memory. EDAC can report statistics on memory error
@@ -58,6 +57,31 @@ config EDAC_MM_EDAC
occurred so that a particular failing memory module can be
replaced. If unsure, select 'Y'.
+config EDAC_AMD64
+ tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h"
+ depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI
+ help
+ Support for error detection and correction on the AMD 64
+ Families of Memory Controllers (K8, F10h and F11h)
+
+config EDAC_AMD64_ERROR_INJECTION
+ bool "Sysfs Error Injection facilities"
+ depends on EDAC_AMD64
+ help
+ Recent Opterons (Family 10h and later) provide for Memory Error
+ Injection into the ECC detection circuits. The amd64_edac module
+ allows the operator/user to inject Uncorrectable and Correctable
+ errors into DRAM.
+
+ When enabled, in each of the respective memory controller directories
+ (/sys/devices/system/edac/mc/mcX), there are 3 input files:
+
+ - inject_section (0..3, 16-byte section of 64-byte cacheline),
+ - inject_word (0..8, 16-bit word of 16-byte section),
+ - inject_ecc_vector (hex ecc vector: select bits of inject word)
+
+ In addition, there are two control files, inject_read and inject_write,
+ which trigger the DRAM ECC Read and Write respectively.
config EDAC_AMD76X
tristate "AMD 76x (760, 762, 768)"
@@ -192,16 +216,20 @@ config EDAC_PPC4XX
config EDAC_AMD8131
tristate "AMD8131 HyperTransport PCI-X Tunnel"
- depends on EDAC_MM_EDAC && PCI
+ depends on EDAC_MM_EDAC && PCI && PPC_MAPLE
help
Support for error detection and correction on the
AMD8131 HyperTransport PCI-X Tunnel chip.
+ Note, add more Kconfig dependency if it's adopted
+ on some machine other than Maple.
config EDAC_AMD8111
tristate "AMD8111 HyperTransport I/O Hub"
- depends on EDAC_MM_EDAC && PCI
+ depends on EDAC_MM_EDAC && PCI && PPC_MAPLE
help
Support for error detection and correction on the
AMD8111 HyperTransport I/O Hub chip.
+ Note, add more Kconfig dependency if it's adopted
+ on some machine other than Maple.
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index a5fdcf02f591..633dc5604ee3 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -30,8 +30,17 @@ obj-$(CONFIG_EDAC_I3000) += i3000_edac.o
obj-$(CONFIG_EDAC_X38) += x38_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
+
+amd64_edac_mod-y := amd64_edac_err_types.o amd64_edac.o
+amd64_edac_mod-$(CONFIG_EDAC_DEBUG) += amd64_edac_dbg.o
+amd64_edac_mod-$(CONFIG_EDAC_AMD64_ERROR_INJECTION) += amd64_edac_inj.o
+
+obj-$(CONFIG_EDAC_AMD64) += amd64_edac_mod.o
+
obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o
obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o
obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o
obj-$(CONFIG_EDAC_CELL) += cell_edac.o
obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o
+obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o
+obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
new file mode 100644
index 000000000000..c36bf40568cf
--- /dev/null
+++ b/drivers/edac/amd64_edac.c
@@ -0,0 +1,3354 @@
+#include "amd64_edac.h"
+#include <asm/k8.h>
+
+static struct edac_pci_ctl_info *amd64_ctl_pci;
+
+static int report_gart_errors;
+module_param(report_gart_errors, int, 0644);
+
+/*
+ * Set by command line parameter. If BIOS has enabled the ECC, this override is
+ * cleared to prevent re-enabling the hardware by this driver.
+ */
+static int ecc_enable_override;
+module_param(ecc_enable_override, int, 0644);
+
+/* Lookup table for all possible MC control instances */
+struct amd64_pvt;
+static struct mem_ctl_info *mci_lookup[MAX_NUMNODES];
+static struct amd64_pvt *pvt_lookup[MAX_NUMNODES];
+
+/*
+ * Memory scrubber control interface. For K8, memory scrubbing is handled by
+ * hardware and can involve L2 cache, dcache as well as the main memory. With
+ * F10, this is extended to L3 cache scrubbing on CPU models sporting that
+ * functionality.
+ *
+ * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
+ * (dram) over to cache lines. This is nasty, so we will use bandwidth in
+ * bytes/sec for the setting.
+ *
+ * Currently, we only do dram scrubbing. If the scrubbing is done in software on
+ * other archs, we might not have access to the caches directly.
+ */
+
+/*
+ * scan the scrub rate mapping table for a close or matching bandwidth value to
+ * issue. If requested is too big, then use last maximum value found.
+ */
+static int amd64_search_set_scrub_rate(struct pci_dev *ctl, u32 new_bw,
+ u32 min_scrubrate)
+{
+ u32 scrubval;
+ int i;
+
+ /*
+ * map the configured rate (new_bw) to a value specific to the AMD64
+ * memory controller and apply to register. Search for the first
+ * bandwidth entry that is greater or equal than the setting requested
+ * and program that. If at last entry, turn off DRAM scrubbing.
+ */
+ for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
+ /*
+ * skip scrub rates which aren't recommended
+ * (see F10 BKDG, F3x58)
+ */
+ if (scrubrates[i].scrubval < min_scrubrate)
+ continue;
+
+ if (scrubrates[i].bandwidth <= new_bw)
+ break;
+
+ /*
+ * if no suitable bandwidth found, turn off DRAM scrubbing
+ * entirely by falling back to the last element in the
+ * scrubrates array.
+ */
+ }
+
+ scrubval = scrubrates[i].scrubval;
+ if (scrubval)
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "Setting scrub rate bandwidth: %u\n",
+ scrubrates[i].bandwidth);
+ else
+ edac_printk(KERN_DEBUG, EDAC_MC, "Turning scrubbing off.\n");
+
+ pci_write_bits32(ctl, K8_SCRCTRL, scrubval, 0x001F);
+
+ return 0;
+}
+
+static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 *bandwidth)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 min_scrubrate = 0x0;
+
+ switch (boot_cpu_data.x86) {
+ case 0xf:
+ min_scrubrate = K8_MIN_SCRUB_RATE_BITS;
+ break;
+ case 0x10:
+ min_scrubrate = F10_MIN_SCRUB_RATE_BITS;
+ break;
+ case 0x11:
+ min_scrubrate = F11_MIN_SCRUB_RATE_BITS;
+ break;
+
+ default:
+ amd64_printk(KERN_ERR, "Unsupported family!\n");
+ break;
+ }
+ return amd64_search_set_scrub_rate(pvt->misc_f3_ctl, *bandwidth,
+ min_scrubrate);
+}
+
+static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 scrubval = 0;
+ int status = -1, i, ret = 0;
+
+ ret = pci_read_config_dword(pvt->misc_f3_ctl, K8_SCRCTRL, &scrubval);
+ if (ret)
+ debugf0("Reading K8_SCRCTRL failed\n");
+
+ scrubval = scrubval & 0x001F;
+
+ edac_printk(KERN_DEBUG, EDAC_MC,
+ "pci-read, sdram scrub control value: %d \n", scrubval);
+
+ for (i = 0; ARRAY_SIZE(scrubrates); i++) {
+ if (scrubrates[i].scrubval == scrubval) {
+ *bw = scrubrates[i].bandwidth;
+ status = 0;
+ break;
+ }
+ }
+
+ return status;
+}
+
+/* Map from a CSROW entry to the mask entry that operates on it */
+static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow)
+{
+ return csrow >> (pvt->num_dcsm >> 3);
+}
+
+/* return the 'base' address the i'th CS entry of the 'dct' DRAM controller */
+static u32 amd64_get_dct_base(struct amd64_pvt *pvt, int dct, int csrow)
+{
+ if (dct == 0)
+ return pvt->dcsb0[csrow];
+ else
+ return pvt->dcsb1[csrow];
+}
+
+/*
+ * Return the 'mask' address the i'th CS entry. This function is needed because
+ * there number of DCSM registers on Rev E and prior vs Rev F and later is
+ * different.
+ */
+static u32 amd64_get_dct_mask(struct amd64_pvt *pvt, int dct, int csrow)
+{
+ if (dct == 0)
+ return pvt->dcsm0[amd64_map_to_dcs_mask(pvt, csrow)];
+ else
+ return pvt->dcsm1[amd64_map_to_dcs_mask(pvt, csrow)];
+}
+
+
+/*
+ * In *base and *limit, pass back the full 40-bit base and limit physical
+ * addresses for the node given by node_id. This information is obtained from
+ * DRAM Base (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers. The
+ * base and limit addresses are of type SysAddr, as defined at the start of
+ * section 3.4.4 (p. 70). They are the lowest and highest physical addresses
+ * in the address range they represent.
+ */
+static void amd64_get_base_and_limit(struct amd64_pvt *pvt, int node_id,
+ u64 *base, u64 *limit)
+{
+ *base = pvt->dram_base[node_id];
+ *limit = pvt->dram_limit[node_id];
+}
+
+/*
+ * Return 1 if the SysAddr given by sys_addr matches the base/limit associated
+ * with node_id
+ */
+static int amd64_base_limit_match(struct amd64_pvt *pvt,
+ u64 sys_addr, int node_id)
+{
+ u64 base, limit, addr;
+
+ amd64_get_base_and_limit(pvt, node_id, &base, &limit);
+
+ /* The K8 treats this as a 40-bit value. However, bits 63-40 will be
+ * all ones if the most significant implemented address bit is 1.
+ * Here we discard bits 63-40. See section 3.4.2 of AMD publication
+ * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
+ * Application Programming.
+ */
+ addr = sys_addr & 0x000000ffffffffffull;
+
+ return (addr >= base) && (addr <= limit);
+}
+
+/*
+ * Attempt to map a SysAddr to a node. On success, return a pointer to the
+ * mem_ctl_info structure for the node that the SysAddr maps to.
+ *
+ * On failure, return NULL.
+ */
+static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
+ u64 sys_addr)
+{
+ struct amd64_pvt *pvt;
+ int node_id;
+ u32 intlv_en, bits;
+
+ /*
+ * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
+ * 3.4.4.2) registers to map the SysAddr to a node ID.
+ */
+ pvt = mci->pvt_info;
+
+ /*
+ * The value of this field should be the same for all DRAM Base
+ * registers. Therefore we arbitrarily choose to read it from the
+ * register for node 0.
+ */
+ intlv_en = pvt->dram_IntlvEn[0];
+
+ if (intlv_en == 0) {
+ for (node_id = 0; ; ) {
+ if (amd64_base_limit_match(pvt, sys_addr, node_id))
+ break;
+
+ if (++node_id >= DRAM_REG_COUNT)
+ goto err_no_match;
+ }
+ goto found;
+ }
+
+ if (unlikely((intlv_en != (0x01 << 8)) &&
+ (intlv_en != (0x03 << 8)) &&
+ (intlv_en != (0x07 << 8)))) {
+ amd64_printk(KERN_WARNING, "junk value of 0x%x extracted from "
+ "IntlvEn field of DRAM Base Register for node 0: "
+ "This probably indicates a BIOS bug.\n", intlv_en);
+ return NULL;
+ }
+
+ bits = (((u32) sys_addr) >> 12) & intlv_en;
+
+ for (node_id = 0; ; ) {
+ if ((pvt->dram_limit[node_id] & intlv_en) == bits)
+ break; /* intlv_sel field matches */
+
+ if (++node_id >= DRAM_REG_COUNT)
+ goto err_no_match;
+ }
+
+ /* sanity test for sys_addr */
+ if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
+ amd64_printk(KERN_WARNING,
+ "%s(): sys_addr 0x%lx falls outside base/limit "
+ "address range for node %d with node interleaving "
+ "enabled.\n", __func__, (unsigned long)sys_addr,
+ node_id);
+ return NULL;
+ }
+
+found:
+ return edac_mc_find(node_id);
+
+err_no_match:
+ debugf2("sys_addr 0x%lx doesn't match any node\n",
+ (unsigned long)sys_addr);
+
+ return NULL;
+}
+
+/*
+ * Extract the DRAM CS base address from selected csrow register.
+ */
+static u64 base_from_dct_base(struct amd64_pvt *pvt, int csrow)
+{
+ return ((u64) (amd64_get_dct_base(pvt, 0, csrow) & pvt->dcsb_base)) <<
+ pvt->dcs_shift;
+}
+
+/*
+ * Extract the mask from the dcsb0[csrow] entry in a CPU revision-specific way.
+ */
+static u64 mask_from_dct_mask(struct amd64_pvt *pvt, int csrow)
+{
+ u64 dcsm_bits, other_bits;
+ u64 mask;
+
+ /* Extract bits from DRAM CS Mask. */
+ dcsm_bits = amd64_get_dct_mask(pvt, 0, csrow) & pvt->dcsm_mask;
+
+ other_bits = pvt->dcsm_mask;
+ other_bits = ~(other_bits << pvt->dcs_shift);
+
+ /*
+ * The extracted bits from DCSM belong in the spaces represented by
+ * the cleared bits in other_bits.
+ */
+ mask = (dcsm_bits << pvt->dcs_shift) | other_bits;
+
+ return mask;
+}
+
+/*
+ * @input_addr is an InputAddr associated with the node given by mci. Return the
+ * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
+ */
+static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
+{
+ struct amd64_pvt *pvt;
+ int csrow;
+ u64 base, mask;
+
+ pvt = mci->pvt_info;
+
+ /*
+ * Here we use the DRAM CS Base and DRAM CS Mask registers. For each CS
+ * base/mask register pair, test the condition shown near the start of
+ * section 3.5.4 (p. 84, BKDG #26094, K8, revA-E).
+ */
+ for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) {
+
+ /* This DRAM chip select is disabled on this node */
+ if ((pvt->dcsb0[csrow] & K8_DCSB_CS_ENABLE) == 0)
+ continue;
+
+ base = base_from_dct_base(pvt, csrow);
+ mask = ~mask_from_dct_mask(pvt, csrow);
+
+ if ((input_addr & mask) == (base & mask)) {
+ debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
+ (unsigned long)input_addr, csrow,
+ pvt->mc_node_id);
+
+ return csrow;
+ }
+ }
+
+ debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
+ (unsigned long)input_addr, pvt->mc_node_id);
+
+ return -1;
+}
+
+/*
+ * Return the base value defined by the DRAM Base register for the node
+ * represented by mci. This function returns the full 40-bit value despite the
+ * fact that the register only stores bits 39-24 of the value. See section
+ * 3.4.4.1 (BKDG #26094, K8, revA-E)
+ */
+static inline u64 get_dram_base(struct mem_ctl_info *mci)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return pvt->dram_base[pvt->mc_node_id];
+}
+
+/*
+ * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
+ * for the node represented by mci. Info is passed back in *hole_base,
+ * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if
+ * info is invalid. Info may be invalid for either of the following reasons:
+ *
+ * - The revision of the node is not E or greater. In this case, the DRAM Hole
+ * Address Register does not exist.
+ *
+ * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
+ * indicating that its contents are not valid.
+ *
+ * The values passed back in *hole_base, *hole_offset, and *hole_size are
+ * complete 32-bit values despite the fact that the bitfields in the DHAR
+ * only represent bits 31-24 of the base and offset values.
+ */
+int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
+ u64 *hole_offset, u64 *hole_size)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u64 base;
+
+ /* only revE and later have the DRAM Hole Address Register */
+ if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_E) {
+ debugf1(" revision %d for node %d does not support DHAR\n",
+ pvt->ext_model, pvt->mc_node_id);
+ return 1;
+ }
+
+ /* only valid for Fam10h */
+ if (boot_cpu_data.x86 == 0x10 &&
+ (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) == 0) {
+ debugf1(" Dram Memory Hoisting is DISABLED on this system\n");
+ return 1;
+ }
+
+ if ((pvt->dhar & DHAR_VALID) == 0) {
+ debugf1(" Dram Memory Hoisting is DISABLED on this node %d\n",
+ pvt->mc_node_id);
+ return 1;
+ }
+
+ /* This node has Memory Hoisting */
+
+ /* +------------------+--------------------+--------------------+-----
+ * | memory | DRAM hole | relocated |
+ * | [0, (x - 1)] | [x, 0xffffffff] | addresses from |
+ * | | | DRAM hole |
+ * | | | [0x100000000, |
+ * | | | (0x100000000+ |
+ * | | | (0xffffffff-x))] |
+ * +------------------+--------------------+--------------------+-----
+ *
+ * Above is a diagram of physical memory showing the DRAM hole and the
+ * relocated addresses from the DRAM hole. As shown, the DRAM hole
+ * starts at address x (the base address) and extends through address
+ * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the
+ * addresses in the hole so that they start at 0x100000000.
+ */
+
+ base = dhar_base(pvt->dhar);
+
+ *hole_base = base;
+ *hole_size = (0x1ull << 32) - base;
+
+ if (boot_cpu_data.x86 > 0xf)
+ *hole_offset = f10_dhar_offset(pvt->dhar);
+ else
+ *hole_offset = k8_dhar_offset(pvt->dhar);
+
+ debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
+ pvt->mc_node_id, (unsigned long)*hole_base,
+ (unsigned long)*hole_offset, (unsigned long)*hole_size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
+
+/*
+ * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
+ * assumed that sys_addr maps to the node given by mci.
+ *
+ * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
+ * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
+ * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
+ * then it is also involved in translating a SysAddr to a DramAddr. Sections
+ * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
+ * These parts of the documentation are unclear. I interpret them as follows:
+ *
+ * When node n receives a SysAddr, it processes the SysAddr as follows:
+ *
+ * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
+ * Limit registers for node n. If the SysAddr is not within the range
+ * specified by the base and limit values, then node n ignores the Sysaddr
+ * (since it does not map to node n). Otherwise continue to step 2 below.
+ *
+ * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
+ * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
+ * the range of relocated addresses (starting at 0x100000000) from the DRAM
+ * hole. If not, skip to step 3 below. Else get the value of the
+ * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
+ * offset defined by this value from the SysAddr.
+ *
+ * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
+ * Base register for node n. To obtain the DramAddr, subtract the base
+ * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
+ */
+static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
+{
+ u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
+ int ret = 0;
+
+ dram_base = get_dram_base(mci);
+
+ ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
+ &hole_size);
+ if (!ret) {
+ if ((sys_addr >= (1ull << 32)) &&
+ (sys_addr < ((1ull << 32) + hole_size))) {
+ /* use DHAR to translate SysAddr to DramAddr */
+ dram_addr = sys_addr - hole_offset;
+
+ debugf2("using DHAR to translate SysAddr 0x%lx to "
+ "DramAddr 0x%lx\n",
+ (unsigned long)sys_addr,
+ (unsigned long)dram_addr);
+
+ return dram_addr;
+ }
+ }
+
+ /*
+ * Translate the SysAddr to a DramAddr as shown near the start of
+ * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
+ * only deals with 40-bit values. Therefore we discard bits 63-40 of
+ * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
+ * discard are all 1s. Otherwise the bits we discard are all 0s. See
+ * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
+ * Programmer's Manual Volume 1 Application Programming.
+ */
+ dram_addr = (sys_addr & 0xffffffffffull) - dram_base;
+
+ debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
+ "DramAddr 0x%lx\n", (unsigned long)sys_addr,
+ (unsigned long)dram_addr);
+ return dram_addr;
+}
+
+/*
+ * @intlv_en is the value of the IntlvEn field from a DRAM Base register
+ * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
+ * for node interleaving.
+ */
+static int num_node_interleave_bits(unsigned intlv_en)
+{
+ static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
+ int n;
+
+ BUG_ON(intlv_en > 7);
+ n = intlv_shift_table[intlv_en];
+ return n;
+}
+
+/* Translate the DramAddr given by @dram_addr to an InputAddr. */
+static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
+{
+ struct amd64_pvt *pvt;
+ int intlv_shift;
+ u64 input_addr;
+
+ pvt = mci->pvt_info;
+
+ /*
+ * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
+ * concerning translating a DramAddr to an InputAddr.
+ */
+ intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
+ input_addr = ((dram_addr >> intlv_shift) & 0xffffff000ull) +
+ (dram_addr & 0xfff);
+
+ debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
+ intlv_shift, (unsigned long)dram_addr,
+ (unsigned long)input_addr);
+
+ return input_addr;
+}
+
+/*
+ * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
+ * assumed that @sys_addr maps to the node given by mci.
+ */
+static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
+{
+ u64 input_addr;
+
+ input_addr =
+ dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
+
+ debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
+ (unsigned long)sys_addr, (unsigned long)input_addr);
+
+ return input_addr;
+}
+
+
+/*
+ * @input_addr is an InputAddr associated with the node represented by mci.
+ * Translate @input_addr to a DramAddr and return the result.
+ */
+static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
+{
+ struct amd64_pvt *pvt;
+ int node_id, intlv_shift;
+ u64 bits, dram_addr;
+ u32 intlv_sel;
+
+ /*
+ * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
+ * shows how to translate a DramAddr to an InputAddr. Here we reverse
+ * this procedure. When translating from a DramAddr to an InputAddr, the
+ * bits used for node interleaving are discarded. Here we recover these
+ * bits from the IntlvSel field of the DRAM Limit register (section
+ * 3.4.4.2) for the node that input_addr is associated with.
+ */
+ pvt = mci->pvt_info;
+ node_id = pvt->mc_node_id;
+ BUG_ON((node_id < 0) || (node_id > 7));
+
+ intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
+
+ if (intlv_shift == 0) {
+ debugf1(" InputAddr 0x%lx translates to DramAddr of "
+ "same value\n", (unsigned long)input_addr);
+
+ return input_addr;
+ }
+
+ bits = ((input_addr & 0xffffff000ull) << intlv_shift) +
+ (input_addr & 0xfff);
+
+ intlv_sel = pvt->dram_IntlvSel[node_id] & ((1 << intlv_shift) - 1);
+ dram_addr = bits + (intlv_sel << 12);
+
+ debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
+ "(%d node interleave bits)\n", (unsigned long)input_addr,
+ (unsigned long)dram_addr, intlv_shift);
+
+ return dram_addr;
+}
+
+/*
+ * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
+ * @dram_addr to a SysAddr.
+ */
+static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u64 hole_base, hole_offset, hole_size, base, limit, sys_addr;
+ int ret = 0;
+
+ ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
+ &hole_size);
+ if (!ret) {
+ if ((dram_addr >= hole_base) &&
+ (dram_addr < (hole_base + hole_size))) {
+ sys_addr = dram_addr + hole_offset;
+
+ debugf1("using DHAR to translate DramAddr 0x%lx to "
+ "SysAddr 0x%lx\n", (unsigned long)dram_addr,
+ (unsigned long)sys_addr);
+
+ return sys_addr;
+ }
+ }
+
+ amd64_get_base_and_limit(pvt, pvt->mc_node_id, &base, &limit);
+ sys_addr = dram_addr + base;
+
+ /*
+ * The sys_addr we have computed up to this point is a 40-bit value
+ * because the k8 deals with 40-bit values. However, the value we are
+ * supposed to return is a full 64-bit physical address. The AMD
+ * x86-64 architecture specifies that the most significant implemented
+ * address bit through bit 63 of a physical address must be either all
+ * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a
+ * 64-bit value below. See section 3.4.2 of AMD publication 24592:
+ * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
+ * Programming.
+ */
+ sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
+
+ debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
+ pvt->mc_node_id, (unsigned long)dram_addr,
+ (unsigned long)sys_addr);
+
+ return sys_addr;
+}
+
+/*
+ * @input_addr is an InputAddr associated with the node given by mci. Translate
+ * @input_addr to a SysAddr.
+ */
+static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
+ u64 input_addr)
+{
+ return dram_addr_to_sys_addr(mci,
+ input_addr_to_dram_addr(mci, input_addr));
+}
+
+/*
+ * Find the minimum and maximum InputAddr values that map to the given @csrow.
+ * Pass back these values in *input_addr_min and *input_addr_max.
+ */
+static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
+ u64 *input_addr_min, u64 *input_addr_max)
+{
+ struct amd64_pvt *pvt;
+ u64 base, mask;
+
+ pvt = mci->pvt_info;
+ BUG_ON((csrow < 0) || (csrow >= CHIPSELECT_COUNT));
+
+ base = base_from_dct_base(pvt, csrow);
+ mask = mask_from_dct_mask(pvt, csrow);
+
+ *input_addr_min = base & ~mask;
+ *input_addr_max = base | mask | pvt->dcs_mask_notused;
+}
+
+/*
+ * Extract error address from MCA NB Address Low (section 3.6.4.5) and MCA NB
+ * Address High (section 3.6.4.6) register values and return the result. Address
+ * is located in the info structure (nbeah and nbeal), the encoding is device
+ * specific.
+ */
+static u64 extract_error_address(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return pvt->ops->get_error_address(mci, info);
+}
+
+
+/* Map the Error address to a PAGE and PAGE OFFSET. */
+static inline void error_address_to_page_and_offset(u64 error_address,
+ u32 *page, u32 *offset)
+{
+ *page = (u32) (error_address >> PAGE_SHIFT);
+ *offset = ((u32) error_address) & ~PAGE_MASK;
+}
+
+/*
+ * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
+ * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
+ * of a node that detected an ECC memory error. mci represents the node that
+ * the error address maps to (possibly different from the node that detected
+ * the error). Return the number of the csrow that sys_addr maps to, or -1 on
+ * error.
+ */
+static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
+{
+ int csrow;
+
+ csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
+
+ if (csrow == -1)
+ amd64_mc_printk(mci, KERN_ERR,
+ "Failed to translate InputAddr to csrow for "
+ "address 0x%lx\n", (unsigned long)sys_addr);
+ return csrow;
+}
+
+static int get_channel_from_ecc_syndrome(unsigned short syndrome);
+
+static void amd64_cpu_display_info(struct amd64_pvt *pvt)
+{
+ if (boot_cpu_data.x86 == 0x11)
+ edac_printk(KERN_DEBUG, EDAC_MC, "F11h CPU detected\n");
+ else if (boot_cpu_data.x86 == 0x10)
+ edac_printk(KERN_DEBUG, EDAC_MC, "F10h CPU detected\n");
+ else if (boot_cpu_data.x86 == 0xf)
+ edac_printk(KERN_DEBUG, EDAC_MC, "%s detected\n",
+ (pvt->ext_model >= OPTERON_CPU_REV_F) ?
+ "Rev F or later" : "Rev E or earlier");
+ else
+ /* we'll hardly ever ever get here */
+ edac_printk(KERN_ERR, EDAC_MC, "Unknown cpu!\n");
+}
+
+/*
+ * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
+ * are ECC capable.
+ */
+static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
+{
+ int bit;
+ enum dev_type edac_cap = EDAC_NONE;
+
+ bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= OPTERON_CPU_REV_F)
+ ? 19
+ : 17;
+
+ if (pvt->dclr0 >> BIT(bit))
+ edac_cap = EDAC_FLAG_SECDED;
+
+ return edac_cap;
+}
+
+
+static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
+ int ganged);
+
+/* Display and decode various NB registers for debug purposes. */
+static void amd64_dump_misc_regs(struct amd64_pvt *pvt)
+{
+ int ganged;
+
+ debugf1(" nbcap:0x%8.08x DctDualCap=%s DualNode=%s 8-Node=%s\n",
+ pvt->nbcap,
+ (pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "True" : "False",
+ (pvt->nbcap & K8_NBCAP_DUAL_NODE) ? "True" : "False",
+ (pvt->nbcap & K8_NBCAP_8_NODE) ? "True" : "False");
+ debugf1(" ECC Capable=%s ChipKill Capable=%s\n",
+ (pvt->nbcap & K8_NBCAP_SECDED) ? "True" : "False",
+ (pvt->nbcap & K8_NBCAP_CHIPKILL) ? "True" : "False");
+ debugf1(" DramCfg0-low=0x%08x DIMM-ECC=%s Parity=%s Width=%s\n",
+ pvt->dclr0,
+ (pvt->dclr0 & BIT(19)) ? "Enabled" : "Disabled",
+ (pvt->dclr0 & BIT(8)) ? "Enabled" : "Disabled",
+ (pvt->dclr0 & BIT(11)) ? "128b" : "64b");
+ debugf1(" DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s DIMM Type=%s\n",
+ (pvt->dclr0 & BIT(12)) ? "Y" : "N",
+ (pvt->dclr0 & BIT(13)) ? "Y" : "N",
+ (pvt->dclr0 & BIT(14)) ? "Y" : "N",
+ (pvt->dclr0 & BIT(15)) ? "Y" : "N",
+ (pvt->dclr0 & BIT(16)) ? "UN-Buffered" : "Buffered");
+
+
+ debugf1(" online-spare: 0x%8.08x\n", pvt->online_spare);
+
+ if (boot_cpu_data.x86 == 0xf) {
+ debugf1(" dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
+ pvt->dhar, dhar_base(pvt->dhar),
+ k8_dhar_offset(pvt->dhar));
+ debugf1(" DramHoleValid=%s\n",
+ (pvt->dhar & DHAR_VALID) ? "True" : "False");
+
+ debugf1(" dbam-dkt: 0x%8.08x\n", pvt->dbam0);
+
+ /* everything below this point is Fam10h and above */
+ return;
+
+ } else {
+ debugf1(" dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
+ pvt->dhar, dhar_base(pvt->dhar),
+ f10_dhar_offset(pvt->dhar));
+ debugf1(" DramMemHoistValid=%s DramHoleValid=%s\n",
+ (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) ?
+ "True" : "False",
+ (pvt->dhar & DHAR_VALID) ?
+ "True" : "False");
+ }
+
+ /* Only if NOT ganged does dcl1 have valid info */
+ if (!dct_ganging_enabled(pvt)) {
+ debugf1(" DramCfg1-low=0x%08x DIMM-ECC=%s Parity=%s "
+ "Width=%s\n", pvt->dclr1,
+ (pvt->dclr1 & BIT(19)) ? "Enabled" : "Disabled",
+ (pvt->dclr1 & BIT(8)) ? "Enabled" : "Disabled",
+ (pvt->dclr1 & BIT(11)) ? "128b" : "64b");
+ debugf1(" DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s "
+ "DIMM Type=%s\n",
+ (pvt->dclr1 & BIT(12)) ? "Y" : "N",
+ (pvt->dclr1 & BIT(13)) ? "Y" : "N",
+ (pvt->dclr1 & BIT(14)) ? "Y" : "N",
+ (pvt->dclr1 & BIT(15)) ? "Y" : "N",
+ (pvt->dclr1 & BIT(16)) ? "UN-Buffered" : "Buffered");
+ }
+
+ /*
+ * Determine if ganged and then dump memory sizes for first controller,
+ * and if NOT ganged dump info for 2nd controller.
+ */
+ ganged = dct_ganging_enabled(pvt);
+
+ f10_debug_display_dimm_sizes(0, pvt, ganged);
+
+ if (!ganged)
+ f10_debug_display_dimm_sizes(1, pvt, ganged);
+}
+
+/* Read in both of DBAM registers */
+static void amd64_read_dbam_reg(struct amd64_pvt *pvt)
+{
+ int err = 0;
+ unsigned int reg;
+
+ reg = DBAM0;
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam0);
+ if (err)
+ goto err_reg;
+
+ if (boot_cpu_data.x86 >= 0x10) {
+ reg = DBAM1;
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam1);
+
+ if (err)
+ goto err_reg;
+ }
+
+err_reg:
+ debugf0("Error reading F2x%03x.\n", reg);
+}
+
+/*
+ * NOTE: CPU Revision Dependent code: Rev E and Rev F
+ *
+ * Set the DCSB and DCSM mask values depending on the CPU revision value. Also
+ * set the shift factor for the DCSB and DCSM values.
+ *
+ * ->dcs_mask_notused, RevE:
+ *
+ * To find the max InputAddr for the csrow, start with the base address and set
+ * all bits that are "don't care" bits in the test at the start of section
+ * 3.5.4 (p. 84).
+ *
+ * The "don't care" bits are all set bits in the mask and all bits in the gaps
+ * between bit ranges [35:25] and [19:13]. The value REV_E_DCS_NOTUSED_BITS
+ * represents bits [24:20] and [12:0], which are all bits in the above-mentioned
+ * gaps.
+ *
+ * ->dcs_mask_notused, RevF and later:
+ *
+ * To find the max InputAddr for the csrow, start with the base address and set
+ * all bits that are "don't care" bits in the test at the start of NPT section
+ * 4.5.4 (p. 87).
+ *
+ * The "don't care" bits are all set bits in the mask and all bits in the gaps
+ * between bit ranges [36:27] and [21:13].
+ *
+ * The value REV_F_F1Xh_DCS_NOTUSED_BITS represents bits [26:22] and [12:0],
+ * which are all bits in the above-mentioned gaps.
+ */
+static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
+{
+ if (pvt->ext_model >= OPTERON_CPU_REV_F) {
+ pvt->dcsb_base = REV_F_F1Xh_DCSB_BASE_BITS;
+ pvt->dcsm_mask = REV_F_F1Xh_DCSM_MASK_BITS;
+ pvt->dcs_mask_notused = REV_F_F1Xh_DCS_NOTUSED_BITS;
+ pvt->dcs_shift = REV_F_F1Xh_DCS_SHIFT;
+
+ switch (boot_cpu_data.x86) {
+ case 0xf:
+ pvt->num_dcsm = REV_F_DCSM_COUNT;
+ break;
+
+ case 0x10:
+ pvt->num_dcsm = F10_DCSM_COUNT;
+ break;
+
+ case 0x11:
+ pvt->num_dcsm = F11_DCSM_COUNT;
+ break;
+
+ default:
+ amd64_printk(KERN_ERR, "Unsupported family!\n");
+ break;
+ }
+ } else {
+ pvt->dcsb_base = REV_E_DCSB_BASE_BITS;
+ pvt->dcsm_mask = REV_E_DCSM_MASK_BITS;
+ pvt->dcs_mask_notused = REV_E_DCS_NOTUSED_BITS;
+ pvt->dcs_shift = REV_E_DCS_SHIFT;
+ pvt->num_dcsm = REV_E_DCSM_COUNT;
+ }
+}
+
+/*
+ * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask hw registers
+ */
+static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
+{
+ int cs, reg, err = 0;
+
+ amd64_set_dct_base_and_mask(pvt);
+
+ for (cs = 0; cs < CHIPSELECT_COUNT; cs++) {
+ reg = K8_DCSB0 + (cs * 4);
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
+ &pvt->dcsb0[cs]);
+ if (unlikely(err))
+ debugf0("Reading K8_DCSB0[%d] failed\n", cs);
+ else
+ debugf0(" DCSB0[%d]=0x%08x reg: F2x%x\n",
+ cs, pvt->dcsb0[cs], reg);
+
+ /* If DCT are NOT ganged, then read in DCT1's base */
+ if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
+ reg = F10_DCSB1 + (cs * 4);
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
+ &pvt->dcsb1[cs]);
+ if (unlikely(err))
+ debugf0("Reading F10_DCSB1[%d] failed\n", cs);
+ else
+ debugf0(" DCSB1[%d]=0x%08x reg: F2x%x\n",
+ cs, pvt->dcsb1[cs], reg);
+ } else {
+ pvt->dcsb1[cs] = 0;
+ }
+ }
+
+ for (cs = 0; cs < pvt->num_dcsm; cs++) {
+ reg = K8_DCSB0 + (cs * 4);
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
+ &pvt->dcsm0[cs]);
+ if (unlikely(err))
+ debugf0("Reading K8_DCSM0 failed\n");
+ else
+ debugf0(" DCSM0[%d]=0x%08x reg: F2x%x\n",
+ cs, pvt->dcsm0[cs], reg);
+
+ /* If DCT are NOT ganged, then read in DCT1's mask */
+ if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
+ reg = F10_DCSM1 + (cs * 4);
+ err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
+ &pvt->dcsm1[cs]);
+ if (unlikely(err))
+ debugf0("Reading F10_DCSM1[%d] failed\n", cs);
+ else
+ debugf0(" DCSM1[%d]=0x%08x reg: F2x%x\n",
+ cs, pvt->dcsm1[cs], reg);
+ } else
+ pvt->dcsm1[cs] = 0;
+ }
+}
+
+static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt)
+{
+ enum mem_type type;
+
+ if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= OPTERON_CPU_REV_F) {
+ /* Rev F and later */
+ type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
+ } else {
+ /* Rev E and earlier */
+ type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
+ }
+
+ debugf1(" Memory type is: %s\n",
+ (type == MEM_DDR2) ? "MEM_DDR2" :
+ (type == MEM_RDDR2) ? "MEM_RDDR2" :
+ (type == MEM_DDR) ? "MEM_DDR" : "MEM_RDDR");
+
+ return type;
+}
+
+/*
+ * Read the DRAM Configuration Low register. It differs between CG, D & E revs
+ * and the later RevF memory controllers (DDR vs DDR2)
+ *
+ * Return:
+ * number of memory channels in operation
+ * Pass back:
+ * contents of the DCL0_LOW register
+ */
+static int k8_early_channel_count(struct amd64_pvt *pvt)
+{
+ int flag, err = 0;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+ if (err)
+ return err;
+
+ if ((boot_cpu_data.x86_model >> 4) >= OPTERON_CPU_REV_F) {
+ /* RevF (NPT) and later */
+ flag = pvt->dclr0 & F10_WIDTH_128;
+ } else {
+ /* RevE and earlier */
+ flag = pvt->dclr0 & REVE_WIDTH_128;
+ }
+
+ /* not used */
+ pvt->dclr1 = 0;
+
+ return (flag) ? 2 : 1;
+}
+
+/* extract the ERROR ADDRESS for the K8 CPUs */
+static u64 k8_get_error_address(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ return (((u64) (info->nbeah & 0xff)) << 32) +
+ (info->nbeal & ~0x03);
+}
+
+/*
+ * Read the Base and Limit registers for K8 based Memory controllers; extract
+ * fields from the 'raw' reg into separate data fields
+ *
+ * Isolates: BASE, LIMIT, IntlvEn, IntlvSel, RW_EN
+ */
+static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
+{
+ u32 low;
+ u32 off = dram << 3; /* 8 bytes between DRAM entries */
+ int err;
+
+ err = pci_read_config_dword(pvt->addr_f1_ctl,
+ K8_DRAM_BASE_LOW + off, &low);
+ if (err)
+ debugf0("Reading K8_DRAM_BASE_LOW failed\n");
+
+ /* Extract parts into separate data entries */
+ pvt->dram_base[dram] = ((u64) low & 0xFFFF0000) << 8;
+ pvt->dram_IntlvEn[dram] = (low >> 8) & 0x7;
+ pvt->dram_rw_en[dram] = (low & 0x3);
+
+ err = pci_read_config_dword(pvt->addr_f1_ctl,
+ K8_DRAM_LIMIT_LOW + off, &low);
+ if (err)
+ debugf0("Reading K8_DRAM_LIMIT_LOW failed\n");
+
+ /*
+ * Extract parts into separate data entries. Limit is the HIGHEST memory
+ * location of the region, so lower 24 bits need to be all ones
+ */
+ pvt->dram_limit[dram] = (((u64) low & 0xFFFF0000) << 8) | 0x00FFFFFF;
+ pvt->dram_IntlvSel[dram] = (low >> 8) & 0x7;
+ pvt->dram_DstNode[dram] = (low & 0x7);
+}
+
+static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info,
+ u64 SystemAddress)
+{
+ struct mem_ctl_info *src_mci;
+ unsigned short syndrome;
+ int channel, csrow;
+ u32 page, offset;
+
+ /* Extract the syndrome parts and form a 16-bit syndrome */
+ syndrome = EXTRACT_HIGH_SYNDROME(info->nbsl) << 8;
+ syndrome |= EXTRACT_LOW_SYNDROME(info->nbsh);
+
+ /* CHIPKILL enabled */
+ if (info->nbcfg & K8_NBCFG_CHIPKILL) {
+ channel = get_channel_from_ecc_syndrome(syndrome);
+ if (channel < 0) {
+ /*
+ * Syndrome didn't map, so we don't know which of the
+ * 2 DIMMs is in error. So we need to ID 'both' of them
+ * as suspect.
+ */
+ amd64_mc_printk(mci, KERN_WARNING,
+ "unknown syndrome 0x%x - possible error "
+ "reporting race\n", syndrome);
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+ return;
+ }
+ } else {
+ /*
+ * non-chipkill ecc mode
+ *
+ * The k8 documentation is unclear about how to determine the
+ * channel number when using non-chipkill memory. This method
+ * was obtained from email communication with someone at AMD.
+ * (Wish the email was placed in this comment - norsk)
+ */
+ channel = ((SystemAddress & BIT(3)) != 0);
+ }
+
+ /*
+ * Find out which node the error address belongs to. This may be
+ * different from the node that detected the error.
+ */
+ src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+ if (src_mci) {
+ amd64_mc_printk(mci, KERN_ERR,
+ "failed to map error address 0x%lx to a node\n",
+ (unsigned long)SystemAddress);
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+ return;
+ }
+
+ /* Now map the SystemAddress to a CSROW */
+ csrow = sys_addr_to_csrow(src_mci, SystemAddress);
+ if (csrow < 0) {
+ edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
+ } else {
+ error_address_to_page_and_offset(SystemAddress, &page, &offset);
+
+ edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
+ channel, EDAC_MOD_STR);
+ }
+}
+
+/*
+ * determrine the number of PAGES in for this DIMM's size based on its DRAM
+ * Address Mapping.
+ *
+ * First step is to calc the number of bits to shift a value of 1 left to
+ * indicate show many pages. Start with the DBAM value as the starting bits,
+ * then proceed to adjust those shift bits, based on CPU rev and the table.
+ * See BKDG on the DBAM
+ */
+static int k8_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+{
+ int nr_pages;
+
+ if (pvt->ext_model >= OPTERON_CPU_REV_F) {
+ nr_pages = 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
+ } else {
+ /*
+ * RevE and less section; this line is tricky. It collapses the
+ * table used by RevD and later to one that matches revisions CG
+ * and earlier.
+ */
+ dram_map -= (pvt->ext_model >= OPTERON_CPU_REV_D) ?
+ (dram_map > 8 ? 4 : (dram_map > 5 ?
+ 3 : (dram_map > 2 ? 1 : 0))) : 0;
+
+ /* 25 shift is 32MiB minimum DIMM size in RevE and prior */
+ nr_pages = 1 << (dram_map + 25 - PAGE_SHIFT);
+ }
+
+ return nr_pages;
+}
+
+/*
+ * Get the number of DCT channels in use.
+ *
+ * Return:
+ * number of Memory Channels in operation
+ * Pass back:
+ * contents of the DCL0_LOW register
+ */
+static int f10_early_channel_count(struct amd64_pvt *pvt)
+{
+ int err = 0, channels = 0;
+ u32 dbam;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1, &pvt->dclr1);
+ if (err)
+ goto err_reg;
+
+ /* If we are in 128 bit mode, then we are using 2 channels */
+ if (pvt->dclr0 & F10_WIDTH_128) {
+ debugf0("Data WIDTH is 128 bits - 2 channels\n");
+ channels = 2;
+ return channels;
+ }
+
+ /*
+ * Need to check if in UN-ganged mode: In such, there are 2 channels,
+ * but they are NOT in 128 bit mode and thus the above 'dcl0' status bit
+ * will be OFF.
+ *
+ * Need to check DCT0[0] and DCT1[0] to see if only one of them has
+ * their CSEnable bit on. If so, then SINGLE DIMM case.
+ */
+ debugf0("Data WIDTH is NOT 128 bits - need more decoding\n");
+
+ /*
+ * Check DRAM Bank Address Mapping values for each DIMM to see if there
+ * is more than just one DIMM present in unganged mode. Need to check
+ * both controllers since DIMMs can be placed in either one.
+ */
+ channels = 0;
+ err = pci_read_config_dword(pvt->dram_f2_ctl, DBAM0, &dbam);
+ if (err)
+ goto err_reg;
+
+ if (DBAM_DIMM(0, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(1, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(2, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(3, dbam) > 0)
+ channels++;
+
+ /* If more than 2 DIMMs are present, then we have 2 channels */
+ if (channels > 2)
+ channels = 2;
+ else if (channels == 0) {
+ /* No DIMMs on DCT0, so look at DCT1 */
+ err = pci_read_config_dword(pvt->dram_f2_ctl, DBAM1, &dbam);
+ if (err)
+ goto err_reg;
+
+ if (DBAM_DIMM(0, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(1, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(2, dbam) > 0)
+ channels++;
+ if (DBAM_DIMM(3, dbam) > 0)
+ channels++;
+
+ if (channels > 2)
+ channels = 2;
+ }
+
+ /* If we found ALL 0 values, then assume just ONE DIMM-ONE Channel */
+ if (channels == 0)
+ channels = 1;
+
+ debugf0("DIMM count= %d\n", channels);
+
+ return channels;
+
+err_reg:
+ return -1;
+
+}
+
+static int f10_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+{
+ return 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
+}
+
+/* Enable extended configuration access via 0xCF8 feature */
+static void amd64_setup(struct amd64_pvt *pvt)
+{
+ u32 reg;
+
+ pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+
+ pvt->flags.cf8_extcfg = !!(reg & F10_NB_CFG_LOW_ENABLE_EXT_CFG);
+ reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
+ pci_write_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, reg);
+}
+
+/* Restore the extended configuration access via 0xCF8 feature */
+static void amd64_teardown(struct amd64_pvt *pvt)
+{
+ u32 reg;
+
+ pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+
+ reg &= ~F10_NB_CFG_LOW_ENABLE_EXT_CFG;
+ if (pvt->flags.cf8_extcfg)
+ reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
+ pci_write_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, reg);
+}
+
+static u64 f10_get_error_address(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ return (((u64) (info->nbeah & 0xffff)) << 32) +
+ (info->nbeal & ~0x01);
+}
+
+/*
+ * Read the Base and Limit registers for F10 based Memory controllers. Extract
+ * fields from the 'raw' reg into separate data fields.
+ *
+ * Isolates: BASE, LIMIT, IntlvEn, IntlvSel, RW_EN.
+ */
+static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
+{
+ u32 high_offset, low_offset, high_base, low_base, high_limit, low_limit;
+
+ low_offset = K8_DRAM_BASE_LOW + (dram << 3);
+ high_offset = F10_DRAM_BASE_HIGH + (dram << 3);
+
+ /* read the 'raw' DRAM BASE Address register */
+ pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_base);
+
+ /* Read from the ECS data register */
+ pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_base);
+
+ /* Extract parts into separate data entries */
+ pvt->dram_rw_en[dram] = (low_base & 0x3);
+
+ if (pvt->dram_rw_en[dram] == 0)
+ return;
+
+ pvt->dram_IntlvEn[dram] = (low_base >> 8) & 0x7;
+
+ pvt->dram_base[dram] = (((((u64) high_base & 0x000000FF) << 32) |
+ ((u64) low_base & 0xFFFF0000))) << 8;
+
+ low_offset = K8_DRAM_LIMIT_LOW + (dram << 3);
+ high_offset = F10_DRAM_LIMIT_HIGH + (dram << 3);
+
+ /* read the 'raw' LIMIT registers */
+ pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_limit);
+
+ /* Read from the ECS data register for the HIGH portion */
+ pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_limit);
+
+ debugf0(" HW Regs: BASE=0x%08x-%08x LIMIT= 0x%08x-%08x\n",
+ high_base, low_base, high_limit, low_limit);
+
+ pvt->dram_DstNode[dram] = (low_limit & 0x7);
+ pvt->dram_IntlvSel[dram] = (low_limit >> 8) & 0x7;
+
+ /*
+ * Extract address values and form a LIMIT address. Limit is the HIGHEST
+ * memory location of the region, so low 24 bits need to be all ones.
+ */
+ low_limit |= 0x0000FFFF;
+ pvt->dram_limit[dram] =
+ ((((u64) high_limit << 32) + (u64) low_limit) << 8) | (0xFF);
+}
+
+static void f10_read_dram_ctl_register(struct amd64_pvt *pvt)
+{
+ int err = 0;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW,
+ &pvt->dram_ctl_select_low);
+ if (err) {
+ debugf0("Reading F10_DCTL_SEL_LOW failed\n");
+ } else {
+ debugf0("DRAM_DCTL_SEL_LOW=0x%x DctSelBaseAddr=0x%x\n",
+ pvt->dram_ctl_select_low, dct_sel_baseaddr(pvt));
+
+ debugf0(" DRAM DCTs are=%s DRAM Is=%s DRAM-Ctl-"
+ "sel-hi-range=%s\n",
+ (dct_ganging_enabled(pvt) ? "GANGED" : "NOT GANGED"),
+ (dct_dram_enabled(pvt) ? "Enabled" : "Disabled"),
+ (dct_high_range_enabled(pvt) ? "Enabled" : "Disabled"));
+
+ debugf0(" DctDatIntLv=%s MemCleared=%s DctSelIntLvAddr=0x%x\n",
+ (dct_data_intlv_enabled(pvt) ? "Enabled" : "Disabled"),
+ (dct_memory_cleared(pvt) ? "True " : "False "),
+ dct_sel_interleave_addr(pvt));
+ }
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH,
+ &pvt->dram_ctl_select_high);
+ if (err)
+ debugf0("Reading F10_DCTL_SEL_HIGH failed\n");
+}
+
+/*
+ * determine channel based on the interleaving mode: F10h BKDG, 2.8.9 Memory
+ * Interleaving Modes.
+ */
+static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
+ int hi_range_sel, u32 intlv_en)
+{
+ u32 cs, temp, dct_sel_high = (pvt->dram_ctl_select_low >> 1) & 1;
+
+ if (dct_ganging_enabled(pvt))
+ cs = 0;
+ else if (hi_range_sel)
+ cs = dct_sel_high;
+ else if (dct_interleave_enabled(pvt)) {
+ /*
+ * see F2x110[DctSelIntLvAddr] - channel interleave mode
+ */
+ if (dct_sel_interleave_addr(pvt) == 0)
+ cs = sys_addr >> 6 & 1;
+ else if ((dct_sel_interleave_addr(pvt) >> 1) & 1) {
+ temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
+
+ if (dct_sel_interleave_addr(pvt) & 1)
+ cs = (sys_addr >> 9 & 1) ^ temp;
+ else
+ cs = (sys_addr >> 6 & 1) ^ temp;
+ } else if (intlv_en & 4)
+ cs = sys_addr >> 15 & 1;
+ else if (intlv_en & 2)
+ cs = sys_addr >> 14 & 1;
+ else if (intlv_en & 1)
+ cs = sys_addr >> 13 & 1;
+ else
+ cs = sys_addr >> 12 & 1;
+ } else if (dct_high_range_enabled(pvt) && !dct_ganging_enabled(pvt))
+ cs = ~dct_sel_high & 1;
+ else
+ cs = 0;
+
+ return cs;
+}
+
+static inline u32 f10_map_intlv_en_to_shift(u32 intlv_en)
+{
+ if (intlv_en == 1)
+ return 1;
+ else if (intlv_en == 3)
+ return 2;
+ else if (intlv_en == 7)
+ return 3;
+
+ return 0;
+}
+
+/* See F10h BKDG, 2.8.10.2 DctSelBaseOffset Programming */
+static inline u64 f10_get_base_addr_offset(u64 sys_addr, int hi_range_sel,
+ u32 dct_sel_base_addr,
+ u64 dct_sel_base_off,
+ u32 hole_valid, u32 hole_off,
+ u64 dram_base)
+{
+ u64 chan_off;
+
+ if (hi_range_sel) {
+ if (!(dct_sel_base_addr & 0xFFFFF800) &&
+ hole_valid && (sys_addr >= 0x100000000ULL))
+ chan_off = hole_off << 16;
+ else
+ chan_off = dct_sel_base_off;
+ } else {
+ if (hole_valid && (sys_addr >= 0x100000000ULL))
+ chan_off = hole_off << 16;
+ else
+ chan_off = dram_base & 0xFFFFF8000000ULL;
+ }
+
+ return (sys_addr & 0x0000FFFFFFFFFFC0ULL) -
+ (chan_off & 0x0000FFFFFF800000ULL);
+}
+
+/* Hack for the time being - Can we get this from BIOS?? */
+#define CH0SPARE_RANK 0
+#define CH1SPARE_RANK 1
+
+/*
+ * checks if the csrow passed in is marked as SPARED, if so returns the new
+ * spare row
+ */
+static inline int f10_process_possible_spare(int csrow,
+ u32 cs, struct amd64_pvt *pvt)
+{
+ u32 swap_done;
+ u32 bad_dram_cs;
+
+ /* Depending on channel, isolate respective SPARING info */
+ if (cs) {
+ swap_done = F10_ONLINE_SPARE_SWAPDONE1(pvt->online_spare);
+ bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS1(pvt->online_spare);
+ if (swap_done && (csrow == bad_dram_cs))
+ csrow = CH1SPARE_RANK;
+ } else {
+ swap_done = F10_ONLINE_SPARE_SWAPDONE0(pvt->online_spare);
+ bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS0(pvt->online_spare);
+ if (swap_done && (csrow == bad_dram_cs))
+ csrow = CH0SPARE_RANK;
+ }
+ return csrow;
+}
+
+/*
+ * Iterate over the DRAM DCT "base" and "mask" registers looking for a
+ * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
+ *
+ * Return:
+ * -EINVAL: NOT FOUND
+ * 0..csrow = Chip-Select Row
+ */
+static int f10_lookup_addr_in_dct(u32 in_addr, u32 nid, u32 cs)
+{
+ struct mem_ctl_info *mci;
+ struct amd64_pvt *pvt;
+ u32 cs_base, cs_mask;
+ int cs_found = -EINVAL;
+ int csrow;
+
+ mci = mci_lookup[nid];
+ if (!mci)
+ return cs_found;
+
+ pvt = mci->pvt_info;
+
+ debugf1("InputAddr=0x%x channelselect=%d\n", in_addr, cs);
+
+ for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) {
+
+ cs_base = amd64_get_dct_base(pvt, cs, csrow);
+ if (!(cs_base & K8_DCSB_CS_ENABLE))
+ continue;
+
+ /*
+ * We have an ENABLED CSROW, Isolate just the MASK bits of the
+ * target: [28:19] and [13:5], which map to [36:27] and [21:13]
+ * of the actual address.
+ */
+ cs_base &= REV_F_F1Xh_DCSB_BASE_BITS;
+
+ /*
+ * Get the DCT Mask, and ENABLE the reserved bits: [18:16] and
+ * [4:0] to become ON. Then mask off bits [28:0] ([36:8])
+ */
+ cs_mask = amd64_get_dct_mask(pvt, cs, csrow);
+
+ debugf1(" CSROW=%d CSBase=0x%x RAW CSMask=0x%x\n",
+ csrow, cs_base, cs_mask);
+
+ cs_mask = (cs_mask | 0x0007C01F) & 0x1FFFFFFF;
+
+ debugf1(" Final CSMask=0x%x\n", cs_mask);
+ debugf1(" (InputAddr & ~CSMask)=0x%x "
+ "(CSBase & ~CSMask)=0x%x\n",
+ (in_addr & ~cs_mask), (cs_base & ~cs_mask));
+
+ if ((in_addr & ~cs_mask) == (cs_base & ~cs_mask)) {
+ cs_found = f10_process_possible_spare(csrow, cs, pvt);
+
+ debugf1(" MATCH csrow=%d\n", cs_found);
+ break;
+ }
+ }
+ return cs_found;
+}
+
+/* For a given @dram_range, check if @sys_addr falls within it. */
+static int f10_match_to_this_node(struct amd64_pvt *pvt, int dram_range,
+ u64 sys_addr, int *nid, int *chan_sel)
+{
+ int node_id, cs_found = -EINVAL, high_range = 0;
+ u32 intlv_en, intlv_sel, intlv_shift, hole_off;
+ u32 hole_valid, tmp, dct_sel_base, channel;
+ u64 dram_base, chan_addr, dct_sel_base_off;
+
+ dram_base = pvt->dram_base[dram_range];
+ intlv_en = pvt->dram_IntlvEn[dram_range];
+
+ node_id = pvt->dram_DstNode[dram_range];
+ intlv_sel = pvt->dram_IntlvSel[dram_range];
+
+ debugf1("(dram=%d) Base=0x%llx SystemAddr= 0x%llx Limit=0x%llx\n",
+ dram_range, dram_base, sys_addr, pvt->dram_limit[dram_range]);
+
+ /*
+ * This assumes that one node's DHAR is the same as all the other
+ * nodes' DHAR.
+ */
+ hole_off = (pvt->dhar & 0x0000FF80);
+ hole_valid = (pvt->dhar & 0x1);
+ dct_sel_base_off = (pvt->dram_ctl_select_high & 0xFFFFFC00) << 16;
+
+ debugf1(" HoleOffset=0x%x HoleValid=0x%x IntlvSel=0x%x\n",
+ hole_off, hole_valid, intlv_sel);
+
+ if (intlv_en ||
+ (intlv_sel != ((sys_addr >> 12) & intlv_en)))
+ return -EINVAL;
+
+ dct_sel_base = dct_sel_baseaddr(pvt);
+
+ /*
+ * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
+ * select between DCT0 and DCT1.
+ */
+ if (dct_high_range_enabled(pvt) &&
+ !dct_ganging_enabled(pvt) &&
+ ((sys_addr >> 27) >= (dct_sel_base >> 11)))
+ high_range = 1;
+
+ channel = f10_determine_channel(pvt, sys_addr, high_range, intlv_en);
+
+ chan_addr = f10_get_base_addr_offset(sys_addr, high_range, dct_sel_base,
+ dct_sel_base_off, hole_valid,
+ hole_off, dram_base);
+
+ intlv_shift = f10_map_intlv_en_to_shift(intlv_en);
+
+ /* remove Node ID (in case of memory interleaving) */
+ tmp = chan_addr & 0xFC0;
+
+ chan_addr = ((chan_addr >> intlv_shift) & 0xFFFFFFFFF000ULL) | tmp;
+
+ /* remove channel interleave and hash */
+ if (dct_interleave_enabled(pvt) &&
+ !dct_high_range_enabled(pvt) &&
+ !dct_ganging_enabled(pvt)) {
+ if (dct_sel_interleave_addr(pvt) != 1)
+ chan_addr = (chan_addr >> 1) & 0xFFFFFFFFFFFFFFC0ULL;
+ else {
+ tmp = chan_addr & 0xFC0;
+ chan_addr = ((chan_addr & 0xFFFFFFFFFFFFC000ULL) >> 1)
+ | tmp;
+ }
+ }
+
+ debugf1(" (ChannelAddrLong=0x%llx) >> 8 becomes InputAddr=0x%x\n",
+ chan_addr, (u32)(chan_addr >> 8));
+
+ cs_found = f10_lookup_addr_in_dct(chan_addr >> 8, node_id, channel);
+
+ if (cs_found >= 0) {
+ *nid = node_id;
+ *chan_sel = channel;
+ }
+ return cs_found;
+}
+
+static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
+ int *node, int *chan_sel)
+{
+ int dram_range, cs_found = -EINVAL;
+ u64 dram_base, dram_limit;
+
+ for (dram_range = 0; dram_range < DRAM_REG_COUNT; dram_range++) {
+
+ if (!pvt->dram_rw_en[dram_range])
+ continue;
+
+ dram_base = pvt->dram_base[dram_range];
+ dram_limit = pvt->dram_limit[dram_range];
+
+ if ((dram_base <= sys_addr) && (sys_addr <= dram_limit)) {
+
+ cs_found = f10_match_to_this_node(pvt, dram_range,
+ sys_addr, node,
+ chan_sel);
+ if (cs_found >= 0)
+ break;
+ }
+ }
+ return cs_found;
+}
+
+/*
+ * This the F10h reference code from AMD to map a @sys_addr to NodeID,
+ * CSROW, Channel.
+ *
+ * The @sys_addr is usually an error address received from the hardware.
+ */
+static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info,
+ u64 sys_addr)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 page, offset;
+ unsigned short syndrome;
+ int nid, csrow, chan = 0;
+
+ csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
+
+ if (csrow >= 0) {
+ error_address_to_page_and_offset(sys_addr, &page, &offset);
+
+ syndrome = EXTRACT_HIGH_SYNDROME(info->nbsl) << 8;
+ syndrome |= EXTRACT_LOW_SYNDROME(info->nbsh);
+
+ /*
+ * Is CHIPKILL on? If so, then we can attempt to use the
+ * syndrome to isolate which channel the error was on.
+ */
+ if (pvt->nbcfg & K8_NBCFG_CHIPKILL)
+ chan = get_channel_from_ecc_syndrome(syndrome);
+
+ if (chan >= 0) {
+ edac_mc_handle_ce(mci, page, offset, syndrome,
+ csrow, chan, EDAC_MOD_STR);
+ } else {
+ /*
+ * Channel unknown, report all channels on this
+ * CSROW as failed.
+ */
+ for (chan = 0; chan < mci->csrows[csrow].nr_channels;
+ chan++) {
+ edac_mc_handle_ce(mci, page, offset,
+ syndrome,
+ csrow, chan,
+ EDAC_MOD_STR);
+ }
+ }
+
+ } else {
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+ }
+}
+
+/*
+ * Input (@index) is the DBAM DIMM value (1 of 4) used as an index into a shift
+ * table (revf_quad_ddr2_shift) which starts at 128MB DIMM size. Index of 0
+ * indicates an empty DIMM slot, as reported by Hardware on empty slots.
+ *
+ * Normalize to 128MB by subracting 27 bit shift.
+ */
+static int map_dbam_to_csrow_size(int index)
+{
+ int mega_bytes = 0;
+
+ if (index > 0 && index <= DBAM_MAX_VALUE)
+ mega_bytes = ((128 << (revf_quad_ddr2_shift[index]-27)));
+
+ return mega_bytes;
+}
+
+/*
+ * debug routine to display the memory sizes of a DIMM (ganged or not) and it
+ * CSROWs as well
+ */
+static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
+ int ganged)
+{
+ int dimm, size0, size1;
+ u32 dbam;
+ u32 *dcsb;
+
+ debugf1(" dbam%d: 0x%8.08x CSROW is %s\n", ctrl,
+ ctrl ? pvt->dbam1 : pvt->dbam0,
+ ganged ? "GANGED - dbam1 not used" : "NON-GANGED");
+
+ dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
+ dcsb = ctrl ? pvt->dcsb1 : pvt->dcsb0;
+
+ /* Dump memory sizes for DIMM and its CSROWs */
+ for (dimm = 0; dimm < 4; dimm++) {
+
+ size0 = 0;
+ if (dcsb[dimm*2] & K8_DCSB_CS_ENABLE)
+ size0 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
+
+ size1 = 0;
+ if (dcsb[dimm*2 + 1] & K8_DCSB_CS_ENABLE)
+ size1 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
+
+ debugf1(" CTRL-%d DIMM-%d=%5dMB CSROW-%d=%5dMB "
+ "CSROW-%d=%5dMB\n",
+ ctrl,
+ dimm,
+ size0 + size1,
+ dimm * 2,
+ size0,
+ dimm * 2 + 1,
+ size1);
+ }
+}
+
+/*
+ * Very early hardware probe on pci_probe thread to determine if this module
+ * supports the hardware.
+ *
+ * Return:
+ * 0 for OK
+ * 1 for error
+ */
+static int f10_probe_valid_hardware(struct amd64_pvt *pvt)
+{
+ int ret = 0;
+
+ /*
+ * If we are on a DDR3 machine, we don't know yet if
+ * we support that properly at this time
+ */
+ if ((pvt->dchr0 & F10_DCHR_Ddr3Mode) ||
+ (pvt->dchr1 & F10_DCHR_Ddr3Mode)) {
+
+ amd64_printk(KERN_WARNING,
+ "%s() This machine is running with DDR3 memory. "
+ "This is not currently supported. "
+ "DCHR0=0x%x DCHR1=0x%x\n",
+ __func__, pvt->dchr0, pvt->dchr1);
+
+ amd64_printk(KERN_WARNING,
+ " Contact '%s' module MAINTAINER to help add"
+ " support.\n",
+ EDAC_MOD_STR);
+
+ ret = 1;
+
+ }
+ return ret;
+}
+
+/*
+ * There currently are 3 types type of MC devices for AMD Athlon/Opterons
+ * (as per PCI DEVICE_IDs):
+ *
+ * Family K8: That is the Athlon64 and Opteron CPUs. They all have the same PCI
+ * DEVICE ID, even though there is differences between the different Revisions
+ * (CG,D,E,F).
+ *
+ * Family F10h and F11h.
+ *
+ */
+static struct amd64_family_type amd64_family_types[] = {
+ [K8_CPUS] = {
+ .ctl_name = "RevF",
+ .addr_f1_ctl = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
+ .misc_f3_ctl = PCI_DEVICE_ID_AMD_K8_NB_MISC,
+ .ops = {
+ .early_channel_count = k8_early_channel_count,
+ .get_error_address = k8_get_error_address,
+ .read_dram_base_limit = k8_read_dram_base_limit,
+ .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
+ .dbam_map_to_pages = k8_dbam_map_to_pages,
+ }
+ },
+ [F10_CPUS] = {
+ .ctl_name = "Family 10h",
+ .addr_f1_ctl = PCI_DEVICE_ID_AMD_10H_NB_MAP,
+ .misc_f3_ctl = PCI_DEVICE_ID_AMD_10H_NB_MISC,
+ .ops = {
+ .probe_valid_hardware = f10_probe_valid_hardware,
+ .early_channel_count = f10_early_channel_count,
+ .get_error_address = f10_get_error_address,
+ .read_dram_base_limit = f10_read_dram_base_limit,
+ .read_dram_ctl_register = f10_read_dram_ctl_register,
+ .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
+ .dbam_map_to_pages = f10_dbam_map_to_pages,
+ }
+ },
+ [F11_CPUS] = {
+ .ctl_name = "Family 11h",
+ .addr_f1_ctl = PCI_DEVICE_ID_AMD_11H_NB_MAP,
+ .misc_f3_ctl = PCI_DEVICE_ID_AMD_11H_NB_MISC,
+ .ops = {
+ .probe_valid_hardware = f10_probe_valid_hardware,
+ .early_channel_count = f10_early_channel_count,
+ .get_error_address = f10_get_error_address,
+ .read_dram_base_limit = f10_read_dram_base_limit,
+ .read_dram_ctl_register = f10_read_dram_ctl_register,
+ .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
+ .dbam_map_to_pages = f10_dbam_map_to_pages,
+ }
+ },
+};
+
+static struct pci_dev *pci_get_related_function(unsigned int vendor,
+ unsigned int device,
+ struct pci_dev *related)
+{
+ struct pci_dev *dev = NULL;
+
+ dev = pci_get_device(vendor, device, dev);
+ while (dev) {
+ if ((dev->bus->number == related->bus->number) &&
+ (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
+ break;
+ dev = pci_get_device(vendor, device, dev);
+ }
+
+ return dev;
+}
+
+/*
+ * syndrome mapping table for ECC ChipKill devices
+ *
+ * The comment in each row is the token (nibble) number that is in error.
+ * The least significant nibble of the syndrome is the mask for the bits
+ * that are in error (need to be toggled) for the particular nibble.
+ *
+ * Each row contains 16 entries.
+ * The first entry (0th) is the channel number for that row of syndromes.
+ * The remaining 15 entries are the syndromes for the respective Error
+ * bit mask index.
+ *
+ * 1st index entry is 0x0001 mask, indicating that the rightmost bit is the
+ * bit in error.
+ * The 2nd index entry is 0x0010 that the second bit is damaged.
+ * The 3rd index entry is 0x0011 indicating that the rightmost 2 bits
+ * are damaged.
+ * Thus so on until index 15, 0x1111, whose entry has the syndrome
+ * indicating that all 4 bits are damaged.
+ *
+ * A search is performed on this table looking for a given syndrome.
+ *
+ * See the AMD documentation for ECC syndromes. This ECC table is valid
+ * across all the versions of the AMD64 processors.
+ *
+ * A fast lookup is to use the LAST four bits of the 16-bit syndrome as a
+ * COLUMN index, then search all ROWS of that column, looking for a match
+ * with the input syndrome. The ROW value will be the token number.
+ *
+ * The 0'th entry on that row, can be returned as the CHANNEL (0 or 1) of this
+ * error.
+ */
+#define NUMBER_ECC_ROWS 36
+static const unsigned short ecc_chipkill_syndromes[NUMBER_ECC_ROWS][16] = {
+ /* Channel 0 syndromes */
+ {/*0*/ 0, 0xe821, 0x7c32, 0x9413, 0xbb44, 0x5365, 0xc776, 0x2f57,
+ 0xdd88, 0x35a9, 0xa1ba, 0x499b, 0x66cc, 0x8eed, 0x1afe, 0xf2df },
+ {/*1*/ 0, 0x5d31, 0xa612, 0xfb23, 0x9584, 0xc8b5, 0x3396, 0x6ea7,
+ 0xeac8, 0xb7f9, 0x4cda, 0x11eb, 0x7f4c, 0x227d, 0xd95e, 0x846f },
+ {/*2*/ 0, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f },
+ {/*3*/ 0, 0x2021, 0x3032, 0x1013, 0x4044, 0x6065, 0x7076, 0x5057,
+ 0x8088, 0xa0a9, 0xb0ba, 0x909b, 0xc0cc, 0xe0ed, 0xf0fe, 0xd0df },
+ {/*4*/ 0, 0x5041, 0xa082, 0xf0c3, 0x9054, 0xc015, 0x30d6, 0x6097,
+ 0xe0a8, 0xb0e9, 0x402a, 0x106b, 0x70fc, 0x20bd, 0xd07e, 0x803f },
+ {/*5*/ 0, 0xbe21, 0xd732, 0x6913, 0x2144, 0x9f65, 0xf676, 0x4857,
+ 0x3288, 0x8ca9, 0xe5ba, 0x5b9b, 0x13cc, 0xaded, 0xc4fe, 0x7adf },
+ {/*6*/ 0, 0x4951, 0x8ea2, 0xc7f3, 0x5394, 0x1ac5, 0xdd36, 0x9467,
+ 0xa1e8, 0xe8b9, 0x2f4a, 0x661b, 0xf27c, 0xbb2d, 0x7cde, 0x358f },
+ {/*7*/ 0, 0x74e1, 0x9872, 0xec93, 0xd6b4, 0xa255, 0x4ec6, 0x3a27,
+ 0x6bd8, 0x1f39, 0xf3aa, 0x874b, 0xbd6c, 0xc98d, 0x251e, 0x51ff },
+ {/*8*/ 0, 0x15c1, 0x2a42, 0x3f83, 0xcef4, 0xdb35, 0xe4b6, 0xf177,
+ 0x4758, 0x5299, 0x6d1a, 0x78db, 0x89ac, 0x9c6d, 0xa3ee, 0xb62f },
+ {/*9*/ 0, 0x3d01, 0x1602, 0x2b03, 0x8504, 0xb805, 0x9306, 0xae07,
+ 0xca08, 0xf709, 0xdc0a, 0xe10b, 0x4f0c, 0x720d, 0x590e, 0x640f },
+ {/*a*/ 0, 0x9801, 0xec02, 0x7403, 0x6b04, 0xf305, 0x8706, 0x1f07,
+ 0xbd08, 0x2509, 0x510a, 0xc90b, 0xd60c, 0x4e0d, 0x3a0e, 0xa20f },
+ {/*b*/ 0, 0xd131, 0x6212, 0xb323, 0x3884, 0xe9b5, 0x5a96, 0x8ba7,
+ 0x1cc8, 0xcdf9, 0x7eda, 0xafeb, 0x244c, 0xf57d, 0x465e, 0x976f },
+ {/*c*/ 0, 0xe1d1, 0x7262, 0x93b3, 0xb834, 0x59e5, 0xca56, 0x2b87,
+ 0xdc18, 0x3dc9, 0xae7a, 0x4fab, 0x542c, 0x85fd, 0x164e, 0xf79f },
+ {/*d*/ 0, 0x6051, 0xb0a2, 0xd0f3, 0x1094, 0x70c5, 0xa036, 0xc067,
+ 0x20e8, 0x40b9, 0x904a, 0x601b, 0x307c, 0x502d, 0x80de, 0xe08f },
+ {/*e*/ 0, 0xa4c1, 0xf842, 0x5c83, 0xe6f4, 0x4235, 0x1eb6, 0xba77,
+ 0x7b58, 0xdf99, 0x831a, 0x27db, 0x9dac, 0x396d, 0x65ee, 0xc12f },
+ {/*f*/ 0, 0x11c1, 0x2242, 0x3383, 0xc8f4, 0xd935, 0xeab6, 0xfb77,
+ 0x4c58, 0x5d99, 0x6e1a, 0x7fdb, 0x84ac, 0x956d, 0xa6ee, 0xb72f },
+
+ /* Channel 1 syndromes */
+ {/*10*/ 1, 0x45d1, 0x8a62, 0xcfb3, 0x5e34, 0x1be5, 0xd456, 0x9187,
+ 0xa718, 0xe2c9, 0x2d7a, 0x68ab, 0xf92c, 0xbcfd, 0x734e, 0x369f },
+ {/*11*/ 1, 0x63e1, 0xb172, 0xd293, 0x14b4, 0x7755, 0xa5c6, 0xc627,
+ 0x28d8, 0x4b39, 0x99aa, 0xfa4b, 0x3c6c, 0x5f8d, 0x8d1e, 0xeeff },
+ {/*12*/ 1, 0xb741, 0xd982, 0x6ec3, 0x2254, 0x9515, 0xfbd6, 0x4c97,
+ 0x33a8, 0x84e9, 0xea2a, 0x5d6b, 0x11fc, 0xa6bd, 0xc87e, 0x7f3f },
+ {/*13*/ 1, 0xdd41, 0x6682, 0xbbc3, 0x3554, 0xe815, 0x53d6, 0xce97,
+ 0x1aa8, 0xc7e9, 0x7c2a, 0xa1fb, 0x2ffc, 0xf2bd, 0x497e, 0x943f },
+ {/*14*/ 1, 0x2bd1, 0x3d62, 0x16b3, 0x4f34, 0x64e5, 0x7256, 0x5987,
+ 0x8518, 0xaec9, 0xb87a, 0x93ab, 0xca2c, 0xe1fd, 0xf74e, 0xdc9f },
+ {/*15*/ 1, 0x83c1, 0xc142, 0x4283, 0xa4f4, 0x2735, 0x65b6, 0xe677,
+ 0xf858, 0x7b99, 0x391a, 0xbadb, 0x5cac, 0xdf6d, 0x9dee, 0x1e2f },
+ {/*16*/ 1, 0x8fd1, 0xc562, 0x4ab3, 0xa934, 0x26e5, 0x6c56, 0xe387,
+ 0xfe18, 0x71c9, 0x3b7a, 0xb4ab, 0x572c, 0xd8fd, 0x924e, 0x1d9f },
+ {/*17*/ 1, 0x4791, 0x89e2, 0xce73, 0x5264, 0x15f5, 0xdb86, 0x9c17,
+ 0xa3b8, 0xe429, 0x2a5a, 0x6dcb, 0xf1dc, 0xb64d, 0x783e, 0x3faf },
+ {/*18*/ 1, 0x5781, 0xa9c2, 0xfe43, 0x92a4, 0xc525, 0x3b66, 0x6ce7,
+ 0xe3f8, 0xb479, 0x4a3a, 0x1dbb, 0x715c, 0x26dd, 0xd89e, 0x8f1f },
+ {/*19*/ 1, 0xbf41, 0xd582, 0x6ac3, 0x2954, 0x9615, 0xfcd6, 0x4397,
+ 0x3ea8, 0x81e9, 0xeb2a, 0x546b, 0x17fc, 0xa8bd, 0xc27e, 0x7d3f },
+ {/*1a*/ 1, 0x9891, 0xe1e2, 0x7273, 0x6464, 0xf7f5, 0x8586, 0x1617,
+ 0xb8b8, 0x2b29, 0x595a, 0xcacb, 0xdcdc, 0x4f4d, 0x3d3e, 0xaeaf },
+ {/*1b*/ 1, 0xcce1, 0x4472, 0x8893, 0xfdb4, 0x3f55, 0xb9c6, 0x7527,
+ 0x56d8, 0x9a39, 0x12aa, 0xde4b, 0xab6c, 0x678d, 0xef1e, 0x23ff },
+ {/*1c*/ 1, 0xa761, 0xf9b2, 0x5ed3, 0xe214, 0x4575, 0x1ba6, 0xbcc7,
+ 0x7328, 0xd449, 0x8a9a, 0x2dfb, 0x913c, 0x365d, 0x688e, 0xcfef },
+ {/*1d*/ 1, 0xff61, 0x55b2, 0xaad3, 0x7914, 0x8675, 0x2ca6, 0xd3c7,
+ 0x9e28, 0x6149, 0xcb9a, 0x34fb, 0xe73c, 0x185d, 0xb28e, 0x4def },
+ {/*1e*/ 1, 0x5451, 0xa8a2, 0xfcf3, 0x9694, 0xc2c5, 0x3e36, 0x6a67,
+ 0xebe8, 0xbfb9, 0x434a, 0x171b, 0x7d7c, 0x292d, 0xd5de, 0x818f },
+ {/*1f*/ 1, 0x6fc1, 0xb542, 0xda83, 0x19f4, 0x7635, 0xacb6, 0xc377,
+ 0x2e58, 0x4199, 0x9b1a, 0xf4db, 0x37ac, 0x586d, 0x82ee, 0xed2f },
+
+ /* ECC bits are also in the set of tokens and they too can go bad
+ * first 2 cover channel 0, while the second 2 cover channel 1
+ */
+ {/*20*/ 0, 0xbe01, 0xd702, 0x6903, 0x2104, 0x9f05, 0xf606, 0x4807,
+ 0x3208, 0x8c09, 0xe50a, 0x5b0b, 0x130c, 0xad0d, 0xc40e, 0x7a0f },
+ {/*21*/ 0, 0x4101, 0x8202, 0xc303, 0x5804, 0x1905, 0xda06, 0x9b07,
+ 0xac08, 0xed09, 0x2e0a, 0x6f0b, 0x640c, 0xb50d, 0x760e, 0x370f },
+ {/*22*/ 1, 0xc441, 0x4882, 0x8cc3, 0xf654, 0x3215, 0xbed6, 0x7a97,
+ 0x5ba8, 0x9fe9, 0x132a, 0xd76b, 0xadfc, 0x69bd, 0xe57e, 0x213f },
+ {/*23*/ 1, 0x7621, 0x9b32, 0xed13, 0xda44, 0xac65, 0x4176, 0x3757,
+ 0x6f88, 0x19a9, 0xf4ba, 0x829b, 0xb5cc, 0xc3ed, 0x2efe, 0x58df }
+};
+
+/*
+ * Given the syndrome argument, scan each of the channel tables for a syndrome
+ * match. Depending on which table it is found, return the channel number.
+ */
+static int get_channel_from_ecc_syndrome(unsigned short syndrome)
+{
+ int row;
+ int column;
+
+ /* Determine column to scan */
+ column = syndrome & 0xF;
+
+ /* Scan all rows, looking for syndrome, or end of table */
+ for (row = 0; row < NUMBER_ECC_ROWS; row++) {
+ if (ecc_chipkill_syndromes[row][column] == syndrome)
+ return ecc_chipkill_syndromes[row][0];
+ }
+
+ debugf0("syndrome(%x) not found\n", syndrome);
+ return -1;
+}
+
+/*
+ * Check for valid error in the NB Status High register. If so, proceed to read
+ * NB Status Low, NB Address Low and NB Address High registers and store data
+ * into error structure.
+ *
+ * Returns:
+ * - 1: if hardware regs contains valid error info
+ * - 0: if no valid error is indicated
+ */
+static int amd64_get_error_info_regs(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *regs)
+{
+ struct amd64_pvt *pvt;
+ struct pci_dev *misc_f3_ctl;
+ int err = 0;
+
+ pvt = mci->pvt_info;
+ misc_f3_ctl = pvt->misc_f3_ctl;
+
+ err = pci_read_config_dword(misc_f3_ctl, K8_NBSH, &regs->nbsh);
+ if (err)
+ goto err_reg;
+
+ if (!(regs->nbsh & K8_NBSH_VALID_BIT))
+ return 0;
+
+ /* valid error, read remaining error information registers */
+ err = pci_read_config_dword(misc_f3_ctl, K8_NBSL, &regs->nbsl);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(misc_f3_ctl, K8_NBEAL, &regs->nbeal);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(misc_f3_ctl, K8_NBEAH, &regs->nbeah);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(misc_f3_ctl, K8_NBCFG, &regs->nbcfg);
+ if (err)
+ goto err_reg;
+
+ return 1;
+
+err_reg:
+ debugf0("Reading error info register failed\n");
+ return 0;
+}
+
+/*
+ * This function is called to retrieve the error data from hardware and store it
+ * in the info structure.
+ *
+ * Returns:
+ * - 1: if a valid error is found
+ * - 0: if no error is found
+ */
+static int amd64_get_error_info(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ struct amd64_pvt *pvt;
+ struct amd64_error_info_regs regs;
+
+ pvt = mci->pvt_info;
+
+ if (!amd64_get_error_info_regs(mci, info))
+ return 0;
+
+ /*
+ * Here's the problem with the K8's EDAC reporting: There are four
+ * registers which report pieces of error information. They are shared
+ * between CEs and UEs. Furthermore, contrary to what is stated in the
+ * BKDG, the overflow bit is never used! Every error always updates the
+ * reporting registers.
+ *
+ * Can you see the race condition? All four error reporting registers
+ * must be read before a new error updates them! There is no way to read
+ * all four registers atomically. The best than can be done is to detect
+ * that a race has occured and then report the error without any kind of
+ * precision.
+ *
+ * What is still positive is that errors are still reported and thus
+ * problems can still be detected - just not localized because the
+ * syndrome and address are spread out across registers.
+ *
+ * Grrrrr!!!!! Here's hoping that AMD fixes this in some future K8 rev.
+ * UEs and CEs should have separate register sets with proper overflow
+ * bits that are used! At very least the problem can be fixed by
+ * honoring the ErrValid bit in 'nbsh' and not updating registers - just
+ * set the overflow bit - unless the current error is CE and the new
+ * error is UE which would be the only situation for overwriting the
+ * current values.
+ */
+
+ regs = *info;
+
+ /* Use info from the second read - most current */
+ if (unlikely(!amd64_get_error_info_regs(mci, info)))
+ return 0;
+
+ /* clear the error bits in hardware */
+ pci_write_bits32(pvt->misc_f3_ctl, K8_NBSH, 0, K8_NBSH_VALID_BIT);
+
+ /* Check for the possible race condition */
+ if ((regs.nbsh != info->nbsh) ||
+ (regs.nbsl != info->nbsl) ||
+ (regs.nbeah != info->nbeah) ||
+ (regs.nbeal != info->nbeal)) {
+ amd64_mc_printk(mci, KERN_WARNING,
+ "hardware STATUS read access race condition "
+ "detected!\n");
+ return 0;
+ }
+ return 1;
+}
+
+static inline void amd64_decode_gart_tlb_error(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ u32 err_code;
+ u32 ec_tt; /* error code transaction type (2b) */
+ u32 ec_ll; /* error code cache level (2b) */
+
+ err_code = EXTRACT_ERROR_CODE(info->nbsl);
+ ec_ll = EXTRACT_LL_CODE(err_code);
+ ec_tt = EXTRACT_TT_CODE(err_code);
+
+ amd64_mc_printk(mci, KERN_ERR,
+ "GART TLB event: transaction type(%s), "
+ "cache level(%s)\n", tt_msgs[ec_tt], ll_msgs[ec_ll]);
+}
+
+static inline void amd64_decode_mem_cache_error(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ u32 err_code;
+ u32 ec_rrrr; /* error code memory transaction (4b) */
+ u32 ec_tt; /* error code transaction type (2b) */
+ u32 ec_ll; /* error code cache level (2b) */
+
+ err_code = EXTRACT_ERROR_CODE(info->nbsl);
+ ec_ll = EXTRACT_LL_CODE(err_code);
+ ec_tt = EXTRACT_TT_CODE(err_code);
+ ec_rrrr = EXTRACT_RRRR_CODE(err_code);
+
+ amd64_mc_printk(mci, KERN_ERR,
+ "cache hierarchy error: memory transaction type(%s), "
+ "transaction type(%s), cache level(%s)\n",
+ rrrr_msgs[ec_rrrr], tt_msgs[ec_tt], ll_msgs[ec_ll]);
+}
+
+
+/*
+ * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
+ * ADDRESS and process.
+ */
+static void amd64_handle_ce(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u64 SystemAddress;
+
+ /* Ensure that the Error Address is VALID */
+ if ((info->nbsh & K8_NBSH_VALID_ERROR_ADDR) == 0) {
+ amd64_mc_printk(mci, KERN_ERR,
+ "HW has no ERROR_ADDRESS available\n");
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+ return;
+ }
+
+ SystemAddress = extract_error_address(mci, info);
+
+ amd64_mc_printk(mci, KERN_ERR,
+ "CE ERROR_ADDRESS= 0x%llx\n", SystemAddress);
+
+ pvt->ops->map_sysaddr_to_csrow(mci, info, SystemAddress);
+}
+
+/* Handle any Un-correctable Errors (UEs) */
+static void amd64_handle_ue(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ int csrow;
+ u64 SystemAddress;
+ u32 page, offset;
+ struct mem_ctl_info *log_mci, *src_mci = NULL;
+
+ log_mci = mci;
+
+ if ((info->nbsh & K8_NBSH_VALID_ERROR_ADDR) == 0) {
+ amd64_mc_printk(mci, KERN_CRIT,
+ "HW has no ERROR_ADDRESS available\n");
+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+ return;
+ }
+
+ SystemAddress = extract_error_address(mci, info);
+
+ /*
+ * Find out which node the error address belongs to. This may be
+ * different from the node that detected the error.
+ */
+ src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+ if (!src_mci) {
+ amd64_mc_printk(mci, KERN_CRIT,
+ "ERROR ADDRESS (0x%lx) value NOT mapped to a MC\n",
+ (unsigned long)SystemAddress);
+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+ return;
+ }
+
+ log_mci = src_mci;
+
+ csrow = sys_addr_to_csrow(log_mci, SystemAddress);
+ if (csrow < 0) {
+ amd64_mc_printk(mci, KERN_CRIT,
+ "ERROR_ADDRESS (0x%lx) value NOT mapped to 'csrow'\n",
+ (unsigned long)SystemAddress);
+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+ } else {
+ error_address_to_page_and_offset(SystemAddress, &page, &offset);
+ edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
+ }
+}
+
+static void amd64_decode_bus_error(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info)
+{
+ u32 err_code, ext_ec;
+ u32 ec_pp; /* error code participating processor (2p) */
+ u32 ec_to; /* error code timed out (1b) */
+ u32 ec_rrrr; /* error code memory transaction (4b) */
+ u32 ec_ii; /* error code memory or I/O (2b) */
+ u32 ec_ll; /* error code cache level (2b) */
+
+ ext_ec = EXTRACT_EXT_ERROR_CODE(info->nbsl);
+ err_code = EXTRACT_ERROR_CODE(info->nbsl);
+
+ ec_ll = EXTRACT_LL_CODE(err_code);
+ ec_ii = EXTRACT_II_CODE(err_code);
+ ec_rrrr = EXTRACT_RRRR_CODE(err_code);
+ ec_to = EXTRACT_TO_CODE(err_code);
+ ec_pp = EXTRACT_PP_CODE(err_code);
+
+ amd64_mc_printk(mci, KERN_ERR,
+ "BUS ERROR:\n"
+ " time-out(%s) mem or i/o(%s)\n"
+ " participating processor(%s)\n"
+ " memory transaction type(%s)\n"
+ " cache level(%s) Error Found by: %s\n",
+ to_msgs[ec_to],
+ ii_msgs[ec_ii],
+ pp_msgs[ec_pp],
+ rrrr_msgs[ec_rrrr],
+ ll_msgs[ec_ll],
+ (info->nbsh & K8_NBSH_ERR_SCRUBER) ?
+ "Scrubber" : "Normal Operation");
+
+ /* If this was an 'observed' error, early out */
+ if (ec_pp == K8_NBSL_PP_OBS)
+ return; /* We aren't the node involved */
+
+ /* Parse out the extended error code for ECC events */
+ switch (ext_ec) {
+ /* F10 changed to one Extended ECC error code */
+ case F10_NBSL_EXT_ERR_RES: /* Reserved field */
+ case F10_NBSL_EXT_ERR_ECC: /* F10 ECC ext err code */
+ break;
+
+ default:
+ amd64_mc_printk(mci, KERN_ERR, "NOT ECC: no special error "
+ "handling for this error\n");
+ return;
+ }
+
+ if (info->nbsh & K8_NBSH_CECC)
+ amd64_handle_ce(mci, info);
+ else if (info->nbsh & K8_NBSH_UECC)
+ amd64_handle_ue(mci, info);
+
+ /*
+ * If main error is CE then overflow must be CE. If main error is UE
+ * then overflow is unknown. We'll call the overflow a CE - if
+ * panic_on_ue is set then we're already panic'ed and won't arrive
+ * here. Else, then apparently someone doesn't think that UE's are
+ * catastrophic.
+ */
+ if (info->nbsh & K8_NBSH_OVERFLOW)
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR
+ "Error Overflow set");
+}
+
+int amd64_process_error_info(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info,
+ int handle_errors)
+{
+ struct amd64_pvt *pvt;
+ struct amd64_error_info_regs *regs;
+ u32 err_code, ext_ec;
+ int gart_tlb_error = 0;
+
+ pvt = mci->pvt_info;
+
+ /* If caller doesn't want us to process the error, return */
+ if (!handle_errors)
+ return 1;
+
+ regs = info;
+
+ debugf1("NorthBridge ERROR: mci(0x%p)\n", mci);
+ debugf1(" MC node(%d) Error-Address(0x%.8x-%.8x)\n",
+ pvt->mc_node_id, regs->nbeah, regs->nbeal);
+ debugf1(" nbsh(0x%.8x) nbsl(0x%.8x)\n",
+ regs->nbsh, regs->nbsl);
+ debugf1(" Valid Error=%s Overflow=%s\n",
+ (regs->nbsh & K8_NBSH_VALID_BIT) ? "True" : "False",
+ (regs->nbsh & K8_NBSH_OVERFLOW) ? "True" : "False");
+ debugf1(" Err Uncorrected=%s MCA Error Reporting=%s\n",
+ (regs->nbsh & K8_NBSH_UNCORRECTED_ERR) ?
+ "True" : "False",
+ (regs->nbsh & K8_NBSH_ERR_ENABLE) ?
+ "True" : "False");
+ debugf1(" MiscErr Valid=%s ErrAddr Valid=%s PCC=%s\n",
+ (regs->nbsh & K8_NBSH_MISC_ERR_VALID) ?
+ "True" : "False",
+ (regs->nbsh & K8_NBSH_VALID_ERROR_ADDR) ?
+ "True" : "False",
+ (regs->nbsh & K8_NBSH_PCC) ?
+ "True" : "False");
+ debugf1(" CECC=%s UECC=%s Found by Scruber=%s\n",
+ (regs->nbsh & K8_NBSH_CECC) ?
+ "True" : "False",
+ (regs->nbsh & K8_NBSH_UECC) ?
+ "True" : "False",
+ (regs->nbsh & K8_NBSH_ERR_SCRUBER) ?
+ "True" : "False");
+ debugf1(" CORE0=%s CORE1=%s CORE2=%s CORE3=%s\n",
+ (regs->nbsh & K8_NBSH_CORE0) ? "True" : "False",
+ (regs->nbsh & K8_NBSH_CORE1) ? "True" : "False",
+ (regs->nbsh & K8_NBSH_CORE2) ? "True" : "False",
+ (regs->nbsh & K8_NBSH_CORE3) ? "True" : "False");
+
+
+ err_code = EXTRACT_ERROR_CODE(regs->nbsl);
+
+ /* Determine which error type:
+ * 1) GART errors - non-fatal, developmental events
+ * 2) MEMORY errors
+ * 3) BUS errors
+ * 4) Unknown error
+ */
+ if (TEST_TLB_ERROR(err_code)) {
+ /*
+ * GART errors are intended to help graphics driver developers
+ * to detect bad GART PTEs. It is recommended by AMD to disable
+ * GART table walk error reporting by default[1] (currently
+ * being disabled in mce_cpu_quirks()) and according to the
+ * comment in mce_cpu_quirks(), such GART errors can be
+ * incorrectly triggered. We may see these errors anyway and
+ * unless requested by the user, they won't be reported.
+ *
+ * [1] section 13.10.1 on BIOS and Kernel Developers Guide for
+ * AMD NPT family 0Fh processors
+ */
+ if (report_gart_errors == 0)
+ return 1;
+
+ /*
+ * Only if GART error reporting is requested should we generate
+ * any logs.
+ */
+ gart_tlb_error = 1;
+
+ debugf1("GART TLB error\n");
+ amd64_decode_gart_tlb_error(mci, info);
+ } else if (TEST_MEM_ERROR(err_code)) {
+ debugf1("Memory/Cache error\n");
+ amd64_decode_mem_cache_error(mci, info);
+ } else if (TEST_BUS_ERROR(err_code)) {
+ debugf1("Bus (Link/DRAM) error\n");
+ amd64_decode_bus_error(mci, info);
+ } else {
+ /* shouldn't reach here! */
+ amd64_mc_printk(mci, KERN_WARNING,
+ "%s(): unknown MCE error 0x%x\n", __func__,
+ err_code);
+ }
+
+ ext_ec = EXTRACT_EXT_ERROR_CODE(regs->nbsl);
+ amd64_mc_printk(mci, KERN_ERR,
+ "ExtErr=(0x%x) %s\n", ext_ec, ext_msgs[ext_ec]);
+
+ if (((ext_ec >= F10_NBSL_EXT_ERR_CRC &&
+ ext_ec <= F10_NBSL_EXT_ERR_TGT) ||
+ (ext_ec == F10_NBSL_EXT_ERR_RMW)) &&
+ EXTRACT_LDT_LINK(info->nbsh)) {
+
+ amd64_mc_printk(mci, KERN_ERR,
+ "Error on hypertransport link: %s\n",
+ htlink_msgs[
+ EXTRACT_LDT_LINK(info->nbsh)]);
+ }
+
+ /*
+ * Check the UE bit of the NB status high register, if set generate some
+ * logs. If NOT a GART error, then process the event as a NO-INFO event.
+ * If it was a GART error, skip that process.
+ */
+ if (regs->nbsh & K8_NBSH_UNCORRECTED_ERR) {
+ amd64_mc_printk(mci, KERN_CRIT, "uncorrected error\n");
+ if (!gart_tlb_error)
+ edac_mc_handle_ue_no_info(mci, "UE bit is set\n");
+ }
+
+ if (regs->nbsh & K8_NBSH_PCC)
+ amd64_mc_printk(mci, KERN_CRIT,
+ "PCC (processor context corrupt) set\n");
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(amd64_process_error_info);
+
+/*
+ * The main polling 'check' function, called FROM the edac core to perform the
+ * error checking and if an error is encountered, error processing.
+ */
+static void amd64_check(struct mem_ctl_info *mci)
+{
+ struct amd64_error_info_regs info;
+
+ if (amd64_get_error_info(mci, &info))
+ amd64_process_error_info(mci, &info, 1);
+}
+
+/*
+ * Input:
+ * 1) struct amd64_pvt which contains pvt->dram_f2_ctl pointer
+ * 2) AMD Family index value
+ *
+ * Ouput:
+ * Upon return of 0, the following filled in:
+ *
+ * struct pvt->addr_f1_ctl
+ * struct pvt->misc_f3_ctl
+ *
+ * Filled in with related device funcitions of 'dram_f2_ctl'
+ * These devices are "reserved" via the pci_get_device()
+ *
+ * Upon return of 1 (error status):
+ *
+ * Nothing reserved
+ */
+static int amd64_reserve_mc_sibling_devices(struct amd64_pvt *pvt, int mc_idx)
+{
+ const struct amd64_family_type *amd64_dev = &amd64_family_types[mc_idx];
+
+ /* Reserve the ADDRESS MAP Device */
+ pvt->addr_f1_ctl = pci_get_related_function(pvt->dram_f2_ctl->vendor,
+ amd64_dev->addr_f1_ctl,
+ pvt->dram_f2_ctl);
+
+ if (!pvt->addr_f1_ctl) {
+ amd64_printk(KERN_ERR, "error address map device not found: "
+ "vendor %x device 0x%x (broken BIOS?)\n",
+ PCI_VENDOR_ID_AMD, amd64_dev->addr_f1_ctl);
+ return 1;
+ }
+
+ /* Reserve the MISC Device */
+ pvt->misc_f3_ctl = pci_get_related_function(pvt->dram_f2_ctl->vendor,
+ amd64_dev->misc_f3_ctl,
+ pvt->dram_f2_ctl);
+
+ if (!pvt->misc_f3_ctl) {
+ pci_dev_put(pvt->addr_f1_ctl);
+ pvt->addr_f1_ctl = NULL;
+
+ amd64_printk(KERN_ERR, "error miscellaneous device not found: "
+ "vendor %x device 0x%x (broken BIOS?)\n",
+ PCI_VENDOR_ID_AMD, amd64_dev->misc_f3_ctl);
+ return 1;
+ }
+
+ debugf1(" Addr Map device PCI Bus ID:\t%s\n",
+ pci_name(pvt->addr_f1_ctl));
+ debugf1(" DRAM MEM-CTL PCI Bus ID:\t%s\n",
+ pci_name(pvt->dram_f2_ctl));
+ debugf1(" Misc device PCI Bus ID:\t%s\n",
+ pci_name(pvt->misc_f3_ctl));
+
+ return 0;
+}
+
+static void amd64_free_mc_sibling_devices(struct amd64_pvt *pvt)
+{
+ pci_dev_put(pvt->addr_f1_ctl);
+ pci_dev_put(pvt->misc_f3_ctl);
+}
+
+/*
+ * Retrieve the hardware registers of the memory controller (this includes the
+ * 'Address Map' and 'Misc' device regs)
+ */
+static void amd64_read_mc_registers(struct amd64_pvt *pvt)
+{
+ u64 msr_val;
+ int dram, err = 0;
+
+ /*
+ * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
+ * those are Read-As-Zero
+ */
+ rdmsrl(MSR_K8_TOP_MEM1, msr_val);
+ pvt->top_mem = msr_val >> 23;
+ debugf0(" TOP_MEM=0x%08llx\n", pvt->top_mem);
+
+ /* check first whether TOP_MEM2 is enabled */
+ rdmsrl(MSR_K8_SYSCFG, msr_val);
+ if (msr_val & (1U << 21)) {
+ rdmsrl(MSR_K8_TOP_MEM2, msr_val);
+ pvt->top_mem2 = msr_val >> 23;
+ debugf0(" TOP_MEM2=0x%08llx\n", pvt->top_mem2);
+ } else
+ debugf0(" TOP_MEM2 disabled.\n");
+
+ amd64_cpu_display_info(pvt);
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap);
+ if (err)
+ goto err_reg;
+
+ if (pvt->ops->read_dram_ctl_register)
+ pvt->ops->read_dram_ctl_register(pvt);
+
+ for (dram = 0; dram < DRAM_REG_COUNT; dram++) {
+ /*
+ * Call CPU specific READ function to get the DRAM Base and
+ * Limit values from the DCT.
+ */
+ pvt->ops->read_dram_base_limit(pvt, dram);
+
+ /*
+ * Only print out debug info on rows with both R and W Enabled.
+ * Normal processing, compiler should optimize this whole 'if'
+ * debug output block away.
+ */
+ if (pvt->dram_rw_en[dram] != 0) {
+ debugf1(" DRAM_BASE[%d]: 0x%8.08x-%8.08x "
+ "DRAM_LIMIT: 0x%8.08x-%8.08x\n",
+ dram,
+ (u32)(pvt->dram_base[dram] >> 32),
+ (u32)(pvt->dram_base[dram] & 0xFFFFFFFF),
+ (u32)(pvt->dram_limit[dram] >> 32),
+ (u32)(pvt->dram_limit[dram] & 0xFFFFFFFF));
+ debugf1(" IntlvEn=%s %s %s "
+ "IntlvSel=%d DstNode=%d\n",
+ pvt->dram_IntlvEn[dram] ?
+ "Enabled" : "Disabled",
+ (pvt->dram_rw_en[dram] & 0x2) ? "W" : "!W",
+ (pvt->dram_rw_en[dram] & 0x1) ? "R" : "!R",
+ pvt->dram_IntlvSel[dram],
+ pvt->dram_DstNode[dram]);
+ }
+ }
+
+ amd64_read_dct_base_mask(pvt);
+
+ err = pci_read_config_dword(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar);
+ if (err)
+ goto err_reg;
+
+ amd64_read_dbam_reg(pvt);
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl,
+ F10_ONLINE_SPARE, &pvt->online_spare);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0);
+ if (err)
+ goto err_reg;
+
+ if (!dct_ganging_enabled(pvt)) {
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1,
+ &pvt->dclr1);
+ if (err)
+ goto err_reg;
+
+ err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_1,
+ &pvt->dchr1);
+ if (err)
+ goto err_reg;
+ }
+
+ amd64_dump_misc_regs(pvt);
+
+err_reg:
+ debugf0("Reading an MC register failed\n");
+
+}
+
+/*
+ * NOTE: CPU Revision Dependent code
+ *
+ * Input:
+ * @csrow_nr ChipSelect Row Number (0..CHIPSELECT_COUNT-1)
+ * k8 private pointer to -->
+ * DRAM Bank Address mapping register
+ * node_id
+ * DCL register where dual_channel_active is
+ *
+ * The DBAM register consists of 4 sets of 4 bits each definitions:
+ *
+ * Bits: CSROWs
+ * 0-3 CSROWs 0 and 1
+ * 4-7 CSROWs 2 and 3
+ * 8-11 CSROWs 4 and 5
+ * 12-15 CSROWs 6 and 7
+ *
+ * Values range from: 0 to 15
+ * The meaning of the values depends on CPU revision and dual-channel state,
+ * see relevant BKDG more info.
+ *
+ * The memory controller provides for total of only 8 CSROWs in its current
+ * architecture. Each "pair" of CSROWs normally represents just one DIMM in
+ * single channel or two (2) DIMMs in dual channel mode.
+ *
+ * The following code logic collapses the various tables for CSROW based on CPU
+ * revision.
+ *
+ * Returns:
+ * The number of PAGE_SIZE pages on the specified CSROW number it
+ * encompasses
+ *
+ */
+static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
+{
+ u32 dram_map, nr_pages;
+
+ /*
+ * The math on this doesn't look right on the surface because x/2*4 can
+ * be simplified to x*2 but this expression makes use of the fact that
+ * it is integral math where 1/2=0. This intermediate value becomes the
+ * number of bits to shift the DBAM register to extract the proper CSROW
+ * field.
+ */
+ dram_map = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
+
+ nr_pages = pvt->ops->dbam_map_to_pages(pvt, dram_map);
+
+ /*
+ * If dual channel then double the memory size of single channel.
+ * Channel count is 1 or 2
+ */
+ nr_pages <<= (pvt->channel_count - 1);
+
+ debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, dram_map);
+ debugf0(" nr_pages= %u channel-count = %d\n",
+ nr_pages, pvt->channel_count);
+
+ return nr_pages;
+}
+
+/*
+ * Initialize the array of csrow attribute instances, based on the values
+ * from pci config hardware registers.
+ */
+static int amd64_init_csrows(struct mem_ctl_info *mci)
+{
+ struct csrow_info *csrow;
+ struct amd64_pvt *pvt;
+ u64 input_addr_min, input_addr_max, sys_addr;
+ int i, err = 0, empty = 1;
+
+ pvt = mci->pvt_info;
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg);
+ if (err)
+ debugf0("Reading K8_NBCFG failed\n");
+
+ debugf0("NBCFG= 0x%x CHIPKILL= %s DRAM ECC= %s\n", pvt->nbcfg,
+ (pvt->nbcfg & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
+ (pvt->nbcfg & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled"
+ );
+
+ for (i = 0; i < CHIPSELECT_COUNT; i++) {
+ csrow = &mci->csrows[i];
+
+ if ((pvt->dcsb0[i] & K8_DCSB_CS_ENABLE) == 0) {
+ debugf1("----CSROW %d EMPTY for node %d\n", i,
+ pvt->mc_node_id);
+ continue;
+ }
+
+ debugf1("----CSROW %d VALID for MC node %d\n",
+ i, pvt->mc_node_id);
+
+ empty = 0;
+ csrow->nr_pages = amd64_csrow_nr_pages(i, pvt);
+ find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
+ sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
+ csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
+ sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
+ csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
+ csrow->page_mask = ~mask_from_dct_mask(pvt, i);
+ /* 8 bytes of resolution */
+
+ csrow->mtype = amd64_determine_memory_type(pvt);
+
+ debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i);
+ debugf1(" input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
+ (unsigned long)input_addr_min,
+ (unsigned long)input_addr_max);
+ debugf1(" sys_addr: 0x%lx page_mask: 0x%lx\n",
+ (unsigned long)sys_addr, csrow->page_mask);
+ debugf1(" nr_pages: %u first_page: 0x%lx "
+ "last_page: 0x%lx\n",
+ (unsigned)csrow->nr_pages,
+ csrow->first_page, csrow->last_page);
+
+ /*
+ * determine whether CHIPKILL or JUST ECC or NO ECC is operating
+ */
+ if (pvt->nbcfg & K8_NBCFG_ECC_ENABLE)
+ csrow->edac_mode =
+ (pvt->nbcfg & K8_NBCFG_CHIPKILL) ?
+ EDAC_S4ECD4ED : EDAC_SECDED;
+ else
+ csrow->edac_mode = EDAC_NONE;
+ }
+
+ return empty;
+}
+
+/*
+ * Only if 'ecc_enable_override' is set AND BIOS had ECC disabled, do "we"
+ * enable it.
+ */
+static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
+ int cpu, idx = 0, err = 0;
+ struct msr msrs[cpumask_weight(cpumask)];
+ u32 value;
+ u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+
+ if (!ecc_enable_override)
+ return;
+
+ memset(msrs, 0, sizeof(msrs));
+
+ amd64_printk(KERN_WARNING,
+ "'ecc_enable_override' parameter is active, "
+ "Enabling AMD ECC hardware now: CAUTION\n");
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
+ if (err)
+ debugf0("Reading K8_NBCTL failed\n");
+
+ /* turn on UECCn and CECCEn bits */
+ pvt->old_nbctl = value & mask;
+ pvt->nbctl_mcgctl_saved = 1;
+
+ value |= mask;
+ pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
+
+ rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
+
+ for_each_cpu(cpu, cpumask) {
+ if (msrs[idx].l & K8_MSR_MCGCTL_NBE)
+ set_bit(idx, &pvt->old_mcgctl);
+
+ msrs[idx].l |= K8_MSR_MCGCTL_NBE;
+ idx++;
+ }
+ wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
+ if (err)
+ debugf0("Reading K8_NBCFG failed\n");
+
+ debugf0("NBCFG(1)= 0x%x CHIPKILL= %s ECC_ENABLE= %s\n", value,
+ (value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
+ (value & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled");
+
+ if (!(value & K8_NBCFG_ECC_ENABLE)) {
+ amd64_printk(KERN_WARNING,
+ "This node reports that DRAM ECC is "
+ "currently Disabled; ENABLING now\n");
+
+ /* Attempt to turn on DRAM ECC Enable */
+ value |= K8_NBCFG_ECC_ENABLE;
+ pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCFG, value);
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
+ if (err)
+ debugf0("Reading K8_NBCFG failed\n");
+
+ if (!(value & K8_NBCFG_ECC_ENABLE)) {
+ amd64_printk(KERN_WARNING,
+ "Hardware rejects Enabling DRAM ECC checking\n"
+ "Check memory DIMM configuration\n");
+ } else {
+ amd64_printk(KERN_DEBUG,
+ "Hardware accepted DRAM ECC Enable\n");
+ }
+ }
+ debugf0("NBCFG(2)= 0x%x CHIPKILL= %s ECC_ENABLE= %s\n", value,
+ (value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
+ (value & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled");
+
+ pvt->ctl_error_info.nbcfg = value;
+}
+
+static void amd64_restore_ecc_error_reporting(struct amd64_pvt *pvt)
+{
+ const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
+ int cpu, idx = 0, err = 0;
+ struct msr msrs[cpumask_weight(cpumask)];
+ u32 value;
+ u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+
+ if (!pvt->nbctl_mcgctl_saved)
+ return;
+
+ memset(msrs, 0, sizeof(msrs));
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
+ if (err)
+ debugf0("Reading K8_NBCTL failed\n");
+ value &= ~mask;
+ value |= pvt->old_nbctl;
+
+ /* restore the NB Enable MCGCTL bit */
+ pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
+
+ rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
+
+ for_each_cpu(cpu, cpumask) {
+ msrs[idx].l &= ~K8_MSR_MCGCTL_NBE;
+ msrs[idx].l |=
+ test_bit(idx, &pvt->old_mcgctl) << K8_MSR_MCGCTL_NBE;
+ idx++;
+ }
+
+ wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
+}
+
+static void check_mcg_ctl(void *ret)
+{
+ u64 msr_val = 0;
+ u8 nbe;
+
+ rdmsrl(MSR_IA32_MCG_CTL, msr_val);
+ nbe = msr_val & K8_MSR_MCGCTL_NBE;
+
+ debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
+ raw_smp_processor_id(), msr_val,
+ (nbe ? "enabled" : "disabled"));
+
+ if (!nbe)
+ *(int *)ret = 0;
+}
+
+/* check MCG_CTL on all the cpus on this node */
+static int amd64_mcg_ctl_enabled_on_cpus(const cpumask_t *mask)
+{
+ int ret = 1;
+ preempt_disable();
+ smp_call_function_many(mask, check_mcg_ctl, &ret, 1);
+ preempt_enable();
+
+ return ret;
+}
+
+/*
+ * EDAC requires that the BIOS have ECC enabled before taking over the
+ * processing of ECC errors. This is because the BIOS can properly initialize
+ * the memory system completely. A command line option allows to force-enable
+ * hardware ECC later in amd64_enable_ecc_error_reporting().
+ */
+static int amd64_check_ecc_enabled(struct amd64_pvt *pvt)
+{
+ u32 value;
+ int err = 0, ret = 0;
+ u8 ecc_enabled = 0;
+
+ err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
+ if (err)
+ debugf0("Reading K8_NBCTL failed\n");
+
+ ecc_enabled = !!(value & K8_NBCFG_ECC_ENABLE);
+
+ ret = amd64_mcg_ctl_enabled_on_cpus(cpumask_of_node(pvt->mc_node_id));
+
+ debugf0("K8_NBCFG=0x%x, DRAM ECC is %s\n", value,
+ (value & K8_NBCFG_ECC_ENABLE ? "enabled" : "disabled"));
+
+ if (!ecc_enabled || !ret) {
+ if (!ecc_enabled) {
+ amd64_printk(KERN_WARNING, "This node reports that "
+ "Memory ECC is currently "
+ "disabled.\n");
+
+ amd64_printk(KERN_WARNING, "bit 0x%lx in register "
+ "F3x%x of the MISC_CONTROL device (%s) "
+ "should be enabled\n", K8_NBCFG_ECC_ENABLE,
+ K8_NBCFG, pci_name(pvt->misc_f3_ctl));
+ }
+ if (!ret) {
+ amd64_printk(KERN_WARNING, "bit 0x%016lx in MSR 0x%08x "
+ "of node %d should be enabled\n",
+ K8_MSR_MCGCTL_NBE, MSR_IA32_MCG_CTL,
+ pvt->mc_node_id);
+ }
+ if (!ecc_enable_override) {
+ amd64_printk(KERN_WARNING, "WARNING: ECC is NOT "
+ "currently enabled by the BIOS. Module "
+ "will NOT be loaded.\n"
+ " Either Enable ECC in the BIOS, "
+ "or use the 'ecc_enable_override' "
+ "parameter.\n"
+ " Might be a BIOS bug, if BIOS says "
+ "ECC is enabled\n"
+ " Use of the override can cause "
+ "unknown side effects.\n");
+ ret = -ENODEV;
+ }
+ } else {
+ amd64_printk(KERN_INFO,
+ "ECC is enabled by BIOS, Proceeding "
+ "with EDAC module initialization\n");
+
+ /* CLEAR the override, since BIOS controlled it */
+ ecc_enable_override = 0;
+ }
+
+ return ret;
+}
+
+struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
+ ARRAY_SIZE(amd64_inj_attrs) +
+ 1];
+
+struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
+
+static void amd64_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
+{
+ unsigned int i = 0, j = 0;
+
+ for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
+ sysfs_attrs[i] = amd64_dbg_attrs[i];
+
+ for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
+ sysfs_attrs[i] = amd64_inj_attrs[j];
+
+ sysfs_attrs[i] = terminator;
+
+ mci->mc_driver_sysfs_attributes = sysfs_attrs;
+}
+
+static void amd64_setup_mci_misc_attributes(struct mem_ctl_info *mci)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE;
+ mci->edac_cap = EDAC_FLAG_NONE;
+
+ if (pvt->nbcap & K8_NBCAP_SECDED)
+ mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
+
+ if (pvt->nbcap & K8_NBCAP_CHIPKILL)
+ mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
+
+ mci->edac_cap = amd64_determine_edac_cap(pvt);
+ mci->mod_name = EDAC_MOD_STR;
+ mci->mod_ver = EDAC_AMD64_VERSION;
+ mci->ctl_name = get_amd_family_name(pvt->mc_type_index);
+ mci->dev_name = pci_name(pvt->dram_f2_ctl);
+ mci->ctl_page_to_phys = NULL;
+
+ /* IMPORTANT: Set the polling 'check' function in this module */
+ mci->edac_check = amd64_check;
+
+ /* memory scrubber interface */
+ mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
+ mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
+}
+
+/*
+ * Init stuff for this DRAM Controller device.
+ *
+ * Due to a hardware feature on Fam10h CPUs, the Enable Extended Configuration
+ * Space feature MUST be enabled on ALL Processors prior to actually reading
+ * from the ECS registers. Since the loading of the module can occur on any
+ * 'core', and cores don't 'see' all the other processors ECS data when the
+ * others are NOT enabled. Our solution is to first enable ECS access in this
+ * routine on all processors, gather some data in a amd64_pvt structure and
+ * later come back in a finish-setup function to perform that final
+ * initialization. See also amd64_init_2nd_stage() for that.
+ */
+static int amd64_probe_one_instance(struct pci_dev *dram_f2_ctl,
+ int mc_type_index)
+{
+ struct amd64_pvt *pvt = NULL;
+ int err = 0, ret;
+
+ ret = -ENOMEM;
+ pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
+ if (!pvt)
+ goto err_exit;
+
+ pvt->mc_node_id = get_mc_node_id_from_pdev(dram_f2_ctl);
+
+ pvt->dram_f2_ctl = dram_f2_ctl;
+ pvt->ext_model = boot_cpu_data.x86_model >> 4;
+ pvt->mc_type_index = mc_type_index;
+ pvt->ops = family_ops(mc_type_index);
+ pvt->old_mcgctl = 0;
+
+ /*
+ * We have the dram_f2_ctl device as an argument, now go reserve its
+ * sibling devices from the PCI system.
+ */
+ ret = -ENODEV;
+ err = amd64_reserve_mc_sibling_devices(pvt, mc_type_index);
+ if (err)
+ goto err_free;
+
+ ret = -EINVAL;
+ err = amd64_check_ecc_enabled(pvt);
+ if (err)
+ goto err_put;
+
+ /*
+ * Key operation here: setup of HW prior to performing ops on it. Some
+ * setup is required to access ECS data. After this is performed, the
+ * 'teardown' function must be called upon error and normal exit paths.
+ */
+ if (boot_cpu_data.x86 >= 0x10)
+ amd64_setup(pvt);
+
+ /*
+ * Save the pointer to the private data for use in 2nd initialization
+ * stage
+ */
+ pvt_lookup[pvt->mc_node_id] = pvt;
+
+ return 0;
+
+err_put:
+ amd64_free_mc_sibling_devices(pvt);
+
+err_free:
+ kfree(pvt);
+
+err_exit:
+ return ret;
+}
+
+/*
+ * This is the finishing stage of the init code. Needs to be performed after all
+ * MCs' hardware have been prepped for accessing extended config space.
+ */
+static int amd64_init_2nd_stage(struct amd64_pvt *pvt)
+{
+ int node_id = pvt->mc_node_id;
+ struct mem_ctl_info *mci;
+ int ret, err = 0;
+
+ amd64_read_mc_registers(pvt);
+
+ ret = -ENODEV;
+ if (pvt->ops->probe_valid_hardware) {
+ err = pvt->ops->probe_valid_hardware(pvt);
+ if (err)
+ goto err_exit;
+ }
+
+ /*
+ * We need to determine how many memory channels there are. Then use
+ * that information for calculating the size of the dynamic instance
+ * tables in the 'mci' structure
+ */
+ pvt->channel_count = pvt->ops->early_channel_count(pvt);
+ if (pvt->channel_count < 0)
+ goto err_exit;
+
+ ret = -ENOMEM;
+ mci = edac_mc_alloc(0, CHIPSELECT_COUNT, pvt->channel_count, node_id);
+ if (!mci)
+ goto err_exit;
+
+ mci->pvt_info = pvt;
+
+ mci->dev = &pvt->dram_f2_ctl->dev;
+ amd64_setup_mci_misc_attributes(mci);
+
+ if (amd64_init_csrows(mci))
+ mci->edac_cap = EDAC_FLAG_NONE;
+
+ amd64_enable_ecc_error_reporting(mci);
+ amd64_set_mc_sysfs_attributes(mci);
+
+ ret = -ENODEV;
+ if (edac_mc_add_mc(mci)) {
+ debugf1("failed edac_mc_add_mc()\n");
+ goto err_add_mc;
+ }
+
+ mci_lookup[node_id] = mci;
+ pvt_lookup[node_id] = NULL;
+ return 0;
+
+err_add_mc:
+ edac_mc_free(mci);
+
+err_exit:
+ debugf0("failure to init 2nd stage: ret=%d\n", ret);
+
+ amd64_restore_ecc_error_reporting(pvt);
+
+ if (boot_cpu_data.x86 > 0xf)
+ amd64_teardown(pvt);
+
+ amd64_free_mc_sibling_devices(pvt);
+
+ kfree(pvt_lookup[pvt->mc_node_id]);
+ pvt_lookup[node_id] = NULL;
+
+ return ret;
+}
+
+
+static int __devinit amd64_init_one_instance(struct pci_dev *pdev,
+ const struct pci_device_id *mc_type)
+{
+ int ret = 0;
+
+ debugf0("(MC node=%d,mc_type='%s')\n",
+ get_mc_node_id_from_pdev(pdev),
+ get_amd_family_name(mc_type->driver_data));
+
+ ret = pci_enable_device(pdev);
+ if (ret < 0)
+ ret = -EIO;
+ else
+ ret = amd64_probe_one_instance(pdev, mc_type->driver_data);
+
+ if (ret < 0)
+ debugf0("ret=%d\n", ret);
+
+ return ret;
+}
+
+static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
+{
+ struct mem_ctl_info *mci;
+ struct amd64_pvt *pvt;
+
+ /* Remove from EDAC CORE tracking list */
+ mci = edac_mc_del_mc(&pdev->dev);
+ if (!mci)
+ return;
+
+ pvt = mci->pvt_info;
+
+ amd64_restore_ecc_error_reporting(pvt);
+
+ if (boot_cpu_data.x86 > 0xf)
+ amd64_teardown(pvt);
+
+ amd64_free_mc_sibling_devices(pvt);
+
+ kfree(pvt);
+ mci->pvt_info = NULL;
+
+ mci_lookup[pvt->mc_node_id] = NULL;
+
+ /* Free the EDAC CORE resources */
+ edac_mc_free(mci);
+}
+
+/*
+ * This table is part of the interface for loading drivers for PCI devices. The
+ * PCI core identifies what devices are on a system during boot, and then
+ * inquiry this table to see if this driver is for a given device found.
+ */
+static const struct pci_device_id amd64_pci_table[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = K8_CPUS
+ },
+ {
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = F10_CPUS
+ },
+ {
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_DEVICE_ID_AMD_11H_NB_DRAM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = F11_CPUS
+ },
+ {0, }
+};
+MODULE_DEVICE_TABLE(pci, amd64_pci_table);
+
+static struct pci_driver amd64_pci_driver = {
+ .name = EDAC_MOD_STR,
+ .probe = amd64_init_one_instance,
+ .remove = __devexit_p(amd64_remove_one_instance),
+ .id_table = amd64_pci_table,
+};
+
+static void amd64_setup_pci_device(void)
+{
+ struct mem_ctl_info *mci;
+ struct amd64_pvt *pvt;
+
+ if (amd64_ctl_pci)
+ return;
+
+ mci = mci_lookup[0];
+ if (mci) {
+
+ pvt = mci->pvt_info;
+ amd64_ctl_pci =
+ edac_pci_create_generic_ctl(&pvt->dram_f2_ctl->dev,
+ EDAC_MOD_STR);
+
+ if (!amd64_ctl_pci) {
+ pr_warning("%s(): Unable to create PCI control\n",
+ __func__);
+
+ pr_warning("%s(): PCI error report via EDAC not set\n",
+ __func__);
+ }
+ }
+}
+
+static int __init amd64_edac_init(void)
+{
+ int nb, err = -ENODEV;
+
+ edac_printk(KERN_INFO, EDAC_MOD_STR, EDAC_AMD64_VERSION "\n");
+
+ opstate_init();
+
+ if (cache_k8_northbridges() < 0)
+ goto err_exit;
+
+ err = pci_register_driver(&amd64_pci_driver);
+ if (err)
+ return err;
+
+ /*
+ * At this point, the array 'pvt_lookup[]' contains pointers to alloc'd
+ * amd64_pvt structs. These will be used in the 2nd stage init function
+ * to finish initialization of the MC instances.
+ */
+ for (nb = 0; nb < num_k8_northbridges; nb++) {
+ if (!pvt_lookup[nb])
+ continue;
+
+ err = amd64_init_2nd_stage(pvt_lookup[nb]);
+ if (err)
+ goto err_exit;
+ }
+
+ amd64_setup_pci_device();
+
+ return 0;
+
+err_exit:
+ debugf0("'finish_setup' stage failed\n");
+ pci_unregister_driver(&amd64_pci_driver);
+
+ return err;
+}
+
+static void __exit amd64_edac_exit(void)
+{
+ if (amd64_ctl_pci)
+ edac_pci_release_generic_ctl(amd64_ctl_pci);
+
+ pci_unregister_driver(&amd64_pci_driver);
+}
+
+module_init(amd64_edac_init);
+module_exit(amd64_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
+ "Dave Peterson, Thayne Harbaugh");
+MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
+ EDAC_AMD64_VERSION);
+
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
new file mode 100644
index 000000000000..a159957e167b
--- /dev/null
+++ b/drivers/edac/amd64_edac.h
@@ -0,0 +1,644 @@
+/*
+ * AMD64 class Memory Controller kernel module
+ *
+ * Copyright (c) 2009 SoftwareBitMaker.
+ * Copyright (c) 2009 Advanced Micro Devices, Inc.
+ *
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * Originally Written by Thayne Harbaugh
+ *
+ * Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
+ * - K8 CPU Revision D and greater support
+ *
+ * Changes by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>:
+ * - Module largely rewritten, with new (and hopefully correct)
+ * code for dealing with node and chip select interleaving,
+ * various code cleanup, and bug fixes
+ * - Added support for memory hoisting using DRAM hole address
+ * register
+ *
+ * Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
+ * -K8 Rev (1207) revision support added, required Revision
+ * specific mini-driver code to support Rev F as well as
+ * prior revisions
+ *
+ * Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
+ * -Family 10h revision support added. New PCI Device IDs,
+ * indicating new changes. Actual registers modified
+ * were slight, less than the Rev E to Rev F transition
+ * but changing the PCI Device ID was the proper thing to
+ * do, as it provides for almost automactic family
+ * detection. The mods to Rev F required more family
+ * information detection.
+ *
+ * Changes/Fixes by Borislav Petkov <borislav.petkov@amd.com>:
+ * - misc fixes and code cleanups
+ *
+ * This module is based on the following documents
+ * (available from http://www.amd.com/):
+ *
+ * Title: BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD
+ * Opteron Processors
+ * AMD publication #: 26094
+ *` Revision: 3.26
+ *
+ * Title: BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh
+ * Processors
+ * AMD publication #: 32559
+ * Revision: 3.00
+ * Issue Date: May 2006
+ *
+ * Title: BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h
+ * Processors
+ * AMD publication #: 31116
+ * Revision: 3.00
+ * Issue Date: September 07, 2007
+ *
+ * Sections in the first 2 documents are no longer in sync with each other.
+ * The Family 10h BKDG was totally re-written from scratch with a new
+ * presentation model.
+ * Therefore, comments that refer to a Document section might be off.
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/mmzone.h>
+#include <linux/edac.h>
+#include <asm/msr.h>
+#include "edac_core.h"
+
+#define amd64_printk(level, fmt, arg...) \
+ edac_printk(level, "amd64", fmt, ##arg)
+
+#define amd64_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "amd64", fmt, ##arg)
+
+/*
+ * Throughout the comments in this code, the following terms are used:
+ *
+ * SysAddr, DramAddr, and InputAddr
+ *
+ * These terms come directly from the amd64 documentation
+ * (AMD publication #26094). They are defined as follows:
+ *
+ * SysAddr:
+ * This is a physical address generated by a CPU core or a device
+ * doing DMA. If generated by a CPU core, a SysAddr is the result of
+ * a virtual to physical address translation by the CPU core's address
+ * translation mechanism (MMU).
+ *
+ * DramAddr:
+ * A DramAddr is derived from a SysAddr by subtracting an offset that
+ * depends on which node the SysAddr maps to and whether the SysAddr
+ * is within a range affected by memory hoisting. The DRAM Base
+ * (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers
+ * determine which node a SysAddr maps to.
+ *
+ * If the DRAM Hole Address Register (DHAR) is enabled and the SysAddr
+ * is within the range of addresses specified by this register, then
+ * a value x from the DHAR is subtracted from the SysAddr to produce a
+ * DramAddr. Here, x represents the base address for the node that
+ * the SysAddr maps to plus an offset due to memory hoisting. See
+ * section 3.4.8 and the comments in amd64_get_dram_hole_info() and
+ * sys_addr_to_dram_addr() below for more information.
+ *
+ * If the SysAddr is not affected by the DHAR then a value y is
+ * subtracted from the SysAddr to produce a DramAddr. Here, y is the
+ * base address for the node that the SysAddr maps to. See section
+ * 3.4.4 and the comments in sys_addr_to_dram_addr() below for more
+ * information.
+ *
+ * InputAddr:
+ * A DramAddr is translated to an InputAddr before being passed to the
+ * memory controller for the node that the DramAddr is associated
+ * with. The memory controller then maps the InputAddr to a csrow.
+ * If node interleaving is not in use, then the InputAddr has the same
+ * value as the DramAddr. Otherwise, the InputAddr is produced by
+ * discarding the bits used for node interleaving from the DramAddr.
+ * See section 3.4.4 for more information.
+ *
+ * The memory controller for a given node uses its DRAM CS Base and
+ * DRAM CS Mask registers to map an InputAddr to a csrow. See
+ * sections 3.5.4 and 3.5.5 for more information.
+ */
+
+#define EDAC_AMD64_VERSION " Ver: 3.2.0 " __DATE__
+#define EDAC_MOD_STR "amd64_edac"
+
+/* Extended Model from CPUID, for CPU Revision numbers */
+#define OPTERON_CPU_LE_REV_C 0
+#define OPTERON_CPU_REV_D 1
+#define OPTERON_CPU_REV_E 2
+
+/* NPT processors have the following Extended Models */
+#define OPTERON_CPU_REV_F 4
+#define OPTERON_CPU_REV_FA 5
+
+/* Hardware limit on ChipSelect rows per MC and processors per system */
+#define CHIPSELECT_COUNT 8
+#define DRAM_REG_COUNT 8
+
+
+/*
+ * PCI-defined configuration space registers
+ */
+
+
+/*
+ * Function 1 - Address Map
+ */
+#define K8_DRAM_BASE_LOW 0x40
+#define K8_DRAM_LIMIT_LOW 0x44
+#define K8_DHAR 0xf0
+
+#define DHAR_VALID BIT(0)
+#define F10_DRAM_MEM_HOIST_VALID BIT(1)
+
+#define DHAR_BASE_MASK 0xff000000
+#define dhar_base(dhar) (dhar & DHAR_BASE_MASK)
+
+#define K8_DHAR_OFFSET_MASK 0x0000ff00
+#define k8_dhar_offset(dhar) ((dhar & K8_DHAR_OFFSET_MASK) << 16)
+
+#define F10_DHAR_OFFSET_MASK 0x0000ff80
+ /* NOTE: Extra mask bit vs K8 */
+#define f10_dhar_offset(dhar) ((dhar & F10_DHAR_OFFSET_MASK) << 16)
+
+
+/* F10 High BASE/LIMIT registers */
+#define F10_DRAM_BASE_HIGH 0x140
+#define F10_DRAM_LIMIT_HIGH 0x144
+
+
+/*
+ * Function 2 - DRAM controller
+ */
+#define K8_DCSB0 0x40
+#define F10_DCSB1 0x140
+
+#define K8_DCSB_CS_ENABLE BIT(0)
+#define K8_DCSB_NPT_SPARE BIT(1)
+#define K8_DCSB_NPT_TESTFAIL BIT(2)
+
+/*
+ * REV E: select [31:21] and [15:9] from DCSB and the shift amount to form
+ * the address
+ */
+#define REV_E_DCSB_BASE_BITS (0xFFE0FE00ULL)
+#define REV_E_DCS_SHIFT 4
+#define REV_E_DCSM_COUNT 8
+
+#define REV_F_F1Xh_DCSB_BASE_BITS (0x1FF83FE0ULL)
+#define REV_F_F1Xh_DCS_SHIFT 8
+
+/*
+ * REV F and later: selects [28:19] and [13:5] from DCSB and the shift amount
+ * to form the address
+ */
+#define REV_F_DCSB_BASE_BITS (0x1FF83FE0ULL)
+#define REV_F_DCS_SHIFT 8
+#define REV_F_DCSM_COUNT 4
+#define F10_DCSM_COUNT 4
+#define F11_DCSM_COUNT 2
+
+/* DRAM CS Mask Registers */
+#define K8_DCSM0 0x60
+#define F10_DCSM1 0x160
+
+/* REV E: select [29:21] and [15:9] from DCSM */
+#define REV_E_DCSM_MASK_BITS 0x3FE0FE00
+
+/* unused bits [24:20] and [12:0] */
+#define REV_E_DCS_NOTUSED_BITS 0x01F01FFF
+
+/* REV F and later: select [28:19] and [13:5] from DCSM */
+#define REV_F_F1Xh_DCSM_MASK_BITS 0x1FF83FE0
+
+/* unused bits [26:22] and [12:0] */
+#define REV_F_F1Xh_DCS_NOTUSED_BITS 0x07C01FFF
+
+#define DBAM0 0x80
+#define DBAM1 0x180
+
+/* Extract the DIMM 'type' on the i'th DIMM from the DBAM reg value passed */
+#define DBAM_DIMM(i, reg) ((((reg) >> (4*i))) & 0xF)
+
+#define DBAM_MAX_VALUE 11
+
+
+#define F10_DCLR_0 0x90
+#define F10_DCLR_1 0x190
+#define REVE_WIDTH_128 BIT(16)
+#define F10_WIDTH_128 BIT(11)
+
+
+#define F10_DCHR_0 0x94
+#define F10_DCHR_1 0x194
+
+#define F10_DCHR_FOUR_RANK_DIMM BIT(18)
+#define F10_DCHR_Ddr3Mode BIT(8)
+#define F10_DCHR_MblMode BIT(6)
+
+
+#define F10_DCTL_SEL_LOW 0x110
+
+#define dct_sel_baseaddr(pvt) \
+ ((pvt->dram_ctl_select_low) & 0xFFFFF800)
+
+#define dct_sel_interleave_addr(pvt) \
+ (((pvt->dram_ctl_select_low) >> 6) & 0x3)
+
+enum {
+ F10_DCTL_SEL_LOW_DctSelHiRngEn = BIT(0),
+ F10_DCTL_SEL_LOW_DctSelIntLvEn = BIT(2),
+ F10_DCTL_SEL_LOW_DctGangEn = BIT(4),
+ F10_DCTL_SEL_LOW_DctDatIntLv = BIT(5),
+ F10_DCTL_SEL_LOW_DramEnable = BIT(8),
+ F10_DCTL_SEL_LOW_MemCleared = BIT(10),
+};
+
+#define dct_high_range_enabled(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_DctSelHiRngEn)
+
+#define dct_interleave_enabled(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_DctSelIntLvEn)
+
+#define dct_ganging_enabled(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_DctGangEn)
+
+#define dct_data_intlv_enabled(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_DctDatIntLv)
+
+#define dct_dram_enabled(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_DramEnable)
+
+#define dct_memory_cleared(pvt) \
+ (pvt->dram_ctl_select_low & F10_DCTL_SEL_LOW_MemCleared)
+
+
+#define F10_DCTL_SEL_HIGH 0x114
+
+
+/*
+ * Function 3 - Misc Control
+ */
+#define K8_NBCTL 0x40
+
+/* Correctable ECC error reporting enable */
+#define K8_NBCTL_CECCEn BIT(0)
+
+/* UnCorrectable ECC error reporting enable */
+#define K8_NBCTL_UECCEn BIT(1)
+
+#define K8_NBCFG 0x44
+#define K8_NBCFG_CHIPKILL BIT(23)
+#define K8_NBCFG_ECC_ENABLE BIT(22)
+
+#define K8_NBSL 0x48
+
+
+#define EXTRACT_HIGH_SYNDROME(x) (((x) >> 24) & 0xff)
+#define EXTRACT_EXT_ERROR_CODE(x) (((x) >> 16) & 0x1f)
+
+/* Family F10h: Normalized Extended Error Codes */
+#define F10_NBSL_EXT_ERR_RES 0x0
+#define F10_NBSL_EXT_ERR_CRC 0x1
+#define F10_NBSL_EXT_ERR_SYNC 0x2
+#define F10_NBSL_EXT_ERR_MST 0x3
+#define F10_NBSL_EXT_ERR_TGT 0x4
+#define F10_NBSL_EXT_ERR_GART 0x5
+#define F10_NBSL_EXT_ERR_RMW 0x6
+#define F10_NBSL_EXT_ERR_WDT 0x7
+#define F10_NBSL_EXT_ERR_ECC 0x8
+#define F10_NBSL_EXT_ERR_DEV 0x9
+#define F10_NBSL_EXT_ERR_LINK_DATA 0xA
+
+/* Next two are overloaded values */
+#define F10_NBSL_EXT_ERR_LINK_PROTO 0xB
+#define F10_NBSL_EXT_ERR_L3_PROTO 0xB
+
+#define F10_NBSL_EXT_ERR_NB_ARRAY 0xC
+#define F10_NBSL_EXT_ERR_DRAM_PARITY 0xD
+#define F10_NBSL_EXT_ERR_LINK_RETRY 0xE
+
+/* Next two are overloaded values */
+#define F10_NBSL_EXT_ERR_GART_WALK 0xF
+#define F10_NBSL_EXT_ERR_DEV_WALK 0xF
+
+/* 0x10 to 0x1B: Reserved */
+#define F10_NBSL_EXT_ERR_L3_DATA 0x1C
+#define F10_NBSL_EXT_ERR_L3_TAG 0x1D
+#define F10_NBSL_EXT_ERR_L3_LRU 0x1E
+
+/* K8: Normalized Extended Error Codes */
+#define K8_NBSL_EXT_ERR_ECC 0x0
+#define K8_NBSL_EXT_ERR_CRC 0x1
+#define K8_NBSL_EXT_ERR_SYNC 0x2
+#define K8_NBSL_EXT_ERR_MST 0x3
+#define K8_NBSL_EXT_ERR_TGT 0x4
+#define K8_NBSL_EXT_ERR_GART 0x5
+#define K8_NBSL_EXT_ERR_RMW 0x6
+#define K8_NBSL_EXT_ERR_WDT 0x7
+#define K8_NBSL_EXT_ERR_CHIPKILL_ECC 0x8
+#define K8_NBSL_EXT_ERR_DRAM_PARITY 0xD
+
+#define EXTRACT_ERROR_CODE(x) ((x) & 0xffff)
+#define TEST_TLB_ERROR(x) (((x) & 0xFFF0) == 0x0010)
+#define TEST_MEM_ERROR(x) (((x) & 0xFF00) == 0x0100)
+#define TEST_BUS_ERROR(x) (((x) & 0xF800) == 0x0800)
+#define EXTRACT_TT_CODE(x) (((x) >> 2) & 0x3)
+#define EXTRACT_II_CODE(x) (((x) >> 2) & 0x3)
+#define EXTRACT_LL_CODE(x) (((x) >> 0) & 0x3)
+#define EXTRACT_RRRR_CODE(x) (((x) >> 4) & 0xf)
+#define EXTRACT_TO_CODE(x) (((x) >> 8) & 0x1)
+#define EXTRACT_PP_CODE(x) (((x) >> 9) & 0x3)
+
+/*
+ * The following are for BUS type errors AFTER values have been normalized by
+ * shifting right
+ */
+#define K8_NBSL_PP_SRC 0x0
+#define K8_NBSL_PP_RES 0x1
+#define K8_NBSL_PP_OBS 0x2
+#define K8_NBSL_PP_GENERIC 0x3
+
+
+#define K8_NBSH 0x4C
+
+#define K8_NBSH_VALID_BIT BIT(31)
+#define K8_NBSH_OVERFLOW BIT(30)
+#define K8_NBSH_UNCORRECTED_ERR BIT(29)
+#define K8_NBSH_ERR_ENABLE BIT(28)
+#define K8_NBSH_MISC_ERR_VALID BIT(27)
+#define K8_NBSH_VALID_ERROR_ADDR BIT(26)
+#define K8_NBSH_PCC BIT(25)
+#define K8_NBSH_CECC BIT(14)
+#define K8_NBSH_UECC BIT(13)
+#define K8_NBSH_ERR_SCRUBER BIT(8)
+#define K8_NBSH_CORE3 BIT(3)
+#define K8_NBSH_CORE2 BIT(2)
+#define K8_NBSH_CORE1 BIT(1)
+#define K8_NBSH_CORE0 BIT(0)
+
+#define EXTRACT_LDT_LINK(x) (((x) >> 4) & 0x7)
+#define EXTRACT_ERR_CPU_MAP(x) ((x) & 0xF)
+#define EXTRACT_LOW_SYNDROME(x) (((x) >> 15) & 0xff)
+
+
+#define K8_NBEAL 0x50
+#define K8_NBEAH 0x54
+#define K8_SCRCTRL 0x58
+
+#define F10_NB_CFG_LOW 0x88
+#define F10_NB_CFG_LOW_ENABLE_EXT_CFG BIT(14)
+
+#define F10_NB_CFG_HIGH 0x8C
+
+#define F10_ONLINE_SPARE 0xB0
+#define F10_ONLINE_SPARE_SWAPDONE0(x) ((x) & BIT(1))
+#define F10_ONLINE_SPARE_SWAPDONE1(x) ((x) & BIT(3))
+#define F10_ONLINE_SPARE_BADDRAM_CS0(x) (((x) >> 4) & 0x00000007)
+#define F10_ONLINE_SPARE_BADDRAM_CS1(x) (((x) >> 8) & 0x00000007)
+
+#define F10_NB_ARRAY_ADDR 0xB8
+
+#define F10_NB_ARRAY_DRAM_ECC 0x80000000
+
+/* Bits [2:1] are used to select 16-byte section within a 64-byte cacheline */
+#define SET_NB_ARRAY_ADDRESS(section) (((section) & 0x3) << 1)
+
+#define F10_NB_ARRAY_DATA 0xBC
+
+#define SET_NB_DRAM_INJECTION_WRITE(word, bits) \
+ (BIT(((word) & 0xF) + 20) | \
+ BIT(17) | \
+ ((bits) & 0xF))
+
+#define SET_NB_DRAM_INJECTION_READ(word, bits) \
+ (BIT(((word) & 0xF) + 20) | \
+ BIT(16) | \
+ ((bits) & 0xF))
+
+#define K8_NBCAP 0xE8
+#define K8_NBCAP_CORES (BIT(12)|BIT(13))
+#define K8_NBCAP_CHIPKILL BIT(4)
+#define K8_NBCAP_SECDED BIT(3)
+#define K8_NBCAP_8_NODE BIT(2)
+#define K8_NBCAP_DUAL_NODE BIT(1)
+#define K8_NBCAP_DCT_DUAL BIT(0)
+
+/*
+ * MSR Regs
+ */
+#define K8_MSR_MCGCTL 0x017b
+#define K8_MSR_MCGCTL_NBE BIT(4)
+
+#define K8_MSR_MC4CTL 0x0410
+#define K8_MSR_MC4STAT 0x0411
+#define K8_MSR_MC4ADDR 0x0412
+
+/* AMD sets the first MC device at device ID 0x18. */
+static inline int get_mc_node_id_from_pdev(struct pci_dev *pdev)
+{
+ return PCI_SLOT(pdev->devfn) - 0x18;
+}
+
+enum amd64_chipset_families {
+ K8_CPUS = 0,
+ F10_CPUS,
+ F11_CPUS,
+};
+
+/*
+ * Structure to hold:
+ *
+ * 1) dynamically read status and error address HW registers
+ * 2) sysfs entered values
+ * 3) MCE values
+ *
+ * Depends on entry into the modules
+ */
+struct amd64_error_info_regs {
+ u32 nbcfg;
+ u32 nbsh;
+ u32 nbsl;
+ u32 nbeah;
+ u32 nbeal;
+};
+
+/* Error injection control structure */
+struct error_injection {
+ u32 section;
+ u32 word;
+ u32 bit_map;
+};
+
+struct amd64_pvt {
+ /* pci_device handles which we utilize */
+ struct pci_dev *addr_f1_ctl;
+ struct pci_dev *dram_f2_ctl;
+ struct pci_dev *misc_f3_ctl;
+
+ int mc_node_id; /* MC index of this MC node */
+ int ext_model; /* extended model value of this node */
+
+ struct low_ops *ops; /* pointer to per PCI Device ID func table */
+
+ int channel_count;
+
+ /* Raw registers */
+ u32 dclr0; /* DRAM Configuration Low DCT0 reg */
+ u32 dclr1; /* DRAM Configuration Low DCT1 reg */
+ u32 dchr0; /* DRAM Configuration High DCT0 reg */
+ u32 dchr1; /* DRAM Configuration High DCT1 reg */
+ u32 nbcap; /* North Bridge Capabilities */
+ u32 nbcfg; /* F10 North Bridge Configuration */
+ u32 ext_nbcfg; /* Extended F10 North Bridge Configuration */
+ u32 dhar; /* DRAM Hoist reg */
+ u32 dbam0; /* DRAM Base Address Mapping reg for DCT0 */
+ u32 dbam1; /* DRAM Base Address Mapping reg for DCT1 */
+
+ /* DRAM CS Base Address Registers F2x[1,0][5C:40] */
+ u32 dcsb0[CHIPSELECT_COUNT];
+ u32 dcsb1[CHIPSELECT_COUNT];
+
+ /* DRAM CS Mask Registers F2x[1,0][6C:60] */
+ u32 dcsm0[CHIPSELECT_COUNT];
+ u32 dcsm1[CHIPSELECT_COUNT];
+
+ /*
+ * Decoded parts of DRAM BASE and LIMIT Registers
+ * F1x[78,70,68,60,58,50,48,40]
+ */
+ u64 dram_base[DRAM_REG_COUNT];
+ u64 dram_limit[DRAM_REG_COUNT];
+ u8 dram_IntlvSel[DRAM_REG_COUNT];
+ u8 dram_IntlvEn[DRAM_REG_COUNT];
+ u8 dram_DstNode[DRAM_REG_COUNT];
+ u8 dram_rw_en[DRAM_REG_COUNT];
+
+ /*
+ * The following fields are set at (load) run time, after CPU revision
+ * has been determined, since the dct_base and dct_mask registers vary
+ * based on revision
+ */
+ u32 dcsb_base; /* DCSB base bits */
+ u32 dcsm_mask; /* DCSM mask bits */
+ u32 num_dcsm; /* Number of DCSM registers */
+ u32 dcs_mask_notused; /* DCSM notused mask bits */
+ u32 dcs_shift; /* DCSB and DCSM shift value */
+
+ u64 top_mem; /* top of memory below 4GB */
+ u64 top_mem2; /* top of memory above 4GB */
+
+ u32 dram_ctl_select_low; /* DRAM Controller Select Low Reg */
+ u32 dram_ctl_select_high; /* DRAM Controller Select High Reg */
+ u32 online_spare; /* On-Line spare Reg */
+
+ /* temp storage for when input is received from sysfs */
+ struct amd64_error_info_regs ctl_error_info;
+
+ /* place to store error injection parameters prior to issue */
+ struct error_injection injection;
+
+ /* Save old hw registers' values before we modified them */
+ u32 nbctl_mcgctl_saved; /* When true, following 2 are valid */
+ u32 old_nbctl;
+ unsigned long old_mcgctl; /* per core on this node */
+
+ /* MC Type Index value: socket F vs Family 10h */
+ u32 mc_type_index;
+
+ /* misc settings */
+ struct flags {
+ unsigned long cf8_extcfg:1;
+ } flags;
+};
+
+struct scrubrate {
+ u32 scrubval; /* bit pattern for scrub rate */
+ u32 bandwidth; /* bandwidth consumed (bytes/sec) */
+};
+
+extern struct scrubrate scrubrates[23];
+extern u32 revf_quad_ddr2_shift[16];
+extern const char *tt_msgs[4];
+extern const char *ll_msgs[4];
+extern const char *rrrr_msgs[16];
+extern const char *to_msgs[2];
+extern const char *pp_msgs[4];
+extern const char *ii_msgs[4];
+extern const char *ext_msgs[32];
+extern const char *htlink_msgs[8];
+
+#ifdef CONFIG_EDAC_DEBUG
+#define NUM_DBG_ATTRS 9
+#else
+#define NUM_DBG_ATTRS 0
+#endif
+
+#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
+#define NUM_INJ_ATTRS 5
+#else
+#define NUM_INJ_ATTRS 0
+#endif
+
+extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS],
+ amd64_inj_attrs[NUM_INJ_ATTRS];
+
+/*
+ * Each of the PCI Device IDs types have their own set of hardware accessor
+ * functions and per device encoding/decoding logic.
+ */
+struct low_ops {
+ int (*probe_valid_hardware)(struct amd64_pvt *pvt);
+ int (*early_channel_count)(struct amd64_pvt *pvt);
+
+ u64 (*get_error_address)(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info);
+ void (*read_dram_base_limit)(struct amd64_pvt *pvt, int dram);
+ void (*read_dram_ctl_register)(struct amd64_pvt *pvt);
+ void (*map_sysaddr_to_csrow)(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info,
+ u64 SystemAddr);
+ int (*dbam_map_to_pages)(struct amd64_pvt *pvt, int dram_map);
+};
+
+struct amd64_family_type {
+ const char *ctl_name;
+ u16 addr_f1_ctl;
+ u16 misc_f3_ctl;
+ struct low_ops ops;
+};
+
+static struct amd64_family_type amd64_family_types[];
+
+static inline const char *get_amd_family_name(int index)
+{
+ return amd64_family_types[index].ctl_name;
+}
+
+static inline struct low_ops *family_ops(int index)
+{
+ return &amd64_family_types[index].ops;
+}
+
+/*
+ * For future CPU versions, verify the following as new 'slow' rates appear and
+ * modify the necessary skip values for the supported CPU.
+ */
+#define K8_MIN_SCRUB_RATE_BITS 0x0
+#define F10_MIN_SCRUB_RATE_BITS 0x5
+#define F11_MIN_SCRUB_RATE_BITS 0x6
+
+int amd64_process_error_info(struct mem_ctl_info *mci,
+ struct amd64_error_info_regs *info,
+ int handle_errors);
+int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
+ u64 *hole_offset, u64 *hole_size);
diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c
new file mode 100644
index 000000000000..0a41b248a4ad
--- /dev/null
+++ b/drivers/edac/amd64_edac_dbg.c
@@ -0,0 +1,255 @@
+#include "amd64_edac.h"
+
+/*
+ * accept a hex value and store it into the virtual error register file, field:
+ * nbeal and nbeah. Assume virtual error values have already been set for: NBSL,
+ * NBSH and NBCFG. Then proceed to map the error values to a MC, CSROW and
+ * CHANNEL
+ */
+static ssize_t amd64_nbea_store(struct mem_ctl_info *mci, const char *data,
+ size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long long value;
+ int ret = 0;
+
+ ret = strict_strtoull(data, 16, &value);
+ if (ret != -EINVAL) {
+ debugf0("received NBEA= 0x%llx\n", value);
+
+ /* place the value into the virtual error packet */
+ pvt->ctl_error_info.nbeal = (u32) value;
+ value >>= 32;
+ pvt->ctl_error_info.nbeah = (u32) value;
+
+ /* Process the Mapping request */
+ /* TODO: Add race prevention */
+ amd64_process_error_info(mci, &pvt->ctl_error_info, 1);
+
+ return count;
+ }
+ return ret;
+}
+
+/* display back what the last NBEA (MCA NB Address (MC4_ADDR)) was written */
+static ssize_t amd64_nbea_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u64 value;
+
+ value = pvt->ctl_error_info.nbeah;
+ value <<= 32;
+ value |= pvt->ctl_error_info.nbeal;
+
+ return sprintf(data, "%llx\n", value);
+}
+
+/* store the NBSL (MCA NB Status Low (MC4_STATUS)) value user desires */
+static ssize_t amd64_nbsl_store(struct mem_ctl_info *mci, const char *data,
+ size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 16, &value);
+ if (ret != -EINVAL) {
+ debugf0("received NBSL= 0x%lx\n", value);
+
+ pvt->ctl_error_info.nbsl = (u32) value;
+
+ return count;
+ }
+ return ret;
+}
+
+/* display back what the last NBSL value written */
+static ssize_t amd64_nbsl_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 value;
+
+ value = pvt->ctl_error_info.nbsl;
+
+ return sprintf(data, "%x\n", value);
+}
+
+/* store the NBSH (MCA NB Status High) value user desires */
+static ssize_t amd64_nbsh_store(struct mem_ctl_info *mci, const char *data,
+ size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 16, &value);
+ if (ret != -EINVAL) {
+ debugf0("received NBSH= 0x%lx\n", value);
+
+ pvt->ctl_error_info.nbsh = (u32) value;
+
+ return count;
+ }
+ return ret;
+}
+
+/* display back what the last NBSH value written */
+static ssize_t amd64_nbsh_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 value;
+
+ value = pvt->ctl_error_info.nbsh;
+
+ return sprintf(data, "%x\n", value);
+}
+
+/* accept and store the NBCFG (MCA NB Configuration) value user desires */
+static ssize_t amd64_nbcfg_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 16, &value);
+ if (ret != -EINVAL) {
+ debugf0("received NBCFG= 0x%lx\n", value);
+
+ pvt->ctl_error_info.nbcfg = (u32) value;
+
+ return count;
+ }
+ return ret;
+}
+
+/* various show routines for the controls of a MCI */
+static ssize_t amd64_nbcfg_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return sprintf(data, "%x\n", pvt->ctl_error_info.nbcfg);
+}
+
+
+static ssize_t amd64_dhar_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return sprintf(data, "%x\n", pvt->dhar);
+}
+
+
+static ssize_t amd64_dbam_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return sprintf(data, "%x\n", pvt->dbam0);
+}
+
+
+static ssize_t amd64_topmem_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return sprintf(data, "%llx\n", pvt->top_mem);
+}
+
+
+static ssize_t amd64_topmem2_show(struct mem_ctl_info *mci, char *data)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+
+ return sprintf(data, "%llx\n", pvt->top_mem2);
+}
+
+static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data)
+{
+ u64 hole_base = 0;
+ u64 hole_offset = 0;
+ u64 hole_size = 0;
+
+ amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
+
+ return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset,
+ hole_size);
+}
+
+/*
+ * update NUM_DBG_ATTRS in case you add new members
+ */
+struct mcidev_sysfs_attribute amd64_dbg_attrs[] = {
+
+ {
+ .attr = {
+ .name = "nbea_ctl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = amd64_nbea_show,
+ .store = amd64_nbea_store,
+ },
+ {
+ .attr = {
+ .name = "nbsl_ctl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = amd64_nbsl_show,
+ .store = amd64_nbsl_store,
+ },
+ {
+ .attr = {
+ .name = "nbsh_ctl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = amd64_nbsh_show,
+ .store = amd64_nbsh_store,
+ },
+ {
+ .attr = {
+ .name = "nbcfg_ctl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = amd64_nbcfg_show,
+ .store = amd64_nbcfg_store,
+ },
+ {
+ .attr = {
+ .name = "dhar",
+ .mode = (S_IRUGO)
+ },
+ .show = amd64_dhar_show,
+ .store = NULL,
+ },
+ {
+ .attr = {
+ .name = "dbam",
+ .mode = (S_IRUGO)
+ },
+ .show = amd64_dbam_show,
+ .store = NULL,
+ },
+ {
+ .attr = {
+ .name = "topmem",
+ .mode = (S_IRUGO)
+ },
+ .show = amd64_topmem_show,
+ .store = NULL,
+ },
+ {
+ .attr = {
+ .name = "topmem2",
+ .mode = (S_IRUGO)
+ },
+ .show = amd64_topmem2_show,
+ .store = NULL,
+ },
+ {
+ .attr = {
+ .name = "dram_hole",
+ .mode = (S_IRUGO)
+ },
+ .show = amd64_hole_show,
+ .store = NULL,
+ },
+};
diff --git a/drivers/edac/amd64_edac_err_types.c b/drivers/edac/amd64_edac_err_types.c
new file mode 100644
index 000000000000..f212ff12a9d8
--- /dev/null
+++ b/drivers/edac/amd64_edac_err_types.c
@@ -0,0 +1,161 @@
+#include "amd64_edac.h"
+
+/*
+ * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only
+ * for DDR2 DRAM mapping.
+ */
+u32 revf_quad_ddr2_shift[] = {
+ 0, /* 0000b NULL DIMM (128mb) */
+ 28, /* 0001b 256mb */
+ 29, /* 0010b 512mb */
+ 29, /* 0011b 512mb */
+ 29, /* 0100b 512mb */
+ 30, /* 0101b 1gb */
+ 30, /* 0110b 1gb */
+ 31, /* 0111b 2gb */
+ 31, /* 1000b 2gb */
+ 32, /* 1001b 4gb */
+ 32, /* 1010b 4gb */
+ 33, /* 1011b 8gb */
+ 0, /* 1100b future */
+ 0, /* 1101b future */
+ 0, /* 1110b future */
+ 0 /* 1111b future */
+};
+
+/*
+ * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
+ * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
+ * or higher value'.
+ *
+ *FIXME: Produce a better mapping/linearisation.
+ */
+
+struct scrubrate scrubrates[] = {
+ { 0x01, 1600000000UL},
+ { 0x02, 800000000UL},
+ { 0x03, 400000000UL},
+ { 0x04, 200000000UL},
+ { 0x05, 100000000UL},
+ { 0x06, 50000000UL},
+ { 0x07, 25000000UL},
+ { 0x08, 12284069UL},
+ { 0x09, 6274509UL},
+ { 0x0A, 3121951UL},
+ { 0x0B, 1560975UL},
+ { 0x0C, 781440UL},
+ { 0x0D, 390720UL},
+ { 0x0E, 195300UL},
+ { 0x0F, 97650UL},
+ { 0x10, 48854UL},
+ { 0x11, 24427UL},
+ { 0x12, 12213UL},
+ { 0x13, 6101UL},
+ { 0x14, 3051UL},
+ { 0x15, 1523UL},
+ { 0x16, 761UL},
+ { 0x00, 0UL}, /* scrubbing off */
+};
+
+/*
+ * string representation for the different MCA reported error types, see F3x48
+ * or MSR0000_0411.
+ */
+const char *tt_msgs[] = { /* transaction type */
+ "instruction",
+ "data",
+ "generic",
+ "reserved"
+};
+
+const char *ll_msgs[] = { /* cache level */
+ "L0",
+ "L1",
+ "L2",
+ "L3/generic"
+};
+
+const char *rrrr_msgs[] = {
+ "generic",
+ "generic read",
+ "generic write",
+ "data read",
+ "data write",
+ "inst fetch",
+ "prefetch",
+ "evict",
+ "snoop",
+ "reserved RRRR= 9",
+ "reserved RRRR= 10",
+ "reserved RRRR= 11",
+ "reserved RRRR= 12",
+ "reserved RRRR= 13",
+ "reserved RRRR= 14",
+ "reserved RRRR= 15"
+};
+
+const char *pp_msgs[] = { /* participating processor */
+ "local node originated (SRC)",
+ "local node responded to request (RES)",
+ "local node observed as 3rd party (OBS)",
+ "generic"
+};
+
+const char *to_msgs[] = {
+ "no timeout",
+ "timed out"
+};
+
+const char *ii_msgs[] = { /* memory or i/o */
+ "mem access",
+ "reserved",
+ "i/o access",
+ "generic"
+};
+
+/* Map the 5 bits of Extended Error code to the string table. */
+const char *ext_msgs[] = { /* extended error */
+ "K8 ECC error/F10 reserved", /* 0_0000b */
+ "CRC error", /* 0_0001b */
+ "sync error", /* 0_0010b */
+ "mst abort", /* 0_0011b */
+ "tgt abort", /* 0_0100b */
+ "GART error", /* 0_0101b */
+ "RMW error", /* 0_0110b */
+ "Wdog timer error", /* 0_0111b */
+ "F10-ECC/K8-Chipkill error", /* 0_1000b */
+ "DEV Error", /* 0_1001b */
+ "Link Data error", /* 0_1010b */
+ "Link or L3 Protocol error", /* 0_1011b */
+ "NB Array error", /* 0_1100b */
+ "DRAM Parity error", /* 0_1101b */
+ "Link Retry/GART Table Walk/DEV Table Walk error", /* 0_1110b */
+ "Res 0x0ff error", /* 0_1111b */
+ "Res 0x100 error", /* 1_0000b */
+ "Res 0x101 error", /* 1_0001b */
+ "Res 0x102 error", /* 1_0010b */
+ "Res 0x103 error", /* 1_0011b */
+ "Res 0x104 error", /* 1_0100b */
+ "Res 0x105 error", /* 1_0101b */
+ "Res 0x106 error", /* 1_0110b */
+ "Res 0x107 error", /* 1_0111b */
+ "Res 0x108 error", /* 1_1000b */
+ "Res 0x109 error", /* 1_1001b */
+ "Res 0x10A error", /* 1_1010b */
+ "Res 0x10B error", /* 1_1011b */
+ "L3 Cache Data error", /* 1_1100b */
+ "L3 CacheTag error", /* 1_1101b */
+ "L3 Cache LRU error", /* 1_1110b */
+ "Res 0x1FF error" /* 1_1111b */
+};
+
+const char *htlink_msgs[] = {
+ "none",
+ "1",
+ "2",
+ "1 2",
+ "3",
+ "1 3",
+ "2 3",
+ "1 2 3"
+};
diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c
new file mode 100644
index 000000000000..d3675b76b3a7
--- /dev/null
+++ b/drivers/edac/amd64_edac_inj.c
@@ -0,0 +1,185 @@
+#include "amd64_edac.h"
+
+/*
+ * store error injection section value which refers to one of 4 16-byte sections
+ * within a 64-byte cacheline
+ *
+ * range: 0..3
+ */
+static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 10, &value);
+ if (ret != -EINVAL) {
+ pvt->injection.section = (u32) value;
+ return count;
+ }
+ return ret;
+}
+
+/*
+ * store error injection word value which refers to one of 9 16-bit word of the
+ * 16-byte (128-bit + ECC bits) section
+ *
+ * range: 0..8
+ */
+static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 10, &value);
+ if (ret != -EINVAL) {
+
+ value = (value <= 8) ? value : 0;
+ pvt->injection.word = (u32) value;
+
+ return count;
+ }
+ return ret;
+}
+
+/*
+ * store 16 bit error injection vector which enables injecting errors to the
+ * corresponding bit within the error injection word above. When used during a
+ * DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
+ */
+static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 16, &value);
+ if (ret != -EINVAL) {
+
+ pvt->injection.bit_map = (u32) value & 0xFFFF;
+
+ return count;
+ }
+ return ret;
+}
+
+/*
+ * Do a DRAM ECC read. Assemble staged values in the pvt area, format into
+ * fields needed by the injection registers and read the NB Array Data Port.
+ */
+static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ u32 section, word_bits;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 10, &value);
+ if (ret != -EINVAL) {
+
+ /* Form value to choose 16-byte section of cacheline */
+ section = F10_NB_ARRAY_DRAM_ECC |
+ SET_NB_ARRAY_ADDRESS(pvt->injection.section);
+ pci_write_config_dword(pvt->misc_f3_ctl,
+ F10_NB_ARRAY_ADDR, section);
+
+ word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection.word,
+ pvt->injection.bit_map);
+
+ /* Issue 'word' and 'bit' along with the READ request */
+ pci_write_config_dword(pvt->misc_f3_ctl,
+ F10_NB_ARRAY_DATA, word_bits);
+
+ debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+
+ return count;
+ }
+ return ret;
+}
+
+/*
+ * Do a DRAM ECC write. Assemble staged values in the pvt area and format into
+ * fields needed by the injection registers.
+ */
+static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ unsigned long value;
+ u32 section, word_bits;
+ int ret = 0;
+
+ ret = strict_strtoul(data, 10, &value);
+ if (ret != -EINVAL) {
+
+ /* Form value to choose 16-byte section of cacheline */
+ section = F10_NB_ARRAY_DRAM_ECC |
+ SET_NB_ARRAY_ADDRESS(pvt->injection.section);
+ pci_write_config_dword(pvt->misc_f3_ctl,
+ F10_NB_ARRAY_ADDR, section);
+
+ word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection.word,
+ pvt->injection.bit_map);
+
+ /* Issue 'word' and 'bit' along with the READ request */
+ pci_write_config_dword(pvt->misc_f3_ctl,
+ F10_NB_ARRAY_DATA, word_bits);
+
+ debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+
+ return count;
+ }
+ return ret;
+}
+
+/*
+ * update NUM_INJ_ATTRS in case you add new members
+ */
+struct mcidev_sysfs_attribute amd64_inj_attrs[] = {
+
+ {
+ .attr = {
+ .name = "inject_section",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = NULL,
+ .store = amd64_inject_section_store,
+ },
+ {
+ .attr = {
+ .name = "inject_word",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = NULL,
+ .store = amd64_inject_word_store,
+ },
+ {
+ .attr = {
+ .name = "inject_ecc_vector",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = NULL,
+ .store = amd64_inject_ecc_vector_store,
+ },
+ {
+ .attr = {
+ .name = "inject_write",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = NULL,
+ .store = amd64_inject_write_store,
+ },
+ {
+ .attr = {
+ .name = "inject_read",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = NULL,
+ .store = amd64_inject_read_store,
+ },
+};
diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c
index 614692181120..2cb58ef743e0 100644
--- a/drivers/edac/amd8111_edac.c
+++ b/drivers/edac/amd8111_edac.c
@@ -389,7 +389,7 @@ static int amd8111_dev_probe(struct pci_dev *dev,
dev_info->edac_dev->dev = &dev_info->dev->dev;
dev_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
dev_info->edac_dev->ctl_name = dev_info->ctl_name;
- dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev);
if (edac_op_state == EDAC_OPSTATE_POLL)
dev_info->edac_dev->edac_check = dev_info->check;
@@ -473,7 +473,7 @@ static int amd8111_pci_probe(struct pci_dev *dev,
pci_info->edac_dev->dev = &pci_info->dev->dev;
pci_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
pci_info->edac_dev->ctl_name = pci_info->ctl_name;
- pci_info->edac_dev->dev_name = pci_info->dev->dev.bus_id;
+ pci_info->edac_dev->dev_name = dev_name(&pci_info->dev->dev);
if (edac_op_state == EDAC_OPSTATE_POLL)
pci_info->edac_dev->edac_check = pci_info->check;
diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c
index c083b31cac5a..b432d60c622a 100644
--- a/drivers/edac/amd8131_edac.c
+++ b/drivers/edac/amd8131_edac.c
@@ -287,7 +287,7 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_info->edac_dev->dev = &dev_info->dev->dev;
dev_info->edac_dev->mod_name = AMD8131_EDAC_MOD_STR;
dev_info->edac_dev->ctl_name = dev_info->ctl_name;
- dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev);
if (edac_op_state == EDAC_OPSTATE_POLL)
dev_info->edac_dev->edac_check = amd8131_chipset.check;
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 6ad95c8d6363..48d3b1409834 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -76,10 +76,11 @@
extern int edac_debug_level;
#ifndef CONFIG_EDAC_DEBUG_VERBOSE
-#define edac_debug_printk(level, fmt, arg...) \
- do { \
- if (level <= edac_debug_level) \
- edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \
+#define edac_debug_printk(level, fmt, arg...) \
+ do { \
+ if (level <= edac_debug_level) \
+ edac_printk(KERN_DEBUG, EDAC_DEBUG, \
+ "%s: " fmt, __func__, ##arg); \
} while (0)
#else /* CONFIG_EDAC_DEBUG_VERBOSE */
#define edac_debug_printk(level, fmt, arg...) \
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 5f1b5400d96a..24c84ae81527 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -596,6 +596,7 @@ int dmi_get_year(int field)
return year;
}
+EXPORT_SYMBOL(dmi_get_year);
/**
* dmi_walk - Walk the DMI table and get called back for every record
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 7b7ddc2d51c9..420a96e7f2db 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -754,11 +754,11 @@ static int __init ibft_check_nic_for(struct ibft_nic *nic, int entry)
rc = 1;
break;
case ibft_eth_ip_addr:
- if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
+ if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr)))
rc = 1;
break;
case ibft_eth_subnet_mask:
- if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
+ if (nic->subnet_mask_prefix)
rc = 1;
break;
case ibft_eth_origin:
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index edb02530e461..11f373971fa5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -69,7 +69,7 @@ comment "Memory mapped GPIO expanders:"
config GPIO_XILINX
bool "Xilinx GPIO support"
- depends on PPC_OF
+ depends on PPC_OF || MICROBLAZE
help
Say yes here to support the Xilinx FPGA GPIO device
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 3a22eb9be378..f5d46e7199d4 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -67,11 +67,18 @@ config DRM_I830
will load the correct one.
config DRM_I915
+ tristate "i915 driver"
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FB
- tristate "i915 driver"
+ select FRAMEBUFFER_CONSOLE if !EMBEDDED
+ # i915 depends on ACPI_VIDEO when ACPI is enabled
+ # but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select VIDEO_OUTPUT_CONTROL if ACPI
+ select BACKLIGHT_CLASS_DEVICE if ACPI
+ select INPUT if ACPI
+ select ACPI_VIDEO if ACPI
help
Choose this option if you have a system that has Intel 830M, 845G,
852GM, 855GM 865G or 915G integrated graphics. If M is selected, the
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 6d80d17f1e96..80a257554b30 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -170,6 +170,14 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
}
DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
(unsigned long long)map->offset, map->size, map->type);
+
+ /* page-align _DRM_SHM maps. They are allocated here so there is no security
+ * hole created by that and it works around various broken drivers that use
+ * a non-aligned quantity to map the SAREA. --BenH
+ */
+ if (map->type == _DRM_SHM)
+ map->size = PAGE_ALIGN(map->size);
+
if ((map->offset & (~(resource_size_t)PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EINVAL;
@@ -363,7 +371,8 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
list->user_token = list->hash.key << PAGE_SHIFT;
mutex_unlock(&dev->struct_mutex);
- list->master = dev->primary->master;
+ if (!(map->flags & _DRM_DRIVER))
+ list->master = dev->primary->master;
*maplist = list;
return 0;
}
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 94a768871734..8fab7890a363 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2294,7 +2294,12 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
}
}
- if (connector->funcs->set_property)
+ /* Do DPMS ourselves */
+ if (property == connector->dev->mode_config.dpms_property) {
+ if (connector->funcs->dpms)
+ (*connector->funcs->dpms)(connector, (int) out_resp->value);
+ ret = 0;
+ } else if (connector->funcs->set_property)
ret = connector->funcs->set_property(connector, property, out_resp->value);
/* store the property value if succesful */
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index a04639dc633d..a6f73f1e99d9 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -199,6 +199,29 @@ static void drm_helper_add_std_modes(struct drm_device *dev,
}
/**
+ * drm_helper_encoder_in_use - check if a given encoder is in use
+ * @encoder: encoder to check
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Walk @encoders's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @encoder is part of the mode_config, false otherwise.
+ */
+bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
+{
+ struct drm_connector *connector;
+ struct drm_device *dev = encoder->dev;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder == encoder)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_helper_encoder_in_use);
+
+/**
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
* @crtc: CRTC to check
*
@@ -216,7 +239,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
/* FIXME: Locking around list access? */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
- if (encoder->crtc == crtc)
+ if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
return true;
return false;
}
@@ -240,7 +263,7 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
encoder_funcs = encoder->helper_private;
- if (!encoder->crtc)
+ if (!drm_helper_encoder_in_use(encoder))
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
}
@@ -561,7 +584,6 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
int saved_x, saved_y;
struct drm_encoder *encoder;
bool ret = true;
- bool depth_changed, bpp_changed;
adjusted_mode = drm_mode_duplicate(dev, mode);
@@ -570,15 +592,6 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
if (!crtc->enabled)
return true;
- if (old_fb && crtc->fb) {
- depth_changed = (old_fb->depth != crtc->fb->depth);
- bpp_changed = (old_fb->bits_per_pixel !=
- crtc->fb->bits_per_pixel);
- } else {
- depth_changed = true;
- bpp_changed = true;
- }
-
saved_mode = crtc->mode;
saved_x = crtc->x;
saved_y = crtc->y;
@@ -590,15 +603,6 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
crtc->x = x;
crtc->y = y;
- if (drm_mode_equal(&saved_mode, &crtc->mode)) {
- if (saved_x != crtc->x || saved_y != crtc->y ||
- depth_changed || bpp_changed) {
- ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
- old_fb);
- goto done;
- }
- }
-
/* Pass our mode to the connectors and the CRTC to give them a chance to
* adjust it according to limitations or connector properties, and also
* a chance to reject the mode entirely.
@@ -954,6 +958,88 @@ bool drm_helper_initial_config(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_helper_initial_config);
+static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
+{
+ int dpms = DRM_MODE_DPMS_OFF;
+ struct drm_connector *connector;
+ struct drm_device *dev = encoder->dev;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder == encoder)
+ if (connector->dpms < dpms)
+ dpms = connector->dpms;
+ return dpms;
+}
+
+static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
+{
+ int dpms = DRM_MODE_DPMS_OFF;
+ struct drm_connector *connector;
+ struct drm_device *dev = crtc->dev;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder && connector->encoder->crtc == crtc)
+ if (connector->dpms < dpms)
+ dpms = connector->dpms;
+ return dpms;
+}
+
+/**
+ * drm_helper_connector_dpms
+ * @connector affected connector
+ * @mode DPMS mode
+ *
+ * Calls the low-level connector DPMS function, then
+ * calls appropriate encoder and crtc DPMS functions as well
+ */
+void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
+{
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
+ int old_dpms;
+
+ if (mode == connector->dpms)
+ return;
+
+ old_dpms = connector->dpms;
+ connector->dpms = mode;
+
+ /* from off to on, do crtc then encoder */
+ if (mode < old_dpms) {
+ if (crtc) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ if (crtc_funcs->dpms)
+ (*crtc_funcs->dpms) (crtc,
+ drm_helper_choose_crtc_dpms(crtc));
+ }
+ if (encoder) {
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ if (encoder_funcs->dpms)
+ (*encoder_funcs->dpms) (encoder,
+ drm_helper_choose_encoder_dpms(encoder));
+ }
+ }
+
+ /* from on to off, do encoder then crtc */
+ if (mode > old_dpms) {
+ if (encoder) {
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ if (encoder_funcs->dpms)
+ (*encoder_funcs->dpms) (encoder,
+ drm_helper_choose_encoder_dpms(encoder));
+ }
+ if (crtc) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ if (crtc_funcs->dpms)
+ (*crtc_funcs->dpms) (crtc,
+ drm_helper_choose_crtc_dpms(crtc));
+ }
+ }
+
+ return;
+}
+EXPORT_SYMBOL(drm_helper_connector_dpms);
+
/**
* drm_hotplug_stage_two
* @dev DRM device
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c4ada8b6295b..019b7c578236 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -456,7 +456,8 @@ int drm_ioctl(struct inode *inode, struct file *filp,
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
- ((ioctl->flags & DRM_MASTER) && !file_priv->is_master)) {
+ ((ioctl->flags & DRM_MASTER) && !file_priv->is_master) ||
+ (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL))) {
retcode = -EACCES;
} else {
if (cmd & (IOC_IN | IOC_OUT)) {
@@ -480,7 +481,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
}
retcode = func(dev, kdata, file_priv);
- if ((retcode == 0) && (cmd & IOC_OUT)) {
+ if (cmd & IOC_OUT) {
if (copy_to_user((void __user *)arg, kdata,
_IOC_SIZE(cmd)) != 0)
retcode = -EFAULT;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index ca9c61656714..801a0d0e0810 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -289,6 +289,11 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
struct drm_display_mode *mode;
struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+ /* ignore tiny modes */
+ if (((pt->hactive_hi << 8) | pt->hactive_lo) < 64 ||
+ ((pt->vactive_hi << 8) | pt->hactive_lo) < 64)
+ return NULL;
+
if (pt->stereo) {
printk(KERN_WARNING "stereo mode not supported\n");
return NULL;
@@ -584,85 +589,13 @@ int drm_do_probe_ddc_edid(struct i2c_adapter *adapter,
}
EXPORT_SYMBOL(drm_do_probe_ddc_edid);
-/**
- * Get EDID information.
- *
- * \param adapter : i2c device adaptor.
- * \param buf : EDID data buffer to be filled
- * \param len : EDID data buffer length
- * \return 0 on success or -1 on failure.
- *
- * Initialize DDC, then fetch EDID information
- * by calling drm_do_probe_ddc_edid function.
- */
-static int drm_ddc_read(struct i2c_adapter *adapter,
- unsigned char *buf, int len)
-{
- struct i2c_algo_bit_data *algo_data = adapter->algo_data;
- int i, j;
- int ret = -1;
-
- algo_data->setscl(algo_data->data, 1);
-
- for (i = 0; i < 1; i++) {
- /* For some old monitors we need the
- * following process to initialize/stop DDC
- */
- algo_data->setsda(algo_data->data, 1);
- msleep(13);
-
- algo_data->setscl(algo_data->data, 1);
- for (j = 0; j < 5; j++) {
- msleep(10);
- if (algo_data->getscl(algo_data->data))
- break;
- }
- if (j == 5)
- continue;
-
- algo_data->setsda(algo_data->data, 0);
- msleep(15);
- algo_data->setscl(algo_data->data, 0);
- msleep(15);
- algo_data->setsda(algo_data->data, 1);
- msleep(15);
-
- /* Do the real work */
- ret = drm_do_probe_ddc_edid(adapter, buf, len);
- algo_data->setsda(algo_data->data, 0);
- algo_data->setscl(algo_data->data, 0);
- msleep(15);
-
- algo_data->setscl(algo_data->data, 1);
- for (j = 0; j < 10; j++) {
- msleep(10);
- if (algo_data->getscl(algo_data->data))
- break;
- }
-
- algo_data->setsda(algo_data->data, 1);
- msleep(15);
- algo_data->setscl(algo_data->data, 0);
- algo_data->setsda(algo_data->data, 0);
- if (ret == 0)
- break;
- }
- /* Release the DDC lines when done or the Apple Cinema HD display
- * will switch off
- */
- algo_data->setsda(algo_data->data, 1);
- algo_data->setscl(algo_data->data, 1);
-
- return ret;
-}
-
static int drm_ddc_read_edid(struct drm_connector *connector,
struct i2c_adapter *adapter,
char *buf, int len)
{
int ret;
- ret = drm_ddc_read(adapter, buf, len);
+ ret = drm_do_probe_ddc_edid(adapter, buf, len);
if (ret != 0) {
dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n",
drm_get_connector_name(connector));
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4984aa89cf3d..ec43005100d9 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -133,7 +133,7 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
BUG_ON((size & (PAGE_SIZE - 1)) != 0);
- obj = kcalloc(1, sizeof(*obj), GFP_KERNEL);
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
obj->dev = dev;
obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
index af539f7d87dd..ac35145c3e20 100644
--- a/drivers/gpu/drm/drm_hashtab.c
+++ b/drivers/gpu/drm/drm_hashtab.c
@@ -62,6 +62,7 @@ int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
}
return 0;
}
+EXPORT_SYMBOL(drm_ht_create);
void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
{
@@ -156,6 +157,7 @@ int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *it
}
return 0;
}
+EXPORT_SYMBOL(drm_ht_just_insert_please);
int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
struct drm_hash_item **item)
@@ -169,6 +171,7 @@ int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
*item = hlist_entry(list, struct drm_hash_item, head);
return 0;
}
+EXPORT_SYMBOL(drm_ht_find_item);
int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
{
@@ -202,3 +205,4 @@ void drm_ht_remove(struct drm_open_hash *ht)
ht->table = NULL;
}
}
+EXPORT_SYMBOL(drm_ht_remove);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 93e677a481f5..fc8e5acd9d9a 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -196,6 +196,7 @@ int drm_irq_install(struct drm_device *dev)
{
int ret = 0;
unsigned long sh_flags = 0;
+ char *irqname;
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return -EINVAL;
@@ -227,8 +228,13 @@ int drm_irq_install(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
sh_flags = IRQF_SHARED;
+ if (dev->devname)
+ irqname = dev->devname;
+ else
+ irqname = dev->driver->name;
+
ret = request_irq(drm_dev_to_irq(dev), dev->driver->irq_handler,
- sh_flags, dev->devname, dev);
+ sh_flags, irqname, dev);
if (ret < 0) {
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 367c590ffbba..7819fd930a51 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -42,8 +42,11 @@
*/
#include "drmP.h"
+#include "drm_mm.h"
#include <linux/slab.h>
+#define MM_UNUSED_TARGET 4
+
unsigned long drm_mm_tail_space(struct drm_mm *mm)
{
struct list_head *tail_node;
@@ -74,16 +77,62 @@ int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size)
return 0;
}
+static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
+{
+ struct drm_mm_node *child;
+
+ if (atomic)
+ child = kmalloc(sizeof(*child), GFP_ATOMIC);
+ else
+ child = kmalloc(sizeof(*child), GFP_KERNEL);
+
+ if (unlikely(child == NULL)) {
+ spin_lock(&mm->unused_lock);
+ if (list_empty(&mm->unused_nodes))
+ child = NULL;
+ else {
+ child =
+ list_entry(mm->unused_nodes.next,
+ struct drm_mm_node, fl_entry);
+ list_del(&child->fl_entry);
+ --mm->num_unused;
+ }
+ spin_unlock(&mm->unused_lock);
+ }
+ return child;
+}
+
+int drm_mm_pre_get(struct drm_mm *mm)
+{
+ struct drm_mm_node *node;
+
+ spin_lock(&mm->unused_lock);
+ while (mm->num_unused < MM_UNUSED_TARGET) {
+ spin_unlock(&mm->unused_lock);
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ spin_lock(&mm->unused_lock);
+
+ if (unlikely(node == NULL)) {
+ int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
+ spin_unlock(&mm->unused_lock);
+ return ret;
+ }
+ ++mm->num_unused;
+ list_add_tail(&node->fl_entry, &mm->unused_nodes);
+ }
+ spin_unlock(&mm->unused_lock);
+ return 0;
+}
+EXPORT_SYMBOL(drm_mm_pre_get);
static int drm_mm_create_tail_node(struct drm_mm *mm,
- unsigned long start,
- unsigned long size)
+ unsigned long start,
+ unsigned long size, int atomic)
{
struct drm_mm_node *child;
- child = (struct drm_mm_node *)
- drm_alloc(sizeof(*child), DRM_MEM_MM);
- if (!child)
+ child = drm_mm_kmalloc(mm, atomic);
+ if (unlikely(child == NULL))
return -ENOMEM;
child->free = 1;
@@ -97,8 +146,7 @@ static int drm_mm_create_tail_node(struct drm_mm *mm,
return 0;
}
-
-int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size)
+int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size, int atomic)
{
struct list_head *tail_node;
struct drm_mm_node *entry;
@@ -106,20 +154,21 @@ int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size)
tail_node = mm->ml_entry.prev;
entry = list_entry(tail_node, struct drm_mm_node, ml_entry);
if (!entry->free) {
- return drm_mm_create_tail_node(mm, entry->start + entry->size, size);
+ return drm_mm_create_tail_node(mm, entry->start + entry->size,
+ size, atomic);
}
entry->size += size;
return 0;
}
static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent,
- unsigned long size)
+ unsigned long size,
+ int atomic)
{
struct drm_mm_node *child;
- child = (struct drm_mm_node *)
- drm_alloc(sizeof(*child), DRM_MEM_MM);
- if (!child)
+ child = drm_mm_kmalloc(parent->mm, atomic);
+ if (unlikely(child == NULL))
return NULL;
INIT_LIST_HEAD(&child->fl_entry);
@@ -151,8 +200,9 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
tmp = parent->start % alignment;
if (tmp) {
- align_splitoff = drm_mm_split_at_start(parent, alignment - tmp);
- if (!align_splitoff)
+ align_splitoff =
+ drm_mm_split_at_start(parent, alignment - tmp, 0);
+ if (unlikely(align_splitoff == NULL))
return NULL;
}
@@ -161,7 +211,7 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
parent->free = 0;
return parent;
} else {
- child = drm_mm_split_at_start(parent, size);
+ child = drm_mm_split_at_start(parent, size, 0);
}
if (align_splitoff)
@@ -169,14 +219,49 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
return child;
}
+
EXPORT_SYMBOL(drm_mm_get_block);
+struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
+ unsigned long size,
+ unsigned alignment)
+{
+
+ struct drm_mm_node *align_splitoff = NULL;
+ struct drm_mm_node *child;
+ unsigned tmp = 0;
+
+ if (alignment)
+ tmp = parent->start % alignment;
+
+ if (tmp) {
+ align_splitoff =
+ drm_mm_split_at_start(parent, alignment - tmp, 1);
+ if (unlikely(align_splitoff == NULL))
+ return NULL;
+ }
+
+ if (parent->size == size) {
+ list_del_init(&parent->fl_entry);
+ parent->free = 0;
+ return parent;
+ } else {
+ child = drm_mm_split_at_start(parent, size, 1);
+ }
+
+ if (align_splitoff)
+ drm_mm_put_block(align_splitoff);
+
+ return child;
+}
+EXPORT_SYMBOL(drm_mm_get_block_atomic);
+
/*
* Put a block. Merge with the previous and / or next block if they are free.
* Otherwise add to the free stack.
*/
-void drm_mm_put_block(struct drm_mm_node * cur)
+void drm_mm_put_block(struct drm_mm_node *cur)
{
struct drm_mm *mm = cur->mm;
@@ -188,21 +273,27 @@ void drm_mm_put_block(struct drm_mm_node * cur)
int merged = 0;
if (cur_head->prev != root_head) {
- prev_node = list_entry(cur_head->prev, struct drm_mm_node, ml_entry);
+ prev_node =
+ list_entry(cur_head->prev, struct drm_mm_node, ml_entry);
if (prev_node->free) {
prev_node->size += cur->size;
merged = 1;
}
}
if (cur_head->next != root_head) {
- next_node = list_entry(cur_head->next, struct drm_mm_node, ml_entry);
+ next_node =
+ list_entry(cur_head->next, struct drm_mm_node, ml_entry);
if (next_node->free) {
if (merged) {
prev_node->size += next_node->size;
list_del(&next_node->ml_entry);
list_del(&next_node->fl_entry);
- drm_free(next_node, sizeof(*next_node),
- DRM_MEM_MM);
+ if (mm->num_unused < MM_UNUSED_TARGET) {
+ list_add(&next_node->fl_entry,
+ &mm->unused_nodes);
+ ++mm->num_unused;
+ } else
+ kfree(next_node);
} else {
next_node->size += cur->size;
next_node->start = cur->start;
@@ -215,14 +306,19 @@ void drm_mm_put_block(struct drm_mm_node * cur)
list_add(&cur->fl_entry, &mm->fl_entry);
} else {
list_del(&cur->ml_entry);
- drm_free(cur, sizeof(*cur), DRM_MEM_MM);
+ if (mm->num_unused < MM_UNUSED_TARGET) {
+ list_add(&cur->fl_entry, &mm->unused_nodes);
+ ++mm->num_unused;
+ } else
+ kfree(cur);
}
}
+
EXPORT_SYMBOL(drm_mm_put_block);
-struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
- unsigned long size,
- unsigned alignment, int best_match)
+struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+ unsigned long size,
+ unsigned alignment, int best_match)
{
struct list_head *list;
const struct list_head *free_stack = &mm->fl_entry;
@@ -247,7 +343,6 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
wasted += alignment - tmp;
}
-
if (entry->size >= size + wasted) {
if (!best_match)
return entry;
@@ -260,6 +355,7 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
return best;
}
+EXPORT_SYMBOL(drm_mm_search_free);
int drm_mm_clean(struct drm_mm * mm)
{
@@ -267,14 +363,17 @@ int drm_mm_clean(struct drm_mm * mm)
return (head->next->next == head);
}
-EXPORT_SYMBOL(drm_mm_search_free);
+EXPORT_SYMBOL(drm_mm_clean);
int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
{
INIT_LIST_HEAD(&mm->ml_entry);
INIT_LIST_HEAD(&mm->fl_entry);
+ INIT_LIST_HEAD(&mm->unused_nodes);
+ mm->num_unused = 0;
+ spin_lock_init(&mm->unused_lock);
- return drm_mm_create_tail_node(mm, start, size);
+ return drm_mm_create_tail_node(mm, start, size, 0);
}
EXPORT_SYMBOL(drm_mm_init);
@@ -282,6 +381,7 @@ void drm_mm_takedown(struct drm_mm * mm)
{
struct list_head *bnode = mm->fl_entry.next;
struct drm_mm_node *entry;
+ struct drm_mm_node *next;
entry = list_entry(bnode, struct drm_mm_node, fl_entry);
@@ -293,7 +393,16 @@ void drm_mm_takedown(struct drm_mm * mm)
list_del(&entry->fl_entry);
list_del(&entry->ml_entry);
+ kfree(entry);
+
+ spin_lock(&mm->unused_lock);
+ list_for_each_entry_safe(entry, next, &mm->unused_nodes, fl_entry) {
+ list_del(&entry->fl_entry);
+ kfree(entry);
+ --mm->num_unused;
+ }
+ spin_unlock(&mm->unused_lock);
- drm_free(entry, sizeof(*entry), DRM_MEM_MM);
+ BUG_ON(mm->num_unused != 0);
}
EXPORT_SYMBOL(drm_mm_takedown);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index c9b80fdd4630..54f492a488a9 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -38,6 +38,7 @@
#include "drm.h"
#include "drm_crtc.h"
+#define DRM_MODESET_DEBUG "drm_mode"
/**
* drm_mode_debug_printmodeline - debug print a mode
* @dev: DRM device
@@ -50,12 +51,13 @@
*/
void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
{
- DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
- mode->base.id, mode->name, mode->vrefresh, mode->clock,
- mode->hdisplay, mode->hsync_start,
- mode->hsync_end, mode->htotal,
- mode->vdisplay, mode->vsync_start,
- mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+ DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
+ "Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
}
EXPORT_SYMBOL(drm_mode_debug_printmodeline);
@@ -401,7 +403,9 @@ void drm_mode_prune_invalid(struct drm_device *dev,
list_del(&mode->head);
if (verbose) {
drm_mode_debug_printmodeline(mode);
- DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+ DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
+ "Not using %s mode %d\n",
+ mode->name, mode->status);
}
drm_mode_destroy(dev, mode);
}
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index ef878615c49f..89050684fe0d 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -51,7 +51,22 @@ struct idr drm_minors_idr;
struct class *drm_class;
struct proc_dir_entry *drm_proc_root;
struct dentry *drm_debugfs_root;
-
+void drm_ut_debug_printk(unsigned int request_level,
+ const char *prefix,
+ const char *function_name,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (drm_debug & request_level) {
+ if (function_name)
+ printk(KERN_DEBUG "[%s:%s], ", prefix, function_name);
+ va_start(args, format);
+ vprintk(format, args);
+ va_end(args);
+ }
+}
+EXPORT_SYMBOL(drm_ut_debug_printk);
static int drm_minor_get_id(struct drm_device *dev, int type)
{
int new_id;
@@ -402,14 +417,14 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
if (dev->driver->load) {
ret = dev->driver->load(dev, ent->driver_data);
if (ret)
- goto err_g3;
+ goto err_g4;
}
/* setup the grouping for the legacy output */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
if (ret)
- goto err_g3;
+ goto err_g4;
}
list_add_tail(&dev->driver_item, &driver->device_list);
@@ -420,8 +435,11 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
return 0;
-err_g3:
+err_g4:
drm_put_minor(&dev->primary);
+err_g3:
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_put_minor(&dev->control);
err_g2:
pci_disable_device(pdev);
err_g1:
@@ -502,11 +520,11 @@ void drm_put_dev(struct drm_device *dev)
dev->agp = NULL;
}
- drm_ht_remove(&dev->map_hash);
- drm_ctxbitmap_cleanup(dev);
-
list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
drm_rmmap(dev, r_list->map);
+ drm_ht_remove(&dev->map_hash);
+
+ drm_ctxbitmap_cleanup(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 022876ae34f0..9987ab880835 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -147,7 +147,7 @@ static ssize_t status_show(struct device *device,
enum drm_connector_status status;
status = connector->funcs->detect(connector);
- return snprintf(buf, PAGE_SIZE, "%s",
+ return snprintf(buf, PAGE_SIZE, "%s\n",
drm_get_connector_status_name(status));
}
@@ -166,7 +166,7 @@ static ssize_t dpms_show(struct device *device,
if (ret)
return 0;
- return snprintf(buf, PAGE_SIZE, "%s",
+ return snprintf(buf, PAGE_SIZE, "%s\n",
drm_get_dpms_name((int)dpms_status));
}
@@ -176,7 +176,7 @@ static ssize_t enabled_show(struct device *device,
{
struct drm_connector *connector = to_drm_connector(device);
- return snprintf(buf, PAGE_SIZE, connector->encoder ? "enabled" :
+ return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" :
"disabled");
}
@@ -317,6 +317,7 @@ static struct device_attribute connector_attrs_opt1[] = {
static struct bin_attribute edid_attr = {
.attr.name = "edid",
+ .attr.mode = 0444,
.size = 128,
.read = edid_show,
};
@@ -489,9 +490,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)
return 0;
- device_unregister(&minor->kdev);
err_out:
-
return err;
}
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index a000cf028826..1a60626f6803 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -33,6 +33,8 @@
#include "i915_drm.h"
#include "i915_drv.h"
+#define I915_DRV "i915_drv"
+
/* 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
@@ -99,7 +101,7 @@ static int i915_init_phys_hws(struct drm_device *dev)
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
- DRM_DEBUG("Enabled hardware status page\n");
+ DRM_DEBUG_DRIVER(I915_DRV, "Enabled hardware status page\n");
return 0;
}
@@ -185,7 +187,8 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
master_priv->sarea_priv = (drm_i915_sarea_t *)
((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
} else {
- DRM_DEBUG("sarea not found assuming DRI2 userspace\n");
+ DRM_DEBUG_DRIVER(I915_DRV,
+ "sarea not found assuming DRI2 userspace\n");
}
if (init->ring_size != 0) {
@@ -235,7 +238,7 @@ static int i915_dma_resume(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- DRM_DEBUG("%s\n", __func__);
+ DRM_DEBUG_DRIVER(I915_DRV, "%s\n", __func__);
if (dev_priv->ring.map.handle == NULL) {
DRM_ERROR("can not ioremap virtual address for"
@@ -248,13 +251,14 @@ static int i915_dma_resume(struct drm_device * dev)
DRM_ERROR("Can not find hardware status page\n");
return -EINVAL;
}
- DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
+ DRM_DEBUG_DRIVER(I915_DRV, "hw status page @ %p\n",
+ dev_priv->hw_status_page);
if (dev_priv->status_gfx_addr != 0)
I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
else
I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
- DRM_DEBUG("Enabled hardware status page\n");
+ DRM_DEBUG_DRIVER(I915_DRV, "Enabled hardware status page\n");
return 0;
}
@@ -548,10 +552,10 @@ static int i915_dispatch_flip(struct drm_device * dev)
if (!master_priv->sarea_priv)
return -EINVAL;
- DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
- __func__,
- dev_priv->current_page,
- master_priv->sarea_priv->pf_current_page);
+ DRM_DEBUG_DRIVER(I915_DRV, "%s: page=%d pfCurrentPage=%d\n",
+ __func__,
+ dev_priv->current_page,
+ master_priv->sarea_priv->pf_current_page);
i915_kernel_lost_context(dev);
@@ -629,8 +633,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
return -EINVAL;
}
- DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
- batch->start, batch->used, batch->num_cliprects);
+ DRM_DEBUG_DRIVER(I915_DRV,
+ "i915 batchbuffer, start %x used %d cliprects %d\n",
+ batch->start, batch->used, batch->num_cliprects);
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -678,8 +683,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
void *batch_data;
int ret;
- DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
- cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
+ DRM_DEBUG_DRIVER(I915_DRV,
+ "i915 cmdbuffer, buf %p sz %d cliprects %d\n",
+ cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -713,18 +719,18 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex);
if (ret) {
DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
- goto fail_batch_free;
+ goto fail_clip_free;
}
if (sarea_priv)
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
-fail_batch_free:
- drm_free(batch_data, cmdbuf->sz, DRM_MEM_DRIVER);
fail_clip_free:
drm_free(cliprects,
cmdbuf->num_cliprects * sizeof(struct drm_clip_rect),
DRM_MEM_DRIVER);
+fail_batch_free:
+ drm_free(batch_data, cmdbuf->sz, DRM_MEM_DRIVER);
return ret;
}
@@ -734,7 +740,7 @@ static int i915_flip_bufs(struct drm_device *dev, void *data,
{
int ret;
- DRM_DEBUG("%s\n", __func__);
+ DRM_DEBUG_DRIVER(I915_DRV, "%s\n", __func__);
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -777,7 +783,8 @@ static int i915_getparam(struct drm_device *dev, void *data,
value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
break;
default:
- DRM_DEBUG("Unknown parameter %d\n", param->param);
+ DRM_DEBUG_DRIVER(I915_DRV, "Unknown parameter %d\n",
+ param->param);
return -EINVAL;
}
@@ -817,7 +824,8 @@ static int i915_setparam(struct drm_device *dev, void *data,
dev_priv->fence_reg_start = param->value;
break;
default:
- DRM_DEBUG("unknown parameter %d\n", param->param);
+ DRM_DEBUG_DRIVER(I915_DRV, "unknown parameter %d\n",
+ param->param);
return -EINVAL;
}
@@ -865,9 +873,10 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
- DRM_DEBUG("load hws HWS_PGA with gfx mem 0x%x\n",
- dev_priv->status_gfx_addr);
- DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page);
+ DRM_DEBUG_DRIVER(I915_DRV, "load hws HWS_PGA with gfx mem 0x%x\n",
+ dev_priv->status_gfx_addr);
+ DRM_DEBUG_DRIVER(I915_DRV, "load hws at %p\n",
+ dev_priv->hw_status_page);
return 0;
}
@@ -922,7 +931,7 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
* Some of the preallocated space is taken by the GTT
* and popup. GTT is 1K per MB of aperture size, and popup is 4K.
*/
- if (IS_G4X(dev) || IS_IGD(dev))
+ if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
overhead = 4096;
else
overhead = (*aperture_size / 1024) + 4096;
@@ -987,12 +996,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
int fb_bar = IS_I9XX(dev) ? 2 : 0;
int ret = 0;
- dev->devname = kstrdup(DRIVER_NAME, GFP_KERNEL);
- if (!dev->devname) {
- ret = -ENOMEM;
- goto out;
- }
-
dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
0xff000000;
@@ -1006,17 +1009,25 @@ static int i915_load_modeset_init(struct drm_device *dev)
ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
if (ret)
- goto kfree_devname;
+ goto out;
/* Basic memrange allocator for stolen space (aka vram) */
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
- /* Let GEM Manage from end of prealloc space to end of aperture */
- i915_gem_do_init(dev, prealloc_size, agp_size);
+ /* Let GEM Manage from end of prealloc space to end of aperture.
+ *
+ * However, leave one page at the end still bound to the scratch page.
+ * There are a number of places where the hardware apparently
+ * prefetches past the end of the object, and we've seen multiple
+ * hangs with the GPU head pointer stuck in a batchbuffer bound
+ * at the last page of the aperture. One page should be enough to
+ * keep any prefetching inside of the aperture.
+ */
+ i915_gem_do_init(dev, prealloc_size, agp_size - 4096);
ret = i915_gem_init_ringbuffer(dev);
if (ret)
- goto kfree_devname;
+ goto out;
/* Allow hardware batchbuffers unless told otherwise.
*/
@@ -1048,8 +1059,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
destroy_ringbuffer:
i915_gem_cleanup_ringbuffer(dev);
-kfree_devname:
- kfree(dev->devname);
out:
return ret;
}
@@ -1153,8 +1162,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
#endif
dev->driver->get_vblank_counter = i915_get_vblank_counter;
- if (IS_GM45(dev))
+ dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+ if (IS_G4X(dev) || IS_IGDNG(dev)) {
+ dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
+ }
i915_gem_load(dev);
@@ -1198,7 +1210,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
}
/* Must be done after probing outputs */
- intel_opregion_init(dev, 0);
+ /* FIXME: verify on IGDNG */
+ if (!IS_IGDNG(dev))
+ intel_opregion_init(dev, 0);
return 0;
@@ -1232,7 +1246,8 @@ int i915_driver_unload(struct drm_device *dev)
if (dev_priv->regs != NULL)
iounmap(dev_priv->regs);
- intel_opregion_free(dev);
+ if (!IS_IGDNG(dev))
+ intel_opregion_free(dev, 0);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev);
@@ -1256,7 +1271,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
{
struct drm_i915_file_private *i915_file_priv;
- DRM_DEBUG("\n");
+ DRM_DEBUG_DRIVER(I915_DRV, "\n");
i915_file_priv = (struct drm_i915_file_private *)
drm_alloc(sizeof(*i915_file_priv), DRM_MEM_FILES);
@@ -1265,8 +1280,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
file_priv->driver_priv = i915_file_priv;
- i915_file_priv->mm.last_gem_seqno = 0;
- i915_file_priv->mm.last_gem_throttle_seqno = 0;
+ INIT_LIST_HEAD(&i915_file_priv->mm.request_list);
return 0;
}
@@ -1303,6 +1317,7 @@ void i915_driver_lastclose(struct drm_device * dev)
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ i915_gem_release(dev, file_priv);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
i915_mem_release(dev, file_priv, dev_priv->agp_heap);
}
@@ -1350,6 +1365,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
};
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 6503e2210f65..98560e1e899a 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -77,7 +77,7 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
drm_irq_uninstall(dev);
}
- intel_opregion_free(dev);
+ intel_opregion_free(dev, 1);
if (state.event == PM_EVENT_SUSPEND) {
/* Shut down the device */
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 473a8f7fbdb5..8ef6bcec211b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -126,6 +126,13 @@ struct drm_i915_fence_reg {
struct drm_gem_object *obj;
};
+struct sdvo_device_mapping {
+ u8 dvo_port;
+ u8 slave_addr;
+ u8 dvo_wiring;
+ u8 initialized;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
@@ -143,6 +150,8 @@ typedef struct drm_i915_private {
drm_local_map_t hws_map;
struct drm_gem_object *hws_obj;
+ struct resource mch_res;
+
unsigned int cpp;
int back_offset;
int front_offset;
@@ -158,6 +167,11 @@ typedef struct drm_i915_private {
/** Cached value of IMR to avoid reads in updating the bitfield */
u32 irq_mask_reg;
u32 pipestat[2];
+ /** splitted irq regs for graphics and display engine on IGDNG,
+ irq_mask_reg is still used for display irq. */
+ u32 gt_irq_mask_reg;
+ u32 gt_irq_enable_reg;
+ u32 de_irq_enable_reg;
u32 hotplug_supported_mask;
struct work_struct hotplug_work;
@@ -180,7 +194,8 @@ typedef struct drm_i915_private {
int backlight_duty_cycle; /* restore backlight to this value */
bool panel_wants_dither;
struct drm_display_mode *panel_fixed_mode;
- struct drm_display_mode *vbt_mode; /* if any */
+ struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
+ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
/* Feature bits from the VBIOS */
unsigned int int_tv_support:1;
@@ -283,6 +298,14 @@ typedef struct drm_i915_private {
u8 saveAR[21];
u8 saveDACMASK;
u8 saveCR[37];
+ uint64_t saveFENCE[16];
+ u32 saveCURACNTR;
+ u32 saveCURAPOS;
+ u32 saveCURABASE;
+ u32 saveCURBCNTR;
+ u32 saveCURBPOS;
+ u32 saveCURBBASE;
+ u32 saveCURSIZE;
struct {
struct drm_mm gtt_space;
@@ -380,6 +403,7 @@ typedef struct drm_i915_private {
/* storage for physical objects */
struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
} mm;
+ struct sdvo_device_mapping sdvo_mappings[2];
} drm_i915_private_t;
/** driver private structure attached to each drm_gem_object */
@@ -489,13 +513,16 @@ struct drm_i915_gem_request {
/** Time at which this request was emitted, in jiffies. */
unsigned long emitted_jiffies;
+ /** global list entry for this request */
struct list_head list;
+
+ /** file_priv list entry for this request */
+ struct list_head client_list;
};
struct drm_i915_file_private {
struct {
- uint32_t last_gem_seqno;
- uint32_t last_gem_throttle_seqno;
+ struct list_head request_list;
} mm;
};
@@ -640,6 +667,7 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
void i915_gem_free_all_phys_object(struct drm_device *dev);
int i915_gem_object_get_pages(struct drm_gem_object *obj);
void i915_gem_object_put_pages(struct drm_gem_object *obj);
+void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
/* i915_gem_tiling.c */
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -674,12 +702,12 @@ extern int i915_restore_state(struct drm_device *dev);
#ifdef CONFIG_ACPI
/* i915_opregion.c */
extern int intel_opregion_init(struct drm_device *dev, int resume);
-extern void intel_opregion_free(struct drm_device *dev);
+extern void intel_opregion_free(struct drm_device *dev, int suspend);
extern void opregion_asle_intr(struct drm_device *dev);
extern void opregion_enable_asle(struct drm_device *dev);
#else
static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
-static inline void intel_opregion_free(struct drm_device *dev) { return; }
+static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
static inline void opregion_asle_intr(struct drm_device *dev) { return; }
static inline void opregion_enable_asle(struct drm_device *dev) { return; }
#endif
@@ -705,13 +733,8 @@ extern void intel_modeset_cleanup(struct drm_device *dev);
#define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg))
#define I915_READ8(reg) readb(dev_priv->regs + (reg))
#define I915_WRITE8(reg, val) writeb(val, dev_priv->regs + (reg))
-#ifdef writeq
#define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
-#else
-#define I915_WRITE64(reg, val) (writel(val, dev_priv->regs + (reg)), \
- writel(upper_32_bits(val), dev_priv->regs + \
- (reg) + 4))
-#endif
+#define I915_READ64(reg) readq(dev_priv->regs + (reg))
#define POSTING_READ(reg) (void)I915_READ(reg)
#define I915_VERBOSE 0
@@ -787,15 +810,20 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
(dev)->pci_device == 0x2A42 || \
(dev)->pci_device == 0x2E02 || \
(dev)->pci_device == 0x2E12 || \
- (dev)->pci_device == 0x2E22)
+ (dev)->pci_device == 0x2E22 || \
+ (dev)->pci_device == 0x2E32 || \
+ (dev)->pci_device == 0x0042 || \
+ (dev)->pci_device == 0x0046)
-#define IS_I965GM(dev) ((dev)->pci_device == 0x2A02)
+#define IS_I965GM(dev) ((dev)->pci_device == 0x2A02 || \
+ (dev)->pci_device == 0x2A12)
#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \
(dev)->pci_device == 0x2E12 || \
(dev)->pci_device == 0x2E22 || \
+ (dev)->pci_device == 0x2E32 || \
IS_GM45(dev))
#define IS_IGDG(dev) ((dev)->pci_device == 0xa001)
@@ -807,20 +835,26 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
(dev)->pci_device == 0x29D2 || \
(IS_IGD(dev)))
+#define IS_IGDNG_D(dev) ((dev)->pci_device == 0x0042)
+#define IS_IGDNG_M(dev) ((dev)->pci_device == 0x0046)
+#define IS_IGDNG(dev) (IS_IGDNG_D(dev) || IS_IGDNG_M(dev))
+
#define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \
- IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev))
+ IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev) || \
+ IS_IGDNG(dev))
#define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \
IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev) || \
- IS_IGD(dev))
+ IS_IGD(dev) || IS_IGDNG_M(dev))
-#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev))
+#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev) || \
+ IS_IGDNG(dev))
/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
* rows, which changed the alignment requirements and fence programming.
*/
#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev)))
-#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
+#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 4642115902d6..c0ae6bbbd9b5 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -349,7 +349,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL)
return -ENOMEM;
@@ -429,7 +429,7 @@ fail_put_user_pages:
SetPageDirty(user_pages[i]);
page_cache_release(user_pages[i]);
}
- kfree(user_pages);
+ drm_free_large(user_pages);
return ret;
}
@@ -649,7 +649,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL)
return -ENOMEM;
@@ -719,7 +719,7 @@ out_unlock:
out_unpin_pages:
for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]);
- kfree(user_pages);
+ drm_free_large(user_pages);
return ret;
}
@@ -824,7 +824,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
if (user_pages == NULL)
return -ENOMEM;
@@ -902,7 +902,7 @@ fail_unlock:
fail_put_user_pages:
for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]);
- kfree(user_pages);
+ drm_free_large(user_pages);
return ret;
}
@@ -989,10 +989,10 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
return -ENODEV;
/* Only handle setting domains to types used by the CPU. */
- if (write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
+ if (write_domain & I915_GEM_GPU_DOMAINS)
return -EINVAL;
- if (read_domains & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
+ if (read_domains & I915_GEM_GPU_DOMAINS)
return -EINVAL;
/* Having something in the write domain implies it's in the read
@@ -1145,7 +1145,14 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
mutex_unlock(&dev->struct_mutex);
return VM_FAULT_SIGBUS;
}
- list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+
+ ret = i915_gem_object_set_to_gtt_domain(obj, write);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return VM_FAULT_SIGBUS;
+ }
+
+ list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
}
/* Need a new fence register? */
@@ -1375,7 +1382,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex);
return ret;
}
- list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+ list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
}
drm_gem_object_unreference(obj);
@@ -1408,9 +1415,7 @@ i915_gem_object_put_pages(struct drm_gem_object *obj)
}
obj_priv->dirty = 0;
- drm_free(obj_priv->pages,
- page_count * sizeof(struct page *),
- DRM_MEM_DRIVER);
+ drm_free_large(obj_priv->pages);
obj_priv->pages = NULL;
}
@@ -1476,14 +1481,19 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
* Returned sequence numbers are nonzero on success.
*/
static uint32_t
-i915_add_request(struct drm_device *dev, uint32_t flush_domains)
+i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+ uint32_t flush_domains)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_file_private *i915_file_priv = NULL;
struct drm_i915_gem_request *request;
uint32_t seqno;
int was_empty;
RING_LOCALS;
+ if (file_priv != NULL)
+ i915_file_priv = file_priv->driver_priv;
+
request = drm_calloc(1, sizeof(*request), DRM_MEM_DRIVER);
if (request == NULL)
return 0;
@@ -1510,6 +1520,12 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
request->emitted_jiffies = jiffies;
was_empty = list_empty(&dev_priv->mm.request_list);
list_add_tail(&request->list, &dev_priv->mm.request_list);
+ if (i915_file_priv) {
+ list_add_tail(&request->client_list,
+ &i915_file_priv->mm.request_list);
+ } else {
+ INIT_LIST_HEAD(&request->client_list);
+ }
/* Associate any objects on the flushing list matching the write
* domain we're flushing with our flush.
@@ -1659,6 +1675,7 @@ i915_gem_retire_requests(struct drm_device *dev)
i915_gem_retire_request(dev, request);
list_del(&request->list);
+ list_del(&request->client_list);
drm_free(request, sizeof(*request), DRM_MEM_DRIVER);
} else
break;
@@ -1691,11 +1708,23 @@ static int
i915_wait_request(struct drm_device *dev, uint32_t seqno)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 ier;
int ret = 0;
BUG_ON(seqno == 0);
if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
+ if (IS_IGDNG(dev))
+ ier = I915_READ(DEIER) | I915_READ(GTIER);
+ else
+ ier = I915_READ(IER);
+ if (!ier) {
+ DRM_ERROR("something (likely vbetool) disabled "
+ "interrupts, re-enabling\n");
+ i915_driver_irq_preinstall(dev);
+ i915_driver_irq_postinstall(dev);
+ }
+
dev_priv->mm.waiting_gem_seqno = seqno;
i915_user_irq_get(dev);
ret = wait_event_interruptible(dev_priv->irq_queue,
@@ -1740,8 +1769,7 @@ i915_gem_flush(struct drm_device *dev,
if (flush_domains & I915_GEM_DOMAIN_CPU)
drm_agp_chipset_flush(dev);
- if ((invalidate_domains | flush_domains) & ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT)) {
+ if ((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) {
/*
* read/write caches:
*
@@ -1963,7 +1991,7 @@ i915_gem_evict_something(struct drm_device *dev)
i915_gem_flush(dev,
obj->write_domain,
obj->write_domain);
- i915_add_request(dev, obj->write_domain);
+ i915_add_request(dev, NULL, obj->write_domain);
obj = NULL;
continue;
@@ -1977,7 +2005,7 @@ i915_gem_evict_something(struct drm_device *dev)
/* If we didn't do any of the above, there's nothing to be done
* and we just can't fit it in.
*/
- return -ENOMEM;
+ return -ENOSPC;
}
return ret;
}
@@ -1992,7 +2020,7 @@ i915_gem_evict_everything(struct drm_device *dev)
if (ret != 0)
break;
}
- if (ret == -ENOMEM)
+ if (ret == -ENOSPC)
return 0;
return ret;
}
@@ -2015,8 +2043,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj)
*/
page_count = obj->size / PAGE_SIZE;
BUG_ON(obj_priv->pages != NULL);
- obj_priv->pages = drm_calloc(page_count, sizeof(struct page *),
- DRM_MEM_DRIVER);
+ obj_priv->pages = drm_calloc_large(page_count, sizeof(struct page *));
if (obj_priv->pages == NULL) {
DRM_ERROR("Faled to allocate page list\n");
obj_priv->pages_refcount--;
@@ -2122,8 +2149,10 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
return;
}
- pitch_val = (obj_priv->stride / 128) - 1;
- WARN_ON(pitch_val & ~0x0000000f);
+ pitch_val = obj_priv->stride / 128;
+ pitch_val = ffs(pitch_val) - 1;
+ WARN_ON(pitch_val > I830_FENCE_MAX_PITCH_VAL);
+
val = obj_priv->gtt_offset;
if (obj_priv->tiling_mode == I915_TILING_Y)
val |= 1 << I830_FENCE_TILING_Y_SHIFT;
@@ -2200,7 +2229,7 @@ try_again:
loff_t offset;
if (avail == 0)
- return -ENOMEM;
+ return -ENOSPC;
for (i = dev_priv->fence_reg_start;
i < dev_priv->num_fence_regs; i++) {
@@ -2233,7 +2262,7 @@ try_again:
i915_gem_flush(dev,
I915_GEM_GPU_DOMAINS,
I915_GEM_GPU_DOMAINS);
- seqno = i915_add_request(dev,
+ seqno = i915_add_request(dev, NULL,
I915_GEM_GPU_DOMAINS);
if (seqno == 0)
return -ENOMEM;
@@ -2245,9 +2274,6 @@ try_again:
goto try_again;
}
- BUG_ON(old_obj_priv->active ||
- (reg->obj->write_domain & I915_GEM_GPU_DOMAINS));
-
/*
* Zap this virtual mapping so we can set up a fence again
* for this object next time we need it.
@@ -2352,7 +2378,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
spin_unlock(&dev_priv->mm.active_list_lock);
if (lists_empty) {
DRM_ERROR("GTT full, but LRU list empty\n");
- return -ENOMEM;
+ return -ENOSPC;
}
ret = i915_gem_evict_something(dev);
@@ -2397,8 +2423,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
* wasn't in the GTT, there shouldn't be any way it could have been in
* a GPU cache
*/
- BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
- BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
+ BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
+ BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
return 0;
}
@@ -2415,6 +2441,16 @@ i915_gem_clflush_object(struct drm_gem_object *obj)
if (obj_priv->pages == NULL)
return;
+ /* XXX: The 865 in particular appears to be weird in how it handles
+ * cache flushing. We haven't figured it out, but the
+ * clflush+agp_chipset_flush doesn't appear to successfully get the
+ * data visible to the PGU, while wbinvd + agp_chipset_flush does.
+ */
+ if (IS_I865G(obj->dev)) {
+ wbinvd();
+ return;
+ }
+
drm_clflush_pages(obj_priv->pages, obj->size / PAGE_SIZE);
}
@@ -2430,7 +2466,7 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
/* Queue the GPU write cache flushing we need. */
i915_gem_flush(dev, 0, obj->write_domain);
- seqno = i915_add_request(dev, obj->write_domain);
+ seqno = i915_add_request(dev, NULL, obj->write_domain);
obj->write_domain = 0;
i915_gem_object_move_to_active(obj, seqno);
}
@@ -3013,20 +3049,12 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
drm_i915_private_t *dev_priv = dev->dev_private;
int nbox = exec->num_cliprects;
int i = 0, count;
- uint32_t exec_start, exec_len;
+ uint32_t exec_start, exec_len;
RING_LOCALS;
exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
exec_len = (uint32_t) exec->batch_len;
- if ((exec_start | exec_len) & 0x7) {
- DRM_ERROR("alignment\n");
- return -EINVAL;
- }
-
- if (!exec_start)
- return -EINVAL;
-
count = nbox ? nbox : 1;
for (i = 0; i < count; i++) {
@@ -3067,6 +3095,10 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
/* Throttle our rendering by waiting until the ring has completed our requests
* emitted over 20 msec ago.
*
+ * Note that if we were to use the current jiffies each time around the loop,
+ * we wouldn't escape the function with any frames outstanding if the time to
+ * render a frame was over 20ms.
+ *
* This should get us reasonable parallelism between CPU and GPU but also
* relatively low latency when blocking on a particular request to finish.
*/
@@ -3075,15 +3107,25 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
{
struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
int ret = 0;
- uint32_t seqno;
+ unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
mutex_lock(&dev->struct_mutex);
- seqno = i915_file_priv->mm.last_gem_throttle_seqno;
- i915_file_priv->mm.last_gem_throttle_seqno =
- i915_file_priv->mm.last_gem_seqno;
- if (seqno)
- ret = i915_wait_request(dev, seqno);
+ while (!list_empty(&i915_file_priv->mm.request_list)) {
+ struct drm_i915_gem_request *request;
+
+ request = list_first_entry(&i915_file_priv->mm.request_list,
+ struct drm_i915_gem_request,
+ client_list);
+
+ if (time_after_eq(request->emitted_jiffies, recent_enough))
+ break;
+
+ ret = i915_wait_request(dev, request->seqno);
+ if (ret != 0)
+ break;
+ }
mutex_unlock(&dev->struct_mutex);
+
return ret;
}
@@ -3102,7 +3144,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
reloc_count += exec_list[i].relocation_count;
}
- *relocs = drm_calloc(reloc_count, sizeof(**relocs), DRM_MEM_DRIVER);
+ *relocs = drm_calloc_large(reloc_count, sizeof(**relocs));
if (*relocs == NULL)
return -ENOMEM;
@@ -3116,8 +3158,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
exec_list[i].relocation_count *
sizeof(**relocs));
if (ret != 0) {
- drm_free(*relocs, reloc_count * sizeof(**relocs),
- DRM_MEM_DRIVER);
+ drm_free_large(*relocs);
*relocs = NULL;
return -EFAULT;
}
@@ -3156,17 +3197,34 @@ i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
}
err:
- drm_free(relocs, reloc_count * sizeof(*relocs), DRM_MEM_DRIVER);
+ drm_free_large(relocs);
return ret;
}
+static int
+i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
+ uint64_t exec_offset)
+{
+ uint32_t exec_start, exec_len;
+
+ exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
+ exec_len = (uint32_t) exec->batch_len;
+
+ if ((exec_start | exec_len) & 0x7)
+ return -EINVAL;
+
+ if (!exec_start)
+ return -EINVAL;
+
+ return 0;
+}
+
int
i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
struct drm_i915_gem_execbuffer *args = data;
struct drm_i915_gem_exec_object *exec_list = NULL;
struct drm_gem_object **object_list = NULL;
@@ -3189,10 +3247,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
return -EINVAL;
}
/* Copy in the exec list from userland */
- exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count,
- DRM_MEM_DRIVER);
- object_list = drm_calloc(sizeof(*object_list), args->buffer_count,
- DRM_MEM_DRIVER);
+ exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count);
+ object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count);
if (exec_list == NULL || object_list == NULL) {
DRM_ERROR("Failed to allocate exec or object list "
"for %d buffers\n",
@@ -3293,7 +3349,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
break;
/* error other than GTT full, or we've already tried again */
- if (ret != -ENOMEM || pin_tries >= 1) {
+ if (ret != -ENOSPC || pin_tries >= 1) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Failed to pin buffers %d\n", ret);
goto err;
@@ -3312,8 +3368,20 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
/* Set the pending read domains for the batch buffer to COMMAND */
batch_obj = object_list[args->buffer_count-1];
- batch_obj->pending_read_domains = I915_GEM_DOMAIN_COMMAND;
- batch_obj->pending_write_domain = 0;
+ if (batch_obj->pending_write_domain) {
+ DRM_ERROR("Attempting to use self-modifying batch buffer\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ batch_obj->pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
+
+ /* Sanity check the batch buffer, prior to moving objects */
+ exec_offset = exec_list[args->buffer_count - 1].offset;
+ ret = i915_gem_check_execbuffer (args, exec_offset);
+ if (ret != 0) {
+ DRM_ERROR("execbuf with invalid offset/length\n");
+ goto err;
+ }
i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -3344,7 +3412,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
dev->invalidate_domains,
dev->flush_domains);
if (dev->flush_domains)
- (void)i915_add_request(dev, dev->flush_domains);
+ (void)i915_add_request(dev, file_priv,
+ dev->flush_domains);
}
for (i = 0; i < args->buffer_count; i++) {
@@ -3362,8 +3431,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
}
#endif
- exec_offset = exec_list[args->buffer_count - 1].offset;
-
#if WATCH_EXEC
i915_gem_dump_object(batch_obj,
args->batch_len,
@@ -3393,9 +3460,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
* *some* interrupts representing completion of buffers that we can
* wait on when trying to clear up gtt space).
*/
- seqno = i915_add_request(dev, flush_domains);
+ seqno = i915_add_request(dev, file_priv, flush_domains);
BUG_ON(seqno == 0);
- i915_file_priv->mm.last_gem_seqno = seqno;
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
@@ -3453,10 +3519,8 @@ err:
}
pre_mutex_err:
- drm_free(object_list, sizeof(*object_list) * args->buffer_count,
- DRM_MEM_DRIVER);
- drm_free(exec_list, sizeof(*exec_list) * args->buffer_count,
- DRM_MEM_DRIVER);
+ drm_free_large(object_list);
+ drm_free_large(exec_list);
drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects,
DRM_MEM_DRIVER);
@@ -3503,8 +3567,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
atomic_inc(&dev->pin_count);
atomic_add(obj->size, &dev->pin_memory);
if (!obj_priv->active &&
- (obj->write_domain & ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT)) == 0 &&
+ (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0 &&
!list_empty(&obj_priv->list))
list_del_init(&obj_priv->list);
}
@@ -3531,8 +3594,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
*/
if (obj_priv->pin_count == 0) {
if (!obj_priv->active &&
- (obj->write_domain & ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT)) == 0)
+ (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
list_move_tail(&obj_priv->list,
&dev_priv->mm.inactive_list);
atomic_dec(&dev->pin_count);
@@ -3636,15 +3698,14 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
- mutex_lock(&dev->struct_mutex);
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
args->handle);
- mutex_unlock(&dev->struct_mutex);
return -EBADF;
}
+ mutex_lock(&dev->struct_mutex);
/* Update the active list for the hardware's current position.
* Otherwise this only updates on a delayed timer or when irqs are
* actually unmasked, and our working set ends up being larger than
@@ -3783,9 +3844,8 @@ i915_gem_idle(struct drm_device *dev)
/* Flush the GPU along with all non-CPU write domains
*/
- i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT),
- ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
- seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU);
+ i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
@@ -4087,8 +4147,10 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
dev_priv->mm.suspended = 0;
ret = i915_gem_init_ringbuffer(dev);
- if (ret != 0)
+ if (ret != 0) {
+ mutex_unlock(&dev->struct_mutex);
return ret;
+ }
spin_lock(&dev_priv->mm.active_list_lock);
BUG_ON(!list_empty(&dev_priv->mm.active_list));
@@ -4333,3 +4395,17 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
drm_agp_chipset_flush(dev);
return 0;
}
+
+void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv)
+{
+ struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
+
+ /* Clean up our request list when the client is going away, so that
+ * later retire_requests won't dereference our soon-to-be-gone
+ * file_priv.
+ */
+ mutex_lock(&dev->struct_mutex);
+ while (!list_empty(&i915_file_priv->mm.request_list))
+ list_del_init(i915_file_priv->mm.request_list.next);
+ mutex_unlock(&dev->struct_mutex);
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index f27e523c764f..9a05cadaa4ad 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -25,6 +25,8 @@
*
*/
+#include <linux/acpi.h>
+#include <linux/pnp.h>
#include "linux/string.h"
#include "linux/bitops.h"
#include "drmP.h"
@@ -81,6 +83,143 @@
* to match what the GPU expects.
*/
+#define MCHBAR_I915 0x44
+#define MCHBAR_I965 0x48
+#define MCHBAR_SIZE (4*4096)
+
+#define DEVEN_REG 0x54
+#define DEVEN_MCHBAR_EN (1 << 28)
+
+/* Allocate space for the MCH regs if needed, return nonzero on error */
+static int
+intel_alloc_mchbar_resource(struct drm_device *dev)
+{
+ struct pci_dev *bridge_dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ u32 temp_lo, temp_hi = 0;
+ u64 mchbar_addr;
+ int ret = 0;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_DEBUG("no bridge dev?!\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (IS_I965G(dev))
+ pci_read_config_dword(bridge_dev, reg + 4, &temp_hi);
+ pci_read_config_dword(bridge_dev, reg, &temp_lo);
+ mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
+
+ /* If ACPI doesn't have it, assume we need to allocate it ourselves */
+ if (mchbar_addr &&
+ pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) {
+ ret = 0;
+ goto out_put;
+ }
+
+ /* Get some space for it */
+ ret = pci_bus_alloc_resource(bridge_dev->bus, &dev_priv->mch_res,
+ MCHBAR_SIZE, MCHBAR_SIZE,
+ PCIBIOS_MIN_MEM,
+ 0, pcibios_align_resource,
+ bridge_dev);
+ if (ret) {
+ DRM_DEBUG("failed bus alloc: %d\n", ret);
+ dev_priv->mch_res.start = 0;
+ goto out_put;
+ }
+
+ if (IS_I965G(dev))
+ pci_write_config_dword(bridge_dev, reg + 4,
+ upper_32_bits(dev_priv->mch_res.start));
+
+ pci_write_config_dword(bridge_dev, reg,
+ lower_32_bits(dev_priv->mch_res.start));
+out_put:
+ pci_dev_put(bridge_dev);
+out:
+ return ret;
+}
+
+/* Setup MCHBAR if possible, return true if we should disable it again */
+static bool
+intel_setup_mchbar(struct drm_device *dev)
+{
+ struct pci_dev *bridge_dev;
+ int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ u32 temp;
+ bool need_disable = false, enabled;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_DEBUG("no bridge dev?!\n");
+ goto out;
+ }
+
+ if (IS_I915G(dev) || IS_I915GM(dev)) {
+ pci_read_config_dword(bridge_dev, DEVEN_REG, &temp);
+ enabled = !!(temp & DEVEN_MCHBAR_EN);
+ } else {
+ pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+ enabled = temp & 1;
+ }
+
+ /* If it's already enabled, don't have to do anything */
+ if (enabled)
+ goto out_put;
+
+ if (intel_alloc_mchbar_resource(dev))
+ goto out_put;
+
+ need_disable = true;
+
+ /* Space is allocated or reserved, so enable it. */
+ if (IS_I915G(dev) || IS_I915GM(dev)) {
+ pci_write_config_dword(bridge_dev, DEVEN_REG,
+ temp | DEVEN_MCHBAR_EN);
+ } else {
+ pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+ pci_write_config_dword(bridge_dev, mchbar_reg, temp | 1);
+ }
+out_put:
+ pci_dev_put(bridge_dev);
+out:
+ return need_disable;
+}
+
+static void
+intel_teardown_mchbar(struct drm_device *dev, bool disable)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct pci_dev *bridge_dev;
+ int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ u32 temp;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_DEBUG("no bridge dev?!\n");
+ return;
+ }
+
+ if (disable) {
+ if (IS_I915G(dev) || IS_I915GM(dev)) {
+ pci_read_config_dword(bridge_dev, DEVEN_REG, &temp);
+ temp &= ~DEVEN_MCHBAR_EN;
+ pci_write_config_dword(bridge_dev, DEVEN_REG, temp);
+ } else {
+ pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+ temp &= ~1;
+ pci_write_config_dword(bridge_dev, mchbar_reg, temp);
+ }
+ }
+
+ if (dev_priv->mch_res.start)
+ release_resource(&dev_priv->mch_res);
+}
+
/**
* Detects bit 6 swizzling of address lookup between IGD access and CPU
* access through main memory.
@@ -91,6 +230,7 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
+ bool need_disable;
if (!IS_I9XX(dev)) {
/* As far as we know, the 865 doesn't have these bit 6
@@ -101,6 +241,9 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
} else if (IS_MOBILE(dev)) {
uint32_t dcc;
+ /* Try to make sure MCHBAR is enabled before poking at it */
+ need_disable = intel_setup_mchbar(dev);
+
/* On mobile 9xx chipsets, channel interleave by the CPU is
* determined by DCC. For single-channel, neither the CPU
* nor the GPU do swizzling. For dual channel interleaved,
@@ -140,6 +283,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
}
+
+ intel_teardown_mchbar(dev, need_disable);
} else {
/* The 965, G33, and newer, have a very flexible memory
* configuration. It will enable dual-channel mode
@@ -170,6 +315,13 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
}
}
+ /* FIXME: check with memory config on IGDNG */
+ if (IS_IGDNG(dev)) {
+ DRM_ERROR("disable tiling on IGDNG...\n");
+ swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
+ swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
+ }
+
dev_priv->mm.bit_6_swizzle_x = swizzle_x;
dev_priv->mm.bit_6_swizzle_y = swizzle_y;
}
@@ -213,7 +365,8 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
if (tiling_mode == I915_TILING_NONE)
return true;
- if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
+ if (!IS_I9XX(dev) ||
+ (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)))
tile_width = 128;
else
tile_width = 512;
@@ -225,11 +378,18 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
if (stride / 128 > I965_FENCE_MAX_PITCH_VAL)
return false;
} else if (IS_I9XX(dev)) {
- if (stride / tile_width > I830_FENCE_MAX_PITCH_VAL ||
+ uint32_t pitch_val = ffs(stride / tile_width) - 1;
+
+ /* XXX: For Y tiling, FENCE_MAX_PITCH_VAL is actually 6 (8KB)
+ * instead of 4 (2KB) on 945s.
+ */
+ if (pitch_val > I915_FENCE_MAX_PITCH_VAL ||
size > (I830_FENCE_MAX_SIZE_VAL << 20))
return false;
} else {
- if (stride / 128 > I830_FENCE_MAX_PITCH_VAL ||
+ uint32_t pitch_val = ffs(stride / tile_width) - 1;
+
+ if (pitch_val > I830_FENCE_MAX_PITCH_VAL ||
size > (I830_FENCE_MAX_SIZE_VAL << 19))
return false;
}
@@ -283,7 +443,6 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
mutex_lock(&dev->struct_mutex);
if (args->tiling_mode == I915_TILING_NONE) {
- obj_priv->tiling_mode = I915_TILING_NONE;
args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
} else {
if (args->tiling_mode == I915_TILING_X)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index ee7ce7b78cf7..b86b7b7130c6 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -58,6 +58,47 @@
DRM_I915_VBLANK_PIPE_B)
void
+igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->gt_irq_mask_reg & mask) != 0) {
+ dev_priv->gt_irq_mask_reg &= ~mask;
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+ (void) I915_READ(GTIMR);
+ }
+}
+
+static inline void
+igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->gt_irq_mask_reg & mask) != mask) {
+ dev_priv->gt_irq_mask_reg |= mask;
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+ (void) I915_READ(GTIMR);
+ }
+}
+
+/* For display hotplug interrupt */
+void
+igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->irq_mask_reg & mask) != 0) {
+ dev_priv->irq_mask_reg &= ~mask;
+ I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(DEIMR);
+ }
+}
+
+static inline void
+igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->irq_mask_reg & mask) != mask) {
+ dev_priv->irq_mask_reg |= mask;
+ I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(DEIMR);
+ }
+}
+
+void
i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
{
if ((dev_priv->irq_mask_reg & mask) != 0) {
@@ -196,6 +237,47 @@ static void i915_hotplug_work_func(struct work_struct *work)
drm_sysfs_hotplug_event(dev);
}
+irqreturn_t igdng_irq_handler(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ int ret = IRQ_NONE;
+ u32 de_iir, gt_iir;
+ u32 new_de_iir, new_gt_iir;
+ struct drm_i915_master_private *master_priv;
+
+ de_iir = I915_READ(DEIIR);
+ gt_iir = I915_READ(GTIIR);
+
+ for (;;) {
+ if (de_iir == 0 && gt_iir == 0)
+ break;
+
+ ret = IRQ_HANDLED;
+
+ I915_WRITE(DEIIR, de_iir);
+ new_de_iir = I915_READ(DEIIR);
+ I915_WRITE(GTIIR, gt_iir);
+ new_gt_iir = I915_READ(GTIIR);
+
+ if (dev->primary->master) {
+ master_priv = dev->primary->master->driver_priv;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch =
+ READ_BREADCRUMB(dev_priv);
+ }
+
+ if (gt_iir & GT_USER_INTERRUPT) {
+ dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
+ DRM_WAKEUP(&dev_priv->irq_queue);
+ }
+
+ de_iir = new_de_iir;
+ gt_iir = new_gt_iir;
+ }
+
+ return ret;
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -212,6 +294,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
atomic_inc(&dev_priv->irq_received);
+ if (IS_IGDNG(dev))
+ return igdng_irq_handler(dev);
+
iir = I915_READ(IIR);
if (IS_I965G(dev)) {
@@ -349,8 +434,12 @@ void i915_user_irq_get(struct drm_device *dev)
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
- if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
- i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+ if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
+ if (IS_IGDNG(dev))
+ igdng_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+ else
+ i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+ }
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
}
@@ -361,8 +450,12 @@ void i915_user_irq_put(struct drm_device *dev)
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
- if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
- i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+ if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
+ if (IS_IGDNG(dev))
+ igdng_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+ else
+ i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+ }
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
}
@@ -406,7 +499,7 @@ int i915_irq_emit(struct drm_device *dev, void *data,
drm_i915_irq_emit_t *emit = data;
int result;
- if (!dev_priv) {
+ if (!dev_priv || !dev_priv->ring.virtual_start) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
@@ -455,6 +548,9 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
if (!(pipeconf & PIPEACONF_ENABLE))
return -EINVAL;
+ if (IS_IGDNG(dev))
+ return 0;
+
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
if (IS_I965G(dev))
i915_enable_pipestat(dev_priv, pipe,
@@ -474,6 +570,9 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
+ if (IS_IGDNG(dev))
+ return;
+
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
i915_disable_pipestat(dev_priv, pipe,
PIPE_VBLANK_INTERRUPT_ENABLE |
@@ -484,7 +583,9 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
void i915_enable_interrupt (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- opregion_enable_asle(dev);
+
+ if (!IS_IGDNG(dev))
+ opregion_enable_asle(dev);
dev_priv->irq_enabled = 1;
}
@@ -545,12 +646,65 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
/* drm_dma.h hooks
*/
+static void igdng_irq_preinstall(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ I915_WRITE(HWSTAM, 0xeffe);
+
+ /* XXX hotplug from PCH */
+
+ I915_WRITE(DEIMR, 0xffffffff);
+ I915_WRITE(DEIER, 0x0);
+ (void) I915_READ(DEIER);
+
+ /* and GT */
+ I915_WRITE(GTIMR, 0xffffffff);
+ I915_WRITE(GTIER, 0x0);
+ (void) I915_READ(GTIER);
+}
+
+static int igdng_irq_postinstall(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ /* enable kind of interrupts always enabled */
+ u32 display_mask = DE_MASTER_IRQ_CONTROL /*| DE_PCH_EVENT */;
+ u32 render_mask = GT_USER_INTERRUPT;
+
+ dev_priv->irq_mask_reg = ~display_mask;
+ dev_priv->de_irq_enable_reg = display_mask;
+
+ /* should always can generate irq */
+ I915_WRITE(DEIIR, I915_READ(DEIIR));
+ I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+ I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
+ (void) I915_READ(DEIER);
+
+ /* user interrupt should be enabled, but masked initial */
+ dev_priv->gt_irq_mask_reg = 0xffffffff;
+ dev_priv->gt_irq_enable_reg = render_mask;
+
+ I915_WRITE(GTIIR, I915_READ(GTIIR));
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+ I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
+ (void) I915_READ(GTIER);
+
+ return 0;
+}
+
void i915_driver_irq_preinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
atomic_set(&dev_priv->irq_received, 0);
+ INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+
+ if (IS_IGDNG(dev)) {
+ igdng_irq_preinstall(dev);
+ return;
+ }
+
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -562,7 +716,6 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0);
(void) I915_READ(IER);
- INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
}
int i915_driver_irq_postinstall(struct drm_device *dev)
@@ -570,9 +723,12 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
+ DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
+
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
- dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+ if (IS_IGDNG(dev))
+ return igdng_irq_postinstall(dev);
/* Unmask the interrupts that we always want on. */
dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
@@ -613,11 +769,24 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
(void) I915_READ(IER);
opregion_enable_asle(dev);
- DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
return 0;
}
+static void igdng_irq_uninstall(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ I915_WRITE(HWSTAM, 0xffffffff);
+
+ I915_WRITE(DEIMR, 0xffffffff);
+ I915_WRITE(DEIER, 0x0);
+ I915_WRITE(DEIIR, I915_READ(DEIIR));
+
+ I915_WRITE(GTIMR, 0xffffffff);
+ I915_WRITE(GTIER, 0x0);
+ I915_WRITE(GTIIR, I915_READ(GTIIR));
+}
+
void i915_driver_irq_uninstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -627,6 +796,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
dev_priv->vblank_pipe = 0;
+ if (IS_IGDNG(dev)) {
+ igdng_irq_uninstall(dev);
+ return;
+ }
+
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
index 8dc1fd3115c2..dc425e74a268 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -386,6 +386,7 @@ int intel_opregion_init(struct drm_device *dev, int resume)
if (mboxes & MBOX_ASLE) {
DRM_DEBUG("ASLE supported\n");
opregion->asle = base + OPREGION_ASLE_OFFSET;
+ opregion_enable_asle(dev);
}
if (!resume)
@@ -409,7 +410,7 @@ err_out:
return err;
}
-void intel_opregion_free(struct drm_device *dev)
+void intel_opregion_free(struct drm_device *dev, int suspend)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
@@ -417,6 +418,9 @@ void intel_opregion_free(struct drm_device *dev)
if (!opregion->enabled)
return;
+ if (!suspend)
+ acpi_video_exit();
+
opregion->acpi->drdy = 0;
system_opregion = NULL;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index e805b590ae71..f6237a0b1133 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -190,7 +190,8 @@
#define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8)
#define I830_FENCE_PITCH_SHIFT 4
#define I830_FENCE_REG_VALID (1<<0)
-#define I830_FENCE_MAX_PITCH_VAL 0x10
+#define I915_FENCE_MAX_PITCH_VAL 0x10
+#define I830_FENCE_MAX_PITCH_VAL 6
#define I830_FENCE_MAX_SIZE_VAL (1<<8)
#define I915_FENCE_START_MASK 0x0ff00000
@@ -449,6 +450,13 @@
#define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13)
#define PLL_REF_INPUT_MASK (3 << 13)
#define PLL_LOAD_PULSE_PHASE_SHIFT 9
+/* IGDNG */
+# define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT 9
+# define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK (7 << 9)
+# define PLL_REF_SDVO_HDMI_MULTIPLIER(x) (((x)-1) << 9)
+# define DPLL_FPA1_P1_POST_DIV_SHIFT 0
+# define DPLL_FPA1_P1_POST_DIV_MASK 0xff
+
/*
* Parallel to Serial Load Pulse phase selection.
* Selects the phase for the 10X DPLL clock for the PCIe
@@ -526,6 +534,7 @@
#define DPLLA_INPUT_BUFFER_ENABLE (1 << 0)
#define D_STATE 0x6104
#define CG_2D_DIS 0x6200
+#define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24)
#define CG_3D_DIS 0x6204
/*
@@ -629,8 +638,11 @@
/* Hotplug control (945+ only) */
#define PORT_HOTPLUG_EN 0x61110
#define HDMIB_HOTPLUG_INT_EN (1 << 29)
+#define DPB_HOTPLUG_INT_EN (1 << 29)
#define HDMIC_HOTPLUG_INT_EN (1 << 28)
+#define DPC_HOTPLUG_INT_EN (1 << 28)
#define HDMID_HOTPLUG_INT_EN (1 << 27)
+#define DPD_HOTPLUG_INT_EN (1 << 27)
#define SDVOB_HOTPLUG_INT_EN (1 << 26)
#define SDVOC_HOTPLUG_INT_EN (1 << 25)
#define TV_HOTPLUG_INT_EN (1 << 18)
@@ -663,8 +675,11 @@
#define PORT_HOTPLUG_STAT 0x61114
#define HDMIB_HOTPLUG_INT_STATUS (1 << 29)
+#define DPB_HOTPLUG_INT_STATUS (1 << 29)
#define HDMIC_HOTPLUG_INT_STATUS (1 << 28)
+#define DPC_HOTPLUG_INT_STATUS (1 << 28)
#define HDMID_HOTPLUG_INT_STATUS (1 << 27)
+#define DPD_HOTPLUG_INT_STATUS (1 << 27)
#define CRT_HOTPLUG_INT_STATUS (1 << 11)
#define TV_HOTPLUG_INT_STATUS (1 << 10)
#define CRT_HOTPLUG_MONITOR_MASK (3 << 8)
@@ -949,15 +964,15 @@
# define DAC_A_1_3_V (0 << 4)
# define DAC_A_1_1_V (1 << 4)
# define DAC_A_0_7_V (2 << 4)
-# define DAC_A_OFF (3 << 4)
+# define DAC_A_MASK (3 << 4)
# define DAC_B_1_3_V (0 << 2)
# define DAC_B_1_1_V (1 << 2)
# define DAC_B_0_7_V (2 << 2)
-# define DAC_B_OFF (3 << 2)
+# define DAC_B_MASK (3 << 2)
# define DAC_C_1_3_V (0 << 0)
# define DAC_C_1_1_V (1 << 0)
# define DAC_C_0_7_V (2 << 0)
-# define DAC_C_OFF (3 << 0)
+# define DAC_C_MASK (3 << 0)
/**
* CSC coefficients are stored in a floating point format with 9 bits of
@@ -1326,6 +1341,163 @@
#define TV_V_CHROMA_0 0x68400
#define TV_V_CHROMA_42 0x684a8
+/* Display Port */
+#define DP_B 0x64100
+#define DP_C 0x64200
+#define DP_D 0x64300
+
+#define DP_PORT_EN (1 << 31)
+#define DP_PIPEB_SELECT (1 << 30)
+
+/* Link training mode - select a suitable mode for each stage */
+#define DP_LINK_TRAIN_PAT_1 (0 << 28)
+#define DP_LINK_TRAIN_PAT_2 (1 << 28)
+#define DP_LINK_TRAIN_PAT_IDLE (2 << 28)
+#define DP_LINK_TRAIN_OFF (3 << 28)
+#define DP_LINK_TRAIN_MASK (3 << 28)
+#define DP_LINK_TRAIN_SHIFT 28
+
+/* Signal voltages. These are mostly controlled by the other end */
+#define DP_VOLTAGE_0_4 (0 << 25)
+#define DP_VOLTAGE_0_6 (1 << 25)
+#define DP_VOLTAGE_0_8 (2 << 25)
+#define DP_VOLTAGE_1_2 (3 << 25)
+#define DP_VOLTAGE_MASK (7 << 25)
+#define DP_VOLTAGE_SHIFT 25
+
+/* Signal pre-emphasis levels, like voltages, the other end tells us what
+ * they want
+ */
+#define DP_PRE_EMPHASIS_0 (0 << 22)
+#define DP_PRE_EMPHASIS_3_5 (1 << 22)
+#define DP_PRE_EMPHASIS_6 (2 << 22)
+#define DP_PRE_EMPHASIS_9_5 (3 << 22)
+#define DP_PRE_EMPHASIS_MASK (7 << 22)
+#define DP_PRE_EMPHASIS_SHIFT 22
+
+/* How many wires to use. I guess 3 was too hard */
+#define DP_PORT_WIDTH_1 (0 << 19)
+#define DP_PORT_WIDTH_2 (1 << 19)
+#define DP_PORT_WIDTH_4 (3 << 19)
+#define DP_PORT_WIDTH_MASK (7 << 19)
+
+/* Mystic DPCD version 1.1 special mode */
+#define DP_ENHANCED_FRAMING (1 << 18)
+
+/** locked once port is enabled */
+#define DP_PORT_REVERSAL (1 << 15)
+
+/** sends the clock on lane 15 of the PEG for debug */
+#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
+
+#define DP_SCRAMBLING_DISABLE (1 << 12)
+
+/** limit RGB values to avoid confusing TVs */
+#define DP_COLOR_RANGE_16_235 (1 << 8)
+
+/** Turn on the audio link */
+#define DP_AUDIO_OUTPUT_ENABLE (1 << 6)
+
+/** vs and hs sync polarity */
+#define DP_SYNC_VS_HIGH (1 << 4)
+#define DP_SYNC_HS_HIGH (1 << 3)
+
+/** A fantasy */
+#define DP_DETECTED (1 << 2)
+
+/** The aux channel provides a way to talk to the
+ * signal sink for DDC etc. Max packet size supported
+ * is 20 bytes in each direction, hence the 5 fixed
+ * data registers
+ */
+#define DPB_AUX_CH_CTL 0x64110
+#define DPB_AUX_CH_DATA1 0x64114
+#define DPB_AUX_CH_DATA2 0x64118
+#define DPB_AUX_CH_DATA3 0x6411c
+#define DPB_AUX_CH_DATA4 0x64120
+#define DPB_AUX_CH_DATA5 0x64124
+
+#define DPC_AUX_CH_CTL 0x64210
+#define DPC_AUX_CH_DATA1 0x64214
+#define DPC_AUX_CH_DATA2 0x64218
+#define DPC_AUX_CH_DATA3 0x6421c
+#define DPC_AUX_CH_DATA4 0x64220
+#define DPC_AUX_CH_DATA5 0x64224
+
+#define DPD_AUX_CH_CTL 0x64310
+#define DPD_AUX_CH_DATA1 0x64314
+#define DPD_AUX_CH_DATA2 0x64318
+#define DPD_AUX_CH_DATA3 0x6431c
+#define DPD_AUX_CH_DATA4 0x64320
+#define DPD_AUX_CH_DATA5 0x64324
+
+#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31)
+#define DP_AUX_CH_CTL_DONE (1 << 30)
+#define DP_AUX_CH_CTL_INTERRUPT (1 << 29)
+#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28)
+#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26)
+#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25)
+#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20)
+#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20
+#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16)
+#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16
+#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15)
+#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14)
+#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13)
+#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12)
+#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11)
+#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff)
+#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0
+
+/*
+ * Computing GMCH M and N values for the Display Port link
+ *
+ * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes
+ *
+ * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz)
+ *
+ * The GMCH value is used internally
+ *
+ * bytes_per_pixel is the number of bytes coming out of the plane,
+ * which is after the LUTs, so we want the bytes for our color format.
+ * For our current usage, this is always 3, one byte for R, G and B.
+ */
+#define PIPEA_GMCH_DATA_M 0x70050
+#define PIPEB_GMCH_DATA_M 0x71050
+
+/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */
+#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25)
+#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25
+
+#define PIPE_GMCH_DATA_M_MASK (0xffffff)
+
+#define PIPEA_GMCH_DATA_N 0x70054
+#define PIPEB_GMCH_DATA_N 0x71054
+#define PIPE_GMCH_DATA_N_MASK (0xffffff)
+
+/*
+ * Computing Link M and N values for the Display Port link
+ *
+ * Link M / N = pixel_clock / ls_clk
+ *
+ * (the DP spec calls pixel_clock the 'strm_clk')
+ *
+ * The Link value is transmitted in the Main Stream
+ * Attributes and VB-ID.
+ */
+
+#define PIPEA_DP_LINK_M 0x70060
+#define PIPEB_DP_LINK_M 0x71060
+#define PIPEA_DP_LINK_M_MASK (0xffffff)
+
+#define PIPEA_DP_LINK_N 0x70064
+#define PIPEB_DP_LINK_N 0x71064
+#define PIPEA_DP_LINK_N_MASK (0xffffff)
+
/* Display & cursor control */
/* Pipe A */
@@ -1409,9 +1581,25 @@
/* Cursor A & B regs */
#define CURACNTR 0x70080
+/* Old style CUR*CNTR flags (desktop 8xx) */
+#define CURSOR_ENABLE 0x80000000
+#define CURSOR_GAMMA_ENABLE 0x40000000
+#define CURSOR_STRIDE_MASK 0x30000000
+#define CURSOR_FORMAT_SHIFT 24
+#define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT)
+#define CURSOR_FORMAT_2C (0x00 << CURSOR_FORMAT_SHIFT)
+#define CURSOR_FORMAT_3C (0x01 << CURSOR_FORMAT_SHIFT)
+#define CURSOR_FORMAT_4C (0x02 << CURSOR_FORMAT_SHIFT)
+#define CURSOR_FORMAT_ARGB (0x04 << CURSOR_FORMAT_SHIFT)
+#define CURSOR_FORMAT_XRGB (0x05 << CURSOR_FORMAT_SHIFT)
+/* New style CUR*CNTR flags */
+#define CURSOR_MODE 0x27
#define CURSOR_MODE_DISABLE 0x00
#define CURSOR_MODE_64_32B_AX 0x07
#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX)
+#define MCURSOR_PIPE_SELECT (1 << 28)
+#define MCURSOR_PIPE_A 0x00
+#define MCURSOR_PIPE_B (1 << 28)
#define MCURSOR_GAMMA_ENABLE (1 << 26)
#define CURABASE 0x70084
#define CURAPOS 0x70088
@@ -1419,6 +1607,7 @@
#define CURSOR_POS_SIGN 0x8000
#define CURSOR_X_SHIFT 0
#define CURSOR_Y_SHIFT 16
+#define CURSIZE 0x700a0
#define CURBCNTR 0x700c0
#define CURBBASE 0x700c4
#define CURBPOS 0x700c8
@@ -1446,6 +1635,7 @@
#define DISPPLANE_NO_LINE_DOUBLE 0
#define DISPPLANE_STEREO_POLARITY_FIRST 0
#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
+#define DISPPLANE_TILED (1<<10)
#define DSPAADDR 0x70184
#define DSPASTRIDE 0x70188
#define DSPAPOS 0x7018C /* reserved */
@@ -1497,4 +1687,444 @@
# define VGA_2X_MODE (1 << 30)
# define VGA_PIPE_B_SELECT (1 << 29)
+/* IGDNG */
+
+#define CPU_VGACNTRL 0x41000
+
+#define DIGITAL_PORT_HOTPLUG_CNTRL 0x44030
+#define DIGITAL_PORTA_HOTPLUG_ENABLE (1 << 4)
+#define DIGITAL_PORTA_SHORT_PULSE_2MS (0 << 2)
+#define DIGITAL_PORTA_SHORT_PULSE_4_5MS (1 << 2)
+#define DIGITAL_PORTA_SHORT_PULSE_6MS (2 << 2)
+#define DIGITAL_PORTA_SHORT_PULSE_100MS (3 << 2)
+#define DIGITAL_PORTA_NO_DETECT (0 << 0)
+#define DIGITAL_PORTA_LONG_PULSE_DETECT_MASK (1 << 1)
+#define DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK (1 << 0)
+
+/* refresh rate hardware control */
+#define RR_HW_CTL 0x45300
+#define RR_HW_LOW_POWER_FRAMES_MASK 0xff
+#define RR_HW_HIGH_POWER_FRAMES_MASK 0xff00
+
+#define FDI_PLL_BIOS_0 0x46000
+#define FDI_PLL_BIOS_1 0x46004
+#define FDI_PLL_BIOS_2 0x46008
+#define DISPLAY_PORT_PLL_BIOS_0 0x4600c
+#define DISPLAY_PORT_PLL_BIOS_1 0x46010
+#define DISPLAY_PORT_PLL_BIOS_2 0x46014
+
+#define FDI_PLL_FREQ_CTL 0x46030
+#define FDI_PLL_FREQ_CHANGE_REQUEST (1<<24)
+#define FDI_PLL_FREQ_LOCK_LIMIT_MASK 0xfff00
+#define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff
+
+
+#define PIPEA_DATA_M1 0x60030
+#define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */
+#define TU_SIZE_MASK 0x7e000000
+#define PIPEA_DATA_M1_OFFSET 0
+#define PIPEA_DATA_N1 0x60034
+#define PIPEA_DATA_N1_OFFSET 0
+
+#define PIPEA_DATA_M2 0x60038
+#define PIPEA_DATA_M2_OFFSET 0
+#define PIPEA_DATA_N2 0x6003c
+#define PIPEA_DATA_N2_OFFSET 0
+
+#define PIPEA_LINK_M1 0x60040
+#define PIPEA_LINK_M1_OFFSET 0
+#define PIPEA_LINK_N1 0x60044
+#define PIPEA_LINK_N1_OFFSET 0
+
+#define PIPEA_LINK_M2 0x60048
+#define PIPEA_LINK_M2_OFFSET 0
+#define PIPEA_LINK_N2 0x6004c
+#define PIPEA_LINK_N2_OFFSET 0
+
+/* PIPEB timing regs are same start from 0x61000 */
+
+#define PIPEB_DATA_M1 0x61030
+#define PIPEB_DATA_M1_OFFSET 0
+#define PIPEB_DATA_N1 0x61034
+#define PIPEB_DATA_N1_OFFSET 0
+
+#define PIPEB_DATA_M2 0x61038
+#define PIPEB_DATA_M2_OFFSET 0
+#define PIPEB_DATA_N2 0x6103c
+#define PIPEB_DATA_N2_OFFSET 0
+
+#define PIPEB_LINK_M1 0x61040
+#define PIPEB_LINK_M1_OFFSET 0
+#define PIPEB_LINK_N1 0x61044
+#define PIPEB_LINK_N1_OFFSET 0
+
+#define PIPEB_LINK_M2 0x61048
+#define PIPEB_LINK_M2_OFFSET 0
+#define PIPEB_LINK_N2 0x6104c
+#define PIPEB_LINK_N2_OFFSET 0
+
+/* CPU panel fitter */
+#define PFA_CTL_1 0x68080
+#define PFB_CTL_1 0x68880
+#define PF_ENABLE (1<<31)
+
+/* legacy palette */
+#define LGC_PALETTE_A 0x4a000
+#define LGC_PALETTE_B 0x4a800
+
+/* interrupts */
+#define DE_MASTER_IRQ_CONTROL (1 << 31)
+#define DE_SPRITEB_FLIP_DONE (1 << 29)
+#define DE_SPRITEA_FLIP_DONE (1 << 28)
+#define DE_PLANEB_FLIP_DONE (1 << 27)
+#define DE_PLANEA_FLIP_DONE (1 << 26)
+#define DE_PCU_EVENT (1 << 25)
+#define DE_GTT_FAULT (1 << 24)
+#define DE_POISON (1 << 23)
+#define DE_PERFORM_COUNTER (1 << 22)
+#define DE_PCH_EVENT (1 << 21)
+#define DE_AUX_CHANNEL_A (1 << 20)
+#define DE_DP_A_HOTPLUG (1 << 19)
+#define DE_GSE (1 << 18)
+#define DE_PIPEB_VBLANK (1 << 15)
+#define DE_PIPEB_EVEN_FIELD (1 << 14)
+#define DE_PIPEB_ODD_FIELD (1 << 13)
+#define DE_PIPEB_LINE_COMPARE (1 << 12)
+#define DE_PIPEB_VSYNC (1 << 11)
+#define DE_PIPEB_FIFO_UNDERRUN (1 << 8)
+#define DE_PIPEA_VBLANK (1 << 7)
+#define DE_PIPEA_EVEN_FIELD (1 << 6)
+#define DE_PIPEA_ODD_FIELD (1 << 5)
+#define DE_PIPEA_LINE_COMPARE (1 << 4)
+#define DE_PIPEA_VSYNC (1 << 3)
+#define DE_PIPEA_FIFO_UNDERRUN (1 << 0)
+
+#define DEISR 0x44000
+#define DEIMR 0x44004
+#define DEIIR 0x44008
+#define DEIER 0x4400c
+
+/* GT interrupt */
+#define GT_SYNC_STATUS (1 << 2)
+#define GT_USER_INTERRUPT (1 << 0)
+
+#define GTISR 0x44010
+#define GTIMR 0x44014
+#define GTIIR 0x44018
+#define GTIER 0x4401c
+
+/* PCH */
+
+/* south display engine interrupt */
+#define SDE_CRT_HOTPLUG (1 << 11)
+#define SDE_PORTD_HOTPLUG (1 << 10)
+#define SDE_PORTC_HOTPLUG (1 << 9)
+#define SDE_PORTB_HOTPLUG (1 << 8)
+#define SDE_SDVOB_HOTPLUG (1 << 6)
+
+#define SDEISR 0xc4000
+#define SDEIMR 0xc4004
+#define SDEIIR 0xc4008
+#define SDEIER 0xc400c
+
+/* digital port hotplug */
+#define PCH_PORT_HOTPLUG 0xc4030
+#define PORTD_HOTPLUG_ENABLE (1 << 20)
+#define PORTD_PULSE_DURATION_2ms (0)
+#define PORTD_PULSE_DURATION_4_5ms (1 << 18)
+#define PORTD_PULSE_DURATION_6ms (2 << 18)
+#define PORTD_PULSE_DURATION_100ms (3 << 18)
+#define PORTD_HOTPLUG_NO_DETECT (0)
+#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16)
+#define PORTD_HOTPLUG_LONG_DETECT (1 << 17)
+#define PORTC_HOTPLUG_ENABLE (1 << 12)
+#define PORTC_PULSE_DURATION_2ms (0)
+#define PORTC_PULSE_DURATION_4_5ms (1 << 10)
+#define PORTC_PULSE_DURATION_6ms (2 << 10)
+#define PORTC_PULSE_DURATION_100ms (3 << 10)
+#define PORTC_HOTPLUG_NO_DETECT (0)
+#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8)
+#define PORTC_HOTPLUG_LONG_DETECT (1 << 9)
+#define PORTB_HOTPLUG_ENABLE (1 << 4)
+#define PORTB_PULSE_DURATION_2ms (0)
+#define PORTB_PULSE_DURATION_4_5ms (1 << 2)
+#define PORTB_PULSE_DURATION_6ms (2 << 2)
+#define PORTB_PULSE_DURATION_100ms (3 << 2)
+#define PORTB_HOTPLUG_NO_DETECT (0)
+#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0)
+#define PORTB_HOTPLUG_LONG_DETECT (1 << 1)
+
+#define PCH_GPIOA 0xc5010
+#define PCH_GPIOB 0xc5014
+#define PCH_GPIOC 0xc5018
+#define PCH_GPIOD 0xc501c
+#define PCH_GPIOE 0xc5020
+#define PCH_GPIOF 0xc5024
+
+#define PCH_DPLL_A 0xc6014
+#define PCH_DPLL_B 0xc6018
+
+#define PCH_FPA0 0xc6040
+#define PCH_FPA1 0xc6044
+#define PCH_FPB0 0xc6048
+#define PCH_FPB1 0xc604c
+
+#define PCH_DPLL_TEST 0xc606c
+
+#define PCH_DREF_CONTROL 0xC6200
+#define DREF_CONTROL_MASK 0x7fc3
+#define DREF_CPU_SOURCE_OUTPUT_DISABLE (0<<13)
+#define DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD (2<<13)
+#define DREF_CPU_SOURCE_OUTPUT_NONSPREAD (3<<13)
+#define DREF_CPU_SOURCE_OUTPUT_MASK (3<<13)
+#define DREF_SSC_SOURCE_DISABLE (0<<11)
+#define DREF_SSC_SOURCE_ENABLE (2<<11)
+#define DREF_SSC_SOURCE_MASK (2<<11)
+#define DREF_NONSPREAD_SOURCE_DISABLE (0<<9)
+#define DREF_NONSPREAD_CK505_ENABLE (1<<9)
+#define DREF_NONSPREAD_SOURCE_ENABLE (2<<9)
+#define DREF_NONSPREAD_SOURCE_MASK (2<<9)
+#define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7)
+#define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7)
+#define DREF_SSC4_DOWNSPREAD (0<<6)
+#define DREF_SSC4_CENTERSPREAD (1<<6)
+#define DREF_SSC1_DISABLE (0<<1)
+#define DREF_SSC1_ENABLE (1<<1)
+#define DREF_SSC4_DISABLE (0)
+#define DREF_SSC4_ENABLE (1)
+
+#define PCH_RAWCLK_FREQ 0xc6204
+#define FDL_TP1_TIMER_SHIFT 12
+#define FDL_TP1_TIMER_MASK (3<<12)
+#define FDL_TP2_TIMER_SHIFT 10
+#define FDL_TP2_TIMER_MASK (3<<10)
+#define RAWCLK_FREQ_MASK 0x3ff
+
+#define PCH_DPLL_TMR_CFG 0xc6208
+
+#define PCH_SSC4_PARMS 0xc6210
+#define PCH_SSC4_AUX_PARMS 0xc6214
+
+/* transcoder */
+
+#define TRANS_HTOTAL_A 0xe0000
+#define TRANS_HTOTAL_SHIFT 16
+#define TRANS_HACTIVE_SHIFT 0
+#define TRANS_HBLANK_A 0xe0004
+#define TRANS_HBLANK_END_SHIFT 16
+#define TRANS_HBLANK_START_SHIFT 0
+#define TRANS_HSYNC_A 0xe0008
+#define TRANS_HSYNC_END_SHIFT 16
+#define TRANS_HSYNC_START_SHIFT 0
+#define TRANS_VTOTAL_A 0xe000c
+#define TRANS_VTOTAL_SHIFT 16
+#define TRANS_VACTIVE_SHIFT 0
+#define TRANS_VBLANK_A 0xe0010
+#define TRANS_VBLANK_END_SHIFT 16
+#define TRANS_VBLANK_START_SHIFT 0
+#define TRANS_VSYNC_A 0xe0014
+#define TRANS_VSYNC_END_SHIFT 16
+#define TRANS_VSYNC_START_SHIFT 0
+
+#define TRANSA_DATA_M1 0xe0030
+#define TRANSA_DATA_N1 0xe0034
+#define TRANSA_DATA_M2 0xe0038
+#define TRANSA_DATA_N2 0xe003c
+#define TRANSA_DP_LINK_M1 0xe0040
+#define TRANSA_DP_LINK_N1 0xe0044
+#define TRANSA_DP_LINK_M2 0xe0048
+#define TRANSA_DP_LINK_N2 0xe004c
+
+#define TRANS_HTOTAL_B 0xe1000
+#define TRANS_HBLANK_B 0xe1004
+#define TRANS_HSYNC_B 0xe1008
+#define TRANS_VTOTAL_B 0xe100c
+#define TRANS_VBLANK_B 0xe1010
+#define TRANS_VSYNC_B 0xe1014
+
+#define TRANSB_DATA_M1 0xe1030
+#define TRANSB_DATA_N1 0xe1034
+#define TRANSB_DATA_M2 0xe1038
+#define TRANSB_DATA_N2 0xe103c
+#define TRANSB_DP_LINK_M1 0xe1040
+#define TRANSB_DP_LINK_N1 0xe1044
+#define TRANSB_DP_LINK_M2 0xe1048
+#define TRANSB_DP_LINK_N2 0xe104c
+
+#define TRANSACONF 0xf0008
+#define TRANSBCONF 0xf1008
+#define TRANS_DISABLE (0<<31)
+#define TRANS_ENABLE (1<<31)
+#define TRANS_STATE_MASK (1<<30)
+#define TRANS_STATE_DISABLE (0<<30)
+#define TRANS_STATE_ENABLE (1<<30)
+#define TRANS_FSYNC_DELAY_HB1 (0<<27)
+#define TRANS_FSYNC_DELAY_HB2 (1<<27)
+#define TRANS_FSYNC_DELAY_HB3 (2<<27)
+#define TRANS_FSYNC_DELAY_HB4 (3<<27)
+#define TRANS_DP_AUDIO_ONLY (1<<26)
+#define TRANS_DP_VIDEO_AUDIO (0<<26)
+#define TRANS_PROGRESSIVE (0<<21)
+#define TRANS_8BPC (0<<5)
+#define TRANS_10BPC (1<<5)
+#define TRANS_6BPC (2<<5)
+#define TRANS_12BPC (3<<5)
+
+#define FDI_RXA_CHICKEN 0xc200c
+#define FDI_RXB_CHICKEN 0xc2010
+#define FDI_RX_PHASE_SYNC_POINTER_ENABLE (1)
+
+/* CPU: FDI_TX */
+#define FDI_TXA_CTL 0x60100
+#define FDI_TXB_CTL 0x61100
+#define FDI_TX_DISABLE (0<<31)
+#define FDI_TX_ENABLE (1<<31)
+#define FDI_LINK_TRAIN_PATTERN_1 (0<<28)
+#define FDI_LINK_TRAIN_PATTERN_2 (1<<28)
+#define FDI_LINK_TRAIN_PATTERN_IDLE (2<<28)
+#define FDI_LINK_TRAIN_NONE (3<<28)
+#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0<<25)
+#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1<<25)
+#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2<<25)
+#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3<<25)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0<<22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1<<22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2<<22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3<<22)
+#define FDI_DP_PORT_WIDTH_X1 (0<<19)
+#define FDI_DP_PORT_WIDTH_X2 (1<<19)
+#define FDI_DP_PORT_WIDTH_X3 (2<<19)
+#define FDI_DP_PORT_WIDTH_X4 (3<<19)
+#define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18)
+/* IGDNG: hardwired to 1 */
+#define FDI_TX_PLL_ENABLE (1<<14)
+/* both Tx and Rx */
+#define FDI_SCRAMBLING_ENABLE (0<<7)
+#define FDI_SCRAMBLING_DISABLE (1<<7)
+
+/* FDI_RX, FDI_X is hard-wired to Transcoder_X */
+#define FDI_RXA_CTL 0xf000c
+#define FDI_RXB_CTL 0xf100c
+#define FDI_RX_ENABLE (1<<31)
+#define FDI_RX_DISABLE (0<<31)
+/* train, dp width same as FDI_TX */
+#define FDI_DP_PORT_WIDTH_X8 (7<<19)
+#define FDI_8BPC (0<<16)
+#define FDI_10BPC (1<<16)
+#define FDI_6BPC (2<<16)
+#define FDI_12BPC (3<<16)
+#define FDI_LINK_REVERSE_OVERWRITE (1<<15)
+#define FDI_DMI_LINK_REVERSE_MASK (1<<14)
+#define FDI_RX_PLL_ENABLE (1<<13)
+#define FDI_FS_ERR_CORRECT_ENABLE (1<<11)
+#define FDI_FE_ERR_CORRECT_ENABLE (1<<10)
+#define FDI_FS_ERR_REPORT_ENABLE (1<<9)
+#define FDI_FE_ERR_REPORT_ENABLE (1<<8)
+#define FDI_RX_ENHANCE_FRAME_ENABLE (1<<6)
+#define FDI_SEL_RAWCLK (0<<4)
+#define FDI_SEL_PCDCLK (1<<4)
+
+#define FDI_RXA_MISC 0xf0010
+#define FDI_RXB_MISC 0xf1010
+#define FDI_RXA_TUSIZE1 0xf0030
+#define FDI_RXA_TUSIZE2 0xf0038
+#define FDI_RXB_TUSIZE1 0xf1030
+#define FDI_RXB_TUSIZE2 0xf1038
+
+/* FDI_RX interrupt register format */
+#define FDI_RX_INTER_LANE_ALIGN (1<<10)
+#define FDI_RX_SYMBOL_LOCK (1<<9) /* train 2 */
+#define FDI_RX_BIT_LOCK (1<<8) /* train 1 */
+#define FDI_RX_TRAIN_PATTERN_2_FAIL (1<<7)
+#define FDI_RX_FS_CODE_ERR (1<<6)
+#define FDI_RX_FE_CODE_ERR (1<<5)
+#define FDI_RX_SYMBOL_ERR_RATE_ABOVE (1<<4)
+#define FDI_RX_HDCP_LINK_FAIL (1<<3)
+#define FDI_RX_PIXEL_FIFO_OVERFLOW (1<<2)
+#define FDI_RX_CROSS_CLOCK_OVERFLOW (1<<1)
+#define FDI_RX_SYMBOL_QUEUE_OVERFLOW (1<<0)
+
+#define FDI_RXA_IIR 0xf0014
+#define FDI_RXA_IMR 0xf0018
+#define FDI_RXB_IIR 0xf1014
+#define FDI_RXB_IMR 0xf1018
+
+#define FDI_PLL_CTL_1 0xfe000
+#define FDI_PLL_CTL_2 0xfe004
+
+/* CRT */
+#define PCH_ADPA 0xe1100
+#define ADPA_TRANS_SELECT_MASK (1<<30)
+#define ADPA_TRANS_A_SELECT 0
+#define ADPA_TRANS_B_SELECT (1<<30)
+#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */
+#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24)
+#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24)
+#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
+#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24)
+#define ADPA_CRT_HOTPLUG_ENABLE (1<<23)
+#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22)
+#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22)
+#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21)
+#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21)
+#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20)
+#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20)
+#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18)
+#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18)
+#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18)
+#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18)
+#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17)
+#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
+#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
+
+/* or SDVOB */
+#define HDMIB 0xe1140
+#define PORT_ENABLE (1 << 31)
+#define TRANSCODER_A (0)
+#define TRANSCODER_B (1 << 30)
+#define COLOR_FORMAT_8bpc (0)
+#define COLOR_FORMAT_12bpc (3 << 26)
+#define SDVOB_HOTPLUG_ENABLE (1 << 23)
+#define SDVO_ENCODING (0)
+#define TMDS_ENCODING (2 << 10)
+#define NULL_PACKET_VSYNC_ENABLE (1 << 9)
+#define SDVOB_BORDER_ENABLE (1 << 7)
+#define AUDIO_ENABLE (1 << 6)
+#define VSYNC_ACTIVE_HIGH (1 << 4)
+#define HSYNC_ACTIVE_HIGH (1 << 3)
+#define PORT_DETECTED (1 << 2)
+
+#define HDMIC 0xe1150
+#define HDMID 0xe1160
+
+#define PCH_LVDS 0xe1180
+#define LVDS_DETECTED (1 << 1)
+
+#define BLC_PWM_CPU_CTL2 0x48250
+#define PWM_ENABLE (1 << 31)
+#define PWM_PIPE_A (0 << 29)
+#define PWM_PIPE_B (1 << 29)
+#define BLC_PWM_CPU_CTL 0x48254
+
+#define BLC_PWM_PCH_CTL1 0xc8250
+#define PWM_PCH_ENABLE (1 << 31)
+#define PWM_POLARITY_ACTIVE_LOW (1 << 29)
+#define PWM_POLARITY_ACTIVE_HIGH (0 << 29)
+#define PWM_POLARITY_ACTIVE_LOW2 (1 << 28)
+#define PWM_POLARITY_ACTIVE_HIGH2 (0 << 28)
+
+#define BLC_PWM_PCH_CTL2 0xc8254
+
+#define PCH_PP_STATUS 0xc7200
+#define PCH_PP_CONTROL 0xc7204
+#define EDP_FORCE_VDD (1 << 3)
+#define EDP_BLC_ENABLE (1 << 2)
+#define PANEL_POWER_RESET (1 << 1)
+#define PANEL_POWER_OFF (0 << 0)
+#define PANEL_POWER_ON (1 << 0)
+#define PCH_PP_ON_DELAYS 0xc7208
+#define EDP_PANEL (1 << 30)
+#define PCH_PP_OFF_DELAYS 0xc720c
+#define PCH_PP_DIVISOR 0xc7210
+
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index d669cc2b42c0..a98e2831ed31 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -295,6 +295,16 @@ int i915_save_state(struct drm_device *dev)
i915_save_palette(dev, PIPE_B);
dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT);
+ /* Cursor state */
+ dev_priv->saveCURACNTR = I915_READ(CURACNTR);
+ dev_priv->saveCURAPOS = I915_READ(CURAPOS);
+ dev_priv->saveCURABASE = I915_READ(CURABASE);
+ dev_priv->saveCURBCNTR = I915_READ(CURBCNTR);
+ dev_priv->saveCURBPOS = I915_READ(CURBPOS);
+ dev_priv->saveCURBBASE = I915_READ(CURBBASE);
+ if (!IS_I9XX(dev))
+ dev_priv->saveCURSIZE = I915_READ(CURSIZE);
+
/* CRT state */
dev_priv->saveADPA = I915_READ(ADPA);
@@ -349,6 +359,18 @@ int i915_save_state(struct drm_device *dev)
for (i = 0; i < 3; i++)
dev_priv->saveSWF2[i] = I915_READ(SWF30 + (i << 2));
+ /* Fences */
+ if (IS_I965G(dev)) {
+ for (i = 0; i < 16; i++)
+ dev_priv->saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + (i * 8));
+ } else {
+ for (i = 0; i < 8; i++)
+ dev_priv->saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4));
+
+ if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+ for (i = 0; i < 8; i++)
+ dev_priv->saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4));
+ }
i915_save_vga(dev);
return 0;
@@ -371,6 +393,18 @@ int i915_restore_state(struct drm_device *dev)
/* Display arbitration */
I915_WRITE(DSPARB, dev_priv->saveDSPARB);
+ /* Fences */
+ if (IS_I965G(dev)) {
+ for (i = 0; i < 16; i++)
+ I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->saveFENCE[i]);
+ } else {
+ for (i = 0; i < 8; i++)
+ I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->saveFENCE[i]);
+ if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+ for (i = 0; i < 8; i++)
+ I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->saveFENCE[i+8]);
+ }
+
/* Pipe & plane A info */
/* Prime the clock */
if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) {
@@ -456,6 +490,16 @@ int i915_restore_state(struct drm_device *dev)
I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR);
I915_WRITE(DSPBADDR, I915_READ(DSPBADDR));
+ /* Cursor state */
+ I915_WRITE(CURAPOS, dev_priv->saveCURAPOS);
+ I915_WRITE(CURACNTR, dev_priv->saveCURACNTR);
+ I915_WRITE(CURABASE, dev_priv->saveCURABASE);
+ I915_WRITE(CURBPOS, dev_priv->saveCURBPOS);
+ I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR);
+ I915_WRITE(CURBBASE, dev_priv->saveCURBBASE);
+ if (!IS_I9XX(dev))
+ I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
+
/* CRT state */
I915_WRITE(ADPA, dev_priv->saveADPA);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index fc28e2bbd542..754dd22fdd77 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -30,6 +30,8 @@
#include "i915_drv.h"
#include "intel_bios.h"
+#define SLAVE_ADDR1 0x70
+#define SLAVE_ADDR2 0x72
static void *
find_section(struct bdb_header *bdb, int section_id)
@@ -57,9 +59,43 @@ find_section(struct bdb_header *bdb, int section_id)
return NULL;
}
-/* Try to find panel data */
static void
-parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
+ struct lvds_dvo_timing *dvo_timing)
+{
+ panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
+ dvo_timing->hactive_lo;
+ panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
+ panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
+ dvo_timing->hsync_pulse_width;
+ panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+
+ panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
+ dvo_timing->vactive_lo;
+ panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
+ dvo_timing->vsync_off;
+ panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
+ dvo_timing->vsync_pulse_width;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
+ ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
+ panel_fixed_mode->clock = dvo_timing->clock * 10;
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+
+ /* Some VBTs have bogus h/vtotal values */
+ if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
+ panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
+ if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
+ panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
+
+ drm_mode_set_name(panel_fixed_mode);
+}
+
+/* Try to find integrated panel data */
+static void
+parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
{
struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data;
@@ -91,38 +127,45 @@ parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
DRM_MEM_DRIVER);
- panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
- dvo_timing->hactive_lo;
- panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
- ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
- panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
- dvo_timing->hsync_pulse_width;
- panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
- ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+ fill_detail_timing_data(panel_fixed_mode, dvo_timing);
- panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
- dvo_timing->vactive_lo;
- panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
- dvo_timing->vsync_off;
- panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
- dvo_timing->vsync_pulse_width;
- panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
- ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
- panel_fixed_mode->clock = dvo_timing->clock * 10;
- panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+ dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
- /* Some VBTs have bogus h/vtotal values */
- if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
- panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
- if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
- panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
+ DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
- drm_mode_set_name(panel_fixed_mode);
+ return;
+}
- dev_priv->vbt_mode = panel_fixed_mode;
+/* Try to find sdvo panel data */
+static void
+parse_sdvo_panel_data(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_sdvo_lvds_options *sdvo_lvds_options;
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
- DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
- drm_mode_debug_printmodeline(panel_fixed_mode);
+ dev_priv->sdvo_lvds_vbt_mode = NULL;
+
+ sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS);
+ if (!sdvo_lvds_options)
+ return;
+
+ dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS);
+ if (!dvo_timing)
+ return;
+
+ panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
+ DRM_MEM_DRIVER);
+
+ if (!panel_fixed_mode)
+ return;
+
+ fill_detail_timing_data(panel_fixed_mode,
+ dvo_timing + sdvo_lvds_options->panel_type);
+
+ dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode;
return;
}
@@ -152,6 +195,88 @@ parse_general_features(struct drm_i915_private *dev_priv,
}
}
+static void
+parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct sdvo_device_mapping *p_mapping;
+ struct bdb_general_definitions *p_defs;
+ struct child_device_config *p_child;
+ int i, child_device_num, count;
+ u16 block_size, *block_ptr;
+
+ p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+ if (!p_defs) {
+ DRM_DEBUG("No general definition block is found\n");
+ return;
+ }
+ /* judge whether the size of child device meets the requirements.
+ * If the child device size obtained from general definition block
+ * is different with sizeof(struct child_device_config), skip the
+ * parsing of sdvo device info
+ */
+ if (p_defs->child_dev_size != sizeof(*p_child)) {
+ /* different child dev size . Ignore it */
+ DRM_DEBUG("different child size is found. Invalid.\n");
+ return;
+ }
+ /* get the block size of general definitions */
+ block_ptr = (u16 *)((char *)p_defs - 2);
+ block_size = *block_ptr;
+ /* get the number of child device */
+ child_device_num = (block_size - sizeof(*p_defs)) /
+ sizeof(*p_child);
+ count = 0;
+ for (i = 0; i < child_device_num; i++) {
+ p_child = &(p_defs->devices[i]);
+ if (!p_child->device_type) {
+ /* skip the device block if device type is invalid */
+ continue;
+ }
+ if (p_child->slave_addr != SLAVE_ADDR1 &&
+ p_child->slave_addr != SLAVE_ADDR2) {
+ /*
+ * If the slave address is neither 0x70 nor 0x72,
+ * it is not a SDVO device. Skip it.
+ */
+ continue;
+ }
+ if (p_child->dvo_port != DEVICE_PORT_DVOB &&
+ p_child->dvo_port != DEVICE_PORT_DVOC) {
+ /* skip the incorrect SDVO port */
+ DRM_DEBUG("Incorrect SDVO port. Skip it \n");
+ continue;
+ }
+ DRM_DEBUG("the SDVO device with slave addr %2x is found on "
+ "%s port\n",
+ p_child->slave_addr,
+ (p_child->dvo_port == DEVICE_PORT_DVOB) ?
+ "SDVOB" : "SDVOC");
+ p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]);
+ if (!p_mapping->initialized) {
+ p_mapping->dvo_port = p_child->dvo_port;
+ p_mapping->slave_addr = p_child->slave_addr;
+ p_mapping->dvo_wiring = p_child->dvo_wiring;
+ p_mapping->initialized = 1;
+ } else {
+ DRM_DEBUG("Maybe one SDVO port is shared by "
+ "two SDVO device.\n");
+ }
+ if (p_child->slave2_addr) {
+ /* Maybe this is a SDVO device with multiple inputs */
+ /* And the mapping info is not added */
+ DRM_DEBUG("there exists the slave2_addr. Maybe this "
+ "is a SDVO device with multiple inputs.\n");
+ }
+ count++;
+ }
+
+ if (!count) {
+ /* No SDVO device info is found */
+ DRM_DEBUG("No SDVO device info is found in VBT\n");
+ }
+ return;
+}
/**
* intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device
@@ -199,8 +324,9 @@ intel_init_bios(struct drm_device *dev)
/* Grab useful general definitions */
parse_general_features(dev_priv, bdb);
- parse_panel_data(dev_priv, bdb);
-
+ parse_lfp_panel_data(dev_priv, bdb);
+ parse_sdvo_panel_data(dev_priv, bdb);
+ parse_sdvo_device_mapping(dev_priv, bdb);
pci_unmap_rom(pdev, bios);
return 0;
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index de621aad85b5..fe72e1c225d8 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -135,6 +135,86 @@ struct bdb_general_features {
u8 rsvd11:6; /* finish byte */
} __attribute__((packed));
+/* pre-915 */
+#define GPIO_PIN_DVI_LVDS 0x03 /* "DVI/LVDS DDC GPIO pins" */
+#define GPIO_PIN_ADD_I2C 0x05 /* "ADDCARD I2C GPIO pins" */
+#define GPIO_PIN_ADD_DDC 0x04 /* "ADDCARD DDC GPIO pins" */
+#define GPIO_PIN_ADD_DDC_I2C 0x06 /* "ADDCARD DDC/I2C GPIO pins" */
+
+/* Pre 915 */
+#define DEVICE_TYPE_NONE 0x00
+#define DEVICE_TYPE_CRT 0x01
+#define DEVICE_TYPE_TV 0x09
+#define DEVICE_TYPE_EFP 0x12
+#define DEVICE_TYPE_LFP 0x22
+/* On 915+ */
+#define DEVICE_TYPE_CRT_DPMS 0x6001
+#define DEVICE_TYPE_CRT_DPMS_HOTPLUG 0x4001
+#define DEVICE_TYPE_TV_COMPOSITE 0x0209
+#define DEVICE_TYPE_TV_MACROVISION 0x0289
+#define DEVICE_TYPE_TV_RF_COMPOSITE 0x020c
+#define DEVICE_TYPE_TV_SVIDEO_COMPOSITE 0x0609
+#define DEVICE_TYPE_TV_SCART 0x0209
+#define DEVICE_TYPE_TV_CODEC_HOTPLUG_PWR 0x6009
+#define DEVICE_TYPE_EFP_HOTPLUG_PWR 0x6012
+#define DEVICE_TYPE_EFP_DVI_HOTPLUG_PWR 0x6052
+#define DEVICE_TYPE_EFP_DVI_I 0x6053
+#define DEVICE_TYPE_EFP_DVI_D_DUAL 0x6152
+#define DEVICE_TYPE_EFP_DVI_D_HDCP 0x60d2
+#define DEVICE_TYPE_OPENLDI_HOTPLUG_PWR 0x6062
+#define DEVICE_TYPE_OPENLDI_DUALPIX 0x6162
+#define DEVICE_TYPE_LFP_PANELLINK 0x5012
+#define DEVICE_TYPE_LFP_CMOS_PWR 0x5042
+#define DEVICE_TYPE_LFP_LVDS_PWR 0x5062
+#define DEVICE_TYPE_LFP_LVDS_DUAL 0x5162
+#define DEVICE_TYPE_LFP_LVDS_DUAL_HDCP 0x51e2
+
+#define DEVICE_CFG_NONE 0x00
+#define DEVICE_CFG_12BIT_DVOB 0x01
+#define DEVICE_CFG_12BIT_DVOC 0x02
+#define DEVICE_CFG_24BIT_DVOBC 0x09
+#define DEVICE_CFG_24BIT_DVOCB 0x0a
+#define DEVICE_CFG_DUAL_DVOB 0x11
+#define DEVICE_CFG_DUAL_DVOC 0x12
+#define DEVICE_CFG_DUAL_DVOBC 0x13
+#define DEVICE_CFG_DUAL_LINK_DVOBC 0x19
+#define DEVICE_CFG_DUAL_LINK_DVOCB 0x1a
+
+#define DEVICE_WIRE_NONE 0x00
+#define DEVICE_WIRE_DVOB 0x01
+#define DEVICE_WIRE_DVOC 0x02
+#define DEVICE_WIRE_DVOBC 0x03
+#define DEVICE_WIRE_DVOBB 0x05
+#define DEVICE_WIRE_DVOCC 0x06
+#define DEVICE_WIRE_DVOB_MASTER 0x0d
+#define DEVICE_WIRE_DVOC_MASTER 0x0e
+
+#define DEVICE_PORT_DVOA 0x00 /* none on 845+ */
+#define DEVICE_PORT_DVOB 0x01
+#define DEVICE_PORT_DVOC 0x02
+
+struct child_device_config {
+ u16 handle;
+ u16 device_type;
+ u8 device_id[10]; /* See DEVICE_TYPE_* above */
+ u16 addin_offset;
+ u8 dvo_port; /* See Device_PORT_* above */
+ u8 i2c_pin;
+ u8 slave_addr;
+ u8 ddc_pin;
+ u16 edid_ptr;
+ u8 dvo_cfg; /* See DEVICE_CFG_* above */
+ u8 dvo2_port;
+ u8 i2c2_pin;
+ u8 slave2_addr;
+ u8 ddc2_pin;
+ u8 capabilities;
+ u8 dvo_wiring;/* See DEVICE_WIRE_* above */
+ u8 dvo2_wiring;
+ u16 extended_type;
+ u8 dvo_function;
+} __attribute__((packed));
+
struct bdb_general_definitions {
/* DDC GPIO */
u8 crt_ddc_gmbus_pin;
@@ -149,14 +229,19 @@ struct bdb_general_definitions {
u8 boot_display[2];
u8 child_dev_size;
- /* device info */
- u8 tv_or_lvds_info[33];
- u8 dev1[33];
- u8 dev2[33];
- u8 dev3[33];
- u8 dev4[33];
- /* may be another device block here on some platforms */
-};
+ /*
+ * Device info:
+ * If TV is present, it'll be at devices[0].
+ * LVDS will be next, either devices[0] or [1], if present.
+ * On some platforms the number of device is 6. But could be as few as
+ * 4 if both TV and LVDS are missing.
+ * And the device num is related with the size of general definition
+ * block. It is obtained by using the following formula:
+ * number = (block_size - sizeof(bdb_general_definitions))/
+ * sizeof(child_device_config);
+ */
+ struct child_device_config devices[0];
+} __attribute__((packed));
struct bdb_lvds_options {
u8 panel_type;
@@ -279,6 +364,23 @@ struct vch_bdb_22 {
struct vch_panel_data panels[16];
} __attribute__((packed));
+struct bdb_sdvo_lvds_options {
+ u8 panel_backlight;
+ u8 h40_set_panel_type;
+ u8 panel_type;
+ u8 ssc_clk_freq;
+ u16 als_low_trip;
+ u16 als_high_trip;
+ u8 sclalarcoeff_tab_row_num;
+ u8 sclalarcoeff_tab_row_size;
+ u8 coefficient[8];
+ u8 panel_misc_bits_1;
+ u8 panel_misc_bits_2;
+ u8 panel_misc_bits_3;
+ u8 panel_misc_bits_4;
+} __attribute__((packed));
+
+
bool intel_init_bios(struct drm_device *dev);
/*
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 9bdd959260a5..6de97fc66029 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -37,9 +37,14 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 temp;
+ u32 temp, reg;
- temp = I915_READ(ADPA);
+ if (IS_IGDNG(dev))
+ reg = PCH_ADPA;
+ else
+ reg = ADPA;
+
+ temp = I915_READ(reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp |= ADPA_DAC_ENABLE;
@@ -58,7 +63,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
break;
}
- I915_WRITE(ADPA, temp);
+ I915_WRITE(reg, temp);
}
static int intel_crt_mode_valid(struct drm_connector *connector,
@@ -101,17 +106,23 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
struct drm_i915_private *dev_priv = dev->dev_private;
int dpll_md_reg;
u32 adpa, dpll_md;
+ u32 adpa_reg;
if (intel_crtc->pipe == 0)
dpll_md_reg = DPLL_A_MD;
else
dpll_md_reg = DPLL_B_MD;
+ if (IS_IGDNG(dev))
+ adpa_reg = PCH_ADPA;
+ else
+ adpa_reg = ADPA;
+
/*
* Disable separate mode multiplier used when cloning SDVO to CRT
* XXX this needs to be adjusted when we really are cloning
*/
- if (IS_I965G(dev)) {
+ if (IS_I965G(dev) && !IS_IGDNG(dev)) {
dpll_md = I915_READ(dpll_md_reg);
I915_WRITE(dpll_md_reg,
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -125,13 +136,53 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (intel_crtc->pipe == 0) {
adpa |= ADPA_PIPE_A_SELECT;
- I915_WRITE(BCLRPAT_A, 0);
+ if (!IS_IGDNG(dev))
+ I915_WRITE(BCLRPAT_A, 0);
} else {
adpa |= ADPA_PIPE_B_SELECT;
- I915_WRITE(BCLRPAT_B, 0);
+ if (!IS_IGDNG(dev))
+ I915_WRITE(BCLRPAT_B, 0);
}
- I915_WRITE(ADPA, adpa);
+ I915_WRITE(adpa_reg, adpa);
+}
+
+static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 adpa, temp;
+ bool ret;
+
+ temp = adpa = I915_READ(PCH_ADPA);
+
+ adpa &= ~ADPA_CRT_HOTPLUG_MASK;
+
+ adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
+ ADPA_CRT_HOTPLUG_WARMUP_10MS |
+ ADPA_CRT_HOTPLUG_SAMPLE_4S |
+ ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */
+ ADPA_CRT_HOTPLUG_VOLREF_325MV |
+ ADPA_CRT_HOTPLUG_ENABLE |
+ ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
+
+ DRM_DEBUG("pch crt adpa 0x%x", adpa);
+ I915_WRITE(PCH_ADPA, adpa);
+
+ /* This might not be needed as not specified in spec...*/
+ udelay(1000);
+
+ /* Check the status to see if both blue and green are on now */
+ adpa = I915_READ(PCH_ADPA);
+ if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
+ ADPA_CRT_HOTPLUG_MONITOR_COLOR)
+ ret = true;
+ else
+ ret = false;
+
+ /* restore origin register */
+ I915_WRITE(PCH_ADPA, temp);
+ return ret;
}
/**
@@ -148,6 +199,10 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 hotplug_en;
int i, tries = 0;
+
+ if (IS_IGDNG(dev))
+ return intel_igdng_crt_detect_hotplug(connector);
+
/*
* On 4 series desktop, CRT detect sequence need to be done twice
* to get a reliable result.
@@ -161,7 +216,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
hotplug_en &= CRT_FORCE_HOTPLUG_MASK;
hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
- if (IS_GM45(dev))
+ if (IS_G4X(dev))
hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
@@ -198,9 +253,142 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
return intel_ddc_probe(intel_output);
}
+static enum drm_connector_status
+intel_crt_load_detect(struct drm_crtc *crtc, struct intel_output *intel_output)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ uint32_t pipe = intel_crtc->pipe;
+ uint32_t save_bclrpat;
+ uint32_t save_vtotal;
+ uint32_t vtotal, vactive;
+ uint32_t vsample;
+ uint32_t vblank, vblank_start, vblank_end;
+ uint32_t dsl;
+ uint32_t bclrpat_reg;
+ uint32_t vtotal_reg;
+ uint32_t vblank_reg;
+ uint32_t vsync_reg;
+ uint32_t pipeconf_reg;
+ uint32_t pipe_dsl_reg;
+ uint8_t st00;
+ enum drm_connector_status status;
+
+ if (pipe == 0) {
+ bclrpat_reg = BCLRPAT_A;
+ vtotal_reg = VTOTAL_A;
+ vblank_reg = VBLANK_A;
+ vsync_reg = VSYNC_A;
+ pipeconf_reg = PIPEACONF;
+ pipe_dsl_reg = PIPEADSL;
+ } else {
+ bclrpat_reg = BCLRPAT_B;
+ vtotal_reg = VTOTAL_B;
+ vblank_reg = VBLANK_B;
+ vsync_reg = VSYNC_B;
+ pipeconf_reg = PIPEBCONF;
+ pipe_dsl_reg = PIPEBDSL;
+ }
+
+ save_bclrpat = I915_READ(bclrpat_reg);
+ save_vtotal = I915_READ(vtotal_reg);
+ vblank = I915_READ(vblank_reg);
+
+ vtotal = ((save_vtotal >> 16) & 0xfff) + 1;
+ vactive = (save_vtotal & 0x7ff) + 1;
+
+ vblank_start = (vblank & 0xfff) + 1;
+ vblank_end = ((vblank >> 16) & 0xfff) + 1;
+
+ /* Set the border color to purple. */
+ I915_WRITE(bclrpat_reg, 0x500050);
+
+ if (IS_I9XX(dev)) {
+ uint32_t pipeconf = I915_READ(pipeconf_reg);
+ I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER);
+ /* Wait for next Vblank to substitue
+ * border color for Color info */
+ intel_wait_for_vblank(dev);
+ st00 = I915_READ8(VGA_MSR_WRITE);
+ status = ((st00 & (1 << 4)) != 0) ?
+ connector_status_connected :
+ connector_status_disconnected;
+
+ I915_WRITE(pipeconf_reg, pipeconf);
+ } else {
+ bool restore_vblank = false;
+ int count, detect;
+
+ /*
+ * If there isn't any border, add some.
+ * Yes, this will flicker
+ */
+ if (vblank_start <= vactive && vblank_end >= vtotal) {
+ uint32_t vsync = I915_READ(vsync_reg);
+ uint32_t vsync_start = (vsync & 0xffff) + 1;
+
+ vblank_start = vsync_start;
+ I915_WRITE(vblank_reg,
+ (vblank_start - 1) |
+ ((vblank_end - 1) << 16));
+ restore_vblank = true;
+ }
+ /* sample in the vertical border, selecting the larger one */
+ if (vblank_start - vactive >= vtotal - vblank_end)
+ vsample = (vblank_start + vactive) >> 1;
+ else
+ vsample = (vtotal + vblank_end) >> 1;
+
+ /*
+ * Wait for the border to be displayed
+ */
+ while (I915_READ(pipe_dsl_reg) >= vactive)
+ ;
+ while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample)
+ ;
+ /*
+ * Watch ST00 for an entire scanline
+ */
+ detect = 0;
+ count = 0;
+ do {
+ count++;
+ /* Read the ST00 VGA status register */
+ st00 = I915_READ8(VGA_MSR_WRITE);
+ if (st00 & (1 << 4))
+ detect++;
+ } while ((I915_READ(pipe_dsl_reg) == dsl));
+
+ /* restore vblank if necessary */
+ if (restore_vblank)
+ I915_WRITE(vblank_reg, vblank);
+ /*
+ * If more than 3/4 of the scanline detected a monitor,
+ * then it is assumed to be present. This works even on i830,
+ * where there isn't any way to force the border color across
+ * the screen
+ */
+ status = detect * 4 > count * 3 ?
+ connector_status_connected :
+ connector_status_disconnected;
+ }
+
+ /* Restore previous settings */
+ I915_WRITE(bclrpat_reg, save_bclrpat);
+
+ return status;
+}
+
static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_crtc *crtc;
+ int dpms_mode;
+ enum drm_connector_status status;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
if (intel_crt_detect_hotplug(connector))
@@ -212,8 +400,20 @@ static enum drm_connector_status intel_crt_detect(struct drm_connector *connecto
if (intel_crt_detect_ddc(connector))
return connector_status_connected;
- /* TODO use load detect */
- return connector_status_unknown;
+ /* for pre-945g platforms use load detect */
+ if (encoder->crtc && encoder->crtc->enabled) {
+ status = intel_crt_load_detect(encoder->crtc, intel_output);
+ } else {
+ crtc = intel_get_load_detect_pipe(intel_output,
+ NULL, &dpms_mode);
+ if (crtc) {
+ status = intel_crt_load_detect(crtc, intel_output);
+ intel_release_load_detect_pipe(intel_output, dpms_mode);
+ } else
+ status = connector_status_unknown;
+ }
+
+ return status;
}
static void intel_crt_destroy(struct drm_connector *connector)
@@ -236,11 +436,6 @@ static int intel_crt_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
- struct drm_device *dev = connector->dev;
-
- if (property == dev->mode_config.dpms_property && connector->encoder)
- intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
-
return 0;
}
@@ -257,6 +452,7 @@ static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
};
static const struct drm_connector_funcs intel_crt_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = intel_crt_destroy,
@@ -282,6 +478,7 @@ void intel_crt_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct intel_output *intel_output;
+ u32 i2c_reg;
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
if (!intel_output)
@@ -298,7 +495,11 @@ void intel_crt_init(struct drm_device *dev)
&intel_output->enc);
/* Set up the DDC bus. */
- intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (IS_IGDNG(dev))
+ i2c_reg = PCH_GPIOA;
+ else
+ i2c_reg = GPIOA;
+ intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
if (!intel_output->ddc_bus) {
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
"failed.\n");
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index c2c8e95ff14d..028f5b66e3d8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -137,6 +137,8 @@ struct intel_limit {
#define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS 7
#define INTEL_LIMIT_IGD_SDVO_DAC 8
#define INTEL_LIMIT_IGD_LVDS 9
+#define INTEL_LIMIT_IGDNG_SDVO_DAC 10
+#define INTEL_LIMIT_IGDNG_LVDS 11
/*The parameter is for SDVO on G4x platform*/
#define G4X_DOT_SDVO_MIN 25000
@@ -216,12 +218,43 @@ struct intel_limit {
#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
+/* IGDNG */
+/* as we calculate clock using (register_value + 2) for
+ N/M1/M2, so here the range value for them is (actual_value-2).
+ */
+#define IGDNG_DOT_MIN 25000
+#define IGDNG_DOT_MAX 350000
+#define IGDNG_VCO_MIN 1760000
+#define IGDNG_VCO_MAX 3510000
+#define IGDNG_N_MIN 1
+#define IGDNG_N_MAX 5
+#define IGDNG_M_MIN 79
+#define IGDNG_M_MAX 118
+#define IGDNG_M1_MIN 12
+#define IGDNG_M1_MAX 23
+#define IGDNG_M2_MIN 5
+#define IGDNG_M2_MAX 9
+#define IGDNG_P_SDVO_DAC_MIN 5
+#define IGDNG_P_SDVO_DAC_MAX 80
+#define IGDNG_P_LVDS_MIN 28
+#define IGDNG_P_LVDS_MAX 112
+#define IGDNG_P1_MIN 1
+#define IGDNG_P1_MAX 8
+#define IGDNG_P2_SDVO_DAC_SLOW 10
+#define IGDNG_P2_SDVO_DAC_FAST 5
+#define IGDNG_P2_LVDS_SLOW 14 /* single channel */
+#define IGDNG_P2_LVDS_FAST 7 /* double channel */
+#define IGDNG_P2_DOT_LIMIT 225000 /* 225Mhz */
+
static bool
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
static bool
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
static const intel_limit_t intel_limits[] = {
{ /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -383,9 +416,47 @@ static const intel_limit_t intel_limits[] = {
.p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW },
.find_pll = intel_find_best_PLL,
},
-
+ { /* INTEL_LIMIT_IGDNG_SDVO_DAC */
+ .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
+ .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
+ .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
+ .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
+ .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
+ .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
+ .p = { .min = IGDNG_P_SDVO_DAC_MIN, .max = IGDNG_P_SDVO_DAC_MAX },
+ .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
+ .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+ .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
+ .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
+ .find_pll = intel_igdng_find_best_PLL,
+ },
+ { /* INTEL_LIMIT_IGDNG_LVDS */
+ .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
+ .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
+ .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
+ .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
+ .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
+ .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
+ .p = { .min = IGDNG_P_LVDS_MIN, .max = IGDNG_P_LVDS_MAX },
+ .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
+ .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+ .p2_slow = IGDNG_P2_LVDS_SLOW,
+ .p2_fast = IGDNG_P2_LVDS_FAST },
+ .find_pll = intel_igdng_find_best_PLL,
+ },
};
+static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
+{
+ const intel_limit_t *limit;
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_IGDNG_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_IGDNG_SDVO_DAC];
+
+ return limit;
+}
+
static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -418,7 +489,9 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
const intel_limit_t *limit;
- if (IS_G4X(dev)) {
+ if (IS_IGDNG(dev))
+ limit = intel_igdng_limit(crtc);
+ else if (IS_G4X(dev)) {
limit = intel_g4x_limit(crtc);
} else if (IS_I9XX(dev) && !IS_IGD(dev)) {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
@@ -630,7 +703,64 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
}
}
}
+ return found;
+}
+
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_clock_t clock;
+ int max_n;
+ bool found;
+ int err_most = 47;
+ found = false;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+ clock.p2 = limit->p2.p2_slow;
+ } else {
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
+ }
+ memset(best_clock, 0, sizeof(*best_clock));
+ max_n = limit->n.max;
+ /* based on hardware requriment prefer smaller n to precision */
+ for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+ /* based on hardware requirment prefere larger m1,m2, p1 */
+ for (clock.m1 = limit->m1.max;
+ clock.m1 >= limit->m1.min; clock.m1--) {
+ for (clock.m2 = limit->m2.max;
+ clock.m2 >= limit->m2.min; clock.m2--) {
+ for (clock.p1 = limit->p1.max;
+ clock.p1 >= limit->p1.min; clock.p1--) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+ if (!intel_PLL_is_valid(crtc, &clock))
+ continue;
+ this_err = abs((10000 - (target*10000/clock.dot)));
+ if (this_err < err_most) {
+ *best_clock = clock;
+ err_most = this_err;
+ max_n = clock.n;
+ found = true;
+ /* found on first matching */
+ goto out;
+ }
+ }
+ }
+ }
+ }
+out:
return found;
}
@@ -657,6 +787,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dsptileoff = (pipe == 0 ? DSPATILEOFF : DSPBTILEOFF);
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
u32 dspcntr, alignment;
int ret;
@@ -733,6 +864,13 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
+ if (IS_I965G(dev)) {
+ if (obj_priv->tiling_mode != I915_TILING_NONE)
+ dspcntr |= DISPPLANE_TILED;
+ else
+ dspcntr &= ~DISPPLANE_TILED;
+ }
+
I915_WRITE(dspcntr_reg, dspcntr);
Start = obj_priv->gtt_offset;
@@ -745,6 +883,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
I915_READ(dspbase);
I915_WRITE(dspsurf, Start);
I915_READ(dspsurf);
+ I915_WRITE(dsptileoff, (y << 16) | x);
} else {
I915_WRITE(dspbase, Start + Offset);
I915_READ(dspbase);
@@ -776,18 +915,292 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
+static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int plane = intel_crtc->pipe;
+ int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+ int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+ int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+ int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+ int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
+ int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
+ int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
+ int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+ int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+ int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+ int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+ int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+ int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+ int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+ int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B;
+ int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B;
+ int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B;
+ int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
+ int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
+ int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
+ u32 temp;
+ int tries = 5, j;
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ DRM_DEBUG("crtc %d dpms on\n", pipe);
+ /* enable PCH DPLL */
+ temp = I915_READ(pch_dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(pch_dpll_reg);
+ }
-/**
- * Sets the power management mode of the pipe and plane.
- *
- * This code should probably grow support for turning the cursor off and back
- * on appropriately at the same time as we're turning the pipe off/on.
- */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+ /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+ temp = I915_READ(fdi_rx_reg);
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+ FDI_SEL_PCDCLK |
+ FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+ I915_READ(fdi_rx_reg);
+ udelay(200);
+
+ /* Enable CPU FDI TX PLL, always on for IGDNG */
+ temp = I915_READ(fdi_tx_reg);
+ if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+ I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+ I915_READ(fdi_tx_reg);
+ udelay(100);
+ }
+
+ /* Enable CPU pipe */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0) {
+ I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ udelay(100);
+ }
+
+ /* configure and enable CPU plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ /* enable CPU FDI TX and PCH FDI RX */
+ temp = I915_READ(fdi_tx_reg);
+ temp |= FDI_TX_ENABLE;
+ temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_tx_reg, temp);
+ I915_READ(fdi_tx_reg);
+
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+ I915_READ(fdi_rx_reg);
+
+ udelay(150);
+
+ /* Train FDI. */
+ /* umask FDI RX Interrupt symbol_lock and bit_lock bit
+ for train result */
+ temp = I915_READ(fdi_rx_imr_reg);
+ temp &= ~FDI_RX_SYMBOL_LOCK;
+ temp &= ~FDI_RX_BIT_LOCK;
+ I915_WRITE(fdi_rx_imr_reg, temp);
+ I915_READ(fdi_rx_imr_reg);
+ udelay(150);
+
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+ if ((temp & FDI_RX_BIT_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ if (temp & FDI_RX_BIT_LOCK)
+ break;
+ udelay(200);
+ }
+ if (j != tries)
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_BIT_LOCK);
+ else
+ DRM_DEBUG("train 1 fail\n");
+ } else {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_BIT_LOCK);
+ DRM_DEBUG("train 1 ok 2!\n");
+ }
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_2;
+ I915_WRITE(fdi_tx_reg, temp);
+
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_2;
+ I915_WRITE(fdi_rx_reg, temp);
+
+ udelay(150);
+
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+ if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ if (temp & FDI_RX_SYMBOL_LOCK)
+ break;
+ udelay(200);
+ }
+ if (j != tries) {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_SYMBOL_LOCK);
+ DRM_DEBUG("train 2 ok 1!\n");
+ } else
+ DRM_DEBUG("train 2 fail\n");
+ } else {
+ I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
+ DRM_DEBUG("train 2 ok 2!\n");
+ }
+ DRM_DEBUG("train done\n");
+
+ /* set transcoder timing */
+ I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+ I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+ I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+
+ I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+ I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+ I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+
+ /* enable PCH transcoder */
+ temp = I915_READ(transconf_reg);
+ I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+ I915_READ(transconf_reg);
+
+ while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+ ;
+
+ /* enable normal */
+
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+ FDI_TX_ENHANCE_FRAME_ENABLE);
+ I915_READ(fdi_tx_reg);
+
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+ FDI_RX_ENHANCE_FRAME_ENABLE);
+ I915_READ(fdi_rx_reg);
+
+ /* wait one idle pattern time */
+ udelay(100);
+
+ intel_crtc_load_lut(crtc);
+
+ break;
+ case DRM_MODE_DPMS_OFF:
+ DRM_DEBUG("crtc %d dpms off\n", pipe);
+
+ /* Disable the VGA plane that we never use */
+ I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ I915_READ(dspbase_reg);
+ }
+
+ /* disable cpu pipe, disable after all planes disabled */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ /* wait for cpu pipe off, pipe state */
+ while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
+ ;
+ } else
+ DRM_DEBUG("crtc %d is disabled\n", pipe);
+
+ /* IGDNG-A : disable cpu panel fitter ? */
+ temp = I915_READ(pf_ctl_reg);
+ if ((temp & PF_ENABLE) != 0) {
+ I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+ I915_READ(pf_ctl_reg);
+ }
+
+ /* disable CPU FDI tx and PCH FDI rx */
+ temp = I915_READ(fdi_tx_reg);
+ I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
+ I915_READ(fdi_tx_reg);
+
+ temp = I915_READ(fdi_rx_reg);
+ I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
+ I915_READ(fdi_rx_reg);
+
+ /* still set train pattern 1 */
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_tx_reg, temp);
+
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_rx_reg, temp);
+
+ /* disable PCH transcoder */
+ temp = I915_READ(transconf_reg);
+ if ((temp & TRANS_ENABLE) != 0) {
+ I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+ I915_READ(transconf_reg);
+ /* wait for PCH transcoder off, transcoder state */
+ while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
+ ;
+ }
+
+ /* disable PCH DPLL */
+ temp = I915_READ(pch_dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) != 0) {
+ I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
+ I915_READ(pch_dpll_reg);
+ }
+
+ temp = I915_READ(fdi_rx_reg);
+ if ((temp & FDI_RX_PLL_ENABLE) != 0) {
+ temp &= ~FDI_SEL_PCDCLK;
+ temp &= ~FDI_RX_PLL_ENABLE;
+ I915_WRITE(fdi_rx_reg, temp);
+ I915_READ(fdi_rx_reg);
+ }
+
+ /* Wait for the clocks to turn off. */
+ udelay(150);
+ break;
+ }
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_master_private *master_priv;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
@@ -796,7 +1209,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
u32 temp;
- bool enabled;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -881,6 +1293,26 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
udelay(150);
break;
}
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_master_private *master_priv;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ bool enabled;
+
+ if (IS_IGDNG(dev))
+ igdng_crtc_dpms(crtc, mode);
+ else
+ i9xx_crtc_dpms(crtc, mode);
if (!dev->primary->master)
return;
@@ -938,6 +1370,12 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct drm_device *dev = crtc->dev;
+ if (IS_IGDNG(dev)) {
+ /* FDI link clock is fixed at 2.7G */
+ if (mode->clock * 3 > 27000 * 4)
+ return MODE_CLOCK_HIGH;
+ }
return true;
}
@@ -1021,6 +1459,48 @@ static int intel_panel_fitter_pipe (struct drm_device *dev)
return 1;
}
+struct fdi_m_n {
+ u32 tu;
+ u32 gmch_m;
+ u32 gmch_n;
+ u32 link_m;
+ u32 link_n;
+};
+
+static void
+fdi_reduce_ratio(u32 *num, u32 *den)
+{
+ while (*num > 0xffffff || *den > 0xffffff) {
+ *num >>= 1;
+ *den >>= 1;
+ }
+}
+
+#define DATA_N 0x800000
+#define LINK_N 0x80000
+
+static void
+igdng_compute_m_n(int bytes_per_pixel, int nlanes,
+ int pixel_clock, int link_clock,
+ struct fdi_m_n *m_n)
+{
+ u64 temp;
+
+ m_n->tu = 64; /* default size */
+
+ temp = (u64) DATA_N * pixel_clock;
+ temp = div_u64(temp, link_clock);
+ m_n->gmch_m = (temp * bytes_per_pixel) / nlanes;
+ m_n->gmch_n = DATA_N;
+ fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+
+ temp = (u64) LINK_N * pixel_clock;
+ m_n->link_m = div_u64(temp, link_clock);
+ m_n->link_n = LINK_N;
+ fdi_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+
static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -1054,6 +1534,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_connector *connector;
const intel_limit_t *limit;
int ret;
+ struct fdi_m_n m_n = {0};
+ int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
+ int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1;
+ int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1;
+ int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1;
+ int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
+ int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+ int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+ int lvds_reg = LVDS;
+ u32 temp;
+ int sdvo_pixel_multiply;
drm_vblank_pre_modeset(dev, pipe);
@@ -1092,6 +1583,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
} else if (IS_I9XX(dev)) {
refclk = 96000;
+ if (IS_IGDNG(dev))
+ refclk = 120000; /* 120Mhz refclk */
} else {
refclk = 48000;
}
@@ -1105,6 +1598,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ drm_vblank_post_modeset(dev, pipe);
return -EINVAL;
}
@@ -1128,12 +1622,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
}
+ /* FDI link */
+ if (IS_IGDNG(dev))
+ igdng_compute_m_n(3, 4, /* lane num 4 */
+ adjusted_mode->clock,
+ 270000, /* lane clock */
+ &m_n);
+
if (IS_IGD(dev))
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
else
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
- dpll = DPLL_VGA_MODE_DIS;
+ if (!IS_IGDNG(dev))
+ dpll = DPLL_VGA_MODE_DIS;
+
if (IS_I9XX(dev)) {
if (is_lvds)
dpll |= DPLLB_MODE_LVDS;
@@ -1141,17 +1644,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= DPLLB_MODE_DAC_SERIAL;
if (is_sdvo) {
dpll |= DPLL_DVO_HIGH_SPEED;
- if (IS_I945G(dev) || IS_I945GM(dev)) {
- int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ if (IS_I945G(dev) || IS_I945GM(dev))
dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
- }
+ else if (IS_IGDNG(dev))
+ dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
}
/* compute bitmask from p1 value */
if (IS_IGD(dev))
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
- else
+ else {
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ /* also FPA1 */
+ if (IS_IGDNG(dev))
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+ }
switch (clock.p2) {
case 5:
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
@@ -1166,7 +1674,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
break;
}
- if (IS_I965G(dev))
+ if (IS_I965G(dev) && !IS_IGDNG(dev))
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
} else {
if (is_lvds) {
@@ -1198,10 +1706,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE;
- if (pipe == 0)
- dspcntr |= DISPPLANE_SEL_PIPE_A;
- else
- dspcntr |= DISPPLANE_SEL_PIPE_B;
+ /* IGDNG's plane is forced to pipe, bit 24 is to
+ enable color space conversion */
+ if (!IS_IGDNG(dev)) {
+ if (pipe == 0)
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+ }
if (pipe == 0 && !IS_I965G(dev)) {
/* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -1222,12 +1734,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Disable the panel fitter if it was on our pipe */
- if (intel_panel_fitter_pipe(dev) == pipe)
+ if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
I915_WRITE(PFIT_CONTROL, 0);
DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
drm_mode_debug_printmodeline(mode);
+ /* assign to IGDNG registers */
+ if (IS_IGDNG(dev)) {
+ fp_reg = pch_fp_reg;
+ dpll_reg = pch_dpll_reg;
+ }
if (dpll & DPLL_VCO_ENABLE) {
I915_WRITE(fp_reg, fp);
@@ -1236,13 +1753,33 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
udelay(150);
}
+ if (IS_IGDNG(dev)) {
+ /* enable PCH clock reference source */
+ /* XXX need to change the setting for other outputs */
+ u32 temp;
+ temp = I915_READ(PCH_DREF_CONTROL);
+ temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+ temp |= DREF_NONSPREAD_CK505_ENABLE;
+ temp &= ~DREF_SSC_SOURCE_MASK;
+ temp |= DREF_SSC_SOURCE_ENABLE;
+ temp &= ~DREF_SSC1_ENABLE;
+ /* if no eDP, disable source output to CPU */
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ }
+
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn't turn
* things on.
*/
if (is_lvds) {
- u32 lvds = I915_READ(LVDS);
+ u32 lvds;
+ if (IS_IGDNG(dev))
+ lvds_reg = PCH_LVDS;
+
+ lvds = I915_READ(lvds_reg);
lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
/* Set the B0-B3 data pairs corresponding to whether we're going to
* set the DPLLs for dual-channel mode or not.
@@ -1257,8 +1794,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* panels behave in the two modes.
*/
- I915_WRITE(LVDS, lvds);
- I915_READ(LVDS);
+ I915_WRITE(lvds_reg, lvds);
+ I915_READ(lvds_reg);
}
I915_WRITE(fp_reg, fp);
@@ -1267,8 +1804,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Wait for the clocks to stabilize. */
udelay(150);
- if (IS_I965G(dev)) {
- int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+ sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
} else {
@@ -1294,9 +1831,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* pipesrc and dspsize control the size that is scaled from, which should
* always be the user's requested size.
*/
- I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
- I915_WRITE(dsppos_reg, 0);
+ if (!IS_IGDNG(dev)) {
+ I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
+ (mode->hdisplay - 1));
+ I915_WRITE(dsppos_reg, 0);
+ }
I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+ if (IS_IGDNG(dev)) {
+ I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
+ I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+ I915_WRITE(link_m1_reg, m_n.link_m);
+ I915_WRITE(link_n1_reg, m_n.link_n);
+
+ /* enable FDI RX PLL too */
+ temp = I915_READ(fdi_rx_reg);
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+ udelay(200);
+ }
+
I915_WRITE(pipeconf_reg, pipeconf);
I915_READ(pipeconf_reg);
@@ -1306,12 +1859,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Flush the plane changes */
ret = intel_pipe_set_base(crtc, x, y, old_fb);
- if (ret != 0)
- return ret;
-
drm_vblank_post_modeset(dev, pipe);
- return 0;
+ return ret;
}
/** Loads the palette/gamma unit for the CRTC with the prepared values */
@@ -1327,6 +1877,11 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
if (!crtc->enabled)
return;
+ /* use legacy palette for IGDNG */
+ if (IS_IGDNG(dev))
+ palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
+ LGC_PALETTE_B;
+
for (i = 0; i < 256; i++) {
I915_WRITE(palreg + 4 * i,
(intel_crtc->lut_r[i] << 16) |
@@ -1348,7 +1903,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
int pipe = intel_crtc->pipe;
uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
- uint32_t temp;
+ uint32_t temp = I915_READ(control);
size_t addr;
int ret;
@@ -1357,7 +1912,12 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
/* if we want to turn off the cursor ignore width and height */
if (!handle) {
DRM_DEBUG("cursor off\n");
- temp = CURSOR_MODE_DISABLE;
+ if (IS_MOBILE(dev) || IS_I9XX(dev)) {
+ temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
+ temp |= CURSOR_MODE_DISABLE;
+ } else {
+ temp &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE);
+ }
addr = 0;
bo = NULL;
mutex_lock(&dev->struct_mutex);
@@ -1400,10 +1960,19 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
addr = obj_priv->phys_obj->handle->busaddr;
}
- temp = 0;
- /* set the pipe for the cursor */
- temp |= (pipe << 28);
- temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+ if (!IS_I9XX(dev))
+ I915_WRITE(CURSIZE, (height << 12) | width);
+
+ /* Hooray for CUR*CNTR differences */
+ if (IS_MOBILE(dev) || IS_I9XX(dev)) {
+ temp &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT);
+ temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+ temp |= (pipe << 28); /* Connect to correct pipe */
+ } else {
+ temp &= ~(CURSOR_FORMAT_MASK);
+ temp |= CURSOR_ENABLE;
+ temp |= CURSOR_FORMAT_ARGB | CURSOR_GAMMA_ENABLE;
+ }
finish:
I915_WRITE(control, temp);
@@ -1441,16 +2010,16 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
uint32_t adder;
if (x < 0) {
- temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+ temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
x = -x;
}
if (y < 0) {
- temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+ temp |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT;
y = -y;
}
- temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
- temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+ temp |= x << CURSOR_X_SHIFT;
+ temp |= y << CURSOR_Y_SHIFT;
adder = intel_crtc->cursor_addr;
I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
@@ -1567,6 +2136,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
}
encoder->crtc = crtc;
+ intel_output->base.encoder = encoder;
intel_output->load_detect_temp = true;
intel_crtc = to_intel_crtc(crtc);
@@ -1602,6 +2172,7 @@ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_
if (intel_output->load_detect_temp) {
encoder->crtc = NULL;
+ intel_output->base.encoder = NULL;
intel_output->load_detect_temp = false;
crtc->enabled = drm_helper_crtc_in_use(crtc);
drm_helper_disable_unused_functions(dev);
@@ -1739,6 +2310,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ if (intel_crtc->mode_set.mode)
+ drm_mode_destroy(crtc->dev, intel_crtc->mode_set.mode);
drm_crtc_cleanup(crtc);
kfree(intel_crtc);
}
@@ -1795,6 +2368,37 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
}
}
+int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
+ struct drm_crtc *crtc = NULL;
+ int pipe = -1;
+
+ if (!dev_priv) {
+ DRM_ERROR("called with no initialization\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ if (crtc->base.id == pipe_from_crtc_id->crtc_id) {
+ pipe = intel_crtc->pipe;
+ break;
+ }
+ }
+
+ if (pipe == -1) {
+ DRM_ERROR("no such CRTC id\n");
+ return -EINVAL;
+ }
+
+ pipe_from_crtc_id->pipe = pipe;
+
+ return 0;
+}
+
struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
{
struct drm_crtc *crtc = NULL;
@@ -1834,7 +2438,24 @@ static void intel_setup_outputs(struct drm_device *dev)
if (IS_MOBILE(dev) && !IS_I830(dev))
intel_lvds_init(dev);
- if (IS_I9XX(dev)) {
+ if (IS_IGDNG(dev)) {
+ int found;
+
+ if (I915_READ(HDMIB) & PORT_DETECTED) {
+ /* check SDVOB */
+ /* found = intel_sdvo_init(dev, HDMIB); */
+ found = 0;
+ if (!found)
+ intel_hdmi_init(dev, HDMIB);
+ }
+
+ if (I915_READ(HDMIC) & PORT_DETECTED)
+ intel_hdmi_init(dev, HDMIC);
+
+ if (I915_READ(HDMID) & PORT_DETECTED)
+ intel_hdmi_init(dev, HDMID);
+
+ } else if (IS_I9XX(dev)) {
int found;
u32 reg;
@@ -1858,7 +2479,7 @@ static void intel_setup_outputs(struct drm_device *dev)
} else
intel_dvo_init(dev);
- if (IS_I9XX(dev) && IS_MOBILE(dev))
+ if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev))
intel_tv_init(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 957daef8edff..cd4b9c5f715e 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -109,7 +109,7 @@ struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
void intel_i2c_destroy(struct intel_i2c_chan *chan);
int intel_ddc_get_modes(struct intel_output *intel_output);
extern bool intel_ddc_probe(struct intel_output *intel_output);
-
+void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
extern void intel_crt_init(struct drm_device *dev);
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
@@ -125,6 +125,8 @@ extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
+int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
extern void intel_wait_for_vblank(struct drm_device *dev);
extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 8b8d6e65cd3f..1ee3007d6ec0 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -316,6 +316,7 @@ static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
};
static const struct drm_connector_funcs intel_dvo_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.save = intel_dvo_save,
.restore = intel_dvo_restore,
.detect = intel_dvo_detect,
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 3e094beecb99..0ecf6b76a401 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -207,7 +207,7 @@ static int intelfb_set_par(struct fb_info *info)
if (var->pixclock != -1) {
- DRM_ERROR("PIXEL CLCOK SET\n");
+ DRM_ERROR("PIXEL CLOCK SET\n");
return -EINVAL;
} else {
struct drm_crtc *crtc;
@@ -674,8 +674,12 @@ static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *
par->crtc_ids[0] = crtc->base.id;
modeset->num_connectors = conn_count;
- if (modeset->mode != modeset->crtc->desired_mode)
- modeset->mode = modeset->crtc->desired_mode;
+ if (modeset->crtc->desired_mode) {
+ if (modeset->mode)
+ drm_mode_destroy(dev, modeset->mode);
+ modeset->mode = drm_mode_duplicate(dev,
+ modeset->crtc->desired_mode);
+ }
par->crtc_count = 1;
@@ -824,8 +828,12 @@ static int intelfb_single_fb_probe(struct drm_device *dev)
par->crtc_ids[crtc_count++] = crtc->base.id;
modeset->num_connectors = conn_count;
- if (modeset->mode != modeset->crtc->desired_mode)
- modeset->mode = modeset->crtc->desired_mode;
+ if (modeset->crtc->desired_mode) {
+ if (modeset->mode)
+ drm_mode_destroy(dev, modeset->mode);
+ modeset->mode = drm_mode_duplicate(dev,
+ modeset->crtc->desired_mode);
+ }
}
par->crtc_count = crtc_count;
@@ -857,14 +865,20 @@ void intelfb_restore(void)
drm_crtc_helper_set_config(&kernelfb_mode);
}
+static void intelfb_restore_work_fn(struct work_struct *ignored)
+{
+ intelfb_restore();
+}
+static DECLARE_WORK(intelfb_restore_work, intelfb_restore_work_fn);
+
static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3)
{
- intelfb_restore();
+ schedule_work(&intelfb_restore_work);
}
static struct sysrq_key_op sysrq_intelfb_restore_op = {
.handler = intelfb_sysrq,
- .help_msg = "force-fb(G)",
+ .help_msg = "force-fb(V)",
.action_msg = "Restore framebuffer console",
};
@@ -898,7 +912,7 @@ int intelfb_probe(struct drm_device *dev)
ret = intelfb_single_fb_probe(dev);
}
- register_sysrq_key('g', &sysrq_intelfb_restore_op);
+ register_sysrq_key('v', &sysrq_intelfb_restore_op);
return ret;
}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 550374225388..4ea2a651b92c 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -56,7 +56,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
sdvox = SDVO_ENCODING_HDMI |
SDVO_BORDER_ENABLE |
SDVO_VSYNC_ACTIVE_HIGH |
- SDVO_HSYNC_ACTIVE_HIGH;
+ SDVO_HSYNC_ACTIVE_HIGH |
+ SDVO_NULL_PACKETS_DURING_VSYNC;
if (hdmi_priv->has_hdmi_sink)
sdvox |= SDVO_AUDIO_ENABLE;
@@ -145,6 +146,22 @@ intel_hdmi_sink_detect(struct drm_connector *connector)
}
static enum drm_connector_status
+igdng_hdmi_detect(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
+
+ /* FIXME hotplug detect */
+
+ hdmi_priv->has_hdmi_sink = false;
+ intel_hdmi_sink_detect(connector);
+ if (hdmi_priv->has_hdmi_sink)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -153,13 +170,23 @@ intel_hdmi_detect(struct drm_connector *connector)
struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
u32 temp, bit;
+ if (IS_IGDNG(dev))
+ return igdng_hdmi_detect(connector);
+
temp = I915_READ(PORT_HOTPLUG_EN);
- I915_WRITE(PORT_HOTPLUG_EN,
- temp |
- HDMIB_HOTPLUG_INT_EN |
- HDMIC_HOTPLUG_INT_EN |
- HDMID_HOTPLUG_INT_EN);
+ switch (hdmi_priv->sdvox_reg) {
+ case SDVOB:
+ temp |= HDMIB_HOTPLUG_INT_EN;
+ break;
+ case SDVOC:
+ temp |= HDMIC_HOTPLUG_INT_EN;
+ break;
+ default:
+ return connector_status_unknown;
+ }
+
+ I915_WRITE(PORT_HOTPLUG_EN, temp);
POSTING_READ(PORT_HOTPLUG_EN);
@@ -212,6 +239,7 @@ static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
};
static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.save = intel_hdmi_save,
.restore = intel_hdmi_restore,
.detect = intel_hdmi_detect,
@@ -261,8 +289,17 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
/* Set up the DDC bus. */
if (sdvox_reg == SDVOB)
intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB");
- else
+ else if (sdvox_reg == SDVOC)
intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC");
+ else if (sdvox_reg == HDMIB)
+ intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOE,
+ "HDMIB");
+ else if (sdvox_reg == HDMIC)
+ intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOD,
+ "HDMIC");
+ else if (sdvox_reg == HDMID)
+ intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOF,
+ "HDMID");
if (!intel_output->ddc_bus)
goto err_connector;
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 5ee9d4c25753..f7061f68d050 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -34,6 +34,21 @@
#include "i915_drm.h"
#include "i915_drv.h"
+void intel_i2c_quirk_set(struct drm_device *dev, bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* When using bit bashing for I2C, this bit needs to be set to 1 */
+ if (!IS_IGD(dev))
+ return;
+ if (enable)
+ I915_WRITE(CG_2D_DIS,
+ I915_READ(CG_2D_DIS) | DPCUNIT_CLOCK_GATE_DISABLE);
+ else
+ I915_WRITE(CG_2D_DIS,
+ I915_READ(CG_2D_DIS) & (~DPCUNIT_CLOCK_GATE_DISABLE));
+}
+
/*
* Intel GPIO access functions
*/
@@ -153,8 +168,10 @@ struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
goto out_free;
/* JJJ: raise SCL and SDA? */
+ intel_i2c_quirk_set(dev, true);
set_data(chan, 1);
set_clock(chan, 1);
+ intel_i2c_quirk_set(dev, false);
udelay(20);
return chan;
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 6619f26e46a5..f073ed8432e8 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -37,6 +37,8 @@
#include "i915_drm.h"
#include "i915_drv.h"
+#define I915_LVDS "i915_lvds"
+
/**
* Sets the backlight level.
*
@@ -45,10 +47,15 @@
static void intel_lvds_set_backlight(struct drm_device *dev, int level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 blc_pwm_ctl;
+ u32 blc_pwm_ctl, reg;
+
+ if (IS_IGDNG(dev))
+ reg = BLC_PWM_CPU_CTL;
+ else
+ reg = BLC_PWM_CTL;
- blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
- I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+ blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(reg, (blc_pwm_ctl |
(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
}
@@ -58,8 +65,14 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
- return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ if (IS_IGDNG(dev))
+ reg = BLC_PWM_PCH_CTL2;
+ else
+ reg = BLC_PWM_CTL;
+
+ return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
}
@@ -69,23 +82,31 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
static void intel_lvds_set_power(struct drm_device *dev, bool on)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pp_status;
+ u32 pp_status, ctl_reg, status_reg;
+
+ if (IS_IGDNG(dev)) {
+ ctl_reg = PCH_PP_CONTROL;
+ status_reg = PCH_PP_STATUS;
+ } else {
+ ctl_reg = PP_CONTROL;
+ status_reg = PP_STATUS;
+ }
if (on) {
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ I915_WRITE(ctl_reg, I915_READ(ctl_reg) |
POWER_TARGET_ON);
do {
- pp_status = I915_READ(PP_STATUS);
+ pp_status = I915_READ(status_reg);
} while ((pp_status & PP_ON) == 0);
intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
} else {
intel_lvds_set_backlight(dev, 0);
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ I915_WRITE(ctl_reg, I915_READ(ctl_reg) &
~POWER_TARGET_ON);
do {
- pp_status = I915_READ(PP_STATUS);
+ pp_status = I915_READ(status_reg);
} while (pp_status & PP_ON);
}
}
@@ -106,12 +127,28 @@ static void intel_lvds_save(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+ u32 pwm_ctl_reg;
+
+ if (IS_IGDNG(dev)) {
+ pp_on_reg = PCH_PP_ON_DELAYS;
+ pp_off_reg = PCH_PP_OFF_DELAYS;
+ pp_ctl_reg = PCH_PP_CONTROL;
+ pp_div_reg = PCH_PP_DIVISOR;
+ pwm_ctl_reg = BLC_PWM_CPU_CTL;
+ } else {
+ pp_on_reg = PP_ON_DELAYS;
+ pp_off_reg = PP_OFF_DELAYS;
+ pp_ctl_reg = PP_CONTROL;
+ pp_div_reg = PP_DIVISOR;
+ pwm_ctl_reg = BLC_PWM_CTL;
+ }
- dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
- dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
- dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
- dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
- dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->savePP_ON = I915_READ(pp_on_reg);
+ dev_priv->savePP_OFF = I915_READ(pp_off_reg);
+ dev_priv->savePP_CONTROL = I915_READ(pp_ctl_reg);
+ dev_priv->savePP_DIVISOR = I915_READ(pp_div_reg);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(pwm_ctl_reg);
dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
BACKLIGHT_DUTY_CYCLE_MASK);
@@ -127,12 +164,28 @@ static void intel_lvds_restore(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+ u32 pwm_ctl_reg;
+
+ if (IS_IGDNG(dev)) {
+ pp_on_reg = PCH_PP_ON_DELAYS;
+ pp_off_reg = PCH_PP_OFF_DELAYS;
+ pp_ctl_reg = PCH_PP_CONTROL;
+ pp_div_reg = PCH_PP_DIVISOR;
+ pwm_ctl_reg = BLC_PWM_CPU_CTL;
+ } else {
+ pp_on_reg = PP_ON_DELAYS;
+ pp_off_reg = PP_OFF_DELAYS;
+ pp_ctl_reg = PP_CONTROL;
+ pp_div_reg = PP_DIVISOR;
+ pwm_ctl_reg = BLC_PWM_CTL;
+ }
- I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
- I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
- I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
- I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
- I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ I915_WRITE(pwm_ctl_reg, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(pp_on_reg, dev_priv->savePP_ON);
+ I915_WRITE(pp_off_reg, dev_priv->savePP_OFF);
+ I915_WRITE(pp_div_reg, dev_priv->savePP_DIVISOR);
+ I915_WRITE(pp_ctl_reg, dev_priv->savePP_CONTROL);
if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
intel_lvds_set_power(dev, true);
else
@@ -216,8 +269,14 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+
+ if (IS_IGDNG(dev))
+ reg = BLC_PWM_CPU_CTL;
+ else
+ reg = BLC_PWM_CTL;
- dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(reg);
dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
BACKLIGHT_DUTY_CYCLE_MASK);
@@ -251,6 +310,10 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
* settings.
*/
+ /* No panel fitting yet, fixme */
+ if (IS_IGDNG(dev))
+ return;
+
/*
* Enable automatic panel scaling so that non-native modes fill the
* screen. Should be enabled before the pipe is enabled, according to
@@ -343,11 +406,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
- struct drm_device *dev = connector->dev;
-
- if (property == dev->mode_config.dpms_property && connector->encoder)
- intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf));
-
return 0;
}
@@ -366,6 +424,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
};
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.save = intel_lvds_save,
.restore = intel_lvds_restore,
.detect = intel_lvds_detect,
@@ -384,7 +443,65 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
.destroy = intel_lvds_enc_destroy,
};
+static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
+{
+ DRM_DEBUG_KMS(I915_LVDS,
+ "Skipping LVDS initialization for %s\n", id->ident);
+ return 1;
+}
+/* These systems claim to have LVDS, but really don't */
+static const struct dmi_system_id intel_no_lvds[] = {
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Apple Mac Mini (Core series)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Apple Mac Mini (Core 2 series)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "MSI IM-945GSE-A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Dell Studio Hybrid",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "AOpen Mini PC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Aopen i945GTt-VFA",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
+ },
+ },
+
+ { } /* terminating entry */
+};
/**
* intel_lvds_init - setup LVDS connectors on this device
@@ -402,16 +519,16 @@ void intel_lvds_init(struct drm_device *dev)
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_crtc *crtc;
u32 lvds;
- int pipe;
-
- /* Blacklist machines that we know falsely report LVDS. */
- /* FIXME: add a check for the Aopen Mini PC */
+ int pipe, gpio = GPIOC;
- /* Apple Mac Mini Core Duo and Mac Mini Core 2 Duo */
- if(dmi_match(DMI_PRODUCT_NAME, "Macmini1,1") ||
- dmi_match(DMI_PRODUCT_NAME, "Macmini2,1")) {
- DRM_DEBUG("Skipping LVDS initialization for Apple Mac Mini\n");
+ /* Skip init on machines we know falsely report LVDS */
+ if (dmi_check_system(intel_no_lvds))
return;
+
+ if (IS_IGDNG(dev)) {
+ if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
+ return;
+ gpio = PCH_GPIOC;
}
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
@@ -448,7 +565,7 @@ void intel_lvds_init(struct drm_device *dev)
*/
/* Set up the DDC bus. */
- intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ intel_output->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C");
if (!intel_output->ddc_bus) {
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
"failed.\n");
@@ -473,10 +590,10 @@ void intel_lvds_init(struct drm_device *dev)
}
/* Failed to get EDID, what about VBT? */
- if (dev_priv->vbt_mode) {
+ if (dev_priv->lfp_lvds_vbt_mode) {
mutex_lock(&dev->mode_config.mutex);
dev_priv->panel_fixed_mode =
- drm_mode_duplicate(dev, dev_priv->vbt_mode);
+ drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
mutex_unlock(&dev->mode_config.mutex);
if (dev_priv->panel_fixed_mode) {
dev_priv->panel_fixed_mode->type |=
@@ -490,6 +607,11 @@ void intel_lvds_init(struct drm_device *dev)
* on. If so, assume that whatever is currently programmed is the
* correct mode.
*/
+
+ /* IGDNG: FIXME if still fail, not try pipe mode now */
+ if (IS_IGDNG(dev))
+ goto failed;
+
lvds = I915_READ(LVDS);
pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
crtc = intel_get_crtc_from_pipe(dev, pipe);
@@ -508,11 +630,22 @@ void intel_lvds_init(struct drm_device *dev)
goto failed;
out:
+ if (IS_IGDNG(dev)) {
+ u32 pwm;
+ /* make sure PWM is enabled */
+ pwm = I915_READ(BLC_PWM_CPU_CTL2);
+ pwm |= (PWM_ENABLE | PWM_PIPE_B);
+ I915_WRITE(BLC_PWM_CPU_CTL2, pwm);
+
+ pwm = I915_READ(BLC_PWM_PCH_CTL1);
+ pwm |= PWM_PCH_ENABLE;
+ I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
+ }
drm_sysfs_connector_add(connector);
return;
failed:
- DRM_DEBUG("No LVDS modes found, disabling.\n");
+ DRM_DEBUG_KMS(I915_LVDS, "No LVDS modes found, disabling.\n");
if (intel_output->ddc_bus)
intel_i2c_destroy(intel_output->ddc_bus);
drm_connector_cleanup(connector);
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 07d7ec976168..e0910fefce87 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -27,6 +27,7 @@
#include <linux/fb.h>
#include "drmP.h"
#include "intel_drv.h"
+#include "i915_drv.h"
/**
* intel_ddc_probe
@@ -52,7 +53,10 @@ bool intel_ddc_probe(struct intel_output *intel_output)
}
};
+ intel_i2c_quirk_set(intel_output->ddc_bus->drm_dev, true);
ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+ intel_i2c_quirk_set(intel_output->ddc_bus->drm_dev, false);
+
if (ret == 2)
return true;
@@ -70,8 +74,10 @@ int intel_ddc_get_modes(struct intel_output *intel_output)
struct edid *edid;
int ret = 0;
+ intel_i2c_quirk_set(intel_output->ddc_bus->drm_dev, true);
edid = drm_get_edid(&intel_output->base,
&intel_output->ddc_bus->adapter);
+ intel_i2c_quirk_set(intel_output->ddc_bus->drm_dev, false);
if (edid) {
drm_mode_connector_update_edid_property(&intel_output->base,
edid);
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 9913651c1e17..9a00adb3a508 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -36,7 +36,7 @@
#include "intel_sdvo_regs.h"
#undef SDVO_DEBUG
-
+#define I915_SDVO "i915_sdvo"
struct intel_sdvo_priv {
struct intel_i2c_chan *i2c_bus;
int slaveaddr;
@@ -69,6 +69,10 @@ struct intel_sdvo_priv {
* This is set if we treat the device as HDMI, instead of DVI.
*/
bool is_hdmi;
+ /**
+ * This is set if we detect output of sdvo device as LVDS.
+ */
+ bool is_lvds;
/**
* Returned SDTV resolutions allowed for the current format, if the
@@ -273,20 +277,21 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int i;
- printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+ DRM_DEBUG_KMS(I915_SDVO, "%s: W: %02X ",
+ SDVO_NAME(sdvo_priv), cmd);
for (i = 0; i < args_len; i++)
- printk(KERN_DEBUG "%02X ", ((u8 *)args)[i]);
+ DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
for (; i < 8; i++)
- printk(KERN_DEBUG " ");
+ DRM_LOG_KMS(" ");
for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
if (cmd == sdvo_cmd_names[i].cmd) {
- printk(KERN_DEBUG "(%s)", sdvo_cmd_names[i].name);
+ DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name);
break;
}
}
if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
- printk(KERN_DEBUG "(%02X)", cmd);
- printk(KERN_DEBUG "\n");
+ DRM_LOG_KMS("(%02X)", cmd);
+ DRM_LOG_KMS("\n");
}
#else
#define intel_sdvo_debug_write(o, c, a, l)
@@ -325,16 +330,16 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int i;
- printk(KERN_DEBUG "%s: R: ", SDVO_NAME(sdvo_priv));
+ DRM_DEBUG_KMS(I915_SDVO, "%s: R: ", SDVO_NAME(sdvo_priv));
for (i = 0; i < response_len; i++)
- printk(KERN_DEBUG "%02X ", ((u8 *)response)[i]);
+ DRM_LOG_KMS("%02X ", ((u8 *)response)[i]);
for (; i < 8; i++)
- printk(KERN_DEBUG " ");
+ DRM_LOG_KMS(" ");
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
- printk(KERN_DEBUG "(%s)", cmd_status_names[status]);
+ DRM_LOG_KMS("(%s)", cmd_status_names[status]);
else
- printk(KERN_DEBUG "(??? %d)", status);
- printk(KERN_DEBUG "\n");
+ DRM_LOG_KMS("(??? %d)", status);
+ DRM_LOG_KMS("\n");
}
#else
#define intel_sdvo_debug_response(o, r, l, s)
@@ -1398,10 +1403,8 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
- struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
/* set the bus switch and get the modes */
- intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus);
intel_ddc_get_modes(intel_output);
#if 0
@@ -1543,6 +1546,37 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
}
}
+static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+
+ /*
+ * Attempt to get the mode list from DDC.
+ * Assume that the preferred modes are
+ * arranged in priority order.
+ */
+ /* set the bus switch and get the modes */
+ intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus);
+ intel_ddc_get_modes(intel_output);
+ if (list_empty(&connector->probed_modes) == false)
+ return;
+
+ /* Fetch modes from VBT */
+ if (dev_priv->sdvo_lvds_vbt_mode != NULL) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(connector->dev,
+ dev_priv->sdvo_lvds_vbt_mode);
+ if (newmode != NULL) {
+ /* Guarantee the mode is preferred */
+ newmode->type = (DRM_MODE_TYPE_PREFERRED |
+ DRM_MODE_TYPE_DRIVER);
+ drm_mode_probed_add(connector, newmode);
+ }
+ }
+}
+
static int intel_sdvo_get_modes(struct drm_connector *connector)
{
struct intel_output *output = to_intel_output(connector);
@@ -1550,6 +1584,8 @@ static int intel_sdvo_get_modes(struct drm_connector *connector)
if (sdvo_priv->is_tv)
intel_sdvo_get_tv_modes(connector);
+ else if (sdvo_priv->is_lvds == true)
+ intel_sdvo_get_lvds_modes(connector);
else
intel_sdvo_get_ddc_modes(connector);
@@ -1564,6 +1600,9 @@ static void intel_sdvo_destroy(struct drm_connector *connector)
if (intel_output->i2c_bus)
intel_i2c_destroy(intel_output->i2c_bus);
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(intel_output);
@@ -1578,6 +1617,7 @@ static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
};
static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.save = intel_sdvo_save,
.restore = intel_sdvo_restore,
.detect = intel_sdvo_detect,
@@ -1660,33 +1700,107 @@ intel_sdvo_get_digital_encoding_mode(struct intel_output *output)
return true;
}
+static struct intel_output *
+intel_sdvo_chan_to_intel_output(struct intel_i2c_chan *chan)
+{
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_connector *connector;
+ struct intel_output *intel_output = NULL;
+
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list, head) {
+ if (to_intel_output(connector)->ddc_bus == chan) {
+ intel_output = to_intel_output(connector);
+ break;
+ }
+ }
+ return intel_output;
+}
+
+static int intel_sdvo_master_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct intel_output *intel_output;
+ struct intel_sdvo_priv *sdvo_priv;
+ struct i2c_algo_bit_data *algo_data;
+ struct i2c_algorithm *algo;
+
+ algo_data = (struct i2c_algo_bit_data *)i2c_adap->algo_data;
+ intel_output =
+ intel_sdvo_chan_to_intel_output(
+ (struct intel_i2c_chan *)(algo_data->data));
+ if (intel_output == NULL)
+ return -EINVAL;
+
+ sdvo_priv = intel_output->dev_priv;
+ algo = (struct i2c_algorithm *)intel_output->i2c_bus->adapter.algo;
+
+ intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus);
+ return algo->master_xfer(i2c_adap, msgs, num);
+}
+
+static struct i2c_algorithm intel_sdvo_i2c_bit_algo = {
+ .master_xfer = intel_sdvo_master_xfer,
+};
+
+static u8
+intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct sdvo_device_mapping *my_mapping, *other_mapping;
+
+ if (output_device == SDVOB) {
+ my_mapping = &dev_priv->sdvo_mappings[0];
+ other_mapping = &dev_priv->sdvo_mappings[1];
+ } else {
+ my_mapping = &dev_priv->sdvo_mappings[1];
+ other_mapping = &dev_priv->sdvo_mappings[0];
+ }
+
+ /* If the BIOS described our SDVO device, take advantage of it. */
+ if (my_mapping->slave_addr)
+ return my_mapping->slave_addr;
+
+ /* If the BIOS only described a different SDVO device, use the
+ * address that it isn't using.
+ */
+ if (other_mapping->slave_addr) {
+ if (other_mapping->slave_addr == 0x70)
+ return 0x72;
+ else
+ return 0x70;
+ }
+
+ /* No SDVO device info is found for another DVO port,
+ * so use mapping assumption we had before BIOS parsing.
+ */
+ if (output_device == SDVOB)
+ return 0x70;
+ else
+ return 0x72;
+}
+
bool intel_sdvo_init(struct drm_device *dev, int output_device)
{
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_sdvo_priv *sdvo_priv;
struct intel_i2c_chan *i2cbus = NULL;
+ struct intel_i2c_chan *ddcbus = NULL;
int connector_type;
u8 ch[0x40];
int i;
int encoder_type, output_id;
+ u8 slave_addr;
intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
if (!intel_output) {
return false;
}
- connector = &intel_output->base;
-
- drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
- DRM_MODE_CONNECTOR_Unknown);
- drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
intel_output->type = INTEL_OUTPUT_SDVO;
- connector->interlace_allowed = 0;
- connector->doublescan_allowed = 0;
-
/* setup the DDC bus. */
if (output_device == SDVOB)
i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
@@ -1694,32 +1808,47 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
if (!i2cbus)
- goto err_connector;
+ goto err_inteloutput;
+ slave_addr = intel_sdvo_get_slave_addr(dev, output_device);
sdvo_priv->i2c_bus = i2cbus;
if (output_device == SDVOB) {
output_id = 1;
- sdvo_priv->i2c_bus->slave_addr = 0x38;
} else {
output_id = 2;
- sdvo_priv->i2c_bus->slave_addr = 0x39;
}
-
+ sdvo_priv->i2c_bus->slave_addr = slave_addr >> 1;
sdvo_priv->output_device = output_device;
intel_output->i2c_bus = i2cbus;
intel_output->dev_priv = sdvo_priv;
-
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
- DRM_DEBUG("No SDVO device found on SDVO%c\n",
- output_device == SDVOB ? 'B' : 'C');
+ DRM_DEBUG_KMS(I915_SDVO,
+ "No SDVO device found on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
goto err_i2c;
}
}
+ /* setup the DDC bus. */
+ if (output_device == SDVOB)
+ ddcbus = intel_i2c_create(dev, GPIOE, "SDVOB DDC BUS");
+ else
+ ddcbus = intel_i2c_create(dev, GPIOE, "SDVOC DDC BUS");
+
+ if (ddcbus == NULL)
+ goto err_i2c;
+
+ intel_sdvo_i2c_bit_algo.functionality =
+ intel_output->i2c_bus->adapter.algo->functionality;
+ ddcbus->adapter.algo = &intel_sdvo_i2c_bit_algo;
+ intel_output->ddc_bus = ddcbus;
+
+ /* In defaut case sdvo lvds is false */
+ sdvo_priv->is_lvds = false;
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
if (sdvo_priv->caps.output_flags &
@@ -1729,7 +1858,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
else
sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_TMDS;
connector_type = DRM_MODE_CONNECTOR_DVID;
@@ -1747,7 +1875,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_TVDAC;
connector_type = DRM_MODE_CONNECTOR_SVIDEO;
sdvo_priv->is_tv = true;
@@ -1756,30 +1883,28 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_LVDS;
connector_type = DRM_MODE_CONNECTOR_LVDS;
+ sdvo_priv->is_lvds = true;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_LVDS;
connector_type = DRM_MODE_CONNECTOR_LVDS;
+ sdvo_priv->is_lvds = true;
}
else
{
@@ -1787,17 +1912,25 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
sdvo_priv->controlled_output = 0;
memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
- DRM_DEBUG("%s: Unknown SDVO output type (0x%02x%02x)\n",
- SDVO_NAME(sdvo_priv),
- bytes[0], bytes[1]);
+ DRM_DEBUG_KMS(I915_SDVO,
+ "%s: Unknown SDVO output type (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
encoder_type = DRM_MODE_ENCODER_NONE;
connector_type = DRM_MODE_CONNECTOR_Unknown;
goto err_i2c;
}
+ connector = &intel_output->base;
+ drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
+ connector_type);
+ drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+
drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
- connector->connector_type = connector_type;
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
drm_sysfs_connector_add(connector);
@@ -1812,31 +1945,30 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
&sdvo_priv->pixel_clock_max);
- DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
- "clock range %dMHz - %dMHz, "
- "input 1: %c, input 2: %c, "
- "output 1: %c, output 2: %c\n",
- SDVO_NAME(sdvo_priv),
- sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
- sdvo_priv->caps.device_rev_id,
- sdvo_priv->pixel_clock_min / 1000,
- sdvo_priv->pixel_clock_max / 1000,
- (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
- (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
- /* check currently supported outputs */
- sdvo_priv->caps.output_flags &
+ DRM_DEBUG_KMS(I915_SDVO, "%s device VID/DID: %02X:%02X.%02X, "
+ "clock range %dMHz - %dMHz, "
+ "input 1: %c, input 2: %c, "
+ "output 1: %c, output 2: %c\n",
+ SDVO_NAME(sdvo_priv),
+ sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+ sdvo_priv->caps.device_rev_id,
+ sdvo_priv->pixel_clock_min / 1000,
+ sdvo_priv->pixel_clock_max / 1000,
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+ /* check currently supported outputs */
+ sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
- sdvo_priv->caps.output_flags &
+ sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
- intel_output->ddc_bus = i2cbus;
-
return true;
err_i2c:
+ if (ddcbus != NULL)
+ intel_i2c_destroy(intel_output->ddc_bus);
intel_i2c_destroy(intel_output->i2c_bus);
-err_connector:
- drm_connector_cleanup(connector);
+err_inteloutput:
kfree(intel_output);
return false;
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index d2c32983242d..50d7ed70b338 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1392,6 +1392,9 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
tv_ctl &= ~TV_TEST_MODE_MASK;
tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
tv_dac &= ~TVDAC_SENSE_MASK;
+ tv_dac &= ~DAC_A_MASK;
+ tv_dac &= ~DAC_B_MASK;
+ tv_dac &= ~DAC_C_MASK;
tv_dac |= (TVDAC_STATE_CHG_EN |
TVDAC_A_SENSE_CTL |
TVDAC_B_SENSE_CTL |
@@ -1626,6 +1629,7 @@ static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
};
static const struct drm_connector_funcs intel_tv_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
.save = intel_tv_save,
.restore = intel_tv_restore,
.detect = intel_tv_detect,
diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c
index 32de4cedc363..077c0455a6b9 100644
--- a/drivers/gpu/drm/r128/r128_cce.c
+++ b/drivers/gpu/drm/r128/r128_cce.c
@@ -511,9 +511,9 @@ static int r128_do_init_cce(struct drm_device * dev, drm_r128_init_t * init)
#if __OS_HAS_AGP
if (!dev_priv->is_pci) {
- drm_core_ioremap(dev_priv->cce_ring, dev);
- drm_core_ioremap(dev_priv->ring_rptr, dev);
- drm_core_ioremap(dev->agp_buffer_map, dev);
+ drm_core_ioremap_wc(dev_priv->cce_ring, dev);
+ drm_core_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_core_ioremap_wc(dev->agp_buffer_map, dev);
if (!dev_priv->cce_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev->agp_buffer_map->handle) {
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index bc9d09dfa8e7..146f3570af8e 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -478,26 +478,27 @@ static void r700_cp_load_microcode(drm_radeon_private_t *dev_priv)
if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV770)) {
RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
- DRM_INFO("Loading RV770 PFP Microcode\n");
+ DRM_INFO("Loading RV770/RV790 PFP Microcode\n");
for (i = 0; i < R700_PFP_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RV770_pfp_microcode[i]);
RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
- DRM_INFO("Loading RV770 CP Microcode\n");
+ DRM_INFO("Loading RV770/RV790 CP Microcode\n");
for (i = 0; i < R700_PM4_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_ME_RAM_DATA, RV770_cp_microcode[i]);
RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
- } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV730)) {
+ } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV730) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)) {
RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
- DRM_INFO("Loading RV730 PFP Microcode\n");
+ DRM_INFO("Loading RV730/RV740 PFP Microcode\n");
for (i = 0; i < R700_PFP_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RV730_pfp_microcode[i]);
RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
- DRM_INFO("Loading RV730 CP Microcode\n");
+ DRM_INFO("Loading RV730/RV740 CP Microcode\n");
for (i = 0; i < R700_PM4_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_ME_RAM_DATA, RV730_cp_microcode[i]);
RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
@@ -1324,6 +1325,10 @@ static void r700_gfx_init(struct drm_device *dev,
dev_priv->r700_sc_prim_fifo_size = 0xf9;
dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
+ if (dev_priv->r600_sx_max_export_pos_size > 16) {
+ dev_priv->r600_sx_max_export_pos_size -= 16;
+ dev_priv->r600_sx_max_export_smx_size += 16;
+ }
break;
case CHIP_RV710:
dev_priv->r600_max_pipes = 2;
@@ -1345,6 +1350,31 @@ static void r700_gfx_init(struct drm_device *dev,
dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
break;
+ case CHIP_RV740:
+ dev_priv->r600_max_pipes = 4;
+ dev_priv->r600_max_tile_pipes = 4;
+ dev_priv->r600_max_simds = 8;
+ dev_priv->r600_max_backends = 4;
+ dev_priv->r600_max_gprs = 256;
+ dev_priv->r600_max_threads = 248;
+ dev_priv->r600_max_stack_entries = 512;
+ dev_priv->r600_max_hw_contexts = 8;
+ dev_priv->r600_max_gs_threads = 16 * 2;
+ dev_priv->r600_sx_max_export_size = 256;
+ dev_priv->r600_sx_max_export_pos_size = 32;
+ dev_priv->r600_sx_max_export_smx_size = 224;
+ dev_priv->r600_sq_num_cf_insts = 2;
+
+ dev_priv->r700_sx_num_of_sets = 7;
+ dev_priv->r700_sc_prim_fifo_size = 0x100;
+ dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
+ dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
+
+ if (dev_priv->r600_sx_max_export_pos_size > 16) {
+ dev_priv->r600_sx_max_export_pos_size -= 16;
+ dev_priv->r600_sx_max_export_smx_size += 16;
+ }
+ break;
default:
break;
}
@@ -1493,6 +1523,7 @@ static void r700_gfx_init(struct drm_device *dev,
break;
case CHIP_RV730:
case CHIP_RV710:
+ case CHIP_RV740:
default:
sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x4);
break;
@@ -1569,6 +1600,7 @@ static void r700_gfx_init(struct drm_device *dev,
switch (dev_priv->flags & RADEON_FAMILY_MASK) {
case CHIP_RV770:
case CHIP_RV730:
+ case CHIP_RV740:
gs_prim_buffer_depth = 384;
break;
case CHIP_RV710:
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index 77a7a4d84650..89c4c44169f7 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -2109,7 +2109,7 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master)
/* prebuild the SAREA */
sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE);
- ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER,
+ ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK,
&master_priv->sarea);
if (ret) {
DRM_ERROR("SAREA setup failed\n");
@@ -2185,9 +2185,9 @@ void radeon_commit_ring(drm_radeon_private_t *dev_priv)
/* check if the ring is padded out to 16-dword alignment */
- tail_aligned = dev_priv->ring.tail & 0xf;
+ tail_aligned = dev_priv->ring.tail & (RADEON_RING_ALIGN-1);
if (tail_aligned) {
- int num_p2 = 16 - tail_aligned;
+ int num_p2 = RADEON_RING_ALIGN - tail_aligned;
ring = dev_priv->ring.start;
/* pad with some CP_PACKET2 */
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index ed4d27e6ee6f..127d0456f628 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -99,9 +99,10 @@
* 1.27- Add support for IGP GART
* 1.28- Add support for VBL on CRTC2
* 1.29- R500 3D cmd buffer support
+ * 1.30- Add support for occlusion queries
*/
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 29
+#define DRIVER_MINOR 30
#define DRIVER_PATCHLEVEL 0
/*
@@ -145,6 +146,7 @@ enum radeon_family {
CHIP_RV770,
CHIP_RV730,
CHIP_RV710,
+ CHIP_RV740,
CHIP_LAST,
};
@@ -1963,11 +1965,14 @@ do { \
#define RING_LOCALS int write, _nr, _align_nr; unsigned int mask; u32 *ring;
+#define RADEON_RING_ALIGN 16
+
#define BEGIN_RING( n ) do { \
if ( RADEON_VERBOSE ) { \
DRM_INFO( "BEGIN_RING( %d )\n", (n)); \
} \
- _align_nr = (n + 0xf) & ~0xf; \
+ _align_nr = RADEON_RING_ALIGN - ((dev_priv->ring.tail + n) & (RADEON_RING_ALIGN-1)); \
+ _align_nr += n; \
if (dev_priv->ring.space <= (_align_nr * sizeof(u32))) { \
COMMIT_RING(); \
radeon_wait_ring( dev_priv, _align_nr * sizeof(u32)); \
diff --git a/drivers/gpu/drm/via/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c
index 409e00afdd07..327380888b4a 100644
--- a/drivers/gpu/drm/via/via_dmablit.c
+++ b/drivers/gpu/drm/via/via_dmablit.c
@@ -195,10 +195,8 @@ via_free_sg_info(struct pci_dev *pdev, drm_via_sg_info_t *vsg)
default:
vsg->state = dr_via_sg_init;
}
- if (vsg->bounce_buffer) {
- vfree(vsg->bounce_buffer);
- vsg->bounce_buffer = NULL;
- }
+ vfree(vsg->bounce_buffer);
+ vsg->bounce_buffer = NULL;
vsg->free_on_sequence = 0;
}
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 7359d9d88e46..acbce5745b0c 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -151,7 +151,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (fnmode) {
int do_translate;
- trans = apple_find_translation((hid->product < 0x220 ||
+ trans = apple_find_translation((hid->product < 0x21d ||
hid->product >= 0x300) ?
powerbook_fn_keys : apple_fn_keys,
usage->code);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 5746a5903bce..8551693d645f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1293,6 +1293,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
@@ -1824,6 +1825,9 @@ int hid_check_keys_pressed(struct hid_device *hid)
struct hid_input *hidinput;
int i;
+ if (!(hid->claimed & HID_CLAIMED_INPUT))
+ return 0;
+
list_for_each_entry(hidinput, &hid->inputs, list) {
for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++)
if (hidinput->input->key[i])
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index bdeda4c7cc13..4d5ee2bbc62b 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -113,6 +113,11 @@
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
+#define USB_VENDOR_ID_CH 0x068e
+#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2
+#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4
+#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff
+
#define USB_VENDOR_ID_CHERRY 0x046a
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
@@ -292,6 +297,7 @@
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
+#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 7b80cb694982..7afbaa0efd18 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -297,6 +297,8 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
.driver_data = LG_FF },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
+ .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
{ }
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index e263d4731179..00ccf4b1985d 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -285,8 +285,10 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
int len;
- if (!hid->name)
- return 0;
+ if (!hid->name) {
+ ret = 0;
+ break;
+ }
len = strlen(hid->name) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
@@ -297,8 +299,10 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
int len;
- if (!hid->phys)
- return 0;
+ if (!hid->phys) {
+ ret = 0;
+ break;
+ }
len = strlen(hid->phys) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 4306cb1b8ce5..ac8049b5f1e9 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -662,8 +662,8 @@ void usbhid_close(struct hid_device *hid)
spin_lock_irq(&usbhid->lock);
if (!--hid->open) {
spin_unlock_irq(&usbhid->lock);
+ hid_cancel_delayed_stuff(usbhid);
usb_kill_urb(usbhid->urbin);
- flush_scheduled_work();
usbhid->intf->needs_remote_wakeup = 0;
} else {
spin_unlock_irq(&usbhid->lock);
@@ -898,7 +898,7 @@ static int usbhid_parse(struct hid_device *hid)
goto err;
}
- hid->quirks = quirks;
+ hid->quirks |= quirks;
return 0;
err:
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 4391717d2519..d8f7423f363e 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -50,6 +50,9 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index 0897edef2574..bff0103610c1 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -348,6 +348,7 @@ static int validate_hwmon_pack(struct atk_data *data, union acpi_object *obj)
return 0;
}
+#ifdef DEBUG
static char const *atk_sensor_type(union acpi_object *flags)
{
u64 type = flags->integer.value & ATK_TYPE_MASK;
@@ -370,6 +371,7 @@ static char const *atk_sensor_type(union acpi_object *flags)
return what;
}
+#endif
static void atk_print_sensor(struct atk_data *data, union acpi_object *obj)
{
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index b5e3b2851698..a1787fdf5b9f 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -182,7 +182,7 @@ static struct platform_driver lm78_isa_driver = {
.name = "lm78",
},
.probe = lm78_isa_probe,
- .remove = lm78_isa_remove,
+ .remove = __devexit_p(lm78_isa_remove),
};
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index dbfb30c588d8..0bdab959b736 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1462,7 +1462,8 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
data->pwm[i] =
w83781d_read_value(data,
W83781D_REG_PWM[i]);
- if ((data->type != w83782d || !client->driver)
+ /* Only W83782D on SMBus has PWM3 and PWM4 */
+ if ((data->type != w83782d || !client)
&& i == 1)
break;
}
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index d420cc5f5633..e25e13980af3 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -104,7 +104,7 @@ static int sclhi(struct i2c_algo_bit_data *adap)
* chips may hold it low ("clock stretching") while they
* are processing data internally.
*/
- if (time_after_eq(jiffies, start + adap->timeout))
+ if (time_after(jiffies, start + adap->timeout))
return -ETIMEDOUT;
cond_resched();
}
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 6318f7ddc1d4..78d42aae0089 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -287,10 +287,21 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after data byte\n");
+ pca_stop(adap);
goto out;
case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */
DEB2("Arbitration lost\n");
+ /*
+ * The PCA9564 data sheet (2006-09-01) says "A
+ * START condition will be transmitted when the
+ * bus becomes free (STOP or SCL and SDA high)"
+ * when the STA bit is set (p. 11).
+ *
+ * In case this won't work, try pca_reset()
+ * instead.
+ */
+ pca_start(adap);
goto out;
case 0x58: /* Data byte has been received; NOT ACK has been returned */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index a48c8aee0218..f1c6ca7e2852 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -467,7 +467,7 @@ config I2C_PXA_SLAVE
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C2410 || ARCH_S3C64XX
help
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index 981e080b32ae..d108450df064 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -284,7 +284,7 @@ static int ali1535_transaction(struct i2c_adapter *adap)
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
result = -ETIMEDOUT;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 39066dee46e3..d627fceb790b 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -306,7 +306,7 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
result = -ETIMEDOUT;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 220f4a1eee1d..f7d6fe9c49ba 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -126,7 +126,7 @@ static int amd756_transaction(struct i2c_adapter *adap)
} while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
(timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp);
goto abort;
}
@@ -143,7 +143,7 @@ static int amd756_transaction(struct i2c_adapter *adap)
} while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&adap->dev, "Completion timeout!\n");
goto abort;
}
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index 3fcf78e906db..b5db8b883615 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -531,16 +531,16 @@ static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm)
rbdf = cpm->rbase;
for (i = 0; i < CPM_MAXBD; i++) {
- cpm->rxbuf[i] = dma_alloc_coherent(
- NULL, CPM_MAX_READ + 1, &cpm->rxdma[i], GFP_KERNEL);
+ cpm->rxbuf[i] = dma_alloc_coherent(&cpm->ofdev->dev,
+ CPM_MAX_READ + 1,
+ &cpm->rxdma[i], GFP_KERNEL);
if (!cpm->rxbuf[i]) {
ret = -ENOMEM;
goto out_muram;
}
out_be32(&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] + 1) & ~1));
- cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(
- NULL, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL);
+ cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL);
if (!cpm->txbuf[i]) {
ret = -ENOMEM;
goto out_muram;
@@ -585,10 +585,10 @@ static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm)
out_muram:
for (i = 0; i < CPM_MAXBD; i++) {
if (cpm->rxbuf[i])
- dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1,
cpm->rxbuf[i], cpm->rxdma[i]);
if (cpm->txbuf[i])
- dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1,
cpm->txbuf[i], cpm->txdma[i]);
}
cpm_muram_free(cpm->dp_addr);
@@ -619,9 +619,9 @@ static void cpm_i2c_shutdown(struct cpm_i2c *cpm)
/* Free all memory */
for (i = 0; i < CPM_MAXBD; i++) {
- dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1,
cpm->rxbuf[i], cpm->rxdma[i]);
- dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1,
cpm->txbuf[i], cpm->txdma[i]);
}
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 10411848fd70..9d2c5adf5d4f 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -237,7 +237,7 @@ static int i801_transaction(int xact)
status = inb_p(SMBHSTSTS);
} while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
- result = i801_check_post(status, timeout >= MAX_TIMEOUT);
+ result = i801_check_post(status, timeout > MAX_TIMEOUT);
if (result < 0)
return result;
@@ -257,9 +257,9 @@ static void i801_wait_hwpec(void)
} while ((!(status & SMBHSTSTS_INTR))
&& (timeout++ < MAX_TIMEOUT));
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT)
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
- }
+
outb_p(status, SMBHSTSTS);
}
@@ -344,7 +344,7 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
while ((!(status & SMBHSTSTS_BYTE_DONE))
&& (timeout++ < MAX_TIMEOUT));
- result = i801_check_post(status, timeout >= MAX_TIMEOUT);
+ result = i801_check_post(status, timeout > MAX_TIMEOUT);
if (result < 0)
return result;
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index b9c01aa90036..9f6b8e0f8632 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -112,7 +112,7 @@ static int sch_transaction(void)
} while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 4af5c09f0e8f..dd778d7ae047 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -164,7 +164,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
return 0;
}
-#ifdef CONFIG_PPC_52xx
+#ifdef CONFIG_PPC_MPC52xx
static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
{20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
{28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02},
@@ -188,7 +188,7 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler)
{
- const struct mpc52xx_i2c_divider *div = NULL;
+ const struct mpc_i2c_divider *div = NULL;
unsigned int pvr = mfspr(SPRN_PVR);
u32 divider;
int i;
@@ -203,7 +203,7 @@ int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler)
* We want to choose an FDR/DFSR that generates an I2C bus speed that
* is equal to or lower than the requested speed.
*/
- for (i = 0; i < ARRAY_SIZE(mpc52xx_i2c_dividers); i++) {
+ for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_52xx); i++) {
div = &mpc_i2c_dividers_52xx[i];
/* Old MPC5200 rev A CPUs do not support the high bits */
if (div->fdr & 0xc0 && pvr == 0x80822011)
@@ -219,20 +219,23 @@ static void mpc_i2c_setclock_52xx(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock, u32 prescaler)
{
- int fdr = mpc52xx_i2c_get_fdr(node, clock, prescaler);
+ int ret, fdr;
+
+ ret = mpc_i2c_get_fdr_52xx(node, clock, prescaler);
+ fdr = (ret >= 0) ? ret : 0x3f; /* backward compatibility */
- if (fdr < 0)
- fdr = 0x3f; /* backward compatibility */
writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);
- dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
+
+ if (ret >= 0)
+ dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
}
-#else /* !CONFIG_PPC_52xx */
+#else /* !CONFIG_PPC_MPC52xx */
static void mpc_i2c_setclock_52xx(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock, u32 prescaler)
{
}
-#endif /* CONFIG_PPC_52xx*/
+#endif /* CONFIG_PPC_MPC52xx*/
#ifdef CONFIG_FSL_SOC
static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
@@ -321,14 +324,17 @@ static void mpc_i2c_setclock_8xxx(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock, u32 prescaler)
{
- int fdr = mpc_i2c_get_fdr_8xxx(node, clock, prescaler);
+ int ret, fdr;
+
+ ret = mpc_i2c_get_fdr_8xxx(node, clock, prescaler);
+ fdr = (ret >= 0) ? ret : 0x1031; /* backward compatibility */
- if (fdr < 0)
- fdr = 0x1031; /* backward compatibility */
writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);
writeb((fdr >> 8) & 0xff, i2c->base + MPC_I2C_DFSRR);
- dev_info(i2c->dev, "clock %d Hz (dfsrr=%d fdr=%d)\n",
- clock, fdr >> 8, fdr & 0xff);
+
+ if (ret >= 0)
+ dev_info(i2c->dev, "clock %d Hz (dfsrr=%d fdr=%d)\n",
+ clock, fdr >> 8, fdr & 0xff);
}
#else /* !CONFIG_FSL_SOC */
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index 2ff4683703a8..ec11d1c4e77b 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -173,7 +173,7 @@ static int nforce2_check_status(struct i2c_adapter *adap)
temp = inb_p(NVIDIA_SMB_STS);
} while ((!temp) && (timeout++ < MAX_TIMEOUT));
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
if (smbus->can_abort)
nforce2_abort(adap);
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index c1405c8f6ba5..acc7143d9655 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -265,10 +265,10 @@ static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c)
show_state(i2c);
}
- if (timeout <= 0)
+ if (timeout < 0)
show_state(i2c);
- return timeout <= 0 ? I2C_RETRY : 0;
+ return timeout < 0 ? I2C_RETRY : 0;
}
static int i2c_pxa_wait_master(struct pxa_i2c *i2c)
@@ -612,7 +612,7 @@ static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c)
show_state(i2c);
}
- if (timeout <= 0) {
+ if (timeout < 0) {
show_state(i2c);
dev_err(&i2c->adap.dev,
"i2c_pxa: timeout waiting for bus free\n");
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index baa28b73ae42..b9680f50f541 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -396,7 +396,7 @@ static int __devinit calc_CCR(unsigned long scl_hz)
signed char cdf, cdfm;
int scgd, scgdm, scgds;
- mclk = clk_get(NULL, "module_clk");
+ mclk = clk_get(NULL, "peripheral_clk");
if (IS_ERR(mclk)) {
return PTR_ERR(mclk);
} else {
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index f320ab27da46..139f0c7f12a4 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -256,7 +256,7 @@ static int sis5595_transaction(struct i2c_adapter *adap)
} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 50c3610e6028..70ca41e90e58 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -173,7 +173,7 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index 7e1594b40579..8295885b2fdb 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -132,7 +132,7 @@ static int sis96x_transaction(int size)
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
+ if (timeout > MAX_TIMEOUT) {
dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp);
result = -ETIMEDOUT;
}
diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c
index 537da1cde16d..e59b6dee9ae2 100644
--- a/drivers/ide/alim15x3.c
+++ b/drivers/ide/alim15x3.c
@@ -402,27 +402,23 @@ static u8 ali_cable_detect(ide_hwif_t *hwif)
return cbl;
}
-#if !defined(CONFIG_SPARC64) && !defined(CONFIG_PPC)
+#ifndef CONFIG_SPARC64
/**
* init_hwif_ali15x3 - Initialize the ALI IDE x86 stuff
* @hwif: interface to configure
*
* Obtain the IRQ tables for an ALi based IDE solution on the PC
* class platforms. This part of the code isn't applicable to the
- * Sparc and PowerPC systems.
+ * Sparc systems.
*/
static void __devinit init_hwif_ali15x3 (ide_hwif_t *hwif)
{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
u8 ideic, inmir;
s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6,
1, 11, 0, 12, 0, 14, 0, 15 };
int irq = -1;
- if (dev->device == PCI_DEVICE_ID_AL_M5229)
- hwif->irq = hwif->channel ? 15 : 14;
-
if (isa_dev) {
/*
* read IDE interface control
@@ -455,7 +451,7 @@ static void __devinit init_hwif_ali15x3 (ide_hwif_t *hwif)
}
#else
#define init_hwif_ali15x3 NULL
-#endif /* !defined(CONFIG_SPARC64) && !defined(CONFIG_PPC) */
+#endif /* CONFIG_SPARC64 */
/**
* init_dma_ali15x3 - set up DMA on ALi15x3
diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c
index 403d0e4265db..fc0949a8cfde 100644
--- a/drivers/ide/at91_ide.c
+++ b/drivers/ide/at91_ide.c
@@ -216,6 +216,7 @@ static const struct ide_port_info at91_ide_port_info __initdata = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | IDE_HFLAG_SINGLE |
IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_UNMASK_IRQS,
.pio_mask = ATA_PIO6,
+ .chipset = ide_generic,
};
/*
@@ -246,8 +247,7 @@ irqreturn_t at91_irq_handler(int irq, void *dev_id)
static int __init at91_ide_probe(struct platform_device *pdev)
{
int ret;
- hw_regs_t hw;
- hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
struct ide_host *host;
struct resource *res;
unsigned long tf_base = 0, ctl_base = 0;
@@ -304,10 +304,9 @@ static int __init at91_ide_probe(struct platform_device *pdev)
ide_std_init_ports(&hw, tf_base, ctl_base + 6);
hw.irq = board->irq_pin;
- hw.chipset = ide_generic;
hw.dev = &pdev->dev;
- host = ide_host_alloc(&at91_ide_port_info, hws);
+ host = ide_host_alloc(&at91_ide_port_info, hws, 1);
if (!host) {
perr("failed to allocate ide host\n");
return -ENOMEM;
diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c
index 46013644c965..58121bd6c115 100644
--- a/drivers/ide/au1xxx-ide.c
+++ b/drivers/ide/au1xxx-ide.c
@@ -449,7 +449,7 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
}
#endif
-static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif)
+static void auide_setup_ports(struct ide_hw *hw, _auide_hwif *ahwif)
{
int i;
unsigned long *ata_regs = hw->io_ports_array;
@@ -499,6 +499,7 @@ static const struct ide_port_info au1xxx_port_info = {
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
.mwdma_mask = ATA_MWDMA2,
#endif
+ .chipset = ide_au1xxx,
};
static int au_ide_probe(struct platform_device *dev)
@@ -507,7 +508,7 @@ static int au_ide_probe(struct platform_device *dev)
struct resource *res;
struct ide_host *host;
int ret = 0;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA)
char *mode = "MWDMA2";
@@ -548,9 +549,8 @@ static int au_ide_probe(struct platform_device *dev)
auide_setup_ports(&hw, ahwif);
hw.irq = ahwif->irq;
hw.dev = &dev->dev;
- hw.chipset = ide_au1xxx;
- ret = ide_host_add(&au1xxx_port_info, hws, &host);
+ ret = ide_host_add(&au1xxx_port_info, hws, 1, &host);
if (ret)
goto out;
diff --git a/drivers/ide/buddha.c b/drivers/ide/buddha.c
index d028f8864bc1..e3c6a5913305 100644
--- a/drivers/ide/buddha.c
+++ b/drivers/ide/buddha.c
@@ -121,7 +121,7 @@ static int xsurf_ack_intr(ide_hwif_t *hwif)
return 1;
}
-static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init buddha_setup_ports(struct ide_hw *hw, unsigned long base,
unsigned long ctl, unsigned long irq_port,
ide_ack_intr_t *ack_intr)
{
@@ -139,13 +139,12 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = IRQ_AMIGA_PORTS;
hw->ack_intr = ack_intr;
-
- hw->chipset = ide_generic;
}
static const struct ide_port_info buddha_port_info = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_generic,
};
/*
@@ -161,7 +160,7 @@ static int __init buddha_init(void)
while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
unsigned long board;
- hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[MAX_NUM_HWIFS], *hws[MAX_NUM_HWIFS];
if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
buddha_num_hwifs = BUDDHA_NUM_HWIFS;
@@ -225,7 +224,7 @@ fail_base2:
hws[i] = &hw[i];
}
- ide_host_add(&buddha_port_info, hws, NULL);
+ ide_host_add(&buddha_port_info, hws, i, NULL);
}
return 0;
diff --git a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c
index 8890276fef7f..1683ed5c7329 100644
--- a/drivers/ide/cmd640.c
+++ b/drivers/ide/cmd640.c
@@ -708,7 +708,7 @@ static int __init cmd640x_init(void)
int second_port_cmd640 = 0, rc;
const char *bus_type, *port2;
u8 b, cfr;
- hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[2], *hws[2];
if (cmd640_vlb && probe_for_cmd640_vlb()) {
bus_type = "VLB";
@@ -762,11 +762,9 @@ static int __init cmd640x_init(void)
ide_std_init_ports(&hw[0], 0x1f0, 0x3f6);
hw[0].irq = 14;
- hw[0].chipset = ide_cmd640;
ide_std_init_ports(&hw[1], 0x170, 0x376);
hw[1].irq = 15;
- hw[1].chipset = ide_cmd640;
printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x"
"\n", 'a' + cmd640_chip_version - 1, bus_type, cfr);
@@ -824,7 +822,8 @@ static int __init cmd640x_init(void)
cmd640_dump_regs();
#endif
- return ide_host_add(&cmd640_port_info, hws, NULL);
+ return ide_host_add(&cmd640_port_info, hws, second_port_cmd640 ? 2 : 1,
+ NULL);
}
module_param_named(probe_vlb, cmd640_vlb, bool, 0);
diff --git a/drivers/ide/cs5520.c b/drivers/ide/cs5520.c
index 87987a7d36c9..bd066bb9d611 100644
--- a/drivers/ide/cs5520.c
+++ b/drivers/ide/cs5520.c
@@ -110,7 +110,7 @@ static const struct ide_port_info cyrix_chipset __devinitdata = {
static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
const struct ide_port_info *d = &cyrix_chipset;
- hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[2], *hws[] = { NULL, NULL };
ide_setup_pci_noise(dev, d);
@@ -136,7 +136,7 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic
ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
hw[0].irq = 14;
- return ide_host_add(d, hws, NULL);
+ return ide_host_add(d, hws, 2, NULL);
}
static const struct pci_device_id cs5520_pci_tbl[] = {
diff --git a/drivers/ide/delkin_cb.c b/drivers/ide/delkin_cb.c
index f153b95619bb..1e10eba62ceb 100644
--- a/drivers/ide/delkin_cb.c
+++ b/drivers/ide/delkin_cb.c
@@ -68,6 +68,7 @@ static const struct ide_port_info delkin_cb_port_info = {
IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
.init_chipset = delkin_cb_init_chipset,
+ .chipset = ide_pci,
};
static int __devinit
@@ -76,7 +77,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
struct ide_host *host;
unsigned long base;
int rc;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
rc = pci_enable_device(dev);
if (rc) {
@@ -97,9 +98,8 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
ide_std_init_ports(&hw, base + 0x10, base + 0x1e);
hw.irq = dev->irq;
hw.dev = &dev->dev;
- hw.chipset = ide_pci; /* this enables IRQ sharing */
- rc = ide_host_add(&delkin_cb_port_info, hws, &host);
+ rc = ide_host_add(&delkin_cb_port_info, hws, 1, &host);
if (rc)
goto out_disable;
diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c
index 0e2df6755ec9..22fa27389c3b 100644
--- a/drivers/ide/falconide.c
+++ b/drivers/ide/falconide.c
@@ -111,9 +111,10 @@ static const struct ide_port_info falconide_port_info = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE |
IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_generic,
};
-static void __init falconide_setup_ports(hw_regs_t *hw)
+static void __init falconide_setup_ports(struct ide_hw *hw)
{
int i;
@@ -128,8 +129,6 @@ static void __init falconide_setup_ports(hw_regs_t *hw)
hw->irq = IRQ_MFP_IDE;
hw->ack_intr = NULL;
-
- hw->chipset = ide_generic;
}
/*
@@ -139,7 +138,7 @@ static void __init falconide_setup_ports(hw_regs_t *hw)
static int __init falconide_init(void)
{
struct ide_host *host;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
int rc;
if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE))
@@ -154,7 +153,7 @@ static int __init falconide_init(void)
falconide_setup_ports(&hw);
- host = ide_host_alloc(&falconide_port_info, hws);
+ host = ide_host_alloc(&falconide_port_info, hws, 1);
if (host == NULL) {
rc = -ENOMEM;
goto err;
diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c
index c7119516c5a7..4451a6a5dfe0 100644
--- a/drivers/ide/gayle.c
+++ b/drivers/ide/gayle.c
@@ -88,7 +88,7 @@ static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
return 1;
}
-static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init gayle_setup_ports(struct ide_hw *hw, unsigned long base,
unsigned long ctl, unsigned long irq_port,
ide_ack_intr_t *ack_intr)
{
@@ -106,14 +106,13 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = IRQ_AMIGA_PORTS;
hw->ack_intr = ack_intr;
-
- hw->chipset = ide_generic;
}
static const struct ide_port_info gayle_port_info = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE |
IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_generic,
};
/*
@@ -126,7 +125,7 @@ static int __init gayle_init(void)
unsigned long base, ctrlport, irqport;
ide_ack_intr_t *ack_intr;
int a4000, i, rc;
- hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[GAYLE_NUM_HWIFS], *hws[GAYLE_NUM_HWIFS];
if (!MACH_IS_AMIGA)
return -ENODEV;
@@ -171,7 +170,7 @@ found:
hws[i] = &hw[i];
}
- rc = ide_host_add(&gayle_port_info, hws, NULL);
+ rc = ide_host_add(&gayle_port_info, hws, i, NULL);
if (rc)
release_mem_region(res_start, res_n);
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index 0feb66c720e1..7ce68ef6b904 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -138,14 +138,6 @@
#undef HPT_RESET_STATE_ENGINE
#undef HPT_DELAY_INTERRUPT
-static const char *quirk_drives[] = {
- "QUANTUM FIREBALLlct08 08",
- "QUANTUM FIREBALLP KA6.4",
- "QUANTUM FIREBALLP LM20.4",
- "QUANTUM FIREBALLP LM20.5",
- NULL
-};
-
static const char *bad_ata100_5[] = {
"IBM-DTLA-307075",
"IBM-DTLA-307060",
@@ -729,27 +721,13 @@ static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
hpt3xx_set_mode(drive, XFER_PIO_0 + pio);
}
-static void hpt3xx_quirkproc(ide_drive_t *drive)
-{
- char *m = (char *)&drive->id[ATA_ID_PROD];
- const char **list = quirk_drives;
-
- while (*list)
- if (strstr(m, *list++)) {
- drive->quirk_list = 1;
- return;
- }
-
- drive->quirk_list = 0;
-}
-
static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
{
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct hpt_info *info = hpt3xx_get_info(hwif->dev);
- if (drive->quirk_list == 0)
+ if ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0)
return;
if (info->chip_type >= HPT370) {
@@ -1404,7 +1382,6 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2)
static const struct ide_port_ops hpt3xx_port_ops = {
.set_pio_mode = hpt3xx_set_pio_mode,
.set_dma_mode = hpt3xx_set_mode,
- .quirkproc = hpt3xx_quirkproc,
.maskproc = hpt3xx_maskproc,
.mdma_filter = hpt3xx_mdma_filter,
.udma_filter = hpt3xx_udma_filter,
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
index 4e16ce68b063..5af3d0ffaf0a 100644
--- a/drivers/ide/icside.c
+++ b/drivers/ide/icside.c
@@ -65,8 +65,6 @@ static struct cardinfo icside_cardinfo_v6_2 = {
};
struct icside_state {
- unsigned int channel;
- unsigned int enabled;
void __iomem *irq_port;
void __iomem *ioc_base;
unsigned int sel;
@@ -116,18 +114,11 @@ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
struct icside_state *state = ec->irq_data;
void __iomem *base = state->irq_port;
- state->enabled = 1;
+ writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
+ readb(base + ICS_ARCIN_V6_INTROFFSET_2);
- switch (state->channel) {
- case 0:
- writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
- readb(base + ICS_ARCIN_V6_INTROFFSET_2);
- break;
- case 1:
- writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
- readb(base + ICS_ARCIN_V6_INTROFFSET_1);
- break;
- }
+ writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
+ readb(base + ICS_ARCIN_V6_INTROFFSET_1);
}
/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
@@ -137,8 +128,6 @@ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
{
struct icside_state *state = ec->irq_data;
- state->enabled = 0;
-
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
}
@@ -160,44 +149,6 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
.irqpending = icside_irqpending_arcin_v6,
};
-/*
- * Handle routing of interrupts. This is called before
- * we write the command to the drive.
- */
-static void icside_maskproc(ide_drive_t *drive, int mask)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct expansion_card *ec = ECARD_DEV(hwif->dev);
- struct icside_state *state = ecard_get_drvdata(ec);
- unsigned long flags;
-
- local_irq_save(flags);
-
- state->channel = hwif->channel;
-
- if (state->enabled && !mask) {
- switch (hwif->channel) {
- case 0:
- writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
- readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
- break;
- case 1:
- writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
- readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
- break;
- }
- } else {
- readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
- readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
- }
-
- local_irq_restore(flags);
-}
-
-static const struct ide_port_ops icside_v6_no_dma_port_ops = {
- .maskproc = icside_maskproc,
-};
-
#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
/*
* SG-DMA support.
@@ -275,7 +226,6 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
static const struct ide_port_ops icside_v6_port_ops = {
.set_dma_mode = icside_set_dma_mode,
- .maskproc = icside_maskproc,
};
static void icside_dma_host_set(ide_drive_t *drive, int on)
@@ -320,11 +270,6 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
BUG_ON(dma_channel_active(ec->dma));
/*
- * Ensure that we have the right interrupt routed.
- */
- icside_maskproc(drive, 0);
-
- /*
* Route the DMA signals to the correct interface.
*/
writeb(state->sel | hwif->channel, state->ioc_base);
@@ -381,7 +326,7 @@ static int icside_dma_off_init(ide_hwif_t *hwif, const struct ide_port_info *d)
return -EOPNOTSUPP;
}
-static void icside_setup_ports(hw_regs_t *hw, void __iomem *base,
+static void icside_setup_ports(struct ide_hw *hw, void __iomem *base,
struct cardinfo *info, struct expansion_card *ec)
{
unsigned long port = (unsigned long)base + info->dataoffset;
@@ -398,11 +343,11 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base,
hw->irq = ec->irq;
hw->dev = &ec->dev;
- hw->chipset = ide_acorn;
}
static const struct ide_port_info icside_v5_port_info = {
.host_flags = IDE_HFLAG_NO_DMA,
+ .chipset = ide_acorn,
};
static int __devinit
@@ -410,7 +355,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
{
void __iomem *base;
struct ide_host *host;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
int ret;
base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
@@ -431,7 +376,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec);
- host = ide_host_alloc(&icside_v5_port_info, hws);
+ host = ide_host_alloc(&icside_v5_port_info, hws, 1);
if (host == NULL)
return -ENODEV;
@@ -452,11 +397,11 @@ err_free:
static const struct ide_port_info icside_v6_port_info __initdata = {
.init_dma = icside_dma_off_init,
- .port_ops = &icside_v6_no_dma_port_ops,
.dma_ops = &icside_v6_dma_ops,
.host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO,
.mwdma_mask = ATA_MWDMA2,
.swdma_mask = ATA_SWDMA2,
+ .chipset = ide_acorn,
};
static int __devinit
@@ -466,7 +411,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
struct ide_host *host;
unsigned int sel = 0;
int ret;
- hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL };
+ struct ide_hw hw[2], *hws[] = { &hw[0], &hw[1] };
struct ide_port_info d = icside_v6_port_info;
ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
@@ -506,7 +451,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec);
icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec);
- host = ide_host_alloc(&d, hws);
+ host = ide_host_alloc(&d, hws, 2);
if (host == NULL)
return -ENODEV;
diff --git a/drivers/ide/ide-4drives.c b/drivers/ide/ide-4drives.c
index 78aca75a2c48..979d342c338a 100644
--- a/drivers/ide/ide-4drives.c
+++ b/drivers/ide/ide-4drives.c
@@ -25,12 +25,13 @@ static const struct ide_port_info ide_4drives_port_info = {
.port_ops = &ide_4drives_port_ops,
.host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA |
IDE_HFLAG_4DRIVES,
+ .chipset = ide_4drives,
};
static int __init ide_4drives_init(void)
{
unsigned long base = 0x1f0, ctl = 0x3f6;
- hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw, &hw };
if (probe_4drives == 0)
return -ENODEV;
@@ -52,9 +53,8 @@ static int __init ide_4drives_init(void)
ide_std_init_ports(&hw, base, ctl);
hw.irq = 14;
- hw.chipset = ide_4drives;
- return ide_host_add(&ide_4drives_port_info, hws, NULL);
+ return ide_host_add(&ide_4drives_port_info, hws, 2, NULL);
}
module_init(ide_4drives_init);
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index 1125ce29809b..6c73421c608b 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -256,7 +256,7 @@ void ide_retry_pc(ide_drive_t *drive)
memcpy(pc->c, sense_rq->cmd, 12);
if (drive->media == ide_tape)
- set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+ drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
/*
* Push back the failed request and put request sense on top
@@ -461,6 +461,12 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0)
dsc = 1;
+ /*
+ * ->pc_callback() might change rq->data_len for
+ * residual count, cache total length.
+ */
+ done = blk_rq_bytes(rq);
+
/* Command finished - Call the callback function */
uptodate = drive->pc_callback(drive, dsc);
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index d299713bfdc1..dcd72445b0cd 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -182,7 +182,7 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive,
(sense->information[2] << 8) |
(sense->information[3]);
- if (drive->queue->hardsect_size == 2048)
+ if (queue_logical_block_size(drive->queue) == 2048)
/* device sector size is 2K */
sector <<= 2;
@@ -282,7 +282,6 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
ide_hwif_t *hwif = drive->hwif;
struct request *rq = hwif->rq;
int err, sense_key, do_end_request = 0;
- u8 quiet = rq->cmd_flags & REQ_QUIET;
/* get the IDE error register */
err = ide_read_error(drive);
@@ -317,7 +316,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
} else {
cdrom_saw_media_change(drive);
- if (blk_fs_request(rq) && !quiet)
+ if (blk_fs_request(rq) && !blk_rq_quiet(rq))
printk(KERN_ERR PFX "%s: tray open\n",
drive->name);
}
@@ -352,7 +351,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
* No point in retrying after an illegal request or data
* protect error.
*/
- if (!quiet)
+ if (!blk_rq_quiet(rq))
ide_dump_status(drive, "command error", stat);
do_end_request = 1;
break;
@@ -361,14 +360,14 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
* No point in re-trying a zillion times on a bad sector.
* If we got here the error is not correctable.
*/
- if (!quiet)
+ if (!blk_rq_quiet(rq))
ide_dump_status(drive, "media error "
"(bad sector)", stat);
do_end_request = 1;
break;
case BLANK_CHECK:
/* disk appears blank? */
- if (!quiet)
+ if (!blk_rq_quiet(rq))
ide_dump_status(drive, "media error (blank)",
stat);
do_end_request = 1;
@@ -653,6 +652,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
out_end:
if (blk_pc_request(rq) && rc == 0) {
+ rq->resid_len = 0;
blk_end_request_all(rq, 0);
hwif->rq = NULL;
} else {
@@ -672,8 +672,7 @@ out_end:
/* make sure it's fully ended */
if (blk_fs_request(rq) == 0) {
- rq->resid_len = blk_rq_bytes(rq) -
- (cmd->nbytes - cmd->nleft);
+ rq->resid_len -= cmd->nbytes - cmd->nleft;
if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE))
rq->resid_len += cmd->last_xfer_len;
}
@@ -692,7 +691,7 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq)
struct request_queue *q = drive->queue;
int write = rq_data_dir(rq) == WRITE;
unsigned short sectors_per_frame =
- queue_hardsect_size(q) >> SECTOR_BITS;
+ queue_logical_block_size(q) >> SECTOR_BITS;
ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, "
"secs_per_frame: %u",
@@ -976,8 +975,8 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense)
/* save a private copy of the TOC capacity for error handling */
drive->probed_capacity = toc->capacity * sectors_per_frame;
- blk_queue_hardsect_size(drive->queue,
- sectors_per_frame << SECTOR_BITS);
+ blk_queue_logical_block_size(drive->queue,
+ sectors_per_frame << SECTOR_BITS);
/* first read just the header, so we know how long the TOC is */
stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr,
@@ -1293,7 +1292,7 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive)
/* standard prep_rq_fn that builds 10 byte cmds */
static int ide_cdrom_prep_fs(struct request_queue *q, struct request *rq)
{
- int hard_sect = queue_hardsect_size(q);
+ int hard_sect = queue_logical_block_size(q);
long block = (long)blk_rq_pos(rq) / (hard_sect >> 9);
unsigned long blocks = blk_rq_sectors(rq) / (hard_sect >> 9);
@@ -1498,7 +1497,7 @@ static int ide_cdrom_setup(ide_drive_t *drive)
nslots = ide_cdrom_probe_capabilities(drive);
- blk_queue_hardsect_size(q, CD_FRAMESIZE);
+ blk_queue_logical_block_size(q, CD_FRAMESIZE);
if (ide_cdrom_register(drive, nslots)) {
printk(KERN_ERR PFX "%s: %s failed to register device with the"
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
index 9e47f3529d55..527908ff298c 100644
--- a/drivers/ide/ide-cs.c
+++ b/drivers/ide/ide-cs.c
@@ -155,6 +155,7 @@ static const struct ide_port_info idecs_port_info = {
.port_ops = &idecs_port_ops,
.host_flags = IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_pci,
};
static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
@@ -163,7 +164,7 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
struct ide_host *host;
ide_hwif_t *hwif;
int i, rc;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
if (!request_region(io, 8, DRV_NAME)) {
printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
@@ -181,10 +182,9 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, io, ctl);
hw.irq = irq;
- hw.chipset = ide_pci;
hw.dev = &handle->dev;
- rc = ide_host_add(&idecs_port_info, hws, &host);
+ rc = ide_host_add(&idecs_port_info, hws, 1, &host);
if (rc)
goto out_release;
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index ad18e14043c5..6a1de2169709 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -302,14 +302,12 @@ static const struct drive_list_entry hpa_list[] = {
{ NULL, NULL }
};
-static void idedisk_check_hpa(ide_drive_t *drive)
+static u64 ide_disk_hpa_get_native_capacity(ide_drive_t *drive, int lba48)
{
- unsigned long long capacity, set_max;
- int lba48 = ata_id_lba48_enabled(drive->id);
+ u64 capacity, set_max;
capacity = drive->capacity64;
-
- set_max = idedisk_read_native_max_address(drive, lba48);
+ set_max = idedisk_read_native_max_address(drive, lba48);
if (ide_in_drive_list(drive->id, hpa_list)) {
/*
@@ -320,9 +318,31 @@ static void idedisk_check_hpa(ide_drive_t *drive)
set_max--;
}
+ return set_max;
+}
+
+static u64 ide_disk_hpa_set_capacity(ide_drive_t *drive, u64 set_max, int lba48)
+{
+ set_max = idedisk_set_max_address(drive, set_max, lba48);
+ if (set_max)
+ drive->capacity64 = set_max;
+
+ return set_max;
+}
+
+static void idedisk_check_hpa(ide_drive_t *drive)
+{
+ u64 capacity, set_max;
+ int lba48 = ata_id_lba48_enabled(drive->id);
+
+ capacity = drive->capacity64;
+ set_max = ide_disk_hpa_get_native_capacity(drive, lba48);
+
if (set_max <= capacity)
return;
+ drive->probed_capacity = set_max;
+
printk(KERN_INFO "%s: Host Protected Area detected.\n"
"\tcurrent capacity is %llu sectors (%llu MB)\n"
"\tnative capacity is %llu sectors (%llu MB)\n",
@@ -330,13 +350,13 @@ static void idedisk_check_hpa(ide_drive_t *drive)
capacity, sectors_to_MB(capacity),
set_max, sectors_to_MB(set_max));
- set_max = idedisk_set_max_address(drive, set_max, lba48);
+ if ((drive->dev_flags & IDE_DFLAG_NOHPA) == 0)
+ return;
- if (set_max) {
- drive->capacity64 = set_max;
+ set_max = ide_disk_hpa_set_capacity(drive, set_max, lba48);
+ if (set_max)
printk(KERN_INFO "%s: Host Protected Area disabled.\n",
drive->name);
- }
}
static int ide_disk_get_capacity(ide_drive_t *drive)
@@ -358,6 +378,8 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
drive->capacity64 = drive->cyl * drive->head * drive->sect;
}
+ drive->probed_capacity = drive->capacity64;
+
if (lba) {
drive->dev_flags |= IDE_DFLAG_LBA;
@@ -376,7 +398,7 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
"%llu sectors (%llu MB)\n",
drive->name, (unsigned long long)drive->capacity64,
sectors_to_MB(drive->capacity64));
- drive->capacity64 = 1ULL << 28;
+ drive->probed_capacity = drive->capacity64 = 1ULL << 28;
}
if ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) &&
@@ -392,6 +414,34 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
return 0;
}
+static u64 ide_disk_set_capacity(ide_drive_t *drive, u64 capacity)
+{
+ u64 set = min(capacity, drive->probed_capacity);
+ u16 *id = drive->id;
+ int lba48 = ata_id_lba48_enabled(id);
+
+ if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 ||
+ ata_id_hpa_enabled(id) == 0)
+ goto out;
+
+ /*
+ * according to the spec the SET MAX ADDRESS command shall be
+ * immediately preceded by a READ NATIVE MAX ADDRESS command
+ */
+ capacity = ide_disk_hpa_get_native_capacity(drive, lba48);
+ if (capacity == 0)
+ goto out;
+
+ set = ide_disk_hpa_set_capacity(drive, set, lba48);
+ if (set) {
+ /* needed for ->resume to disable HPA */
+ drive->dev_flags |= IDE_DFLAG_NOHPA;
+ return set;
+ }
+out:
+ return drive->capacity64;
+}
+
static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
{
ide_drive_t *drive = q->queuedata;
@@ -428,14 +478,14 @@ static int set_multcount(ide_drive_t *drive, int arg)
if (arg < 0 || arg > (drive->id[ATA_ID_MAX_MULTSECT] & 0xff))
return -EINVAL;
- if (drive->special.b.set_multmode)
+ if (drive->special_flags & IDE_SFLAG_SET_MULTMODE)
return -EBUSY;
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
drive->mult_req = arg;
- drive->special.b.set_multmode = 1;
+ drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
error = blk_execute_rq(drive->queue, NULL, rq, 0);
blk_put_request(rq);
@@ -639,7 +689,7 @@ static void ide_disk_setup(ide_drive_t *drive)
}
printk(KERN_INFO "%s: max request size: %dKiB\n", drive->name,
- q->max_sectors / 2);
+ queue_max_sectors(q) / 2);
if (ata_id_is_ssd(id))
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
@@ -740,6 +790,7 @@ static int ide_disk_set_doorlock(ide_drive_t *drive, struct gendisk *disk,
const struct ide_disk_ops ide_ata_disk_ops = {
.check = ide_disk_check,
+ .set_capacity = ide_disk_set_capacity,
.get_capacity = ide_disk_get_capacity,
.setup = ide_disk_setup,
.flush = ide_disk_flush,
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 001f68f0bb28..219e6fb78dc6 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -347,7 +347,6 @@ u8 ide_find_dma_mode(ide_drive_t *drive, u8 req_mode)
return mode;
}
-EXPORT_SYMBOL_GPL(ide_find_dma_mode);
static int ide_tune_dma(ide_drive_t *drive)
{
diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c
index 5d5fb961b5ce..2b9141979613 100644
--- a/drivers/ide/ide-eh.c
+++ b/drivers/ide/ide-eh.c
@@ -52,7 +52,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq,
}
if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
- drive->special.b.recalibrate = 1;
+ drive->special_flags |= IDE_SFLAG_RECALIBRATE;
++rq->errors;
@@ -268,9 +268,8 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
{
int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1;
- drive->special.all = 0;
- drive->special.b.set_geometry = legacy;
- drive->special.b.recalibrate = legacy;
+ drive->special_flags =
+ legacy ? (IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE) : 0;
drive->mult_count = 0;
drive->dev_flags &= ~IDE_DFLAG_PARKED;
@@ -280,7 +279,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
drive->mult_req = 0;
if (drive->mult_req != drive->mult_count)
- drive->special.b.set_multmode = 1;
+ drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
}
static void pre_reset(ide_drive_t *drive)
@@ -408,8 +407,9 @@ static ide_startstop_t do_reset1(ide_drive_t *drive, int do_not_try_atapi)
/* more than enough time */
udelay(10);
/* clear SRST, leave nIEN (unless device is on the quirk list) */
- tp_ops->write_devctl(hwif, (drive->quirk_list == 2 ? 0 : ATA_NIEN) |
- ATA_DEVCTL_OBS);
+ tp_ops->write_devctl(hwif,
+ ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) ? 0 : ATA_NIEN) |
+ ATA_DEVCTL_OBS);
/* more than enough time */
udelay(10);
hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c
index 4b6b71e2cdf5..214119026b3f 100644
--- a/drivers/ide/ide-gd.c
+++ b/drivers/ide/ide-gd.c
@@ -287,6 +287,19 @@ static int ide_gd_media_changed(struct gendisk *disk)
return ret;
}
+static unsigned long long ide_gd_set_capacity(struct gendisk *disk,
+ unsigned long long capacity)
+{
+ struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
+ ide_drive_t *drive = idkp->drive;
+ const struct ide_disk_ops *disk_ops = drive->disk_ops;
+
+ if (disk_ops->set_capacity)
+ return disk_ops->set_capacity(drive, capacity);
+
+ return drive->capacity64;
+}
+
static int ide_gd_revalidate_disk(struct gendisk *disk)
{
struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
@@ -315,6 +328,7 @@ static struct block_device_operations ide_gd_ops = {
.locked_ioctl = ide_gd_ioctl,
.getgeo = ide_gd_getgeo,
.media_changed = ide_gd_media_changed,
+ .set_capacity = ide_gd_set_capacity,
.revalidate_disk = ide_gd_revalidate_disk
};
diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c
index 7812ca0be13b..54d7c4685d23 100644
--- a/drivers/ide/ide-generic.c
+++ b/drivers/ide/ide-generic.c
@@ -29,6 +29,7 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports");
static const struct ide_port_info ide_generic_port_info = {
.host_flags = IDE_HFLAG_NO_DMA,
+ .chipset = ide_generic,
};
#ifdef CONFIG_ARM
@@ -85,7 +86,7 @@ static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary)
static int __init ide_generic_init(void)
{
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
unsigned long io_addr;
int i, rc = 0, primary = 0, secondary = 0;
@@ -132,9 +133,7 @@ static int __init ide_generic_init(void)
#else
hw.irq = legacy_irqs[i];
#endif
- hw.chipset = ide_generic;
-
- rc = ide_host_add(&ide_generic_port_info, hws, NULL);
+ rc = ide_host_add(&ide_generic_port_info, hws, 1, NULL);
if (rc) {
release_region(io_addr + 0x206, 1);
release_region(io_addr, 8);
diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c
index c06ebdc4a130..520f42c5445a 100644
--- a/drivers/ide/ide-h8300.c
+++ b/drivers/ide/ide-h8300.c
@@ -64,26 +64,26 @@ static const struct ide_tp_ops h8300_tp_ops = {
#define H8300_IDE_GAP (2)
-static inline void hw_setup(hw_regs_t *hw)
+static inline void hw_setup(struct ide_hw *hw)
{
int i;
- memset(hw, 0, sizeof(hw_regs_t));
+ memset(hw, 0, sizeof(*hw));
for (i = 0; i <= 7; i++)
hw->io_ports_array[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i;
hw->io_ports.ctl_addr = CONFIG_H8300_IDE_ALT;
hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ;
- hw->chipset = ide_generic;
}
static const struct ide_port_info h8300_port_info = {
.tp_ops = &h8300_tp_ops,
.host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA,
+ .chipset = ide_generic,
};
static int __init h8300_ide_init(void)
{
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n");
@@ -96,7 +96,7 @@ static int __init h8300_ide_init(void)
hw_setup(&hw);
- return ide_host_add(&h8300_port_info, hws, NULL);
+ return ide_host_add(&h8300_port_info, hws, 1, NULL);
out_busy:
printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n");
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index e4e3a0e3201e..272cc38f6dbe 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -184,29 +184,42 @@ static void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
tf->command = ATA_CMD_SET_MULTI;
}
-static ide_startstop_t ide_disk_special(ide_drive_t *drive)
+/**
+ * do_special - issue some special commands
+ * @drive: drive the command is for
+ *
+ * do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS,
+ * ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive.
+ */
+
+static ide_startstop_t do_special(ide_drive_t *drive)
{
- special_t *s = &drive->special;
struct ide_cmd cmd;
+#ifdef DEBUG
+ printk(KERN_DEBUG "%s: %s: 0x%02x\n", drive->name, __func__,
+ drive->special_flags);
+#endif
+ if (drive->media != ide_disk) {
+ drive->special_flags = 0;
+ drive->mult_req = 0;
+ return ide_stopped;
+ }
+
memset(&cmd, 0, sizeof(cmd));
cmd.protocol = ATA_PROT_NODATA;
- if (s->b.set_geometry) {
- s->b.set_geometry = 0;
+ if (drive->special_flags & IDE_SFLAG_SET_GEOMETRY) {
+ drive->special_flags &= ~IDE_SFLAG_SET_GEOMETRY;
ide_tf_set_specify_cmd(drive, &cmd.tf);
- } else if (s->b.recalibrate) {
- s->b.recalibrate = 0;
+ } else if (drive->special_flags & IDE_SFLAG_RECALIBRATE) {
+ drive->special_flags &= ~IDE_SFLAG_RECALIBRATE;
ide_tf_set_restore_cmd(drive, &cmd.tf);
- } else if (s->b.set_multmode) {
- s->b.set_multmode = 0;
+ } else if (drive->special_flags & IDE_SFLAG_SET_MULTMODE) {
+ drive->special_flags &= ~IDE_SFLAG_SET_MULTMODE;
ide_tf_set_setmult_cmd(drive, &cmd.tf);
- } else if (s->all) {
- int special = s->all;
- s->all = 0;
- printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
- return ide_stopped;
- }
+ } else
+ BUG();
cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE;
@@ -217,31 +230,6 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive)
return ide_started;
}
-/**
- * do_special - issue some special commands
- * @drive: drive the command is for
- *
- * do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS,
- * ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive.
- *
- * It used to do much more, but has been scaled back.
- */
-
-static ide_startstop_t do_special (ide_drive_t *drive)
-{
- special_t *s = &drive->special;
-
-#ifdef DEBUG
- printk("%s: do_special: 0x%02x\n", drive->name, s->all);
-#endif
- if (drive->media == ide_disk)
- return ide_disk_special(drive);
-
- s->all = 0;
- drive->mult_req = 0;
- return ide_stopped;
-}
-
void ide_map_sg(ide_drive_t *drive, struct ide_cmd *cmd)
{
ide_hwif_t *hwif = drive->hwif;
@@ -351,7 +339,8 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
printk(KERN_ERR "%s: drive not ready for command\n", drive->name);
return startstop;
}
- if (!drive->special.all) {
+
+ if (drive->special_flags == 0) {
struct ide_driver *drv;
/*
@@ -499,11 +488,15 @@ repeat:
if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) &&
hwif != prev_port) {
+ ide_drive_t *cur_dev =
+ prev_port ? prev_port->cur_dev : NULL;
+
/*
* set nIEN for previous port, drives in the
- * quirk_list may not like intr setups/cleanups
+ * quirk list may not like intr setups/cleanups
*/
- if (prev_port && prev_port->cur_dev->quirk_list == 0)
+ if (cur_dev &&
+ (cur_dev->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0)
prev_port->tp_ops->write_devctl(prev_port,
ATA_NIEN |
ATA_DEVCTL_OBS);
@@ -704,7 +697,7 @@ void ide_timer_expiry (unsigned long data)
}
spin_lock_irq(&hwif->lock);
enable_irq(hwif->irq);
- if (startstop == ide_stopped) {
+ if (startstop == ide_stopped && hwif->polling == 0) {
rq_in_flight = hwif->rq;
hwif->rq = NULL;
ide_unlock_port(hwif);
@@ -879,7 +872,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
* same irq as is currently being serviced here, and Linux
* won't allow another of the same (on any CPU) until we return.
*/
- if (startstop == ide_stopped) {
+ if (startstop == ide_stopped && hwif->polling == 0) {
BUG_ON(hwif->handler);
rq_in_flight = hwif->rq;
hwif->rq = NULL;
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index c19a221b1e18..fa047150a1c6 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -206,8 +206,6 @@ EXPORT_SYMBOL_GPL(ide_in_drive_list);
/*
* Early UDMA66 devices don't set bit14 to 1, only bit13 is valid.
- * We list them here and depend on the device side cable detection for them.
- *
* Some optical devices with the buggy firmwares have the same problem.
*/
static const struct drive_list_entry ivb_list[] = {
@@ -251,10 +249,25 @@ u8 eighty_ninty_three(ide_drive_t *drive)
* - force bit13 (80c cable present) check also for !ivb devices
* (unless the slave device is pre-ATA3)
*/
- if ((id[ATA_ID_HW_CONFIG] & 0x4000) ||
- (ivb && (id[ATA_ID_HW_CONFIG] & 0x2000)))
+ if (id[ATA_ID_HW_CONFIG] & 0x4000)
return 1;
+ if (ivb) {
+ const char *model = (char *)&id[ATA_ID_PROD];
+
+ if (strstr(model, "TSSTcorp CDDVDW SH-S202")) {
+ /*
+ * These ATAPI devices always report 80c cable
+ * so we have to depend on the host in this case.
+ */
+ if (hwif->cbl == ATA_CBL_PATA80)
+ return 1;
+ } else {
+ /* Depend on the device side cable detection. */
+ if (id[ATA_ID_HW_CONFIG] & 0x2000)
+ return 1;
+ }
+ }
no_80w:
if (drive->dev_flags & IDE_DFLAG_UDMA33_WARNED)
return 0;
@@ -269,6 +282,29 @@ no_80w:
return 0;
}
+static const char *nien_quirk_list[] = {
+ "QUANTUM FIREBALLlct08 08",
+ "QUANTUM FIREBALLP KA6.4",
+ "QUANTUM FIREBALLP KA9.1",
+ "QUANTUM FIREBALLP KX13.6",
+ "QUANTUM FIREBALLP KX20.5",
+ "QUANTUM FIREBALLP KX27.3",
+ "QUANTUM FIREBALLP LM20.4",
+ "QUANTUM FIREBALLP LM20.5",
+ NULL
+};
+
+void ide_check_nien_quirk_list(ide_drive_t *drive)
+{
+ const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
+
+ for (list = nien_quirk_list; *list != NULL; list++)
+ if (strstr(m, *list) != NULL) {
+ drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK;
+ return;
+ }
+}
+
int ide_driveid_update(ide_drive_t *drive)
{
u16 *id;
@@ -298,7 +334,6 @@ int ide_driveid_update(ide_drive_t *drive)
return 1;
out_err:
- SELECT_MASK(drive, 0);
if (rc == 2)
printk(KERN_ERR "%s: %s: bad status\n", drive->name, __func__);
kfree(id);
@@ -352,7 +387,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
tp_ops->exec_command(hwif, ATA_CMD_SET_FEATURES);
- if (drive->quirk_list == 2)
+ if (drive->dev_flags & IDE_DFLAG_NIEN_QUIRK)
tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS);
error = __ide_wait_stat(drive, drive->ready_stat,
diff --git a/drivers/ide/ide-legacy.c b/drivers/ide/ide-legacy.c
index 8c5dcbf22547..b9654a7bb7be 100644
--- a/drivers/ide/ide-legacy.c
+++ b/drivers/ide/ide-legacy.c
@@ -1,7 +1,7 @@
#include <linux/kernel.h>
#include <linux/ide.h>
-static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
+static void ide_legacy_init_one(struct ide_hw **hws, struct ide_hw *hw,
u8 port_no, const struct ide_port_info *d,
unsigned long config)
{
@@ -33,7 +33,6 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
ide_std_init_ports(hw, base, ctl);
hw->irq = irq;
- hw->chipset = d->chipset;
hw->config = config;
hws[port_no] = hw;
@@ -41,7 +40,7 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
{
- hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[2], *hws[] = { NULL, NULL };
memset(&hw, 0, sizeof(hw));
@@ -53,6 +52,6 @@ int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
(d->host_flags & IDE_HFLAG_SINGLE))
return -ENOENT;
- return ide_host_add(d, hws, NULL);
+ return ide_host_add(d, hws, 2, NULL);
}
EXPORT_SYMBOL_GPL(ide_legacy_device_add);
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 05b7fbc7ead5..e386a32dc9ba 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -31,24 +31,6 @@ void ide_toggle_bounce(ide_drive_t *drive, int on)
blk_queue_bounce_limit(drive->queue, addr);
}
-static void ide_dump_opcode(ide_drive_t *drive)
-{
- struct request *rq = drive->hwif->rq;
- struct ide_cmd *cmd = NULL;
-
- if (!rq)
- return;
-
- if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
- cmd = rq->special;
-
- printk(KERN_ERR "ide: failed opcode was: ");
- if (cmd == NULL)
- printk(KERN_CONT "unknown\n");
- else
- printk(KERN_CONT "0x%02x\n", cmd->tf.command);
-}
-
u64 ide_get_lba_addr(struct ide_cmd *cmd, int lba48)
{
struct ide_taskfile *tf = &cmd->tf;
@@ -91,7 +73,7 @@ static void ide_dump_sector(ide_drive_t *drive)
static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
{
- printk(KERN_ERR "{ ");
+ printk(KERN_CONT "{ ");
if (err & ATA_ABORTED)
printk(KERN_CONT "DriveStatusError ");
if (err & ATA_ICRC)
@@ -121,7 +103,7 @@ static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
{
- printk(KERN_ERR "{ ");
+ printk(KERN_CONT "{ ");
if (err & ATAPI_ILI)
printk(KERN_CONT "IllegalLengthIndication ");
if (err & ATAPI_EOM)
@@ -179,7 +161,10 @@ u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
else
ide_dump_atapi_error(drive, err);
}
- ide_dump_opcode(drive);
+
+ printk(KERN_ERR "%s: possibly failed opcode: 0x%02x\n",
+ drive->name, drive->hwif->cmd.tf.command);
+
return err;
}
EXPORT_SYMBOL(ide_dump_status);
diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c
index 61111fd27130..39d4e01f5c9c 100644
--- a/drivers/ide/ide-pci-generic.c
+++ b/drivers/ide/ide-pci-generic.c
@@ -33,6 +33,16 @@ static int ide_generic_all; /* Set to claim all devices */
module_param_named(all_generic_ide, ide_generic_all, bool, 0444);
MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers.");
+static void netcell_quirkproc(ide_drive_t *drive)
+{
+ /* mark words 85-87 as valid */
+ drive->id[ATA_ID_CSF_DEFAULT] |= 0x4000;
+}
+
+static const struct ide_port_ops netcell_port_ops = {
+ .quirkproc = netcell_quirkproc,
+};
+
#define DECLARE_GENERIC_PCI_DEV(extra_flags) \
{ \
.name = DRV_NAME, \
@@ -74,6 +84,7 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = {
{ /* 6: Revolution */
.name = DRV_NAME,
+ .port_ops = &netcell_port_ops,
.host_flags = IDE_HFLAG_CLEAR_SIMPLEX |
IDE_HFLAG_TRUST_BIOS_FOR_DMA |
IDE_HFLAG_OFF_BOARD,
diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c
index 6e80b774e88a..017b1df3b805 100644
--- a/drivers/ide/ide-pnp.c
+++ b/drivers/ide/ide-pnp.c
@@ -29,6 +29,7 @@ static struct pnp_device_id idepnp_devices[] = {
static const struct ide_port_info ide_pnp_port_info = {
.host_flags = IDE_HFLAG_NO_DMA,
+ .chipset = ide_generic,
};
static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
@@ -36,7 +37,7 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
struct ide_host *host;
unsigned long base, ctl;
int rc;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n");
@@ -62,9 +63,8 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, base, ctl);
hw.irq = pnp_irq(dev, 0);
- hw.chipset = ide_generic;
- rc = ide_host_add(&ide_pnp_port_info, hws, &host);
+ rc = ide_host_add(&ide_pnp_port_info, hws, 1, &host);
if (rc)
goto out;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 7f264ed1141b..f371b0de314f 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -97,7 +97,7 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
drive->mult_req = id[ATA_ID_MULTSECT] & 0xff;
if (drive->mult_req)
- drive->special.b.set_multmode = 1;
+ drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
}
}
@@ -295,7 +295,7 @@ int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id)
timeout = ((cmd == ATA_CMD_ID_ATA) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
- if (ide_busy_sleep(hwif, timeout, use_altstatus))
+ if (ide_busy_sleep(drive, timeout, use_altstatus))
return 1;
/* wait for IRQ and ATA_DRQ */
@@ -316,8 +316,9 @@ int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id)
return rc;
}
-int ide_busy_sleep(ide_hwif_t *hwif, unsigned long timeout, int altstatus)
+int ide_busy_sleep(ide_drive_t *drive, unsigned long timeout, int altstatus)
{
+ ide_hwif_t *hwif = drive->hwif;
u8 stat;
timeout += jiffies;
@@ -330,6 +331,8 @@ int ide_busy_sleep(ide_hwif_t *hwif, unsigned long timeout, int altstatus)
return 0;
} while (time_before(jiffies, timeout));
+ printk(KERN_ERR "%s: timeout in %s\n", drive->name, __func__);
+
return 1; /* drive timed-out */
}
@@ -420,7 +423,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd)
tp_ops->dev_select(drive);
msleep(50);
tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
- (void)ide_busy_sleep(hwif, WAIT_WORSTCASE, 0);
+ (void)ide_busy_sleep(drive, WAIT_WORSTCASE, 0);
rc = ide_dev_read_id(drive, cmd, id);
}
@@ -462,23 +465,8 @@ static u8 probe_for_drive(ide_drive_t *drive)
int rc;
u8 cmd;
- /*
- * In order to keep things simple we have an id
- * block for all drives at all times. If the device
- * is pre ATA or refuses ATA/ATAPI identify we
- * will add faked data to this.
- *
- * Also note that 0 everywhere means "can't do X"
- */
-
drive->dev_flags &= ~IDE_DFLAG_ID_READ;
- drive->id = kzalloc(SECTOR_SIZE, GFP_KERNEL);
- if (drive->id == NULL) {
- printk(KERN_ERR "ide: out of memory for id data.\n");
- return 0;
- }
-
m = (char *)&drive->id[ATA_ID_PROD];
strcpy(m, "UNKNOWN");
@@ -494,7 +482,7 @@ static u8 probe_for_drive(ide_drive_t *drive)
}
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- goto out_free;
+ return 0;
/* identification failed? */
if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
@@ -518,7 +506,7 @@ static u8 probe_for_drive(ide_drive_t *drive)
}
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- goto out_free;
+ return 0;
/* The drive wasn't being helpful. Add generic info only */
if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
@@ -532,9 +520,6 @@ static u8 probe_for_drive(ide_drive_t *drive)
}
return 1;
-out_free:
- kfree(drive->id);
- return 0;
}
static void hwif_release_dev(struct device *dev)
@@ -699,8 +684,14 @@ static int ide_probe_port(ide_hwif_t *hwif)
if (irqd)
disable_irq(hwif->irq);
- if (ide_port_wait_ready(hwif) == -EBUSY)
- printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name);
+ rc = ide_port_wait_ready(hwif);
+ if (rc == -ENODEV) {
+ printk(KERN_INFO "%s: no devices on the port\n", hwif->name);
+ goto out;
+ } else if (rc == -EBUSY)
+ printk(KERN_ERR "%s: not ready before the probe\n", hwif->name);
+ else
+ rc = -ENODEV;
/*
* Second drive should only exist if first drive was found,
@@ -711,7 +702,7 @@ static int ide_probe_port(ide_hwif_t *hwif)
if (drive->dev_flags & IDE_DFLAG_PRESENT)
rc = 0;
}
-
+out:
/*
* Use cached IRQ number. It might be (and is...) changed by probe
* code above
@@ -729,6 +720,8 @@ static void ide_port_tune_devices(ide_hwif_t *hwif)
int i;
ide_port_for_each_present_dev(i, drive, hwif) {
+ ide_check_nien_quirk_list(drive);
+
if (port_ops && port_ops->quirkproc)
port_ops->quirkproc(drive);
}
@@ -814,8 +807,6 @@ static int ide_port_setup_devices(ide_hwif_t *hwif)
if (ide_init_queue(drive)) {
printk(KERN_ERR "ide: failed to init %s\n",
drive->name);
- kfree(drive->id);
- drive->id = NULL;
drive->dev_flags &= ~IDE_DFLAG_PRESENT;
continue;
}
@@ -944,9 +935,6 @@ static void drive_release_dev (struct device *dev)
blk_cleanup_queue(drive->queue);
drive->queue = NULL;
- kfree(drive->id);
- drive->id = NULL;
-
drive->dev_flags &= ~IDE_DFLAG_PRESENT;
complete(&drive->gendev_rel_comp);
@@ -1032,6 +1020,15 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
if (port_ops && port_ops->init_dev)
port_ops->init_dev(drive);
}
+
+ ide_port_for_each_dev(i, drive, hwif) {
+ /*
+ * default to PIO Mode 0 before we figure out
+ * the most suited mode for the attached device
+ */
+ if (port_ops && port_ops->set_pio_mode)
+ port_ops->set_pio_mode(drive, 0);
+ }
}
static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
@@ -1039,8 +1036,7 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
{
hwif->channel = port;
- if (d->chipset)
- hwif->chipset = d->chipset;
+ hwif->chipset = d->chipset ? d->chipset : ide_pci;
if (d->init_iops)
d->init_iops(hwif);
@@ -1121,16 +1117,19 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
ide_port_for_each_dev(i, drive, hwif) {
u8 j = (hwif->index * MAX_DRIVES) + i;
+ u16 *saved_id = drive->id;
memset(drive, 0, sizeof(*drive));
+ memset(saved_id, 0, SECTOR_SIZE);
+ drive->id = saved_id;
drive->media = ide_disk;
drive->select = (i << 4) | ATA_DEVICE_OBS;
drive->hwif = hwif;
drive->ready_stat = ATA_DRDY;
drive->bad_wstat = BAD_W_STAT;
- drive->special.b.recalibrate = 1;
- drive->special.b.set_geometry = 1;
+ drive->special_flags = IDE_SFLAG_RECALIBRATE |
+ IDE_SFLAG_SET_GEOMETRY;
drive->name[0] = 'h';
drive->name[1] = 'd';
drive->name[2] = 'a' + j;
@@ -1165,11 +1164,10 @@ static void ide_init_port_data(ide_hwif_t *hwif, unsigned int index)
ide_port_init_devices_data(hwif);
}
-static void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw)
+static void ide_init_port_hw(ide_hwif_t *hwif, struct ide_hw *hw)
{
memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports));
hwif->irq = hw->irq;
- hwif->chipset = hw->chipset;
hwif->dev = hw->dev;
hwif->gendev.parent = hw->parent ? hw->parent : hw->dev;
hwif->ack_intr = hw->ack_intr;
@@ -1230,8 +1228,10 @@ static void ide_port_free_devices(ide_hwif_t *hwif)
ide_drive_t *drive;
int i;
- ide_port_for_each_dev(i, drive, hwif)
+ ide_port_for_each_dev(i, drive, hwif) {
+ kfree(drive->id);
kfree(drive);
+ }
}
static int ide_port_alloc_devices(ide_hwif_t *hwif, int node)
@@ -1245,6 +1245,18 @@ static int ide_port_alloc_devices(ide_hwif_t *hwif, int node)
if (drive == NULL)
goto out_nomem;
+ /*
+ * In order to keep things simple we have an id
+ * block for all drives at all times. If the device
+ * is pre ATA or refuses ATA/ATAPI identify we
+ * will add faked data to this.
+ *
+ * Also note that 0 everywhere means "can't do X"
+ */
+ drive->id = kzalloc_node(SECTOR_SIZE, GFP_KERNEL, node);
+ if (drive->id == NULL)
+ goto out_nomem;
+
hwif->devices[i] = drive;
}
return 0;
@@ -1254,7 +1266,8 @@ out_nomem:
return -ENOMEM;
}
-struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
+struct ide_host *ide_host_alloc(const struct ide_port_info *d,
+ struct ide_hw **hws, unsigned int n_ports)
{
struct ide_host *host;
struct device *dev = hws[0] ? hws[0]->dev : NULL;
@@ -1265,7 +1278,7 @@ struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
if (host == NULL)
return NULL;
- for (i = 0; i < MAX_HOST_PORTS; i++) {
+ for (i = 0; i < n_ports; i++) {
ide_hwif_t *hwif;
int idx;
@@ -1285,6 +1298,7 @@ struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
if (idx < 0) {
printk(KERN_ERR "%s: no free slot for interface\n",
d ? d->name : "ide");
+ ide_port_free_devices(hwif);
kfree(hwif);
continue;
}
@@ -1341,7 +1355,7 @@ static void ide_disable_port(ide_hwif_t *hwif)
}
int ide_host_register(struct ide_host *host, const struct ide_port_info *d,
- hw_regs_t **hws)
+ struct ide_hw **hws)
{
ide_hwif_t *hwif, *mate = NULL;
int i, j = 0;
@@ -1435,13 +1449,13 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d,
}
EXPORT_SYMBOL_GPL(ide_host_register);
-int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws,
- struct ide_host **hostp)
+int ide_host_add(const struct ide_port_info *d, struct ide_hw **hws,
+ unsigned int n_ports, struct ide_host **hostp)
{
struct ide_host *host;
int rc;
- host = ide_host_alloc(d, hws);
+ host = ide_host_alloc(d, hws, n_ports);
if (host == NULL)
return -ENOMEM;
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 9ca2665faf33..bb71e1e99dad 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -240,18 +240,27 @@ static struct class *idetape_sysfs_class;
static void ide_tape_release(struct device *);
-static struct ide_tape_obj *ide_tape_get(struct gendisk *disk)
+static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES];
+
+static struct ide_tape_obj *ide_tape_get(struct gendisk *disk, bool cdev,
+ unsigned int i)
{
struct ide_tape_obj *tape = NULL;
mutex_lock(&idetape_ref_mutex);
- tape = ide_drv_g(disk, ide_tape_obj);
+
+ if (cdev)
+ tape = idetape_devs[i];
+ else
+ tape = ide_drv_g(disk, ide_tape_obj);
+
if (tape) {
if (ide_device_get(tape->drive))
tape = NULL;
else
get_device(&tape->dev);
}
+
mutex_unlock(&idetape_ref_mutex);
return tape;
}
@@ -267,24 +276,6 @@ static void ide_tape_put(struct ide_tape_obj *tape)
}
/*
- * The variables below are used for the character device interface. Additional
- * state variables are defined in our ide_drive_t structure.
- */
-static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES];
-
-static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i)
-{
- struct ide_tape_obj *tape = NULL;
-
- mutex_lock(&idetape_ref_mutex);
- tape = idetape_devs[i];
- if (tape)
- get_device(&tape->dev);
- mutex_unlock(&idetape_ref_mutex);
- return tape;
-}
-
-/*
* called on each failed packet command retry to analyze the request sense. We
* currently do not utilize this information.
*/
@@ -631,15 +622,15 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
if ((drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) == 0 &&
(rq->cmd[13] & REQ_IDETAPE_PC2) == 0)
- set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+ drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
if (drive->dev_flags & IDE_DFLAG_POST_RESET) {
- set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+ drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
drive->dev_flags &= ~IDE_DFLAG_POST_RESET;
}
- if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) &&
- (stat & ATA_DSC) == 0) {
+ if (!(drive->atapi_flags & IDE_AFLAG_IGNORE_DSC) &&
+ !(stat & ATA_DSC)) {
if (postponed_rq == NULL) {
tape->dsc_polling_start = jiffies;
tape->dsc_poll_freq = tape->best_dsc_rw_freq;
@@ -659,7 +650,9 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
tape->dsc_poll_freq = IDETAPE_DSC_MA_SLOW;
idetape_postpone_request(drive);
return ide_stopped;
- }
+ } else
+ drive->atapi_flags &= ~IDE_AFLAG_IGNORE_DSC;
+
if (rq->cmd[13] & REQ_IDETAPE_READ) {
pc = &tape->queued_pc;
ide_tape_create_rw_cmd(tape, pc, rq, READ_6);
@@ -719,7 +712,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout)
int load_attempted = 0;
/* Wait for the tape to become ready */
- set_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
+ set_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT), &drive->atapi_flags);
timeout += jiffies;
while (time_before(jiffies, timeout)) {
if (ide_do_test_unit_ready(drive, disk) == 0)
@@ -779,7 +772,8 @@ static int ide_tape_read_position(ide_drive_t *drive)
if (buf[0] & 0x4) {
printk(KERN_INFO "ide-tape: Block location is unknown"
"to the tape\n");
- clear_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_ADDRESS_VALID),
+ &drive->atapi_flags);
return -1;
} else {
debug_log(DBG_SENSE, "Block Location - %u\n",
@@ -787,7 +781,8 @@ static int ide_tape_read_position(ide_drive_t *drive)
tape->partition = buf[1];
tape->first_frame = be32_to_cpup((__be32 *)&buf[4]);
- set_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
+ set_bit(ilog2(IDE_AFLAG_ADDRESS_VALID),
+ &drive->atapi_flags);
}
}
@@ -813,7 +808,7 @@ static void __ide_tape_discard_merge_buffer(ide_drive_t *drive)
if (tape->chrdev_dir != IDETAPE_DIR_READ)
return;
- clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags);
tape->valid = 0;
if (tape->buf != NULL) {
kfree(tape->buf);
@@ -887,6 +882,7 @@ static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int size)
rq->cmd_type = REQ_TYPE_SPECIAL;
rq->cmd[13] = cmd;
rq->rq_disk = tape->disk;
+ rq->__sector = tape->first_frame;
if (size) {
ret = blk_rq_map_kern(drive->queue, rq, tape->buf, size,
@@ -1106,7 +1102,8 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op,
if (tape->chrdev_dir == IDETAPE_DIR_READ) {
tape->valid = 0;
- if (test_and_clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
+ if (test_and_clear_bit(ilog2(IDE_AFLAG_FILEMARK),
+ &drive->atapi_flags))
++count;
ide_tape_discard_merge_buffer(drive, 0);
}
@@ -1161,7 +1158,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
debug_log(DBG_CHRDEV, "Enter %s, count %Zd\n", __func__, count);
if (tape->chrdev_dir != IDETAPE_DIR_READ) {
- if (test_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags))
+ if (test_bit(ilog2(IDE_AFLAG_DETECT_BS), &drive->atapi_flags))
if (count > tape->blk_size &&
(count % tape->blk_size) == 0)
tape->user_bs_factor = count / tape->blk_size;
@@ -1177,7 +1174,8 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
/* refill if staging buffer is empty */
if (!tape->valid) {
/* If we are at a filemark, nothing more to read */
- if (test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
+ if (test_bit(ilog2(IDE_AFLAG_FILEMARK),
+ &drive->atapi_flags))
break;
/* read */
if (idetape_queue_rw_tail(drive, REQ_IDETAPE_READ,
@@ -1195,7 +1193,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
done += todo;
}
- if (!done && test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags)) {
+ if (!done && test_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags)) {
debug_log(DBG_SENSE, "%s: spacing over filemark\n", tape->name);
idetape_space_over_filemarks(drive, MTFSF, 1);
@@ -1329,7 +1327,8 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
ide_tape_discard_merge_buffer(drive, 0);
retval = ide_do_start_stop(drive, disk, !IDETAPE_LU_LOAD_MASK);
if (!retval)
- clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT),
+ &drive->atapi_flags);
return retval;
case MTNOP:
ide_tape_discard_merge_buffer(drive, 0);
@@ -1351,9 +1350,11 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
mt_count % tape->blk_size)
return -EIO;
tape->user_bs_factor = mt_count / tape->blk_size;
- clear_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_DETECT_BS),
+ &drive->atapi_flags);
} else
- set_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags);
+ set_bit(ilog2(IDE_AFLAG_DETECT_BS),
+ &drive->atapi_flags);
return 0;
case MTSEEK:
ide_tape_discard_merge_buffer(drive, 0);
@@ -1480,7 +1481,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
return -ENXIO;
lock_kernel();
- tape = ide_tape_chrdev_get(i);
+ tape = ide_tape_get(NULL, true, i);
if (!tape) {
unlock_kernel();
return -ENXIO;
@@ -1499,20 +1500,20 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
filp->private_data = tape;
- if (test_and_set_bit(IDE_AFLAG_BUSY, &drive->atapi_flags)) {
+ if (test_and_set_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags)) {
retval = -EBUSY;
goto out_put_tape;
}
retval = idetape_wait_ready(drive, 60 * HZ);
if (retval) {
- clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
goto out_put_tape;
}
- ide_tape_read_position(drive);
- if (!test_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags))
+ idetape_read_position(drive);
+ if (!test_bit(ilog2(IDE_AFLAG_ADDRESS_VALID), &drive->atapi_flags))
(void)idetape_rewind_tape(drive);
/* Read block size and write protect status from drive. */
@@ -1528,7 +1529,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
if (tape->write_prot) {
if ((filp->f_flags & O_ACCMODE) == O_WRONLY ||
(filp->f_flags & O_ACCMODE) == O_RDWR) {
- clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
retval = -EROFS;
goto out_put_tape;
}
@@ -1585,15 +1586,17 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
ide_tape_discard_merge_buffer(drive, 1);
}
- if (minor < 128 && test_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags))
+ if (minor < 128 && test_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT),
+ &drive->atapi_flags))
(void) idetape_rewind_tape(drive);
+
if (tape->chrdev_dir == IDETAPE_DIR_NONE) {
if (tape->door_locked == DOOR_LOCKED) {
if (!ide_set_media_lock(drive, tape->disk, 0))
tape->door_locked = DOOR_UNLOCKED;
}
}
- clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+ clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
ide_tape_put(tape);
unlock_kernel();
return 0;
@@ -1896,7 +1899,7 @@ static const struct file_operations idetape_fops = {
static int idetape_open(struct block_device *bdev, fmode_t mode)
{
- struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk);
+ struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk, false, 0);
if (!tape)
return -ENXIO;
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index a0c3e1b2f73c..75b85a8cd2d4 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -98,7 +98,6 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd)
if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
ide_tf_dump(drive->name, cmd);
tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS);
- SELECT_MASK(drive, 0);
if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) {
u8 data[2] = { cmd->tf.data, cmd->hob.data };
@@ -166,7 +165,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) {
if (custom && tf->command == ATA_CMD_SET_MULTI) {
drive->mult_req = drive->mult_count = 0;
- drive->special.b.recalibrate = 1;
+ drive->special_flags |= IDE_SFLAG_RECALIBRATE;
(void)ide_dump_status(drive, __func__, stat);
return ide_stopped;
} else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) {
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 92c9b90931e7..16d056939f9f 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -211,6 +211,11 @@ static unsigned int ide_noflush;
module_param_call(noflush, ide_set_dev_param_mask, NULL, &ide_noflush, 0);
MODULE_PARM_DESC(noflush, "disable flush requests for a device");
+static unsigned int ide_nohpa;
+
+module_param_call(nohpa, ide_set_dev_param_mask, NULL, &ide_nohpa, 0);
+MODULE_PARM_DESC(nohpa, "disable Host Protected Area for a device");
+
static unsigned int ide_noprobe;
module_param_call(noprobe, ide_set_dev_param_mask, NULL, &ide_noprobe, 0);
@@ -281,6 +286,11 @@ static void ide_dev_apply_params(ide_drive_t *drive, u8 unit)
drive->name);
drive->dev_flags |= IDE_DFLAG_NOFLUSH;
}
+ if (ide_nohpa & (1 << i)) {
+ printk(KERN_INFO "ide: disabling Host Protected Area for %s\n",
+ drive->name);
+ drive->dev_flags |= IDE_DFLAG_NOHPA;
+ }
if (ide_noprobe & (1 << i)) {
printk(KERN_INFO "ide: skipping probe for %s\n", drive->name);
drive->dev_flags |= IDE_DFLAG_NOPROBE;
diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c
index 051b4ab0f359..ee9b55ecc62b 100644
--- a/drivers/ide/ide_platform.c
+++ b/drivers/ide/ide_platform.c
@@ -21,7 +21,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-static void __devinit plat_ide_setup_ports(hw_regs_t *hw,
+static void __devinit plat_ide_setup_ports(struct ide_hw *hw,
void __iomem *base,
void __iomem *ctrl,
struct pata_platform_info *pdata,
@@ -40,12 +40,11 @@ static void __devinit plat_ide_setup_ports(hw_regs_t *hw,
hw->io_ports.ctl_addr = (unsigned long)ctrl;
hw->irq = irq;
-
- hw->chipset = ide_generic;
}
static const struct ide_port_info platform_ide_port_info = {
.host_flags = IDE_HFLAG_NO_DMA,
+ .chipset = ide_generic,
};
static int __devinit plat_ide_probe(struct platform_device *pdev)
@@ -55,7 +54,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
struct pata_platform_info *pdata;
struct ide_host *host;
int ret = 0, mmio = 0;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
struct ide_port_info d = platform_ide_port_info;
pdata = pdev->dev.platform_data;
@@ -99,7 +98,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
if (mmio)
d.host_flags |= IDE_HFLAG_MMIO;
- ret = ide_host_add(&d, hws, &host);
+ ret = ide_host_add(&d, hws, 1, &host);
if (ret)
goto out;
diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c
index 4b1718e83283..1447c8c90565 100644
--- a/drivers/ide/macide.c
+++ b/drivers/ide/macide.c
@@ -62,7 +62,7 @@ int macide_ack_intr(ide_hwif_t* hwif)
return 0;
}
-static void __init macide_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init macide_setup_ports(struct ide_hw *hw, unsigned long base,
int irq, ide_ack_intr_t *ack_intr)
{
int i;
@@ -76,13 +76,12 @@ static void __init macide_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = irq;
hw->ack_intr = ack_intr;
-
- hw->chipset = ide_generic;
}
static const struct ide_port_info macide_port_info = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_generic,
};
static const char *mac_ide_name[] =
@@ -97,7 +96,7 @@ static int __init macide_init(void)
ide_ack_intr_t *ack_intr;
unsigned long base;
int irq;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
if (!MACH_IS_MAC)
return -ENODEV;
@@ -127,7 +126,7 @@ static int __init macide_init(void)
macide_setup_ports(&hw, base, irq, ack_intr);
- return ide_host_add(&macide_port_info, hws, NULL);
+ return ide_host_add(&macide_port_info, hws, 1, NULL);
}
module_init(macide_init);
diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c
index 09d813d313f4..3c1dc0152153 100644
--- a/drivers/ide/palm_bk3710.c
+++ b/drivers/ide/palm_bk3710.c
@@ -306,6 +306,7 @@ static struct ide_port_info __devinitdata palm_bk3710_port_info = {
.host_flags = IDE_HFLAG_MMIO,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
+ .chipset = ide_palm3710,
};
static int __init palm_bk3710_probe(struct platform_device *pdev)
@@ -315,7 +316,7 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
void __iomem *base;
unsigned long rate, mem_size;
int i, rc;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
clk = clk_get(&pdev->dev, "IDECLK");
if (IS_ERR(clk))
@@ -363,13 +364,12 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
(base + IDE_PALM_ATA_PRI_CTL_OFFSET);
hw.irq = irq->start;
hw.dev = &pdev->dev;
- hw.chipset = ide_palm3710;
palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 :
ATA_UDMA5;
/* Register the IDE interface with Linux */
- rc = ide_host_add(&palm_bk3710_port_info, hws, NULL);
+ rc = ide_host_add(&palm_bk3710_port_info, hws, 1, NULL);
if (rc)
goto out;
diff --git a/drivers/ide/pdc202xx_new.c b/drivers/ide/pdc202xx_new.c
index b68906c3c17e..65ba8239e7b5 100644
--- a/drivers/ide/pdc202xx_new.c
+++ b/drivers/ide/pdc202xx_new.c
@@ -40,18 +40,6 @@
#define DBG(fmt, args...)
#endif
-static const char *pdc_quirk_drives[] = {
- "QUANTUM FIREBALLlct08 08",
- "QUANTUM FIREBALLP KA6.4",
- "QUANTUM FIREBALLP KA9.1",
- "QUANTUM FIREBALLP LM20.4",
- "QUANTUM FIREBALLP KX13.6",
- "QUANTUM FIREBALLP KX20.5",
- "QUANTUM FIREBALLP KX27.3",
- "QUANTUM FIREBALLP LM20.5",
- NULL
-};
-
static u8 max_dma_rate(struct pci_dev *pdev)
{
u8 mode;
@@ -200,19 +188,6 @@ static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
return ATA_CBL_PATA80;
}
-static void pdcnew_quirkproc(ide_drive_t *drive)
-{
- const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
-
- for (list = pdc_quirk_drives; *list != NULL; list++)
- if (strstr(m, *list) != NULL) {
- drive->quirk_list = 2;
- return;
- }
-
- drive->quirk_list = 0;
-}
-
static void pdcnew_reset(ide_drive_t *drive)
{
/*
@@ -473,7 +448,6 @@ static struct pci_dev * __devinit pdc20270_get_dev2(struct pci_dev *dev)
static const struct ide_port_ops pdcnew_port_ops = {
.set_pio_mode = pdcnew_set_pio_mode,
.set_dma_mode = pdcnew_set_dma_mode,
- .quirkproc = pdcnew_quirkproc,
.resetproc = pdcnew_reset,
.cable_detect = pdcnew_cable_detect,
};
diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c
index c2a16a8f486d..b6abf7e52cac 100644
--- a/drivers/ide/pdc202xx_old.c
+++ b/drivers/ide/pdc202xx_old.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>
- * Copyright (C) 2006-2007 MontaVista Software, Inc.
+ * Copyright (C) 2006-2007, 2009 MontaVista Software, Inc.
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
*
* Portions Copyright (C) 1999 Promise Technology, Inc.
@@ -23,18 +23,6 @@
#define PDC202XX_DEBUG_DRIVE_INFO 0
-static const char *pdc_quirk_drives[] = {
- "QUANTUM FIREBALLlct08 08",
- "QUANTUM FIREBALLP KA6.4",
- "QUANTUM FIREBALLP KA9.1",
- "QUANTUM FIREBALLP LM20.4",
- "QUANTUM FIREBALLP KX13.6",
- "QUANTUM FIREBALLP KX20.5",
- "QUANTUM FIREBALLP KX27.3",
- "QUANTUM FIREBALLP LM20.5",
- NULL
-};
-
static void pdc_old_disable_66MHz_clock(ide_hwif_t *);
static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
@@ -151,19 +139,6 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
}
-static void pdc202xx_quirkproc(ide_drive_t *drive)
-{
- const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
-
- for (list = pdc_quirk_drives; *list != NULL; list++)
- if (strstr(m, *list) != NULL) {
- drive->quirk_list = 2;
- return;
- }
-
- drive->quirk_list = 0;
-}
-
static void pdc202xx_dma_start(ide_drive_t *drive)
{
if (drive->current_speed > XFER_UDMA_2)
@@ -203,61 +178,6 @@ static int pdc202xx_dma_end(ide_drive_t *drive)
return ide_dma_end(drive);
}
-static int pdc202xx_dma_test_irq(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- unsigned long high_16 = hwif->extra_base - 16;
- u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS);
- u8 sc1d = inb(high_16 + 0x001d);
-
- if (hwif->channel) {
- /* bit7: Error, bit6: Interrupting, bit5: FIFO Full, bit4: FIFO Empty */
- if ((sc1d & 0x50) == 0x50)
- goto somebody_else;
- else if ((sc1d & 0x40) == 0x40)
- return (dma_stat & 4) == 4;
- } else {
- /* bit3: Error, bit2: Interrupting, bit1: FIFO Full, bit0: FIFO Empty */
- if ((sc1d & 0x05) == 0x05)
- goto somebody_else;
- else if ((sc1d & 0x04) == 0x04)
- return (dma_stat & 4) == 4;
- }
-somebody_else:
- return (dma_stat & 4) == 4; /* return 1 if INTR asserted */
-}
-
-static void pdc202xx_reset_host (ide_hwif_t *hwif)
-{
- unsigned long high_16 = hwif->extra_base - 16;
- u8 udma_speed_flag = inb(high_16 | 0x001f);
-
- outb(udma_speed_flag | 0x10, high_16 | 0x001f);
- mdelay(100);
- outb(udma_speed_flag & ~0x10, high_16 | 0x001f);
- mdelay(2000); /* 2 seconds ?! */
-
- printk(KERN_WARNING "PDC202XX: %s channel reset.\n",
- hwif->channel ? "Secondary" : "Primary");
-}
-
-static void pdc202xx_reset (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- ide_hwif_t *mate = hwif->mate;
-
- pdc202xx_reset_host(hwif);
- pdc202xx_reset_host(mate);
-
- ide_set_max_pio(drive);
-}
-
-static void pdc202xx_dma_lost_irq(ide_drive_t *drive)
-{
- pdc202xx_reset(drive);
- ide_dma_lost_irq(drive);
-}
-
static int init_chipset_pdc202xx(struct pci_dev *dev)
{
unsigned long dmabase = pci_resource_start(dev, 4);
@@ -311,38 +231,22 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
static const struct ide_port_ops pdc20246_port_ops = {
.set_pio_mode = pdc202xx_set_pio_mode,
.set_dma_mode = pdc202xx_set_mode,
- .quirkproc = pdc202xx_quirkproc,
};
static const struct ide_port_ops pdc2026x_port_ops = {
.set_pio_mode = pdc202xx_set_pio_mode,
.set_dma_mode = pdc202xx_set_mode,
- .quirkproc = pdc202xx_quirkproc,
- .resetproc = pdc202xx_reset,
.cable_detect = pdc2026x_cable_detect,
};
-static const struct ide_dma_ops pdc20246_dma_ops = {
- .dma_host_set = ide_dma_host_set,
- .dma_setup = ide_dma_setup,
- .dma_start = ide_dma_start,
- .dma_end = ide_dma_end,
- .dma_test_irq = pdc202xx_dma_test_irq,
- .dma_lost_irq = pdc202xx_dma_lost_irq,
- .dma_timer_expiry = ide_dma_sff_timer_expiry,
- .dma_clear = pdc202xx_reset,
- .dma_sff_read_status = ide_dma_sff_read_status,
-};
-
static const struct ide_dma_ops pdc2026x_dma_ops = {
.dma_host_set = ide_dma_host_set,
.dma_setup = ide_dma_setup,
.dma_start = pdc202xx_dma_start,
.dma_end = pdc202xx_dma_end,
- .dma_test_irq = pdc202xx_dma_test_irq,
- .dma_lost_irq = pdc202xx_dma_lost_irq,
+ .dma_test_irq = ide_dma_test_irq,
+ .dma_lost_irq = ide_dma_lost_irq,
.dma_timer_expiry = ide_dma_sff_timer_expiry,
- .dma_clear = pdc202xx_reset,
.dma_sff_read_status = ide_dma_sff_read_status,
};
@@ -364,7 +268,7 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
.name = DRV_NAME,
.init_chipset = init_chipset_pdc202xx,
.port_ops = &pdc20246_port_ops,
- .dma_ops = &pdc20246_dma_ops,
+ .dma_ops = &sff_dma_ops,
.host_flags = IDE_HFLAGS_PDC202XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c
index 2aa699933064..69860dea3820 100644
--- a/drivers/ide/piix.c
+++ b/drivers/ide/piix.c
@@ -263,6 +263,7 @@ static const struct ich_laptop ich_laptop[] = {
{ 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */
{ 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */
{ 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
+ { 0x27df, 0x104d, 0x900e }, /* ICH7 on Sony TZ-90 */
/* end marker */
{ 0, }
};
diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c
index f76e4e6b408f..97642a7a79c4 100644
--- a/drivers/ide/pmac.c
+++ b/drivers/ide/pmac.c
@@ -1023,13 +1023,14 @@ static const struct ide_port_info pmac_port_info = {
* Setup, register & probe an IDE channel driven by this driver, this is
* called by one of the 2 probe functions (macio or PCI).
*/
-static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
+static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif,
+ struct ide_hw *hw)
{
struct device_node *np = pmif->node;
const int *bidp;
struct ide_host *host;
ide_hwif_t *hwif;
- hw_regs_t *hws[] = { hw, NULL, NULL, NULL };
+ struct ide_hw *hws[] = { hw };
struct ide_port_info d = pmac_port_info;
int rc;
@@ -1077,7 +1078,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
/* Make sure we have sane timings */
sanitize_timings(pmif);
- host = ide_host_alloc(&d, hws);
+ host = ide_host_alloc(&d, hws, 1);
if (host == NULL)
return -ENOMEM;
hwif = host->ports[0];
@@ -1124,7 +1125,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
return 0;
}
-static void __devinit pmac_ide_init_ports(hw_regs_t *hw, unsigned long base)
+static void __devinit pmac_ide_init_ports(struct ide_hw *hw, unsigned long base)
{
int i;
@@ -1144,7 +1145,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
unsigned long regbase;
pmac_ide_hwif_t *pmif;
int irq, rc;
- hw_regs_t hw;
+ struct ide_hw hw;
pmif = kzalloc(sizeof(*pmif), GFP_KERNEL);
if (pmif == NULL)
@@ -1268,7 +1269,7 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
void __iomem *base;
unsigned long rbase, rlen;
int rc;
- hw_regs_t hw;
+ struct ide_hw hw;
np = pci_device_to_OF_node(pdev);
if (np == NULL) {
diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c
index c79346679244..ab49a97023d9 100644
--- a/drivers/ide/q40ide.c
+++ b/drivers/ide/q40ide.c
@@ -51,11 +51,11 @@ static int q40ide_default_irq(unsigned long base)
/*
* Addresses are pretranslated for Q40 ISA access.
*/
-static void q40_ide_setup_ports(hw_regs_t *hw, unsigned long base,
+static void q40_ide_setup_ports(struct ide_hw *hw, unsigned long base,
ide_ack_intr_t *ack_intr,
int irq)
{
- memset(hw, 0, sizeof(hw_regs_t));
+ memset(hw, 0, sizeof(*hw));
/* BIG FAT WARNING:
assumption: only DATA port is ever used in 16 bit mode */
hw->io_ports.data_addr = Q40_ISA_IO_W(base);
@@ -70,8 +70,6 @@ static void q40_ide_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = irq;
hw->ack_intr = ack_intr;
-
- hw->chipset = ide_generic;
}
static void q40ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
@@ -119,6 +117,7 @@ static const struct ide_port_info q40ide_port_info = {
.tp_ops = &q40ide_tp_ops,
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
.irq_flags = IRQF_SHARED,
+ .chipset = ide_generic,
};
/*
@@ -136,7 +135,7 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={
static int __init q40ide_init(void)
{
int i;
- hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+ struct ide_hw hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL };
if (!MACH_IS_Q40)
return -ENODEV;
@@ -163,7 +162,7 @@ static int __init q40ide_init(void)
hws[i] = &hw[i];
}
- return ide_host_add(&q40ide_port_info, hws, NULL);
+ return ide_host_add(&q40ide_port_info, hws, Q40IDE_NUM_HWIFS, NULL);
}
module_init(q40ide_init);
diff --git a/drivers/ide/rapide.c b/drivers/ide/rapide.c
index d5003ca69801..00f54248f41f 100644
--- a/drivers/ide/rapide.c
+++ b/drivers/ide/rapide.c
@@ -13,9 +13,10 @@
static const struct ide_port_info rapide_port_info = {
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
+ .chipset = ide_generic,
};
-static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base,
+static void rapide_setup_ports(struct ide_hw *hw, void __iomem *base,
void __iomem *ctrl, unsigned int sz, int irq)
{
unsigned long port = (unsigned long)base;
@@ -35,7 +36,7 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
void __iomem *base;
struct ide_host *host;
int ret;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
ret = ecard_request_resources(ec);
if (ret)
@@ -49,10 +50,9 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
memset(&hw, 0, sizeof(hw));
rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq);
- hw.chipset = ide_generic;
hw.dev = &ec->dev;
- ret = ide_host_add(&rapide_port_info, hws, &host);
+ ret = ide_host_add(&rapide_port_info, hws, 1, &host);
if (ret)
goto release;
diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c
index 5be41f25204f..1104bb301eb9 100644
--- a/drivers/ide/scc_pata.c
+++ b/drivers/ide/scc_pata.c
@@ -559,7 +559,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev,
{
struct scc_ports *ports = pci_get_drvdata(dev);
struct ide_host *host;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
int i, rc;
memset(&hw, 0, sizeof(hw));
@@ -567,9 +567,8 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev,
hw.io_ports_array[i] = ports->dma + 0x20 + i * 4;
hw.irq = dev->irq;
hw.dev = &dev->dev;
- hw.chipset = ide_pci;
- rc = ide_host_add(d, hws, &host);
+ rc = ide_host_add(d, hws, 1, &host);
if (rc)
return rc;
@@ -823,6 +822,7 @@ static const struct ide_port_info scc_chipset __devinitdata = {
.host_flags = IDE_HFLAG_SINGLE,
.irq_flags = IRQF_SHARED,
.pio_mask = ATA_PIO4,
+ .chipset = ide_pci,
};
/**
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index 7a3a12d6e638..ab3db61d2ba0 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
* Copyright (C) 1995-1998 Mark Lord
- * Copyright (C) 2007 Bartlomiej Zolnierkiewicz
+ * Copyright (C) 2007-2009 Bartlomiej Zolnierkiewicz
*
* May be copied or modified under the terms of the GNU General Public License
*/
@@ -301,11 +301,11 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *
}
/**
- * ide_hw_configure - configure a hw_regs_t instance
+ * ide_hw_configure - configure a struct ide_hw instance
* @dev: PCI device holding interface
* @d: IDE port info
* @port: port number
- * @hw: hw_regs_t instance corresponding to this port
+ * @hw: struct ide_hw instance corresponding to this port
*
* Perform the initial set up for the hardware interface structure. This
* is done per interface port rather than per PCI device. There may be
@@ -315,7 +315,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *
*/
static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
- unsigned int port, hw_regs_t *hw)
+ unsigned int port, struct ide_hw *hw)
{
unsigned long ctl = 0, base = 0;
@@ -344,7 +344,6 @@ static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
memset(hw, 0, sizeof(*hw));
hw->dev = &dev->dev;
- hw->chipset = d->chipset ? d->chipset : ide_pci;
ide_std_init_ports(hw, base, ctl | 2);
return 0;
@@ -446,8 +445,8 @@ out:
* ide_pci_setup_ports - configure ports/devices on PCI IDE
* @dev: PCI device
* @d: IDE port info
- * @hw: hw_regs_t instances corresponding to this PCI IDE device
- * @hws: hw_regs_t pointers table to update
+ * @hw: struct ide_hw instances corresponding to this PCI IDE device
+ * @hws: struct ide_hw pointers table to update
*
* Scan the interfaces attached to this device and do any
* necessary per port setup. Attach the devices and ask the
@@ -459,7 +458,7 @@ out:
*/
void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d,
- hw_regs_t *hw, hw_regs_t **hws)
+ struct ide_hw *hw, struct ide_hw **hws)
{
int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port;
u8 tmp;
@@ -535,61 +534,15 @@ out:
return ret;
}
-int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
- void *priv)
-{
- struct ide_host *host;
- hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
- int ret;
-
- ret = ide_setup_pci_controller(dev, d, 1);
- if (ret < 0)
- goto out;
-
- ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
-
- host = ide_host_alloc(d, hws);
- if (host == NULL) {
- ret = -ENOMEM;
- goto out;
- }
-
- host->dev[0] = &dev->dev;
-
- host->host_priv = priv;
-
- host->irq_flags = IRQF_SHARED;
-
- pci_set_drvdata(dev, host);
-
- ret = do_ide_setup_pci_device(dev, d, 1);
- if (ret < 0)
- goto out;
-
- /* fixup IRQ */
- if (ide_pci_is_in_compatibility_mode(dev)) {
- hw[0].irq = pci_get_legacy_ide_irq(dev, 0);
- hw[1].irq = pci_get_legacy_ide_irq(dev, 1);
- } else
- hw[1].irq = hw[0].irq = ret;
-
- ret = ide_host_register(host, d, hws);
- if (ret)
- ide_host_free(host);
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(ide_pci_init_one);
-
int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
const struct ide_port_info *d, void *priv)
{
struct pci_dev *pdev[] = { dev1, dev2 };
struct ide_host *host;
- int ret, i;
- hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
+ int ret, i, n_ports = dev2 ? 4 : 2;
+ struct ide_hw hw[4], *hws[] = { NULL, NULL, NULL, NULL };
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < n_ports / 2; i++) {
ret = ide_setup_pci_controller(pdev[i], d, !i);
if (ret < 0)
goto out;
@@ -597,23 +550,24 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]);
}
- host = ide_host_alloc(d, hws);
+ host = ide_host_alloc(d, hws, n_ports);
if (host == NULL) {
ret = -ENOMEM;
goto out;
}
host->dev[0] = &dev1->dev;
- host->dev[1] = &dev2->dev;
+ if (dev2)
+ host->dev[1] = &dev2->dev;
host->host_priv = priv;
-
host->irq_flags = IRQF_SHARED;
pci_set_drvdata(pdev[0], host);
- pci_set_drvdata(pdev[1], host);
+ if (dev2)
+ pci_set_drvdata(pdev[1], host);
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < n_ports / 2; i++) {
ret = do_ide_setup_pci_device(pdev[i], d, !i);
/*
@@ -639,6 +593,13 @@ out:
}
EXPORT_SYMBOL_GPL(ide_pci_init_two);
+int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
+ void *priv)
+{
+ return ide_pci_init_two(dev, NULL, d, priv);
+}
+EXPORT_SYMBOL_GPL(ide_pci_init_one);
+
void ide_pci_remove(struct pci_dev *dev)
{
struct ide_host *host = pci_get_drvdata(dev);
diff --git a/drivers/ide/sgiioc4.c b/drivers/ide/sgiioc4.c
index e5d2a48a84de..5f37f168f944 100644
--- a/drivers/ide/sgiioc4.c
+++ b/drivers/ide/sgiioc4.c
@@ -91,7 +91,7 @@ typedef struct {
static void
-sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
+sgiioc4_init_hwif_ports(struct ide_hw *hw, unsigned long data_port,
unsigned long ctrl_port, unsigned long irq_port)
{
unsigned long reg = data_port;
@@ -546,7 +546,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
unsigned long cmd_base, irqport;
unsigned long bar0, cmd_phys_base, ctl;
void __iomem *virt_base;
- hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
int rc;
/* Get the CmdBlk and CtrlBlk Base Registers */
@@ -575,13 +575,12 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
memset(&hw, 0, sizeof(hw));
sgiioc4_init_hwif_ports(&hw, cmd_base, ctl, irqport);
hw.irq = dev->irq;
- hw.chipset = ide_pci;
hw.dev = &dev->dev;
/* Initializing chipset IRQ Registers */
writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4));
- rc = ide_host_add(&sgiioc4_port_info, hws, NULL);
+ rc = ide_host_add(&sgiioc4_port_info, hws, 1, NULL);
if (!rc)
return 0;
diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c
index e4973cd1fba9..bd82d228608c 100644
--- a/drivers/ide/siimage.c
+++ b/drivers/ide/siimage.c
@@ -451,8 +451,8 @@ static int sil_sata_reset_poll(ide_drive_t *drive)
static void sil_sata_pre_reset(ide_drive_t *drive)
{
if (drive->media == ide_disk) {
- drive->special.b.set_geometry = 0;
- drive->special.b.recalibrate = 0;
+ drive->special_flags &=
+ ~(IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE);
}
}
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
index b0a460625335..0924abff52ff 100644
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -10,7 +10,7 @@
* with the timing registers setup.
* -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org
*
- * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (C) 2006-2007,2009 MontaVista Software, Inc. <source@mvista.com>
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
*/
@@ -146,14 +146,15 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive)
u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
u8 dma_cmd;
- printk("sl82c105: lost IRQ, resetting host\n");
+ printk(KERN_WARNING "sl82c105: lost IRQ, resetting host\n");
/*
* Check the raw interrupt from the drive.
*/
pci_read_config_dword(dev, 0x40, &val);
if (val & mask)
- printk("sl82c105: drive was requesting IRQ, but host lost it\n");
+ printk(KERN_INFO "sl82c105: drive was requesting IRQ, "
+ "but host lost it\n");
/*
* Was DMA enabled? If so, disable it - we're resetting the
@@ -162,7 +163,7 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive)
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
if (dma_cmd & 1) {
outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
- printk("sl82c105: DMA was enabled\n");
+ printk(KERN_INFO "sl82c105: DMA was enabled\n");
}
sl82c105_reset_host(dev);
diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c
index e33d764e2945..ea89fddeed91 100644
--- a/drivers/ide/tx4938ide.c
+++ b/drivers/ide/tx4938ide.c
@@ -130,8 +130,7 @@ static const struct ide_port_info tx4938ide_port_info __initdata = {
static int __init tx4938ide_probe(struct platform_device *pdev)
{
- hw_regs_t hw;
- hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
struct ide_host *host;
struct resource *res;
struct tx4938ide_platform_info *pdata = pdev->dev.platform_data;
@@ -183,7 +182,7 @@ static int __init tx4938ide_probe(struct platform_device *pdev)
tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, 0);
else
d.port_ops = NULL;
- ret = ide_host_add(&d, hws, &host);
+ ret = ide_host_add(&d, hws, 1, &host);
if (!ret)
platform_set_drvdata(pdev, host);
return ret;
diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c
index 5ca76224f6d1..64b58ecc3f0e 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -537,8 +537,7 @@ static const struct ide_port_info tx4939ide_port_info __initdata = {
static int __init tx4939ide_probe(struct platform_device *pdev)
{
- hw_regs_t hw;
- hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_hw hw, *hws[] = { &hw };
struct ide_host *host;
struct resource *res;
int irq, ret;
@@ -581,7 +580,7 @@ static int __init tx4939ide_probe(struct platform_device *pdev)
hw.dev = &pdev->dev;
pr_info("TX4939 IDE interface (base %#lx, irq %d)\n", mapbase, irq);
- host = ide_host_alloc(&tx4939ide_port_info, hws);
+ host = ide_host_alloc(&tx4939ide_port_info, hws, 1);
if (!host)
return -ENOMEM;
/* use extra_base for base address of the all registers */
diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
index 3ff7231e4858..028de26a25fe 100644
--- a/drivers/ide/via82cxxx.c
+++ b/drivers/ide/via82cxxx.c
@@ -67,6 +67,7 @@ static struct via_isa_bridge {
u8 udma_mask;
u8 flags;
} via_isa_bridges[] = {
+ { "vx855", PCI_DEVICE_ID_VIA_VX855, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
{ "vx800", PCI_DEVICE_ID_VIA_VX800, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
{ "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
{ "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
@@ -474,6 +475,7 @@ static const struct pci_device_id via_pci_tbl[] = {
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), 0 },
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1), 0 },
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_CX700_IDE), 0 },
+ { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_VX855_IDE), 0 },
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410), 1 },
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), 1 },
{ 0, },
diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c
index bf740394d704..949c97ff57e3 100644
--- a/drivers/idle/i7300_idle.c
+++ b/drivers/idle/i7300_idle.c
@@ -41,6 +41,10 @@ static int debug;
module_param_named(debug, debug, uint, 0644);
MODULE_PARM_DESC(debug, "Enable debug printks in this driver");
+static int forceload;
+module_param_named(forceload, forceload, uint, 0644);
+MODULE_PARM_DESC(debug, "Enable driver testing on unvalidated i5000");
+
#define dprintk(fmt, arg...) \
do { if (debug) printk(KERN_INFO I7300_PRINT fmt, ##arg); } while (0)
@@ -552,7 +556,7 @@ static int __init i7300_idle_init(void)
cpus_clear(idle_cpumask);
total_us = 0;
- if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev))
+ if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev, forceload))
return -ENODEV;
if (i7300_idle_thrt_save())
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
index 823a6297a1af..2cd00b5b45b4 100644
--- a/drivers/ieee1394/dv1394.c
+++ b/drivers/ieee1394/dv1394.c
@@ -1789,12 +1789,13 @@ static int dv1394_open(struct inode *inode, struct file *file)
} else {
/* look up the card by ID */
unsigned long flags;
+ int idx = ieee1394_file_to_instance(file);
spin_lock_irqsave(&dv1394_cards_lock, flags);
if (!list_empty(&dv1394_cards)) {
struct video_card *p;
list_for_each_entry(p, &dv1394_cards, list) {
- if ((p->id) == ieee1394_file_to_instance(file)) {
+ if ((p->id) == idx) {
video = p;
break;
}
@@ -1803,7 +1804,7 @@ static int dv1394_open(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&dv1394_cards_lock, flags);
if (!video) {
- debug_printk("dv1394: OHCI card %d not found", ieee1394_file_to_instance(file));
+ debug_printk("dv1394: OHCI card %d not found", idx);
return -ENODEV;
}
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
index 21d50f73a210..28b9f58bafd2 100644
--- a/drivers/ieee1394/ieee1394_core.h
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -5,6 +5,7 @@
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/types.h>
+#include <linux/cdev.h>
#include <asm/atomic.h>
#include "hosts.h"
@@ -155,7 +156,10 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
*/
static inline unsigned char ieee1394_file_to_instance(struct file *file)
{
- return file->f_path.dentry->d_inode->i_cindex;
+ int idx = cdev_index(file->f_path.dentry->d_inode);
+ if (idx < 0)
+ idx = 0;
+ return idx;
}
extern int hpsb_disable_irm;
diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c
index 8d71086f5a1c..62f9cf2f94ec 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_hal.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c
@@ -410,6 +410,7 @@ int cxio_flush_sq(struct t3_wq *wq, struct t3_cq *cq, int count)
ptr = wq->sq_rptr + count;
sqp = wq->sq + Q_PTR2IDX(ptr, wq->sq_size_log2);
while (ptr != wq->sq_wptr) {
+ sqp->signaled = 0;
insert_sq_cqe(wq, cq, sqp);
ptr++;
sqp = wq->sq + Q_PTR2IDX(ptr, wq->sq_size_log2);
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index fef3f1ae7225..52d7bb0c2a12 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1830,6 +1830,10 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
ep->com.rpl_err = 0;
ep->ird = conn_param->ird;
ep->ord = conn_param->ord;
+
+ if (peer2peer && ep->ird == 0)
+ ep->ird = 1;
+
PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord);
get_ep(&ep->com);
@@ -1915,6 +1919,10 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
conn_param->private_data, ep->plen);
ep->ird = conn_param->ird;
ep->ord = conn_param->ord;
+
+ if (peer2peer && ep->ord == 0)
+ ep->ord = 1;
+
ep->com.tdev = h->rdev.t3cdev_p;
cm_id->add_ref(cm_id);
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index 2f546a625330..27bbdc8e773a 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -1069,7 +1069,6 @@ int iwch_modify_qp(struct iwch_dev *rhp, struct iwch_qp *qhp,
goto out;
}
qhp->attr.state = IWCH_QP_STATE_IDLE;
- memset(&qhp->attr, 0, sizeof(qhp->attr));
break;
case IWCH_QP_STATE_TERMINATE:
if (!internal) {
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
index 8dc2bb781605..b3684060465e 100644
--- a/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -347,7 +347,7 @@ static int ipathfs_fill_super(struct super_block *sb, void *data,
spin_unlock_irqrestore(&ipath_devs_lock, flags);
ret = create_device_files(sb, dd);
if (ret) {
- deactivate_super(sb);
+ deactivate_locked_super(sb);
goto bail;
}
spin_lock_irqsave(&ipath_devs_lock, flags);
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 9974e886b8de..8a7dd6795fa0 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -86,6 +86,7 @@ struct mlx4_ib_mr {
struct mlx4_ib_fast_reg_page_list {
struct ib_fast_reg_page_list ibfrpl;
+ __be64 *mapped_page_list;
dma_addr_t map;
};
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 8e4d26d56a95..8f3666b20ea4 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -231,7 +231,11 @@ struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device
if (!mfrpl)
return ERR_PTR(-ENOMEM);
- mfrpl->ibfrpl.page_list = dma_alloc_coherent(&dev->dev->pdev->dev,
+ mfrpl->ibfrpl.page_list = kmalloc(size, GFP_KERNEL);
+ if (!mfrpl->ibfrpl.page_list)
+ goto err_free;
+
+ mfrpl->mapped_page_list = dma_alloc_coherent(&dev->dev->pdev->dev,
size, &mfrpl->map,
GFP_KERNEL);
if (!mfrpl->ibfrpl.page_list)
@@ -242,6 +246,7 @@ struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device
return &mfrpl->ibfrpl;
err_free:
+ kfree(mfrpl->ibfrpl.page_list);
kfree(mfrpl);
return ERR_PTR(-ENOMEM);
}
@@ -252,8 +257,9 @@ void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list)
struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list);
int size = page_list->max_page_list_len * sizeof (u64);
- dma_free_coherent(&dev->dev->pdev->dev, size, page_list->page_list,
+ dma_free_coherent(&dev->dev->pdev->dev, size, mfrpl->mapped_page_list,
mfrpl->map);
+ kfree(mfrpl->ibfrpl.page_list);
kfree(mfrpl);
}
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index f385a24d31d2..20724aee76f4 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1365,7 +1365,7 @@ static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr)
int i;
for (i = 0; i < wr->wr.fast_reg.page_list_len; ++i)
- wr->wr.fast_reg.page_list->page_list[i] =
+ mfrpl->mapped_page_list[i] =
cpu_to_be64(wr->wr.fast_reg.page_list->page_list[i] |
MLX4_MTT_FLAG_PRESENT);
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
index c33e1c53c799..6d55f9d748f6 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.c
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.c
@@ -157,13 +157,15 @@ enum {
enum {
CMD_TIME_CLASS_A = (HZ + 999) / 1000 + 1,
CMD_TIME_CLASS_B = (HZ + 99) / 100 + 1,
- CMD_TIME_CLASS_C = (HZ + 9) / 10 + 1
+ CMD_TIME_CLASS_C = (HZ + 9) / 10 + 1,
+ CMD_TIME_CLASS_D = 60 * HZ
};
#else
enum {
CMD_TIME_CLASS_A = 60 * HZ,
CMD_TIME_CLASS_B = 60 * HZ,
- CMD_TIME_CLASS_C = 60 * HZ
+ CMD_TIME_CLASS_C = 60 * HZ,
+ CMD_TIME_CLASS_D = 60 * HZ
};
#endif
@@ -598,7 +600,7 @@ int mthca_SYS_EN(struct mthca_dev *dev, u8 *status)
u64 out;
int ret;
- ret = mthca_cmd_imm(dev, 0, &out, 0, 0, CMD_SYS_EN, HZ, status);
+ ret = mthca_cmd_imm(dev, 0, &out, 0, 0, CMD_SYS_EN, CMD_TIME_CLASS_D, status);
if (*status == MTHCA_CMD_STAT_DDR_MEM_ERR)
mthca_warn(dev, "SYS_EN DDR error: syn=%x, sock=%d, "
@@ -611,7 +613,7 @@ int mthca_SYS_EN(struct mthca_dev *dev, u8 *status)
int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status)
{
- return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, HZ, status);
+ return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C, status);
}
static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm,
@@ -1390,7 +1392,7 @@ int mthca_INIT_HCA(struct mthca_dev *dev,
MTHCA_PUT(inbox, param->uarc_base, INIT_HCA_UAR_CTX_BASE_OFFSET);
}
- err = mthca_cmd(dev, mailbox->dma, 0, 0, CMD_INIT_HCA, HZ, status);
+ err = mthca_cmd(dev, mailbox->dma, 0, 0, CMD_INIT_HCA, CMD_TIME_CLASS_D, status);
mthca_free_mailbox(dev, mailbox);
return err;
@@ -1450,12 +1452,12 @@ int mthca_INIT_IB(struct mthca_dev *dev,
int mthca_CLOSE_IB(struct mthca_dev *dev, int port, u8 *status)
{
- return mthca_cmd(dev, 0, port, 0, CMD_CLOSE_IB, HZ, status);
+ return mthca_cmd(dev, 0, port, 0, CMD_CLOSE_IB, CMD_TIME_CLASS_A, status);
}
int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status)
{
- return mthca_cmd(dev, 0, 0, panic, CMD_CLOSE_HCA, HZ, status);
+ return mthca_cmd(dev, 0, 0, panic, CMD_CLOSE_HCA, CMD_TIME_CLASS_C, status);
}
int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
index 17621de54a9f..bf1720f7f35f 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -56,10 +56,8 @@
#define QUEUE_DISCONNECTS
-#define DRV_BUILD "1"
-
#define DRV_NAME "iw_nes"
-#define DRV_VERSION "1.0 KO Build " DRV_BUILD
+#define DRV_VERSION "1.5.0.0"
#define PFX DRV_NAME ": "
/*
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index dbd9a75474e3..11c7d6642014 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -56,6 +56,7 @@
#include <net/neighbour.h>
#include <net/route.h>
#include <net/ip_fib.h>
+#include <net/tcp.h>
#include "nes.h"
@@ -540,6 +541,7 @@ static void nes_cm_timer_tick(unsigned long pass)
struct list_head *list_node;
struct nes_cm_core *cm_core = g_cm_core;
u32 settimer = 0;
+ unsigned long timetosend;
int ret = NETDEV_TX_OK;
struct list_head timer_list;
@@ -644,8 +646,11 @@ static void nes_cm_timer_tick(unsigned long pass)
send_entry->retrycount);
if (send_entry->send_retrans) {
send_entry->retranscount--;
+ timetosend = (NES_RETRY_TIMEOUT <<
+ (NES_DEFAULT_RETRANS - send_entry->retranscount));
+
send_entry->timetosend = jiffies +
- NES_RETRY_TIMEOUT;
+ min(timetosend, NES_MAX_TIMEOUT);
if (nexttimeout > send_entry->timetosend ||
!settimer) {
nexttimeout = send_entry->timetosend;
@@ -854,7 +859,6 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
{
unsigned long flags;
struct nes_cm_listener *listen_node;
- __be32 tmp_addr = cpu_to_be32(dst_addr);
/* walk list and find cm_node associated with this session ID */
spin_lock_irqsave(&cm_core->listen_list_lock, flags);
@@ -871,9 +875,6 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
}
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
- nes_debug(NES_DBG_CM, "Unable to find listener for %pI4:%x\n",
- &tmp_addr, dst_port);
-
/* no listener */
return NULL;
}
@@ -1325,18 +1326,20 @@ static void handle_fin_pkt(struct nes_cm_node *cm_node)
nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
"refcnt=%d\n", cm_node, cm_node->state,
atomic_read(&cm_node->ref_count));
- cm_node->tcp_cntxt.rcv_nxt++;
- cleanup_retrans_entry(cm_node);
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_ESTABLISHED:
case NES_CM_STATE_MPAREQ_SENT:
case NES_CM_STATE_MPAREJ_RCVD:
+ cm_node->tcp_cntxt.rcv_nxt++;
+ cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_LAST_ACK;
send_fin(cm_node, NULL);
break;
case NES_CM_STATE_FIN_WAIT1:
+ cm_node->tcp_cntxt.rcv_nxt++;
+ cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSING;
send_ack(cm_node, NULL);
/* Wait for ACK as this is simultanous close..
@@ -1344,11 +1347,15 @@ static void handle_fin_pkt(struct nes_cm_node *cm_node)
* Just rm the node.. Done.. */
break;
case NES_CM_STATE_FIN_WAIT2:
+ cm_node->tcp_cntxt.rcv_nxt++;
+ cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_TIME_WAIT;
send_ack(cm_node, NULL);
schedule_nes_timer(cm_node, NULL, NES_TIMER_TYPE_CLOSE, 1, 0);
break;
case NES_CM_STATE_TIME_WAIT:
+ cm_node->tcp_cntxt.rcv_nxt++;
+ cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
rem_ref_cm_node(cm_node->cm_core, cm_node);
break;
@@ -1384,7 +1391,6 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
passive_state = atomic_add_return(1, &cm_node->passive_state);
if (passive_state == NES_SEND_RESET_EVENT)
create_event(cm_node, NES_CM_EVENT_RESET);
- cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
dev_kfree_skb_any(skb);
break;
@@ -1398,17 +1404,16 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
active_open_err(cm_node, skb, reset);
break;
case NES_CM_STATE_CLOSED:
- cleanup_retrans_entry(cm_node);
drop_packet(skb);
break;
+ case NES_CM_STATE_LAST_ACK:
+ cm_node->cm_id->rem_ref(cm_node->cm_id);
case NES_CM_STATE_TIME_WAIT:
- cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
rem_ref_cm_node(cm_node->cm_core, cm_node);
drop_packet(skb);
break;
case NES_CM_STATE_FIN_WAIT1:
- cleanup_retrans_entry(cm_node);
nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__);
default:
drop_packet(skb);
@@ -1455,6 +1460,7 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
NES_PASSIVE_STATE_INDICATED);
break;
case NES_CM_STATE_MPAREQ_SENT:
+ cleanup_retrans_entry(cm_node);
if (res_type == NES_MPA_REQUEST_REJECT) {
type = NES_CM_EVENT_MPA_REJECT;
cm_node->state = NES_CM_STATE_MPAREJ_RCVD;
@@ -1518,7 +1524,7 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
rcv_wnd = cm_node->tcp_cntxt.rcv_wnd;
if (ack_seq != loc_seq_num)
err = 1;
- else if ((seq + rcv_wnd) < rcv_nxt)
+ else if (!between(seq, rcv_nxt, (rcv_nxt+rcv_wnd)))
err = 1;
if (err) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
@@ -1652,49 +1658,39 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
}
-static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
struct tcphdr *tcph)
{
int datasize = 0;
u32 inc_sequence;
u32 rem_seq_ack;
u32 rem_seq;
- int ret;
+ int ret = 0;
int optionsize;
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
if (check_seq(cm_node, tcph, skb))
- return;
+ return -EINVAL;
skb_pull(skb, tcph->doff << 2);
inc_sequence = ntohl(tcph->seq);
rem_seq = ntohl(tcph->seq);
rem_seq_ack = ntohl(tcph->ack_seq);
datasize = skb->len;
- cleanup_retrans_entry(cm_node);
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
/* Passive OPEN */
+ cleanup_retrans_entry(cm_node);
ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 1);
if (ret)
break;
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
- if (cm_node->tcp_cntxt.rem_ack_num !=
- cm_node->tcp_cntxt.loc_seq_num) {
- nes_debug(NES_DBG_CM, "rem_ack_num != loc_seq_num\n");
- cleanup_retrans_entry(cm_node);
- send_reset(cm_node, skb);
- return;
- }
cm_node->state = NES_CM_STATE_ESTABLISHED;
- cleanup_retrans_entry(cm_node);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else { /* rcvd ACK only */
+ } else /* rcvd ACK only */
dev_kfree_skb_any(skb);
- cleanup_retrans_entry(cm_node);
- }
break;
case NES_CM_STATE_ESTABLISHED:
/* Passive OPEN */
@@ -1706,15 +1702,12 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
drop_packet(skb);
break;
case NES_CM_STATE_MPAREQ_SENT:
- cleanup_retrans_entry(cm_node);
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb);
- } else { /* Could be just an ack pkt.. */
- cleanup_retrans_entry(cm_node);
+ } else /* Could be just an ack pkt.. */
dev_kfree_skb_any(skb);
- }
break;
case NES_CM_STATE_LISTENING:
case NES_CM_STATE_CLOSED:
@@ -1722,11 +1715,10 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
send_reset(cm_node, skb);
break;
case NES_CM_STATE_LAST_ACK:
+ case NES_CM_STATE_CLOSING:
cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
cm_node->cm_id->rem_ref(cm_node->cm_id);
- case NES_CM_STATE_CLOSING:
- cleanup_retrans_entry(cm_node);
rem_ref_cm_node(cm_node->cm_core, cm_node);
drop_packet(skb);
break;
@@ -1741,9 +1733,11 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_MPAREQ_RCVD:
case NES_CM_STATE_UNKNOWN:
default:
+ cleanup_retrans_entry(cm_node);
drop_packet(skb);
break;
}
+ return ret;
}
@@ -1849,6 +1843,7 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
struct tcphdr *tcph = tcp_hdr(skb);
u32 fin_set = 0;
+ int ret = 0;
skb_pull(skb, ip_hdr(skb)->ihl << 2);
nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
@@ -1874,17 +1869,17 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
handle_synack_pkt(cm_node, skb, tcph);
break;
case NES_PKT_TYPE_ACK:
- handle_ack_pkt(cm_node, skb, tcph);
- if (fin_set)
+ ret = handle_ack_pkt(cm_node, skb, tcph);
+ if (fin_set && !ret)
handle_fin_pkt(cm_node);
break;
case NES_PKT_TYPE_RST:
handle_rst_pkt(cm_node, skb, tcph);
break;
default:
- drop_packet(skb);
- if (fin_set)
+ if ((fin_set) && (!check_seq(cm_node, tcph, skb)))
handle_fin_pkt(cm_node);
+ drop_packet(skb);
break;
}
}
@@ -2710,7 +2705,6 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* associate the node with the QP */
nesqp->cm_node = (void *)cm_node;
cm_node->nesqp = nesqp;
- nes_add_ref(&nesqp->ibqp);
nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu listener = %p\n",
nesqp->hwqp.qp_id, cm_node, jiffies, cm_node->listener);
@@ -2763,6 +2757,9 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nes_debug(NES_DBG_CM, "Unable to register memory region"
"for lSMM for cm_node = %p \n",
cm_node);
+ pci_free_consistent(nesdev->pcidev,
+ nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
return -ENOMEM;
}
@@ -2879,6 +2876,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* notify OF layer that accept event was successful */
cm_id->add_ref(cm_id);
+ nes_add_ref(&nesqp->ibqp);
cm_event.event = IW_CM_EVENT_ESTABLISHED;
cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
@@ -2959,6 +2957,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct nes_device *nesdev;
struct nes_cm_node *cm_node;
struct nes_cm_info cm_info;
+ int apbvt_set = 0;
ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
if (!ibqp)
@@ -2996,9 +2995,11 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
conn_param->private_data_len);
if (cm_id->local_addr.sin_addr.s_addr !=
- cm_id->remote_addr.sin_addr.s_addr)
+ cm_id->remote_addr.sin_addr.s_addr) {
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+ apbvt_set = 1;
+ }
/* set up the connection params for the node */
cm_info.loc_addr = htonl(cm_id->local_addr.sin_addr.s_addr);
@@ -3015,8 +3016,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
conn_param->private_data_len, (void *)conn_param->private_data,
&cm_info);
if (!cm_node) {
- if (cm_id->local_addr.sin_addr.s_addr !=
- cm_id->remote_addr.sin_addr.s_addr)
+ if (apbvt_set)
nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
PCI_FUNC(nesdev->pcidev->devfn),
NES_MANAGE_APBVT_DEL);
@@ -3025,7 +3025,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
return -ENOMEM;
}
- cm_node->apbvt_set = 1;
+ cm_node->apbvt_set = apbvt_set;
nesqp->cm_node = cm_node;
cm_node->nesqp = nesqp;
nes_add_ref(&nesqp->ibqp);
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index 80bba1892571..8b7e7c0e496e 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -149,6 +149,7 @@ struct nes_timer_entry {
#endif
#define NES_SHORT_TIME (10)
#define NES_LONG_TIME (2000*HZ/1000)
+#define NES_MAX_TIMEOUT ((unsigned long) (12*HZ))
#define NES_CM_HASHTABLE_SIZE 1024
#define NES_CM_TCP_TIMER_INTERVAL 3000
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index d6fc9ae44062..b832a7b814a2 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -550,11 +550,8 @@ struct nes_adapter *nes_init_adapter(struct nes_device *nesdev, u8 hw_rev) {
msleep(1);
}
if (int_cnt > 1) {
- u32 sds;
spin_lock_irqsave(&nesadapter->phy_lock, flags);
- sds = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
- sds |= 0x00000040;
- nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, sds);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F0C8);
mh_detected++;
reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
reset_value |= 0x0000003d;
@@ -579,7 +576,7 @@ struct nes_adapter *nes_init_adapter(struct nes_device *nesdev, u8 hw_rev) {
if (++ext_cnt > int_cnt) {
spin_lock_irqsave(&nesadapter->phy_lock, flags);
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1,
- 0x0000F0C8);
+ 0x0000F088);
mh_detected++;
reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
reset_value |= 0x0000003d;
@@ -764,6 +761,9 @@ static int nes_init_serdes(struct nes_device *nesdev, u8 hw_rev, u8 port_count,
return 0;
/* init serdes 1 */
+ if (!(OneG_Mode && (nesadapter->phy_type[1] != NES_PHY_TYPE_PUMA_1G)))
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL1, 0x000000FF);
+
switch (nesadapter->phy_type[1]) {
case NES_PHY_TYPE_ARGUS:
case NES_PHY_TYPE_SFP_D:
@@ -771,21 +771,20 @@ static int nes_init_serdes(struct nes_device *nesdev, u8 hw_rev, u8 port_count,
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_EMP1, 0x00000000);
break;
case NES_PHY_TYPE_CX4:
- sds = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
- sds &= 0xFFFFFFBF;
- nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, sds);
if (wide_ppm_offset)
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL1, 0x000FFFAA);
- else
- nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL1, 0x000000FF);
break;
case NES_PHY_TYPE_PUMA_1G:
sds = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
sds |= 0x000000100;
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, sds);
}
- if (!OneG_Mode)
+ if (!OneG_Mode) {
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_HIGHZ_LANE_MODE1, 0x11110000);
+ sds = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
+ sds &= 0xFFFFFFBF;
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, sds);
+ }
} else {
/* init serdes 0 */
nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL0, 0x00000008);
@@ -913,6 +912,12 @@ static void nes_init_csr_ne020(struct nes_device *nesdev, u8 hw_rev, u8 port_cou
u32temp &= 0x7fffffff;
u32temp |= 0x7fff0010;
nes_write_indexed(nesdev, 0x000021f8, u32temp);
+ if (port_count > 1) {
+ u32temp = nes_read_indexed(nesdev, 0x000023f8);
+ u32temp &= 0x7fffffff;
+ u32temp |= 0x7fff0010;
+ nes_write_indexed(nesdev, 0x000023f8, u32temp);
+ }
}
}
@@ -1366,13 +1371,14 @@ int nes_init_phy(struct nes_device *nesdev)
if (phy_type == NES_PHY_TYPE_ARGUS) {
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xc302, 0x000C);
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xc319, 0x0008);
+ nes_write_10G_phy_reg(nesdev, phy_index, 0x3, 0x0027, 0x0001);
} else {
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xc302, 0x0004);
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xc319, 0x0038);
+ nes_write_10G_phy_reg(nesdev, phy_index, 0x3, 0x0027, 0x0013);
}
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xc31a, 0x0098);
nes_write_10G_phy_reg(nesdev, phy_index, 0x3, 0x0026, 0x0E00);
- nes_write_10G_phy_reg(nesdev, phy_index, 0x3, 0x0027, 0x0001);
/* setup LEDs */
nes_write_10G_phy_reg(nesdev, phy_index, 0x1, 0xd006, 0x0007);
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 7e5b5ba13a74..64d5cfd8f380 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1627,6 +1627,7 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
nescq->hw_cq.cq_number = nes_ucontext->mcrqf & 0xffff;
else
nescq->hw_cq.cq_number = nesvnic->mcrq_qp_id + nes_ucontext->mcrqf-1;
+ nescq->mcrqf = nes_ucontext->mcrqf;
nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
}
nes_debug(NES_DBG_CQ, "CQ Virtual Address = %08lX, size = %u.\n",
@@ -1682,6 +1683,12 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
+ else {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size,
+ nespbl->pbl_vbase, nespbl->pbl_pbase);
+ kfree(nespbl);
+ }
+
nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
kfree(nescq);
return ERR_PTR(-ENOMEM);
@@ -1705,6 +1712,11 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
+ else {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size,
+ nespbl->pbl_vbase, nespbl->pbl_pbase);
+ kfree(nespbl);
+ }
nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
kfree(nescq);
return ERR_PTR(-ENOMEM);
@@ -1722,6 +1734,11 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
+ else {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size,
+ nespbl->pbl_vbase, nespbl->pbl_pbase);
+ kfree(nespbl);
+ }
nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
kfree(nescq);
return ERR_PTR(-ENOMEM);
@@ -1774,6 +1791,11 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
+ else {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size,
+ nespbl->pbl_vbase, nespbl->pbl_pbase);
+ kfree(nespbl);
+ }
nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
kfree(nescq);
return ERR_PTR(-EIO);
@@ -1855,7 +1877,9 @@ static int nes_destroy_cq(struct ib_cq *ib_cq)
set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
(nescq->hw_cq.cq_number | ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 16)));
- nes_free_resource(nesadapter, nesadapter->allocated_cqs, nescq->hw_cq.cq_number);
+ if (!nescq->mcrqf)
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, nescq->hw_cq.cq_number);
+
atomic_set(&cqp_request->refcount, 2);
nes_post_cqp_request(nesdev, cqp_request);
@@ -1895,8 +1919,7 @@ static int nes_destroy_cq(struct ib_cq *ib_cq)
static u32 root_256(struct nes_device *nesdev,
struct nes_root_vpbl *root_vpbl,
struct nes_root_vpbl *new_root,
- u16 pbl_count_4k,
- u16 pbl_count_256)
+ u16 pbl_count_4k)
{
u64 leaf_pbl;
int i, j, k;
@@ -1952,7 +1975,7 @@ static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
int ret;
struct nes_adapter *nesadapter = nesdev->nesadapter;
uint pg_cnt = 0;
- u16 pbl_count_256;
+ u16 pbl_count_256 = 0;
u16 pbl_count = 0;
u8 use_256_pbls = 0;
u8 use_4k_pbls = 0;
@@ -2012,7 +2035,7 @@ static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
}
if (use_256_pbls && use_two_level) {
- if (root_256(nesdev, root_vpbl, &new_root, pbl_count_4k, pbl_count_256) == 1) {
+ if (root_256(nesdev, root_vpbl, &new_root, pbl_count_4k) == 1) {
if (new_root.pbl_pbase != 0)
root_vpbl = &new_root;
} else {
@@ -2122,6 +2145,7 @@ static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
struct nes_root_vpbl root_vpbl;
u32 stag;
u32 i;
+ unsigned long mask;
u32 stag_index = 0;
u32 next_stag_index = 0;
u32 driver_key = 0;
@@ -2150,6 +2174,9 @@ static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
return ERR_PTR(-E2BIG);
}
+ if ((buffer_list[0].addr ^ *iova_start) & ~PAGE_MASK)
+ return ERR_PTR(-EINVAL);
+
err = nes_alloc_resource(nesadapter, nesadapter->allocated_mrs, nesadapter->max_mr,
&stag_index, &next_stag_index);
if (err) {
@@ -2215,19 +2242,16 @@ static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
root_pbl_index++;
cur_pbl_index = 0;
}
- if (buffer_list[i].addr & ~PAGE_MASK) {
- /* TODO: Unwind allocated buffers */
- nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
- (unsigned int) buffer_list[i].addr);
- ibmr = ERR_PTR(-EINVAL);
- kfree(nesmr);
- goto reg_phys_err;
- }
- if (!buffer_list[i].size) {
+ mask = !buffer_list[i].size;
+ if (i != 0)
+ mask |= buffer_list[i].addr;
+ if (i != num_phys_buf - 1)
+ mask |= buffer_list[i].addr + buffer_list[i].size;
+
+ if (mask & ~PAGE_MASK) {
nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
+ nes_debug(NES_DBG_MR, "Invalid buffer addr or size\n");
ibmr = ERR_PTR(-EINVAL);
kfree(nesmr);
goto reg_phys_err;
@@ -2238,7 +2262,7 @@ static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
if ((buffer_list[i-1].addr+PAGE_SIZE) != buffer_list[i].addr)
single_page = 0;
}
- vpbl.pbl_vbase[cur_pbl_index].pa_low = cpu_to_le32((u32)buffer_list[i].addr);
+ vpbl.pbl_vbase[cur_pbl_index].pa_low = cpu_to_le32((u32)buffer_list[i].addr & PAGE_MASK);
vpbl.pbl_vbase[cur_pbl_index++].pa_high =
cpu_to_le32((u32)((((u64)buffer_list[i].addr) >> 32)));
}
@@ -2251,8 +2275,6 @@ static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
" length = 0x%016lX, index = 0x%08X\n",
stag, (unsigned long)*iova_start, (unsigned long)region_length, stag_index);
- region_length -= (*iova_start)&PAGE_MASK;
-
/* Make the leaf PBL the root if only one PBL */
if (root_pbl_index == 1) {
root_vpbl.pbl_pbase = vpbl.pbl_pbase;
@@ -2786,10 +2808,9 @@ static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
struct nes_vnic *nesvnic = nesibdev->nesvnic;
nes_debug(NES_DBG_INIT, "\n");
- return sprintf(buf, "%x.%x.%x\n",
- (int)(nesvnic->nesdev->nesadapter->fw_ver >> 32),
- (int)(nesvnic->nesdev->nesadapter->fw_ver >> 16) & 0xffff,
- (int)(nesvnic->nesdev->nesadapter->fw_ver & 0xffff));
+ return sprintf(buf, "%u.%u\n",
+ (nesvnic->nesdev->nesadapter->firmware_version >> 16),
+ (nesvnic->nesdev->nesadapter->firmware_version & 0x000000ff));
}
diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
index 5e48f67fbe8d..41c07f29f7c9 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.h
+++ b/drivers/infiniband/hw/nes/nes_verbs.h
@@ -112,6 +112,7 @@ struct nes_cq {
spinlock_t lock;
u8 virtual_cq;
u8 pad[3];
+ u32 mcrqf;
};
struct nes_wq {
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index da6082739839..e7e5adf84e84 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -685,7 +685,8 @@ int ipoib_ib_dev_open(struct net_device *dev)
queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task,
round_jiffies_relative(HZ));
- set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags);
+ if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
+ napi_enable(&priv->napi);
return 0;
}
@@ -804,7 +805,8 @@ int ipoib_ib_dev_stop(struct net_device *dev, int flush)
struct ipoib_tx_buf *tx_req;
int i;
- clear_bit(IPOIB_FLAG_INITIALIZED, &priv->flags);
+ if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
+ napi_disable(&priv->napi);
ipoib_cm_dev_stop(dev);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 421a6640c9bd..ab2c192c76bc 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -106,8 +106,7 @@ int ipoib_open(struct net_device *dev)
ipoib_dbg(priv, "bringing up interface\n");
- if (!test_and_set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
- napi_enable(&priv->napi);
+ set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
if (ipoib_pkey_dev_delay_open(dev))
return 0;
@@ -143,7 +142,6 @@ err_stop:
ipoib_ib_dev_stop(dev, 1);
err_disable:
- napi_disable(&priv->napi);
clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
return -EINVAL;
@@ -156,7 +154,6 @@ static int ipoib_stop(struct net_device *dev)
ipoib_dbg(priv, "stopping interface\n");
clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
- napi_disable(&priv->napi);
netif_stop_queue(dev);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 13d7674b293d..0ba6ec876296 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -257,11 +257,8 @@ static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- /*
- * mgmt tasks do not need special cleanup and we do not
- * allocate anything in the init task callout
- */
- if (!task->sc || task->state == ISCSI_TASK_PENDING)
+ /* mgmt tasks do not need special cleanup */
+ if (!task->sc)
return;
if (iser_task->status == ISER_TASK_STATUS_STARTED) {
@@ -517,7 +514,8 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
}
static struct iscsi_endpoint *
-iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
+iscsi_iser_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
+ int non_blocking)
{
int err;
struct iser_conn *ib_conn;
@@ -599,6 +597,7 @@ static struct scsi_host_template iscsi_iser_sht = {
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler= iscsi_eh_target_reset,
+ .target_alloc = iscsi_target_alloc,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "iscsi_iser",
.this_id = -1,
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index bc4e40f3ede7..2d1415e16834 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -226,7 +226,7 @@ static int get_compatible_type(struct ff_device *ff, int effect_type)
*/
static void ml_combine_effects(struct ff_effect *effect,
struct ml_effect_state *state,
- int gain)
+ unsigned int gain)
{
struct ff_effect *new = state->effect;
unsigned int strong, weak, i;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 935a1835de2d..5d445f48789b 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -29,6 +29,24 @@ MODULE_LICENSE("GPL");
#define INPUT_DEVICES 256
+/*
+ * EV_ABS events which should not be cached are listed here.
+ */
+static unsigned int input_abs_bypass_init_data[] __initdata = {
+ ABS_MT_TOUCH_MAJOR,
+ ABS_MT_TOUCH_MINOR,
+ ABS_MT_WIDTH_MAJOR,
+ ABS_MT_WIDTH_MINOR,
+ ABS_MT_ORIENTATION,
+ ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y,
+ ABS_MT_TOOL_TYPE,
+ ABS_MT_BLOB_ID,
+ ABS_MT_TRACKING_ID,
+ 0
+};
+static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)];
+
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
@@ -161,6 +179,10 @@ static void input_handle_event(struct input_dev *dev,
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
+ case SYN_MT_REPORT:
+ dev->sync = 0;
+ disposition = INPUT_PASS_TO_HANDLERS;
+ break;
}
break;
@@ -192,6 +214,11 @@ static void input_handle_event(struct input_dev *dev,
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
+ if (test_bit(code, input_abs_bypass)) {
+ disposition = INPUT_PASS_TO_HANDLERS;
+ break;
+ }
+
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
@@ -1634,10 +1661,20 @@ static const struct file_operations input_fops = {
.open = input_open_file,
};
+static void __init input_init_abs_bypass(void)
+{
+ const unsigned int *p;
+
+ for (p = input_abs_bypass_init_data; *p; p++)
+ input_abs_bypass[BIT_WORD(*p)] |= BIT_MASK(*p);
+}
+
static int __init input_init(void)
{
int err;
+ input_init_abs_bypass();
+
err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 4224f0112849..012a5e753991 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -843,7 +843,13 @@ static const struct input_device_id joydev_blacklist[] = {
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT_MASK(EV_KEY) },
.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
- }, /* Avoid itouchpads, touchscreens and tablets */
+ }, /* Avoid itouchpads and touchscreens */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_KEYBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_DIGI) },
+ }, /* Avoid tablets, digitisers and similar devices */
{ } /* Terminating entry */
};
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 444dec07e5d8..df3f8aa68115 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -895,6 +895,13 @@ static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = {
};
/*
+ * Amilo Xi 3650 key release for light touch bar not working
+ */
+static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = {
+ 0x67, 0xed, 0x90, 0xa2, 0x99, 0xa4, 0xae, 0xb0, -1U
+};
+
+/*
* atkbd_set_keycode_table() initializes keyboard's keycode table
* according to the selected scancode set
*/
@@ -1560,6 +1567,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.callback = atkbd_setup_forced_release,
.driver_data = atkbd_amilo_pa1510_forced_release_keys,
},
+ {
+ .ident = "Fujitsu Amilo Xi 3650",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkbd_amilo_xi3650_forced_release_keys,
+ },
{ }
};
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 058fa8b02c21..87ec7b18ac69 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -100,8 +100,20 @@ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
/* disable keyboard interrupt and schedule for handling */
if (cpu_is_omap24xx()) {
int i;
- for (i = 0; i < omap_kp->rows; i++)
- disable_irq(gpio_to_irq(row_gpios[i]));
+
+ for (i = 0; i < omap_kp->rows; i++) {
+ int gpio_irq = gpio_to_irq(row_gpios[i]);
+ /*
+ * The interrupt which we're currently handling should
+ * be disabled _nosync() to avoid deadlocks waiting
+ * for this handler to complete. All others should
+ * be disabled the regular way for SMP safety.
+ */
+ if (gpio_irq == irq)
+ disable_irq_nosync(gpio_irq);
+ else
+ disable_irq(gpio_irq);
+ }
} else
/* disable keyboard interrupt and schedule for handling */
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index cbedf957cc58..daecc75c72e6 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -37,6 +37,7 @@
#define ALPS_FW_BK_2 0x40
static const struct alps_model_info alps_model_data[] = {
+ { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
{ { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */
{ { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
{ { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 454b96112f03..e0140fdc02a5 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -255,15 +255,22 @@ MODULE_PARM_DESC(debug, "Activate debugging output");
*/
static int atp_geyser_init(struct usb_device *udev)
{
- char data[8];
+ char *data;
int size;
int i;
+ int ret;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data) {
+ err("Out of memory");
+ return -ENOMEM;
+ }
size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER_MODE_REQUEST_VALUE,
- ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
+ ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dprintk("atp_geyser_init: read error\n");
@@ -271,7 +278,8 @@ static int atp_geyser_init(struct usb_device *udev)
dprintk("appletouch[%d]: %d\n", i, data[i]);
err("Failed to read mode from device.");
- return -EIO;
+ ret = -EIO;
+ goto out_free;
}
/* Apply the mode switch */
@@ -281,7 +289,7 @@ static int atp_geyser_init(struct usb_device *udev)
ATP_GEYSER_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER_MODE_REQUEST_VALUE,
- ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
+ ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dprintk("atp_geyser_init: write error\n");
@@ -289,9 +297,13 @@ static int atp_geyser_init(struct usb_device *udev)
dprintk("appletouch[%d]: %d\n", i, data[i]);
err("Failed to request geyser raw mode");
- return -EIO;
+ ret = -EIO;
+ goto out_free;
}
- return 0;
+ ret = 0;
+out_free:
+ kfree(data);
+ return ret;
}
/*
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 2998a6ac9ae4..2d8fc0bf6923 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -51,6 +51,10 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
+/* Macbook5,1 (unibody), aka wellspring3 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
@@ -72,6 +76,10 @@ static const struct usb_device_id bcm5974_table[] = {
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+ /* Macbook5,1 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
/* Terminating entry */
{}
};
@@ -96,14 +104,23 @@ struct bt_data {
u8 rel_y; /* relative y coordinate */
};
-/* trackpad header structure */
-struct tp_header {
- u8 unknown1[16]; /* constants, timers, etc */
- u8 fingers; /* number of fingers on trackpad */
- u8 unknown2[9]; /* constants, timers, etc */
+/* trackpad header types */
+enum tp_type {
+ TYPE1, /* plain trackpad */
+ TYPE2 /* button integrated in trackpad */
};
-/* trackpad finger structure */
+/* trackpad finger data offsets, le16-aligned */
+#define FINGER_TYPE1 (13 * sizeof(__le16))
+#define FINGER_TYPE2 (15 * sizeof(__le16))
+
+/* trackpad button data offsets */
+#define BUTTON_TYPE2 15
+
+/* list of device capability bits */
+#define HAS_INTEGRATED_BUTTON 1
+
+/* trackpad finger structure, le16-aligned */
struct tp_finger {
__le16 origin; /* zero when switching track finger */
__le16 abs_x; /* absolute x coodinate */
@@ -117,13 +134,11 @@ struct tp_finger {
__le16 force_minor; /* trackpad force, minor axis? */
__le16 unused[3]; /* zeros */
__le16 multi; /* one finger: varies, more fingers: constant */
-};
+} __attribute__((packed,aligned(2)));
-/* trackpad data structure, empirically at least ten fingers */
-struct tp_data {
- struct tp_header header;
- struct tp_finger finger[16];
-};
+/* trackpad finger data size, empirically at least ten fingers */
+#define SIZEOF_FINGER sizeof(struct tp_finger)
+#define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER)
/* device-specific parameters */
struct bcm5974_param {
@@ -136,9 +151,12 @@ struct bcm5974_param {
/* device-specific configuration */
struct bcm5974_config {
int ansi, iso, jis; /* the product id of this device */
+ int caps; /* device capability bitmask */
int bt_ep; /* the endpoint of the button interface */
int bt_datalen; /* data length of the button interface */
int tp_ep; /* the endpoint of the trackpad interface */
+ enum tp_type tp_type; /* type of trackpad interface */
+ int tp_offset; /* offset to trackpad finger data */
int tp_datalen; /* data length of the trackpad interface */
struct bcm5974_param p; /* finger pressure limits */
struct bcm5974_param w; /* finger width limits */
@@ -158,7 +176,7 @@ struct bcm5974 {
struct urb *bt_urb; /* button usb request block */
struct bt_data *bt_data; /* button transferred data */
struct urb *tp_urb; /* trackpad usb request block */
- struct tp_data *tp_data; /* trackpad transferred data */
+ u8 *tp_data; /* trackpad transferred data */
int fingers; /* number of fingers on trackpad */
};
@@ -183,8 +201,9 @@ static const struct bcm5974_config bcm5974_config_table[] = {
USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
+ 0,
0x84, sizeof(struct bt_data),
- 0x81, sizeof(struct tp_data),
+ 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
{ DIM_X, DIM_X / SN_COORD, -4824, 5342 },
@@ -194,13 +213,26 @@ static const struct bcm5974_config bcm5974_config_table[] = {
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
+ 0,
0x84, sizeof(struct bt_data),
- 0x81, sizeof(struct tp_data),
+ 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
{ DIM_X, DIM_X / SN_COORD, -4824, 4824 },
{ DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
},
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
+ HAS_INTEGRATED_BUTTON,
+ 0x84, sizeof(struct bt_data),
+ 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4460, 5166 },
+ { DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
+ },
{}
};
@@ -257,6 +289,7 @@ static void setup_events_to_report(struct input_dev *input_dev,
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
__set_bit(BTN_LEFT, input_dev->keybit);
}
@@ -266,6 +299,11 @@ static int report_bt_state(struct bcm5974 *dev, int size)
if (size != sizeof(struct bt_data))
return -EIO;
+ dprintk(7,
+ "bcm5974: button data: %x %x %x %x\n",
+ dev->bt_data->unknown1, dev->bt_data->button,
+ dev->bt_data->rel_x, dev->bt_data->rel_y);
+
input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
input_sync(dev->input);
@@ -276,29 +314,37 @@ static int report_bt_state(struct bcm5974 *dev, int size)
static int report_tp_state(struct bcm5974 *dev, int size)
{
const struct bcm5974_config *c = &dev->cfg;
- const struct tp_finger *f = dev->tp_data->finger;
+ const struct tp_finger *f;
struct input_dev *input = dev->input;
- const int fingers = (size - 26) / 28;
- int raw_p, raw_w, raw_x, raw_y;
- int ptest = 0, origin = 0, nmin = 0, nmax = 0;
+ int raw_p, raw_w, raw_x, raw_y, raw_n;
+ int ptest = 0, origin = 0, ibt = 0, nmin = 0, nmax = 0;
int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
- if (size < 26 || (size - 26) % 28 != 0)
+ if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
return -EIO;
+ /* finger data, le16-aligned */
+ f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
+ raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
+
/* always track the first finger; when detached, start over */
- if (fingers) {
+ if (raw_n) {
raw_p = raw2int(f->force_major);
raw_w = raw2int(f->size_major);
raw_x = raw2int(f->abs_x);
raw_y = raw2int(f->abs_y);
dprintk(9,
- "bcm5974: raw: p: %+05d w: %+05d x: %+05d y: %+05d\n",
- raw_p, raw_w, raw_x, raw_y);
+ "bcm5974: "
+ "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
+ raw_p, raw_w, raw_x, raw_y, raw_n);
ptest = int2bound(&c->p, raw_p);
origin = raw2int(f->origin);
+
+ /* set the integrated button if applicable */
+ if (c->tp_type == TYPE2)
+ ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
}
/* while tracking finger still valid, count all fingers */
@@ -307,12 +353,13 @@ static int report_tp_state(struct bcm5974 *dev, int size)
abs_w = int2bound(&c->w, raw_w);
abs_x = int2bound(&c->x, raw_x - c->x.devmin);
abs_y = int2bound(&c->y, c->y.devmax - raw_y);
- for (; f != dev->tp_data->finger + fingers; f++) {
+ while (raw_n--) {
ptest = int2bound(&c->p, raw2int(f->force_major));
if (ptest > PRESSURE_LOW)
nmax++;
if (ptest > PRESSURE_HIGH)
nmin++;
+ f++;
}
}
@@ -324,7 +371,8 @@ static int report_tp_state(struct bcm5974 *dev, int size)
input_report_key(input, BTN_TOUCH, dev->fingers > 0);
input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
- input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers > 2);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
+ input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
input_report_abs(input, ABS_PRESSURE, abs_p);
input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
@@ -335,11 +383,15 @@ static int report_tp_state(struct bcm5974 *dev, int size)
dprintk(8,
"bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
- "nmin: %d nmax: %d n: %d\n",
- abs_p, abs_w, abs_x, abs_y, nmin, nmax, dev->fingers);
+ "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
+ abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
}
+ /* type 2 reports button events via ibt only */
+ if (c->tp_type == TYPE2)
+ input_report_key(input, BTN_LEFT, ibt);
+
input_sync(input);
return 0;
@@ -649,6 +701,8 @@ static int bcm5974_probe(struct usb_interface *iface,
input_dev->name = "bcm5974";
input_dev->phys = dev->phys;
usb_to_input_id(dev->udev, &input_dev->id);
+ /* report driver capabilities via the version field */
+ input_dev->id.version = cfg->caps;
input_dev->dev.parent = &iface->dev;
input_set_drvdata(input_dev, dev);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 6ab0eb1ada1c..4bc78892ba91 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1,7 +1,7 @@
/*
- * Elantech Touchpad driver (v5)
+ * Elantech Touchpad driver (v6)
*
- * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -178,6 +178,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
struct elantech_data *etd = psmouse->private;
unsigned char *packet = psmouse->packet;
int fingers;
+ static int old_fingers;
if (etd->fw_version_maj == 0x01) {
/* byte 0: D U p1 p2 1 p3 R L
@@ -190,6 +191,14 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
fingers = (packet[0] & 0xc0) >> 6;
}
+ if (etd->jumpy_cursor) {
+ /* Discard packets that are likely to have bogus coordinates */
+ if (fingers > old_fingers) {
+ elantech_debug("elantech.c: discarding packet\n");
+ goto discard_packet_v1;
+ }
+ }
+
input_report_key(dev, BTN_TOUCH, fingers != 0);
/* byte 2: x7 x6 x5 x4 x3 x2 x1 x0
@@ -216,6 +225,9 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
}
input_sync(dev);
+
+ discard_packet_v1:
+ old_fingers = fingers;
}
/*
@@ -363,9 +375,14 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
rc = -1;
break;
}
+ }
+
+ if (rc == 0) {
/*
- * Read back reg 0x10. The touchpad is probably initalising
- * and not ready until we read back the value we just wrote.
+ * Read back reg 0x10. For hardware version 1 we must make
+ * sure the absolute mode bit is set. For hardware version 2
+ * the touchpad is probably initalising and not ready until
+ * we read back the value we just wrote.
*/
do {
rc = elantech_read_reg(psmouse, 0x10, &val);
@@ -373,12 +390,18 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
break;
tries--;
elantech_debug("elantech.c: retrying read (%d).\n",
- tries);
+ tries);
msleep(ETP_READ_BACK_DELAY);
} while (tries > 0);
- if (rc)
+
+ if (rc) {
pr_err("elantech.c: failed to read back register 0x10.\n");
- break;
+ } else if (etd->hw_version == 1 &&
+ !(val & ETP_R10_ABSOLUTE_MODE)) {
+ pr_err("elantech.c: touchpad refuses "
+ "to switch to absolute mode.\n");
+ rc = -1;
+ }
}
if (rc)
@@ -662,6 +685,17 @@ int elantech_init(struct psmouse *psmouse)
param[0], param[1], param[2]);
etd->capabilities = param[0];
+ /*
+ * This firmware seems to suffer from misreporting coordinates when
+ * a touch action starts causing the mouse cursor or scrolled page
+ * to jump. Enable a workaround.
+ */
+ if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x22) {
+ pr_info("elantech.c: firmware version 2.34 detected, "
+ "enabling jumpy cursor workaround\n");
+ etd->jumpy_cursor = 1;
+ }
+
if (elantech_set_absolute_mode(psmouse)) {
pr_err("elantech.c: failed to put touchpad into absolute mode.\n");
goto init_fail;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index bee282b540bc..ed848cc80814 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -1,7 +1,7 @@
/*
- * Elantech Touchpad driver (v5)
+ * Elantech Touchpad driver (v6)
*
- * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -104,6 +104,7 @@ struct elantech_data {
unsigned char fw_version_min;
unsigned char hw_version;
unsigned char paritycheck;
+ unsigned char jumpy_cursor;
unsigned char parity[256];
};
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index df81b0aaa9f8..15ac3205ac05 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -61,6 +61,12 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
},
},
{
+ .ident = "Lifebook B-2130",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"),
+ },
+ },
+ {
.ident = "Lifebook B213x/B2150",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
index d196abfb68bc..5f278176eb9b 100644
--- a/drivers/input/mouse/maplemouse.c
+++ b/drivers/input/mouse/maplemouse.c
@@ -2,8 +2,8 @@
* SEGA Dreamcast mouse driver
* Based on drivers/usb/usbmouse.c
*
- * Copyright Yaegashi Takeshi, 2001
- * Adrian McMenamin, 2008
+ * Copyright (c) Yaegashi Takeshi, 2001
+ * Copyright (c) Adrian McMenamin, 2008 - 2009
*/
#include <linux/kernel.h>
@@ -29,7 +29,7 @@ static void dc_mouse_callback(struct mapleq *mq)
struct maple_device *mapledev = mq->dev;
struct dc_mouse *mse = maple_get_drvdata(mapledev);
struct input_dev *dev = mse->dev;
- unsigned char *res = mq->recvbuf;
+ unsigned char *res = mq->recvbuf->buf;
buttons = ~res[8];
relx = *(unsigned short *)(res + 12) - 512;
@@ -47,7 +47,7 @@ static void dc_mouse_callback(struct mapleq *mq)
static int dc_mouse_open(struct input_dev *dev)
{
- struct dc_mouse *mse = dev->dev.platform_data;
+ struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
MAPLE_FUNC_MOUSE);
@@ -57,29 +57,33 @@ static int dc_mouse_open(struct input_dev *dev)
static void dc_mouse_close(struct input_dev *dev)
{
- struct dc_mouse *mse = dev->dev.platform_data;
+ struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
MAPLE_FUNC_MOUSE);
}
-
+/* allow the mouse to be used */
static int __devinit probe_maple_mouse(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ int error;
struct input_dev *input_dev;
struct dc_mouse *mse;
- int error;
mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
- input_dev = input_allocate_device();
-
- if (!mse || !input_dev) {
+ if (!mse) {
error = -ENOMEM;
goto fail;
}
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto fail_nomem;
+ }
+
mse->dev = input_dev;
mse->mdev = mdev;
@@ -89,25 +93,24 @@ static int __devinit probe_maple_mouse(struct device *dev)
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
BIT_MASK(REL_WHEEL);
- input_dev->name = mdev->product_name;
- input_dev->id.bustype = BUS_HOST;
input_dev->open = dc_mouse_open;
input_dev->close = dc_mouse_close;
+ input_dev->name = mdev->product_name;
+ input_dev->id.bustype = BUS_HOST;
+ error = input_register_device(input_dev);
+ if (error)
+ goto fail_register;
mdev->driver = mdrv;
maple_set_drvdata(mdev, mse);
- error = input_register_device(input_dev);
- if (error)
- goto fail;
-
- return 0;
+ return error;
-fail:
+fail_register:
input_free_device(input_dev);
- maple_set_drvdata(mdev, NULL);
+fail_nomem:
kfree(mse);
- mdev->driver = NULL;
+fail:
return error;
}
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index da3c3a5d2689..c4b3fbd1a80f 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -192,7 +192,7 @@ config SERIO_RAW
config SERIO_XILINX_XPS_PS2
tristate "Xilinx XPS PS/2 Controller Support"
- depends on PPC
+ depends on PPC || MICROBLAZE
help
This driver supports XPS PS/2 IP from the Xilinx EDK on
PowerPC platform.
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index e29cdc13a199..a28c06d686e1 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -107,7 +107,7 @@ static void amba_kmi_close(struct serio *io)
clk_disable(kmi->clk);
}
-static int amba_kmi_probe(struct amba_device *dev, void *id)
+static int amba_kmi_probe(struct amba_device *dev, struct amba_id *id)
{
struct amba_kmi_port *kmi;
struct serio *io;
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 67248c31e19a..be5bbbb8ae4e 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -210,7 +210,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
timeout = wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD1), timeout);
- if (ps2dev->cmdcnt && timeout > 0) {
+ if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) {
timeout = ps2_adjust_timeout(ps2dev, command, timeout);
wait_event_timeout(ps2dev->wait,
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index 57953c0eb82f..f412c69478a8 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -77,7 +77,7 @@ static irqreturn_t ps2_txint(int irq, void *dev_id)
spin_lock(&ps2if->lock);
status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
if (ps2if->head == ps2if->tail) {
- disable_irq(irq);
+ disable_irq_nosync(irq);
/* done */
} else if (status & PS2STAT_TXE) {
sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA);
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index 677680e9f54f..9710bfd49cf9 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -11,7 +11,7 @@
* Copyright (c) 2000 Daniel Egger <egger@suse.de>
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
- * Copyright (c) 2002-2008 Ping Cheng <pingc@wacom.com>
+ * Copyright (c) 2002-2009 Ping Cheng <pingc@wacom.com>
*
* ChangeLog:
* v0.1 (vp) - Initial release
@@ -67,6 +67,7 @@
* v1.47 (pc) - Added support for Bamboo
* v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX
* v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A)
+ * v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28
*/
/*
@@ -87,7 +88,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.49"
+#define DRIVER_VERSION "v1.50"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
#define DRIVER_LICENSE "GPL"
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 484496daa0f3..b8624f27abf9 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -289,6 +289,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
5000); /* 5 secs */
} while (result < 0 && limit++ < 5);
+ /* No need to parse the Descriptor. It isn't an error though */
if (result < 0)
goto out;
@@ -368,9 +369,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
}
}
- result = 0;
-
out:
+ result = 0;
kfree(report);
return result;
}
@@ -425,6 +425,15 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
endpoint = &intf->cur_altsetting->endpoint[0].desc;
+ /* Initialize touch_x_max and touch_y_max in case it is not defined */
+ if (wacom_wac->features->type == TABLETPC) {
+ features->touch_x_max = 1023;
+ features->touch_y_max = 1023;
+ } else {
+ features->touch_x_max = 0;
+ features->touch_y_max = 0;
+ }
+
/* TabletPC need to retrieve the physical and logical maximum from report descriptor */
if (wacom_wac->features->type == TABLETPC) {
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 4ab070246892..948e167557f1 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -200,8 +200,9 @@ static int tsc2007_read_values(struct tsc2007 *tsc)
static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
{
struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
+ unsigned long flags;
- spin_lock_irq(&ts->lock);
+ spin_lock_irqsave(&ts->lock, flags);
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input;
@@ -222,7 +223,7 @@ static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
tsc2007_send_event(ts);
}
- spin_unlock_irq(&ts->lock);
+ spin_unlock_irqrestore(&ts->lock, flags);
return HRTIMER_NORESTART;
}
@@ -235,7 +236,7 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
spin_lock_irqsave(&ts->lock, flags);
if (likely(ts->get_pendown_state())) {
- disable_irq(ts->irq);
+ disable_irq_nosync(ts->irq);
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
HRTIMER_MODE_REL);
}
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index e868264fe799..6954f5500108 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -256,7 +256,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
struct ucb1400_ts *ucb = devid;
if (irqnr == ucb->irq) {
- disable_irq(ucb->irq);
+ disable_irq_nosync(ucb->irq);
ucb->irq_pending = 1;
wake_up(&ucb->ts_wait);
return IRQ_HANDLED;
@@ -419,7 +419,7 @@ static int ucb1400_ts_remove(struct platform_device *dev)
#ifdef CONFIG_PM
static int ucb1400_ts_resume(struct platform_device *dev)
{
- struct ucb1400_ts *ucb = platform_get_drvdata(dev);
+ struct ucb1400_ts *ucb = dev->dev.platform_data;
if (ucb->ts_task) {
/*
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
index b129409925af..bff72d81f263 100644
--- a/drivers/isdn/capi/capifs.c
+++ b/drivers/isdn/capi/capifs.c
@@ -75,15 +75,17 @@ static int capifs_remount(struct super_block *s, int *flags, char *data)
}
}
- kfree(s->s_options);
- s->s_options = new_opt;
+ mutex_lock(&s->s_root->d_inode->i_mutex);
+ replace_mount_options(s, new_opt);
config.setuid = setuid;
config.setgid = setgid;
config.uid = uid;
config.gid = gid;
config.mode = mode;
+ mutex_unlock(&s->s_root->d_inode->i_mutex);
+
return 0;
}
@@ -154,13 +156,16 @@ void capifs_new_ncci(unsigned int number, dev_t device)
if (!inode)
return;
inode->i_ino = number+2;
+
+ dentry = get_node(number);
+
+ /* config contents is protected by root's i_mutex */
inode->i_uid = config.setuid ? config.uid : current_fsuid();
inode->i_gid = config.setgid ? config.gid : current_fsgid();
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device);
//inode->i_op = &capifs_file_inode_operations;
- dentry = get_node(number);
if (!IS_ERR(dentry) && !dentry->d_inode)
d_instantiate(dentry, inode);
mutex_unlock(&capifs_root->d_inode->i_mutex);
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
index 5360c4fd4739..f33170368cd1 100644
--- a/drivers/isdn/capi/kcapi.c
+++ b/drivers/isdn/capi/kcapi.c
@@ -270,6 +270,15 @@ static void recv_handler(struct work_struct *work)
mutex_unlock(&ap->recv_mtx);
}
+/**
+ * capi_ctr_handle_message() - handle incoming CAPI message
+ * @card: controller descriptor structure.
+ * @appl: application ID.
+ * @skb: message.
+ *
+ * Called by hardware driver to pass a CAPI message to the application.
+ */
+
void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
{
struct capi20_appl *ap;
@@ -348,6 +357,13 @@ error:
EXPORT_SYMBOL(capi_ctr_handle_message);
+/**
+ * capi_ctr_ready() - signal CAPI controller ready
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to signal that the controller is up and running.
+ */
+
void capi_ctr_ready(struct capi_ctr * card)
{
card->cardstate = CARD_RUNNING;
@@ -360,6 +376,14 @@ void capi_ctr_ready(struct capi_ctr * card)
EXPORT_SYMBOL(capi_ctr_ready);
+/**
+ * capi_ctr_reseted() - signal CAPI controller reset
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to signal that the controller is down and
+ * unavailable for use.
+ */
+
void capi_ctr_reseted(struct capi_ctr * card)
{
u16 appl;
@@ -391,6 +415,13 @@ void capi_ctr_reseted(struct capi_ctr * card)
EXPORT_SYMBOL(capi_ctr_reseted);
+/**
+ * capi_ctr_suspend_output() - suspend controller
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to stop data flow.
+ */
+
void capi_ctr_suspend_output(struct capi_ctr *card)
{
if (!card->blocked) {
@@ -401,6 +432,13 @@ void capi_ctr_suspend_output(struct capi_ctr *card)
EXPORT_SYMBOL(capi_ctr_suspend_output);
+/**
+ * capi_ctr_resume_output() - resume controller
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to resume data flow.
+ */
+
void capi_ctr_resume_output(struct capi_ctr *card)
{
if (card->blocked) {
@@ -413,6 +451,14 @@ EXPORT_SYMBOL(capi_ctr_resume_output);
/* ------------------------------------------------------------- */
+/**
+ * attach_capi_ctr() - register CAPI controller
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to register a controller with the CAPI subsystem.
+ * Return value: 0 on success, error code < 0 on error
+ */
+
int
attach_capi_ctr(struct capi_ctr *card)
{
@@ -459,6 +505,15 @@ attach_capi_ctr(struct capi_ctr *card)
EXPORT_SYMBOL(attach_capi_ctr);
+/**
+ * detach_capi_ctr() - unregister CAPI controller
+ * @card: controller descriptor structure.
+ *
+ * Called by hardware driver to remove the registration of a controller
+ * with the CAPI subsystem.
+ * Return value: 0 on success, error code < 0 on error
+ */
+
int detach_capi_ctr(struct capi_ctr *card)
{
if (card->cardstate != CARD_DETECTED)
@@ -479,6 +534,13 @@ int detach_capi_ctr(struct capi_ctr *card)
EXPORT_SYMBOL(detach_capi_ctr);
+/**
+ * register_capi_driver() - register CAPI driver
+ * @driver: driver descriptor structure.
+ *
+ * Called by hardware driver to register itself with the CAPI subsystem.
+ */
+
void register_capi_driver(struct capi_driver *driver)
{
unsigned long flags;
@@ -490,6 +552,13 @@ void register_capi_driver(struct capi_driver *driver)
EXPORT_SYMBOL(register_capi_driver);
+/**
+ * unregister_capi_driver() - unregister CAPI driver
+ * @driver: driver descriptor structure.
+ *
+ * Called by hardware driver to unregister itself from the CAPI subsystem.
+ */
+
void unregister_capi_driver(struct capi_driver *driver)
{
unsigned long flags;
@@ -505,6 +574,13 @@ EXPORT_SYMBOL(unregister_capi_driver);
/* -------- CAPI2.0 Interface ---------------------------------- */
/* ------------------------------------------------------------- */
+/**
+ * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED
+ *
+ * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller
+ * is ready for use, CAPI_REGNOTINSTALLED otherwise)
+ */
+
u16 capi20_isinstalled(void)
{
int i;
@@ -517,6 +593,18 @@ u16 capi20_isinstalled(void)
EXPORT_SYMBOL(capi20_isinstalled);
+/**
+ * capi20_register() - CAPI 2.0 operation CAPI_REGISTER
+ * @ap: CAPI application descriptor structure.
+ *
+ * Register an application's presence with CAPI.
+ * A unique application ID is assigned and stored in @ap->applid.
+ * After this function returns successfully, the message receive
+ * callback function @ap->recv_message() may be called at any time
+ * until capi20_release() has been called for the same @ap.
+ * Return value: CAPI result code
+ */
+
u16 capi20_register(struct capi20_appl *ap)
{
int i;
@@ -571,6 +659,16 @@ u16 capi20_register(struct capi20_appl *ap)
EXPORT_SYMBOL(capi20_register);
+/**
+ * capi20_release() - CAPI 2.0 operation CAPI_RELEASE
+ * @ap: CAPI application descriptor structure.
+ *
+ * Terminate an application's registration with CAPI.
+ * After this function returns successfully, the message receive
+ * callback function @ap->recv_message() will no longer be called.
+ * Return value: CAPI result code
+ */
+
u16 capi20_release(struct capi20_appl *ap)
{
int i;
@@ -603,6 +701,15 @@ u16 capi20_release(struct capi20_appl *ap)
EXPORT_SYMBOL(capi20_release);
+/**
+ * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE
+ * @ap: CAPI application descriptor structure.
+ * @skb: CAPI message.
+ *
+ * Transfer a single message to CAPI.
+ * Return value: CAPI result code
+ */
+
u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
{
struct capi_ctr *card;
@@ -668,6 +775,16 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
EXPORT_SYMBOL(capi20_put_message);
+/**
+ * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER
+ * @contr: controller number.
+ * @buf: result buffer (64 bytes).
+ *
+ * Retrieve information about the manufacturer of the specified ISDN controller
+ * or (for @contr == 0) the driver itself.
+ * Return value: CAPI result code
+ */
+
u16 capi20_get_manufacturer(u32 contr, u8 *buf)
{
struct capi_ctr *card;
@@ -685,6 +802,16 @@ u16 capi20_get_manufacturer(u32 contr, u8 *buf)
EXPORT_SYMBOL(capi20_get_manufacturer);
+/**
+ * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION
+ * @contr: controller number.
+ * @verp: result structure.
+ *
+ * Retrieve version information for the specified ISDN controller
+ * or (for @contr == 0) the driver itself.
+ * Return value: CAPI result code
+ */
+
u16 capi20_get_version(u32 contr, struct capi_version *verp)
{
struct capi_ctr *card;
@@ -703,6 +830,16 @@ u16 capi20_get_version(u32 contr, struct capi_version *verp)
EXPORT_SYMBOL(capi20_get_version);
+/**
+ * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER
+ * @contr: controller number.
+ * @serial: result buffer (8 bytes).
+ *
+ * Retrieve the serial number of the specified ISDN controller
+ * or (for @contr == 0) the driver itself.
+ * Return value: CAPI result code
+ */
+
u16 capi20_get_serial(u32 contr, u8 *serial)
{
struct capi_ctr *card;
@@ -721,6 +858,16 @@ u16 capi20_get_serial(u32 contr, u8 *serial)
EXPORT_SYMBOL(capi20_get_serial);
+/**
+ * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE
+ * @contr: controller number.
+ * @profp: result structure.
+ *
+ * Retrieve capability information for the specified ISDN controller
+ * or (for @contr == 0) the number of installed controllers.
+ * Return value: CAPI result code
+ */
+
u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
{
struct capi_ctr *card;
@@ -903,6 +1050,15 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
}
#endif
+/**
+ * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER
+ * @cmd: command.
+ * @data: parameter.
+ *
+ * Perform manufacturer specific command.
+ * Return value: CAPI result code
+ */
+
int capi20_manufacturer(unsigned int cmd, void __user *data)
{
struct capi_ctr *card;
@@ -981,6 +1137,21 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
EXPORT_SYMBOL(capi20_manufacturer);
/* temporary hack */
+
+/**
+ * capi20_set_callback() - set CAPI application notification callback function
+ * @ap: CAPI application descriptor structure.
+ * @callback: callback function (NULL to remove).
+ *
+ * If not NULL, the callback function will be called to notify the
+ * application of the addition or removal of a controller.
+ * The first argument (cmd) will tell whether the controller was added
+ * (KCI_CONTRUP) or removed (KCI_CONTRDOWN).
+ * The second argument (contr) will be the controller number.
+ * For cmd==KCI_CONTRUP the third argument (data) will be a pointer to the
+ * new controller's capability profile structure.
+ */
+
void capi20_set_callback(struct capi20_appl *ap,
void (*callback) (unsigned int cmd, __u32 contr, void *data))
{
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index b171e75cb52e..29808c4fb1cb 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -175,7 +175,7 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
return -EINVAL;
}
src = iwb->read;
- if (unlikely(limit > BAS_OUTBUFSIZE + BAS_OUTBUFPAD ||
+ if (unlikely(limit >= BAS_OUTBUFSIZE + BAS_OUTBUFPAD ||
(read < src && limit >= src))) {
pr_err("isoc write buffer frame reservation violated\n");
return -EFAULT;
diff --git a/drivers/lguest/Kconfig b/drivers/lguest/Kconfig
index a3d3cbab359a..0aaa0597a622 100644
--- a/drivers/lguest/Kconfig
+++ b/drivers/lguest/Kconfig
@@ -1,6 +1,6 @@
config LGUEST
tristate "Linux hypervisor example code"
- depends on X86_32 && EXPERIMENTAL && !X86_PAE && FUTEX
+ depends on X86_32 && EXPERIMENTAL && EVENTFD
select HVC_DRIVER
---help---
This is a very simple module which allows you to run
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c
index 4845fb3cf74b..a6974e9b8ebf 100644
--- a/drivers/lguest/core.c
+++ b/drivers/lguest/core.c
@@ -95,7 +95,7 @@ static __init int map_switcher(void)
* array of struct pages. It increments that pointer, but we don't
* care. */
pagep = switcher_page;
- err = map_vm_area(switcher_vma, PAGE_KERNEL, &pagep);
+ err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, &pagep);
if (err) {
printk("lguest: map_vm_area failed: %i\n", err);
goto free_vma;
@@ -188,6 +188,9 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
{
/* We stop running once the Guest is dead. */
while (!cpu->lg->dead) {
+ unsigned int irq;
+ bool more;
+
/* First we run any hypercalls the Guest wants done. */
if (cpu->hcall)
do_hypercalls(cpu);
@@ -195,23 +198,23 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
/* It's possible the Guest did a NOTIFY hypercall to the
* Launcher, in which case we return from the read() now. */
if (cpu->pending_notify) {
- if (put_user(cpu->pending_notify, user))
- return -EFAULT;
- return sizeof(cpu->pending_notify);
+ if (!send_notify_to_eventfd(cpu)) {
+ if (put_user(cpu->pending_notify, user))
+ return -EFAULT;
+ return sizeof(cpu->pending_notify);
+ }
}
/* Check for signals */
if (signal_pending(current))
return -ERESTARTSYS;
- /* If Waker set break_out, return to Launcher. */
- if (cpu->break_out)
- return -EAGAIN;
-
/* Check if there are any interrupts which can be delivered now:
* if so, this sets up the hander to be executed when we next
* run the Guest. */
- maybe_do_interrupt(cpu);
+ irq = interrupt_pending(cpu, &more);
+ if (irq < LGUEST_IRQS)
+ try_deliver_interrupt(cpu, irq, more);
/* All long-lived kernel loops need to check with this horrible
* thing called the freezer. If the Host is trying to suspend,
@@ -224,10 +227,15 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
break;
/* If the Guest asked to be stopped, we sleep. The Guest's
- * clock timer or LHREQ_BREAK from the Waker will wake us. */
+ * clock timer will wake us. */
if (cpu->halted) {
set_current_state(TASK_INTERRUPTIBLE);
- schedule();
+ /* Just before we sleep, make sure no interrupt snuck in
+ * which we should be doing. */
+ if (interrupt_pending(cpu, &more) < LGUEST_IRQS)
+ set_current_state(TASK_RUNNING);
+ else
+ schedule();
continue;
}
diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c
index 54d66f05fefa..c29ffa19cb74 100644
--- a/drivers/lguest/hypercalls.c
+++ b/drivers/lguest/hypercalls.c
@@ -37,6 +37,10 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
/* This call does nothing, except by breaking out of the Guest
* it makes us process all the asynchronous hypercalls. */
break;
+ case LHCALL_SEND_INTERRUPTS:
+ /* This call does nothing too, but by breaking out of the Guest
+ * it makes us process any pending interrupts. */
+ break;
case LHCALL_LGUEST_INIT:
/* You can't get here unless you're already initialized. Don't
* do that. */
@@ -73,11 +77,21 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
guest_set_stack(cpu, args->arg1, args->arg2, args->arg3);
break;
case LHCALL_SET_PTE:
+#ifdef CONFIG_X86_PAE
+ guest_set_pte(cpu, args->arg1, args->arg2,
+ __pte(args->arg3 | (u64)args->arg4 << 32));
+#else
guest_set_pte(cpu, args->arg1, args->arg2, __pte(args->arg3));
+#endif
+ break;
+ case LHCALL_SET_PGD:
+ guest_set_pgd(cpu->lg, args->arg1, args->arg2);
break;
+#ifdef CONFIG_X86_PAE
case LHCALL_SET_PMD:
guest_set_pmd(cpu->lg, args->arg1, args->arg2);
break;
+#endif
case LHCALL_SET_CLOCKEVENT:
guest_set_clockevent(cpu, args->arg1);
break;
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c
index 6e99adbe1946..0e9067b0d507 100644
--- a/drivers/lguest/interrupts_and_traps.c
+++ b/drivers/lguest/interrupts_and_traps.c
@@ -128,30 +128,39 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
/*H:205
* Virtual Interrupts.
*
- * maybe_do_interrupt() gets called before every entry to the Guest, to see if
- * we should divert the Guest to running an interrupt handler. */
-void maybe_do_interrupt(struct lg_cpu *cpu)
+ * interrupt_pending() returns the first pending interrupt which isn't blocked
+ * by the Guest. It is called before every entry to the Guest, and just before
+ * we go to sleep when the Guest has halted itself. */
+unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more)
{
unsigned int irq;
DECLARE_BITMAP(blk, LGUEST_IRQS);
- struct desc_struct *idt;
/* If the Guest hasn't even initialized yet, we can do nothing. */
if (!cpu->lg->lguest_data)
- return;
+ return LGUEST_IRQS;
/* Take our "irqs_pending" array and remove any interrupts the Guest
* wants blocked: the result ends up in "blk". */
if (copy_from_user(&blk, cpu->lg->lguest_data->blocked_interrupts,
sizeof(blk)))
- return;
+ return LGUEST_IRQS;
bitmap_andnot(blk, cpu->irqs_pending, blk, LGUEST_IRQS);
/* Find the first interrupt. */
irq = find_first_bit(blk, LGUEST_IRQS);
- /* None? Nothing to do */
- if (irq >= LGUEST_IRQS)
- return;
+ *more = find_next_bit(blk, LGUEST_IRQS, irq+1);
+
+ return irq;
+}
+
+/* This actually diverts the Guest to running an interrupt handler, once an
+ * interrupt has been identified by interrupt_pending(). */
+void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
+{
+ struct desc_struct *idt;
+
+ BUG_ON(irq >= LGUEST_IRQS);
/* They may be in the middle of an iret, where they asked us never to
* deliver interrupts. */
@@ -170,8 +179,12 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
u32 irq_enabled;
if (get_user(irq_enabled, &cpu->lg->lguest_data->irq_enabled))
irq_enabled = 0;
- if (!irq_enabled)
+ if (!irq_enabled) {
+ /* Make sure they know an IRQ is pending. */
+ put_user(X86_EFLAGS_IF,
+ &cpu->lg->lguest_data->irq_pending);
return;
+ }
}
/* Look at the IDT entry the Guest gave us for this interrupt. The
@@ -194,6 +207,25 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
* here is a compromise which means at least it gets updated every
* timer interrupt. */
write_timestamp(cpu);
+
+ /* If there are no other interrupts we want to deliver, clear
+ * the pending flag. */
+ if (!more)
+ put_user(0, &cpu->lg->lguest_data->irq_pending);
+}
+
+/* And this is the routine when we want to set an interrupt for the Guest. */
+void set_interrupt(struct lg_cpu *cpu, unsigned int irq)
+{
+ /* Next time the Guest runs, the core code will see if it can deliver
+ * this interrupt. */
+ set_bit(irq, cpu->irqs_pending);
+
+ /* Make sure it sees it; it might be asleep (eg. halted), or
+ * running the Guest right now, in which case kick_process()
+ * will knock it out. */
+ if (!wake_up_process(cpu->tsk))
+ kick_process(cpu->tsk);
}
/*:*/
@@ -510,10 +542,7 @@ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
struct lg_cpu *cpu = container_of(timer, struct lg_cpu, hrt);
/* Remember the first interrupt is the timer interrupt. */
- set_bit(0, cpu->irqs_pending);
- /* If the Guest is actually stopped, we need to wake it up. */
- if (cpu->halted)
- wake_up_process(cpu->tsk);
+ set_interrupt(cpu, 0);
return HRTIMER_NORESTART;
}
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index af92a176697f..d4e8979735cb 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -49,7 +49,7 @@ struct lg_cpu {
u32 cr2;
int ts;
u32 esp1;
- u8 ss1;
+ u16 ss1;
/* Bitmap of what has changed: see CHANGED_* above. */
int changed;
@@ -71,9 +71,7 @@ struct lg_cpu {
/* Virtual clock device */
struct hrtimer hrt;
- /* Do we need to stop what we're doing and return to userspace? */
- int break_out;
- wait_queue_head_t break_wq;
+ /* Did the Guest tell us to halt? */
int halted;
/* Pending virtual interrupts */
@@ -82,6 +80,16 @@ struct lg_cpu {
struct lg_cpu_arch arch;
};
+struct lg_eventfd {
+ unsigned long addr;
+ struct file *event;
+};
+
+struct lg_eventfd_map {
+ unsigned int num;
+ struct lg_eventfd map[];
+};
+
/* The private info the thread maintains about the guest. */
struct lguest
{
@@ -102,6 +110,8 @@ struct lguest
unsigned int stack_pages;
u32 tsc_khz;
+ struct lg_eventfd_map *eventfds;
+
/* Dead? */
const char *dead;
};
@@ -137,9 +147,13 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user);
* in the kernel. */
#define pgd_flags(x) (pgd_val(x) & ~PAGE_MASK)
#define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT)
+#define pmd_flags(x) (pmd_val(x) & ~PAGE_MASK)
+#define pmd_pfn(x) (pmd_val(x) >> PAGE_SHIFT)
/* interrupts_and_traps.c: */
-void maybe_do_interrupt(struct lg_cpu *cpu);
+unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more);
+void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more);
+void set_interrupt(struct lg_cpu *cpu, unsigned int irq);
bool deliver_trap(struct lg_cpu *cpu, unsigned int num);
void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int i,
u32 low, u32 hi);
@@ -150,6 +164,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state,
void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt,
const unsigned long *def);
void guest_set_clockevent(struct lg_cpu *cpu, unsigned long delta);
+bool send_notify_to_eventfd(struct lg_cpu *cpu);
void init_clockdev(struct lg_cpu *cpu);
bool check_syscall_vector(struct lguest *lg);
int init_interrupts(void);
@@ -168,7 +183,10 @@ void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt);
int init_guest_pagetable(struct lguest *lg);
void free_guest_pagetable(struct lguest *lg);
void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable);
+void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 i);
+#ifdef CONFIG_X86_PAE
void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 i);
+#endif
void guest_pagetable_clear_all(struct lg_cpu *cpu);
void guest_pagetable_flush_user(struct lg_cpu *cpu);
void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir,
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index df44d962626d..e082cdac88b4 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -228,7 +228,8 @@ extern void lguest_setup_irq(unsigned int irq);
* function. */
static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
unsigned index,
- void (*callback)(struct virtqueue *vq))
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
{
struct lguest_device *ldev = to_lgdev(vdev);
struct lguest_vq_info *lvq;
@@ -263,7 +264,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* OK, tell virtio_ring.c to set up a virtqueue now we know its size
* and we've got a pointer to its pages. */
vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN,
- vdev, lvq->pages, lg_notify, callback);
+ vdev, lvq->pages, lg_notify, callback, name);
if (!vq) {
err = -ENOMEM;
goto unmap;
@@ -312,6 +313,38 @@ static void lg_del_vq(struct virtqueue *vq)
kfree(lvq);
}
+static void lg_del_vqs(struct virtio_device *vdev)
+{
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ lg_del_vq(vq);
+}
+
+static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct lguest_device *ldev = to_lgdev(vdev);
+ int i;
+
+ /* We must have this many virtqueues. */
+ if (nvqs > ldev->desc->num_vq)
+ return -ENOENT;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = lg_find_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i]))
+ goto error;
+ }
+ return 0;
+
+error:
+ lg_del_vqs(vdev);
+ return PTR_ERR(vqs[i]);
+}
+
/* The ops structure which hooks everything together. */
static struct virtio_config_ops lguest_config_ops = {
.get_features = lg_get_features,
@@ -321,8 +354,8 @@ static struct virtio_config_ops lguest_config_ops = {
.get_status = lg_get_status,
.set_status = lg_set_status,
.reset = lg_reset,
- .find_vq = lg_find_vq,
- .del_vq = lg_del_vq,
+ .find_vqs = lg_find_vqs,
+ .del_vqs = lg_del_vqs,
};
/* The root device for the lguest virtio devices. This makes them appear as
diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c
index b8ee103eed5f..32e297121058 100644
--- a/drivers/lguest/lguest_user.c
+++ b/drivers/lguest/lguest_user.c
@@ -7,32 +7,83 @@
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/sched.h>
+#include <linux/eventfd.h>
+#include <linux/file.h>
#include "lg.h"
-/*L:055 When something happens, the Waker process needs a way to stop the
- * kernel running the Guest and return to the Launcher. So the Waker writes
- * LHREQ_BREAK and the value "1" to /dev/lguest to do this. Once the Launcher
- * has done whatever needs attention, it writes LHREQ_BREAK and "0" to release
- * the Waker. */
-static int break_guest_out(struct lg_cpu *cpu, const unsigned long __user*input)
+bool send_notify_to_eventfd(struct lg_cpu *cpu)
{
- unsigned long on;
+ unsigned int i;
+ struct lg_eventfd_map *map;
+
+ /* lg->eventfds is RCU-protected */
+ rcu_read_lock();
+ map = rcu_dereference(cpu->lg->eventfds);
+ for (i = 0; i < map->num; i++) {
+ if (map->map[i].addr == cpu->pending_notify) {
+ eventfd_signal(map->map[i].event, 1);
+ cpu->pending_notify = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return cpu->pending_notify == 0;
+}
- /* Fetch whether they're turning break on or off. */
- if (get_user(on, input) != 0)
- return -EFAULT;
+static int add_eventfd(struct lguest *lg, unsigned long addr, int fd)
+{
+ struct lg_eventfd_map *new, *old = lg->eventfds;
- if (on) {
- cpu->break_out = 1;
- /* Pop it out of the Guest (may be running on different CPU) */
- wake_up_process(cpu->tsk);
- /* Wait for them to reset it */
- return wait_event_interruptible(cpu->break_wq, !cpu->break_out);
- } else {
- cpu->break_out = 0;
- wake_up(&cpu->break_wq);
- return 0;
+ if (!addr)
+ return -EINVAL;
+
+ /* Replace the old array with the new one, carefully: others can
+ * be accessing it at the same time */
+ new = kmalloc(sizeof(*new) + sizeof(new->map[0]) * (old->num + 1),
+ GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ /* First make identical copy. */
+ memcpy(new->map, old->map, sizeof(old->map[0]) * old->num);
+ new->num = old->num;
+
+ /* Now append new entry. */
+ new->map[new->num].addr = addr;
+ new->map[new->num].event = eventfd_fget(fd);
+ if (IS_ERR(new->map[new->num].event)) {
+ kfree(new);
+ return PTR_ERR(new->map[new->num].event);
}
+ new->num++;
+
+ /* Now put new one in place. */
+ rcu_assign_pointer(lg->eventfds, new);
+
+ /* We're not in a big hurry. Wait until noone's looking at old
+ * version, then delete it. */
+ synchronize_rcu();
+ kfree(old);
+
+ return 0;
+}
+
+static int attach_eventfd(struct lguest *lg, const unsigned long __user *input)
+{
+ unsigned long addr, fd;
+ int err;
+
+ if (get_user(addr, input) != 0)
+ return -EFAULT;
+ input++;
+ if (get_user(fd, input) != 0)
+ return -EFAULT;
+
+ mutex_lock(&lguest_lock);
+ err = add_eventfd(lg, addr, fd);
+ mutex_unlock(&lguest_lock);
+
+ return 0;
}
/*L:050 Sending an interrupt is done by writing LHREQ_IRQ and an interrupt
@@ -45,9 +96,8 @@ static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input)
return -EFAULT;
if (irq >= LGUEST_IRQS)
return -EINVAL;
- /* Next time the Guest runs, the core code will see if it can deliver
- * this interrupt. */
- set_bit(irq, cpu->irqs_pending);
+
+ set_interrupt(cpu, irq);
return 0;
}
@@ -126,9 +176,6 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
* address. */
lguest_arch_setup_regs(cpu, start_ip);
- /* Initialize the queue for the Waker to wait on */
- init_waitqueue_head(&cpu->break_wq);
-
/* We keep a pointer to the Launcher task (ie. current task) for when
* other Guests want to wake this one (eg. console input). */
cpu->tsk = current;
@@ -185,6 +232,13 @@ static int initialize(struct file *file, const unsigned long __user *input)
goto unlock;
}
+ lg->eventfds = kmalloc(sizeof(*lg->eventfds), GFP_KERNEL);
+ if (!lg->eventfds) {
+ err = -ENOMEM;
+ goto free_lg;
+ }
+ lg->eventfds->num = 0;
+
/* Populate the easy fields of our "struct lguest" */
lg->mem_base = (void __user *)args[0];
lg->pfn_limit = args[1];
@@ -192,7 +246,7 @@ static int initialize(struct file *file, const unsigned long __user *input)
/* This is the first cpu (cpu 0) and it will start booting at args[2] */
err = lg_cpu_start(&lg->cpus[0], 0, args[2]);
if (err)
- goto release_guest;
+ goto free_eventfds;
/* Initialize the Guest's shadow page tables, using the toplevel
* address the Launcher gave us. This allocates memory, so can fail. */
@@ -211,7 +265,9 @@ static int initialize(struct file *file, const unsigned long __user *input)
free_regs:
/* FIXME: This should be in free_vcpu */
free_page(lg->cpus[0].regs_page);
-release_guest:
+free_eventfds:
+ kfree(lg->eventfds);
+free_lg:
kfree(lg);
unlock:
mutex_unlock(&lguest_lock);
@@ -252,11 +308,6 @@ static ssize_t write(struct file *file, const char __user *in,
/* Once the Guest is dead, you can only read() why it died. */
if (lg->dead)
return -ENOENT;
-
- /* If you're not the task which owns the Guest, all you can do
- * is break the Launcher out of running the Guest. */
- if (current != cpu->tsk && req != LHREQ_BREAK)
- return -EPERM;
}
switch (req) {
@@ -264,8 +315,8 @@ static ssize_t write(struct file *file, const char __user *in,
return initialize(file, input);
case LHREQ_IRQ:
return user_send_irq(cpu, input);
- case LHREQ_BREAK:
- return break_guest_out(cpu, input);
+ case LHREQ_EVENTFD:
+ return attach_eventfd(lg, input);
default:
return -EINVAL;
}
@@ -303,6 +354,12 @@ static int close(struct inode *inode, struct file *file)
* the Launcher's memory management structure. */
mmput(lg->cpus[i].mm);
}
+
+ /* Release any eventfds they registered. */
+ for (i = 0; i < lg->eventfds->num; i++)
+ fput(lg->eventfds->map[i].event);
+ kfree(lg->eventfds);
+
/* If lg->dead doesn't contain an error code it will be NULL or a
* kmalloc()ed string, either of which is ok to hand to kfree(). */
if (!IS_ERR(lg->dead))
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c
index a059cf9980f7..a6fe1abda240 100644
--- a/drivers/lguest/page_tables.c
+++ b/drivers/lguest/page_tables.c
@@ -53,6 +53,17 @@
* page. */
#define SWITCHER_PGD_INDEX (PTRS_PER_PGD - 1)
+/* For PAE we need the PMD index as well. We use the last 2MB, so we
+ * will need the last pmd entry of the last pmd page. */
+#ifdef CONFIG_X86_PAE
+#define SWITCHER_PMD_INDEX (PTRS_PER_PMD - 1)
+#define RESERVE_MEM 2U
+#define CHECK_GPGD_MASK _PAGE_PRESENT
+#else
+#define RESERVE_MEM 4U
+#define CHECK_GPGD_MASK _PAGE_TABLE
+#endif
+
/* We actually need a separate PTE page for each CPU. Remember that after the
* Switcher code itself comes two pages for each CPU, and we don't want this
* CPU's guest to see the pages of any other CPU. */
@@ -73,24 +84,59 @@ static pgd_t *spgd_addr(struct lg_cpu *cpu, u32 i, unsigned long vaddr)
{
unsigned int index = pgd_index(vaddr);
+#ifndef CONFIG_X86_PAE
/* We kill any Guest trying to touch the Switcher addresses. */
if (index >= SWITCHER_PGD_INDEX) {
kill_guest(cpu, "attempt to access switcher pages");
index = 0;
}
+#endif
/* Return a pointer index'th pgd entry for the i'th page table. */
return &cpu->lg->pgdirs[i].pgdir[index];
}
+#ifdef CONFIG_X86_PAE
+/* This routine then takes the PGD entry given above, which contains the
+ * address of the PMD page. It then returns a pointer to the PMD entry for the
+ * given address. */
+static pmd_t *spmd_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr)
+{
+ unsigned int index = pmd_index(vaddr);
+ pmd_t *page;
+
+ /* We kill any Guest trying to touch the Switcher addresses. */
+ if (pgd_index(vaddr) == SWITCHER_PGD_INDEX &&
+ index >= SWITCHER_PMD_INDEX) {
+ kill_guest(cpu, "attempt to access switcher pages");
+ index = 0;
+ }
+
+ /* You should never call this if the PGD entry wasn't valid */
+ BUG_ON(!(pgd_flags(spgd) & _PAGE_PRESENT));
+ page = __va(pgd_pfn(spgd) << PAGE_SHIFT);
+
+ return &page[index];
+}
+#endif
+
/* This routine then takes the page directory entry returned above, which
* contains the address of the page table entry (PTE) page. It then returns a
* pointer to the PTE entry for the given address. */
-static pte_t *spte_addr(pgd_t spgd, unsigned long vaddr)
+static pte_t *spte_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr)
{
+#ifdef CONFIG_X86_PAE
+ pmd_t *pmd = spmd_addr(cpu, spgd, vaddr);
+ pte_t *page = __va(pmd_pfn(*pmd) << PAGE_SHIFT);
+
+ /* You should never call this if the PMD entry wasn't valid */
+ BUG_ON(!(pmd_flags(*pmd) & _PAGE_PRESENT));
+#else
pte_t *page = __va(pgd_pfn(spgd) << PAGE_SHIFT);
/* You should never call this if the PGD entry wasn't valid */
BUG_ON(!(pgd_flags(spgd) & _PAGE_PRESENT));
- return &page[(vaddr >> PAGE_SHIFT) % PTRS_PER_PTE];
+#endif
+
+ return &page[pte_index(vaddr)];
}
/* These two functions just like the above two, except they access the Guest
@@ -101,12 +147,32 @@ static unsigned long gpgd_addr(struct lg_cpu *cpu, unsigned long vaddr)
return cpu->lg->pgdirs[cpu->cpu_pgd].gpgdir + index * sizeof(pgd_t);
}
-static unsigned long gpte_addr(pgd_t gpgd, unsigned long vaddr)
+#ifdef CONFIG_X86_PAE
+static unsigned long gpmd_addr(pgd_t gpgd, unsigned long vaddr)
+{
+ unsigned long gpage = pgd_pfn(gpgd) << PAGE_SHIFT;
+ BUG_ON(!(pgd_flags(gpgd) & _PAGE_PRESENT));
+ return gpage + pmd_index(vaddr) * sizeof(pmd_t);
+}
+
+static unsigned long gpte_addr(struct lg_cpu *cpu,
+ pmd_t gpmd, unsigned long vaddr)
+{
+ unsigned long gpage = pmd_pfn(gpmd) << PAGE_SHIFT;
+
+ BUG_ON(!(pmd_flags(gpmd) & _PAGE_PRESENT));
+ return gpage + pte_index(vaddr) * sizeof(pte_t);
+}
+#else
+static unsigned long gpte_addr(struct lg_cpu *cpu,
+ pgd_t gpgd, unsigned long vaddr)
{
unsigned long gpage = pgd_pfn(gpgd) << PAGE_SHIFT;
+
BUG_ON(!(pgd_flags(gpgd) & _PAGE_PRESENT));
- return gpage + ((vaddr>>PAGE_SHIFT) % PTRS_PER_PTE) * sizeof(pte_t);
+ return gpage + pte_index(vaddr) * sizeof(pte_t);
}
+#endif
/*:*/
/*M:014 get_pfn is slow: we could probably try to grab batches of pages here as
@@ -171,7 +237,7 @@ static void release_pte(pte_t pte)
/* Remember that get_user_pages_fast() took a reference to the page, in
* get_pfn()? We have to put it back now. */
if (pte_flags(pte) & _PAGE_PRESENT)
- put_page(pfn_to_page(pte_pfn(pte)));
+ put_page(pte_page(pte));
}
/*:*/
@@ -184,11 +250,20 @@ static void check_gpte(struct lg_cpu *cpu, pte_t gpte)
static void check_gpgd(struct lg_cpu *cpu, pgd_t gpgd)
{
- if ((pgd_flags(gpgd) & ~_PAGE_TABLE) ||
+ if ((pgd_flags(gpgd) & ~CHECK_GPGD_MASK) ||
(pgd_pfn(gpgd) >= cpu->lg->pfn_limit))
kill_guest(cpu, "bad page directory entry");
}
+#ifdef CONFIG_X86_PAE
+static void check_gpmd(struct lg_cpu *cpu, pmd_t gpmd)
+{
+ if ((pmd_flags(gpmd) & ~_PAGE_TABLE) ||
+ (pmd_pfn(gpmd) >= cpu->lg->pfn_limit))
+ kill_guest(cpu, "bad page middle directory entry");
+}
+#endif
+
/*H:330
* (i) Looking up a page table entry when the Guest faults.
*
@@ -207,6 +282,11 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
pte_t gpte;
pte_t *spte;
+#ifdef CONFIG_X86_PAE
+ pmd_t *spmd;
+ pmd_t gpmd;
+#endif
+
/* First step: get the top-level Guest page table entry. */
gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
/* Toplevel not present? We can't map it in. */
@@ -228,12 +308,45 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
check_gpgd(cpu, gpgd);
/* And we copy the flags to the shadow PGD entry. The page
* number in the shadow PGD is the page we just allocated. */
- *spgd = __pgd(__pa(ptepage) | pgd_flags(gpgd));
+ set_pgd(spgd, __pgd(__pa(ptepage) | pgd_flags(gpgd)));
}
+#ifdef CONFIG_X86_PAE
+ gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
+ /* middle level not present? We can't map it in. */
+ if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+ return false;
+
+ /* Now look at the matching shadow entry. */
+ spmd = spmd_addr(cpu, *spgd, vaddr);
+
+ if (!(pmd_flags(*spmd) & _PAGE_PRESENT)) {
+ /* No shadow entry: allocate a new shadow PTE page. */
+ unsigned long ptepage = get_zeroed_page(GFP_KERNEL);
+
+ /* This is not really the Guest's fault, but killing it is
+ * simple for this corner case. */
+ if (!ptepage) {
+ kill_guest(cpu, "out of memory allocating pte page");
+ return false;
+ }
+
+ /* We check that the Guest pmd is OK. */
+ check_gpmd(cpu, gpmd);
+
+ /* And we copy the flags to the shadow PMD entry. The page
+ * number in the shadow PMD is the page we just allocated. */
+ native_set_pmd(spmd, __pmd(__pa(ptepage) | pmd_flags(gpmd)));
+ }
+
+ /* OK, now we look at the lower level in the Guest page table: keep its
+ * address, because we might update it later. */
+ gpte_ptr = gpte_addr(cpu, gpmd, vaddr);
+#else
/* OK, now we look at the lower level in the Guest page table: keep its
* address, because we might update it later. */
- gpte_ptr = gpte_addr(gpgd, vaddr);
+ gpte_ptr = gpte_addr(cpu, gpgd, vaddr);
+#endif
gpte = lgread(cpu, gpte_ptr, pte_t);
/* If this page isn't in the Guest page tables, we can't page it in. */
@@ -259,7 +372,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
gpte = pte_mkdirty(gpte);
/* Get the pointer to the shadow PTE entry we're going to set. */
- spte = spte_addr(*spgd, vaddr);
+ spte = spte_addr(cpu, *spgd, vaddr);
/* If there was a valid shadow PTE entry here before, we release it.
* This can happen with a write to a previously read-only entry. */
release_pte(*spte);
@@ -273,7 +386,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
* table entry, even if the Guest says it's writable. That way
* we will come back here when a write does actually occur, so
* we can update the Guest's _PAGE_DIRTY flag. */
- *spte = gpte_to_spte(cpu, pte_wrprotect(gpte), 0);
+ native_set_pte(spte, gpte_to_spte(cpu, pte_wrprotect(gpte), 0));
/* Finally, we write the Guest PTE entry back: we've set the
* _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */
@@ -301,14 +414,23 @@ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr)
pgd_t *spgd;
unsigned long flags;
+#ifdef CONFIG_X86_PAE
+ pmd_t *spmd;
+#endif
/* Look at the current top level entry: is it present? */
spgd = spgd_addr(cpu, cpu->cpu_pgd, vaddr);
if (!(pgd_flags(*spgd) & _PAGE_PRESENT))
return false;
+#ifdef CONFIG_X86_PAE
+ spmd = spmd_addr(cpu, *spgd, vaddr);
+ if (!(pmd_flags(*spmd) & _PAGE_PRESENT))
+ return false;
+#endif
+
/* Check the flags on the pte entry itself: it must be present and
* writable. */
- flags = pte_flags(*(spte_addr(*spgd, vaddr)));
+ flags = pte_flags(*(spte_addr(cpu, *spgd, vaddr)));
return (flags & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW);
}
@@ -322,8 +444,43 @@ void pin_page(struct lg_cpu *cpu, unsigned long vaddr)
kill_guest(cpu, "bad stack page %#lx", vaddr);
}
+#ifdef CONFIG_X86_PAE
+static void release_pmd(pmd_t *spmd)
+{
+ /* If the entry's not present, there's nothing to release. */
+ if (pmd_flags(*spmd) & _PAGE_PRESENT) {
+ unsigned int i;
+ pte_t *ptepage = __va(pmd_pfn(*spmd) << PAGE_SHIFT);
+ /* For each entry in the page, we might need to release it. */
+ for (i = 0; i < PTRS_PER_PTE; i++)
+ release_pte(ptepage[i]);
+ /* Now we can free the page of PTEs */
+ free_page((long)ptepage);
+ /* And zero out the PMD entry so we never release it twice. */
+ native_set_pmd(spmd, __pmd(0));
+ }
+}
+
+static void release_pgd(pgd_t *spgd)
+{
+ /* If the entry's not present, there's nothing to release. */
+ if (pgd_flags(*spgd) & _PAGE_PRESENT) {
+ unsigned int i;
+ pmd_t *pmdpage = __va(pgd_pfn(*spgd) << PAGE_SHIFT);
+
+ for (i = 0; i < PTRS_PER_PMD; i++)
+ release_pmd(&pmdpage[i]);
+
+ /* Now we can free the page of PMDs */
+ free_page((long)pmdpage);
+ /* And zero out the PGD entry so we never release it twice. */
+ set_pgd(spgd, __pgd(0));
+ }
+}
+
+#else /* !CONFIG_X86_PAE */
/*H:450 If we chase down the release_pgd() code, it looks like this: */
-static void release_pgd(struct lguest *lg, pgd_t *spgd)
+static void release_pgd(pgd_t *spgd)
{
/* If the entry's not present, there's nothing to release. */
if (pgd_flags(*spgd) & _PAGE_PRESENT) {
@@ -341,7 +498,7 @@ static void release_pgd(struct lguest *lg, pgd_t *spgd)
*spgd = __pgd(0);
}
}
-
+#endif
/*H:445 We saw flush_user_mappings() twice: once from the flush_user_mappings()
* hypercall and once in new_pgdir() when we re-used a top-level pgdir page.
* It simply releases every PTE page from 0 up to the Guest's kernel address. */
@@ -350,7 +507,7 @@ static void flush_user_mappings(struct lguest *lg, int idx)
unsigned int i;
/* Release every pgd entry up to the kernel's address. */
for (i = 0; i < pgd_index(lg->kernel_address); i++)
- release_pgd(lg, lg->pgdirs[idx].pgdir + i);
+ release_pgd(lg->pgdirs[idx].pgdir + i);
}
/*H:440 (v) Flushing (throwing away) page tables,
@@ -369,7 +526,9 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
{
pgd_t gpgd;
pte_t gpte;
-
+#ifdef CONFIG_X86_PAE
+ pmd_t gpmd;
+#endif
/* First step: get the top-level Guest page table entry. */
gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
/* Toplevel not present? We can't map it in. */
@@ -378,7 +537,14 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
return -1UL;
}
- gpte = lgread(cpu, gpte_addr(gpgd, vaddr), pte_t);
+#ifdef CONFIG_X86_PAE
+ gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
+ if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+ kill_guest(cpu, "Bad address %#lx", vaddr);
+ gpte = lgread(cpu, gpte_addr(cpu, gpmd, vaddr), pte_t);
+#else
+ gpte = lgread(cpu, gpte_addr(cpu, gpgd, vaddr), pte_t);
+#endif
if (!(pte_flags(gpte) & _PAGE_PRESENT))
kill_guest(cpu, "Bad address %#lx", vaddr);
@@ -405,6 +571,9 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
int *blank_pgdir)
{
unsigned int next;
+#ifdef CONFIG_X86_PAE
+ pmd_t *pmd_table;
+#endif
/* We pick one entry at random to throw out. Choosing the Least
* Recently Used might be better, but this is easy. */
@@ -416,10 +585,27 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
/* If the allocation fails, just keep using the one we have */
if (!cpu->lg->pgdirs[next].pgdir)
next = cpu->cpu_pgd;
- else
- /* This is a blank page, so there are no kernel
- * mappings: caller must map the stack! */
+ else {
+#ifdef CONFIG_X86_PAE
+ /* In PAE mode, allocate a pmd page and populate the
+ * last pgd entry. */
+ pmd_table = (pmd_t *)get_zeroed_page(GFP_KERNEL);
+ if (!pmd_table) {
+ free_page((long)cpu->lg->pgdirs[next].pgdir);
+ set_pgd(cpu->lg->pgdirs[next].pgdir, __pgd(0));
+ next = cpu->cpu_pgd;
+ } else {
+ set_pgd(cpu->lg->pgdirs[next].pgdir +
+ SWITCHER_PGD_INDEX,
+ __pgd(__pa(pmd_table) | _PAGE_PRESENT));
+ /* This is a blank page, so there are no kernel
+ * mappings: caller must map the stack! */
+ *blank_pgdir = 1;
+ }
+#else
*blank_pgdir = 1;
+#endif
+ }
}
/* Record which Guest toplevel this shadows. */
cpu->lg->pgdirs[next].gpgdir = gpgdir;
@@ -431,7 +617,7 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
/*H:430 (iv) Switching page tables
*
- * Now we've seen all the page table setting and manipulation, let's see what
+ * Now we've seen all the page table setting and manipulation, let's see
* what happens when the Guest changes page tables (ie. changes the top-level
* pgdir). This occurs on almost every context switch. */
void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable)
@@ -460,10 +646,25 @@ static void release_all_pagetables(struct lguest *lg)
/* Every shadow pagetable this Guest has */
for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
- if (lg->pgdirs[i].pgdir)
+ if (lg->pgdirs[i].pgdir) {
+#ifdef CONFIG_X86_PAE
+ pgd_t *spgd;
+ pmd_t *pmdpage;
+ unsigned int k;
+
+ /* Get the last pmd page. */
+ spgd = lg->pgdirs[i].pgdir + SWITCHER_PGD_INDEX;
+ pmdpage = __va(pgd_pfn(*spgd) << PAGE_SHIFT);
+
+ /* And release the pmd entries of that pmd page,
+ * except for the switcher pmd. */
+ for (k = 0; k < SWITCHER_PMD_INDEX; k++)
+ release_pmd(&pmdpage[k]);
+#endif
/* Every PGD entry except the Switcher at the top */
for (j = 0; j < SWITCHER_PGD_INDEX; j++)
- release_pgd(lg, lg->pgdirs[i].pgdir + j);
+ release_pgd(lg->pgdirs[i].pgdir + j);
+ }
}
/* We also throw away everything when a Guest tells us it's changed a kernel
@@ -504,24 +705,37 @@ static void do_set_pte(struct lg_cpu *cpu, int idx,
{
/* Look up the matching shadow page directory entry. */
pgd_t *spgd = spgd_addr(cpu, idx, vaddr);
+#ifdef CONFIG_X86_PAE
+ pmd_t *spmd;
+#endif
/* If the top level isn't present, there's no entry to update. */
if (pgd_flags(*spgd) & _PAGE_PRESENT) {
- /* Otherwise, we start by releasing the existing entry. */
- pte_t *spte = spte_addr(*spgd, vaddr);
- release_pte(*spte);
-
- /* If they're setting this entry as dirty or accessed, we might
- * as well put that entry they've given us in now. This shaves
- * 10% off a copy-on-write micro-benchmark. */
- if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
- check_gpte(cpu, gpte);
- *spte = gpte_to_spte(cpu, gpte,
- pte_flags(gpte) & _PAGE_DIRTY);
- } else
- /* Otherwise kill it and we can demand_page() it in
- * later. */
- *spte = __pte(0);
+#ifdef CONFIG_X86_PAE
+ spmd = spmd_addr(cpu, *spgd, vaddr);
+ if (pmd_flags(*spmd) & _PAGE_PRESENT) {
+#endif
+ /* Otherwise, we start by releasing
+ * the existing entry. */
+ pte_t *spte = spte_addr(cpu, *spgd, vaddr);
+ release_pte(*spte);
+
+ /* If they're setting this entry as dirty or accessed,
+ * we might as well put that entry they've given us
+ * in now. This shaves 10% off a
+ * copy-on-write micro-benchmark. */
+ if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
+ check_gpte(cpu, gpte);
+ native_set_pte(spte,
+ gpte_to_spte(cpu, gpte,
+ pte_flags(gpte) & _PAGE_DIRTY));
+ } else
+ /* Otherwise kill it and we can demand_page()
+ * it in later. */
+ native_set_pte(spte, __pte(0));
+#ifdef CONFIG_X86_PAE
+ }
+#endif
}
}
@@ -568,12 +782,10 @@ void guest_set_pte(struct lg_cpu *cpu,
*
* So with that in mind here's our code to to update a (top-level) PGD entry:
*/
-void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
+void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 idx)
{
int pgdir;
- /* The kernel seems to try to initialize this early on: we ignore its
- * attempts to map over the Switcher. */
if (idx >= SWITCHER_PGD_INDEX)
return;
@@ -581,8 +793,14 @@ void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
pgdir = find_pgdir(lg, gpgdir);
if (pgdir < ARRAY_SIZE(lg->pgdirs))
/* ... throw it away. */
- release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx);
+ release_pgd(lg->pgdirs[pgdir].pgdir + idx);
}
+#ifdef CONFIG_X86_PAE
+void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx)
+{
+ guest_pagetable_clear_all(&lg->cpus[0]);
+}
+#endif
/* Once we know how much memory we have we can construct simple identity
* (which set virtual == physical) and linear mappings
@@ -596,8 +814,16 @@ static unsigned long setup_pagetables(struct lguest *lg,
{
pgd_t __user *pgdir;
pte_t __user *linear;
- unsigned int mapped_pages, i, linear_pages, phys_linear;
unsigned long mem_base = (unsigned long)lg->mem_base;
+ unsigned int mapped_pages, i, linear_pages;
+#ifdef CONFIG_X86_PAE
+ pmd_t __user *pmds;
+ unsigned int j;
+ pgd_t pgd;
+ pmd_t pmd;
+#else
+ unsigned int phys_linear;
+#endif
/* We have mapped_pages frames to map, so we need
* linear_pages page tables to map them. */
@@ -610,6 +836,9 @@ static unsigned long setup_pagetables(struct lguest *lg,
/* Now we use the next linear_pages pages as pte pages */
linear = (void *)pgdir - linear_pages * PAGE_SIZE;
+#ifdef CONFIG_X86_PAE
+ pmds = (void *)linear - PAGE_SIZE;
+#endif
/* Linear mapping is easy: put every page's address into the
* mapping in order. */
for (i = 0; i < mapped_pages; i++) {
@@ -621,6 +850,22 @@ static unsigned long setup_pagetables(struct lguest *lg,
/* The top level points to the linear page table pages above.
* We setup the identity and linear mappings here. */
+#ifdef CONFIG_X86_PAE
+ for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD;
+ i += PTRS_PER_PTE, j++) {
+ native_set_pmd(&pmd, __pmd(((unsigned long)(linear + i)
+ - mem_base) | _PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+ if (copy_to_user(&pmds[j], &pmd, sizeof(pmd)) != 0)
+ return -EFAULT;
+ }
+
+ set_pgd(&pgd, __pgd(((u32)pmds - mem_base) | _PAGE_PRESENT));
+ if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0)
+ return -EFAULT;
+ if (copy_to_user(&pgdir[3], &pgd, sizeof(pgd)) != 0)
+ return -EFAULT;
+#else
phys_linear = (unsigned long)linear - mem_base;
for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
pgd_t pgd;
@@ -633,6 +878,7 @@ static unsigned long setup_pagetables(struct lguest *lg,
&pgd, sizeof(pgd)))
return -EFAULT;
}
+#endif
/* We return the top level (guest-physical) address: remember where
* this is. */
@@ -648,7 +894,10 @@ int init_guest_pagetable(struct lguest *lg)
u64 mem;
u32 initrd_size;
struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
-
+#ifdef CONFIG_X86_PAE
+ pgd_t *pgd;
+ pmd_t *pmd_table;
+#endif
/* Get the Guest memory size and the ramdisk size from the boot header
* located at lg->mem_base (Guest address 0). */
if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
@@ -663,6 +912,15 @@ int init_guest_pagetable(struct lguest *lg)
lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
if (!lg->pgdirs[0].pgdir)
return -ENOMEM;
+#ifdef CONFIG_X86_PAE
+ pgd = lg->pgdirs[0].pgdir;
+ pmd_table = (pmd_t *) get_zeroed_page(GFP_KERNEL);
+ if (!pmd_table)
+ return -ENOMEM;
+
+ set_pgd(pgd + SWITCHER_PGD_INDEX,
+ __pgd(__pa(pmd_table) | _PAGE_PRESENT));
+#endif
lg->cpus[0].cpu_pgd = 0;
return 0;
}
@@ -672,17 +930,24 @@ void page_table_guest_data_init(struct lg_cpu *cpu)
{
/* We get the kernel address: above this is all kernel memory. */
if (get_user(cpu->lg->kernel_address,
- &cpu->lg->lguest_data->kernel_address)
- /* We tell the Guest that it can't use the top 4MB of virtual
- * addresses used by the Switcher. */
- || put_user(4U*1024*1024, &cpu->lg->lguest_data->reserve_mem)
- || put_user(cpu->lg->pgdirs[0].gpgdir, &cpu->lg->lguest_data->pgdir))
+ &cpu->lg->lguest_data->kernel_address)
+ /* We tell the Guest that it can't use the top 2 or 4 MB
+ * of virtual addresses used by the Switcher. */
+ || put_user(RESERVE_MEM * 1024 * 1024,
+ &cpu->lg->lguest_data->reserve_mem)
+ || put_user(cpu->lg->pgdirs[0].gpgdir,
+ &cpu->lg->lguest_data->pgdir))
kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);
/* In flush_user_mappings() we loop from 0 to
* "pgd_index(lg->kernel_address)". This assumes it won't hit the
* Switcher mappings, so check that now. */
+#ifdef CONFIG_X86_PAE
+ if (pgd_index(cpu->lg->kernel_address) == SWITCHER_PGD_INDEX &&
+ pmd_index(cpu->lg->kernel_address) == SWITCHER_PMD_INDEX)
+#else
if (pgd_index(cpu->lg->kernel_address) >= SWITCHER_PGD_INDEX)
+#endif
kill_guest(cpu, "bad kernel address %#lx",
cpu->lg->kernel_address);
}
@@ -708,16 +973,30 @@ void free_guest_pagetable(struct lguest *lg)
void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
{
pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages);
- pgd_t switcher_pgd;
pte_t regs_pte;
unsigned long pfn;
+#ifdef CONFIG_X86_PAE
+ pmd_t switcher_pmd;
+ pmd_t *pmd_table;
+
+ native_set_pmd(&switcher_pmd, pfn_pmd(__pa(switcher_pte_page) >>
+ PAGE_SHIFT, PAGE_KERNEL_EXEC));
+
+ pmd_table = __va(pgd_pfn(cpu->lg->
+ pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX])
+ << PAGE_SHIFT);
+ native_set_pmd(&pmd_table[SWITCHER_PMD_INDEX], switcher_pmd);
+#else
+ pgd_t switcher_pgd;
+
/* Make the last PGD entry for this Guest point to the Switcher's PTE
* page for this CPU (with appropriate flags). */
- switcher_pgd = __pgd(__pa(switcher_pte_page) | __PAGE_KERNEL);
+ switcher_pgd = __pgd(__pa(switcher_pte_page) | __PAGE_KERNEL_EXEC);
cpu->lg->pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX] = switcher_pgd;
+#endif
/* We also change the Switcher PTE page. When we're running the Guest,
* we want the Guest's "regs" page to appear where the first Switcher
* page for this CPU is. This is an optimization: when the Switcher
@@ -726,8 +1005,9 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
* page is already mapped there, we don't have to copy them out
* again. */
pfn = __pa(cpu->regs_page) >> PAGE_SHIFT;
- regs_pte = pfn_pte(pfn, __pgprot(__PAGE_KERNEL));
- switcher_pte_page[(unsigned long)pages/PAGE_SIZE%PTRS_PER_PTE] = regs_pte;
+ native_set_pte(&regs_pte, pfn_pte(pfn, PAGE_KERNEL));
+ native_set_pte(&switcher_pte_page[pte_index((unsigned long)pages)],
+ regs_pte);
}
/*:*/
@@ -752,21 +1032,21 @@ static __init void populate_switcher_pte_page(unsigned int cpu,
/* The first entries are easy: they map the Switcher code. */
for (i = 0; i < pages; i++) {
- pte[i] = mk_pte(switcher_page[i],
- __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED));
+ native_set_pte(&pte[i], mk_pte(switcher_page[i],
+ __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED)));
}
/* The only other thing we map is this CPU's pair of pages. */
i = pages + cpu*2;
/* First page (Guest registers) is writable from the Guest */
- pte[i] = pfn_pte(page_to_pfn(switcher_page[i]),
- __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW));
+ native_set_pte(&pte[i], pfn_pte(page_to_pfn(switcher_page[i]),
+ __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW)));
/* The second page contains the "struct lguest_ro_state", and is
* read-only. */
- pte[i+1] = pfn_pte(page_to_pfn(switcher_page[i+1]),
- __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED));
+ native_set_pte(&pte[i+1], pfn_pte(page_to_pfn(switcher_page[i+1]),
+ __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED)));
}
/* We've made it through the page table code. Perhaps our tired brains are
diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c
index 7ede64ffeef9..482ed5a18750 100644
--- a/drivers/lguest/segments.c
+++ b/drivers/lguest/segments.c
@@ -150,7 +150,7 @@ void load_guest_gdt_entry(struct lg_cpu *cpu, u32 num, u32 lo, u32 hi)
{
/* We assume the Guest has the same number of GDT entries as the
* Host, otherwise we'd have to dynamically allocate the Guest GDT. */
- if (num > ARRAY_SIZE(cpu->arch.gdt))
+ if (num >= ARRAY_SIZE(cpu->arch.gdt))
kill_guest(cpu, "too many gdt entries %i", num);
/* Set it up, then fix it. */
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 1a83910f674f..eaf722fe309a 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -358,6 +358,16 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
if (emulate_insn(cpu))
return;
}
+ /* If KVM is active, the vmcall instruction triggers a
+ * General Protection Fault. Normally it triggers an
+ * invalid opcode fault (6): */
+ case 6:
+ /* We need to check if ring == GUEST_PL and
+ * faulting instruction == vmcall. */
+ if (is_hypercall(cpu)) {
+ rewrite_hypercall(cpu);
+ return;
+ }
break;
case 14: /* We've intercepted a Page Fault. */
/* The Guest accessed a virtual address that wasn't mapped.
@@ -403,15 +413,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
* up the pointer now to indicate a hypercall is pending. */
cpu->hcall = (struct hcall_args *)cpu->regs;
return;
- case 6:
- /* kvm hypercalls trigger an invalid opcode fault (6).
- * We need to check if ring == GUEST_PL and
- * faulting instruction == vmcall. */
- if (is_hypercall(cpu)) {
- rewrite_hypercall(cpu);
- return;
- }
- break;
}
/* We didn't handle the trap, so it needs to go to the Guest. */
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 1fb91edc7de2..3319c2fec28e 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -232,7 +232,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset,
target = rdev->sb_start + offset + index * (PAGE_SIZE/512);
if (sync_page_io(rdev->bdev, target,
- roundup(size, bdev_hardsect_size(rdev->bdev)),
+ roundup(size, bdev_logical_block_size(rdev->bdev)),
page, READ)) {
page->index = index;
attach_page_buffers(page, NULL); /* so that free_buffer will
@@ -287,7 +287,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
int size = PAGE_SIZE;
if (page->index == bitmap->file_pages-1)
size = roundup(bitmap->last_page_size,
- bdev_hardsect_size(rdev->bdev));
+ bdev_logical_block_size(rdev->bdev));
/* Just make sure we aren't corrupting data or
* metadata
*/
@@ -986,6 +986,9 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
oldindex = index;
oldpage = page;
+ bitmap->filemap[bitmap->file_pages++] = page;
+ bitmap->last_page_size = count;
+
if (outofdate) {
/*
* if bitmap is out of date, dirty the
@@ -998,15 +1001,9 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
write_page(bitmap, page, 1);
ret = -EIO;
- if (bitmap->flags & BITMAP_WRITE_ERROR) {
- /* release, page not in filemap yet */
- put_page(page);
+ if (bitmap->flags & BITMAP_WRITE_ERROR)
goto err;
- }
}
-
- bitmap->filemap[bitmap->file_pages++] = page;
- bitmap->last_page_size = count;
}
paddr = kmap_atomic(page, KM_USER0);
if (bitmap->flags & BITMAP_HOSTENDIAN)
@@ -1016,9 +1013,11 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
kunmap_atomic(paddr, KM_USER0);
if (b) {
/* if the disk bit is set, set the memory bit */
- bitmap_set_memory_bits(bitmap, i << CHUNK_BLOCK_SHIFT(bitmap),
- ((i+1) << (CHUNK_BLOCK_SHIFT(bitmap)) >= start)
- );
+ int needed = ((sector_t)(i+1) << (CHUNK_BLOCK_SHIFT(bitmap))
+ >= start);
+ bitmap_set_memory_bits(bitmap,
+ (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
+ needed);
bit_cnt++;
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
}
@@ -1098,14 +1097,12 @@ void bitmap_daemon_work(struct bitmap *bitmap)
}
bitmap->allclean = 1;
+ spin_lock_irqsave(&bitmap->lock, flags);
for (j = 0; j < bitmap->chunks; j++) {
bitmap_counter_t *bmc;
- spin_lock_irqsave(&bitmap->lock, flags);
- if (!bitmap->filemap) {
+ if (!bitmap->filemap)
/* error or shutdown */
- spin_unlock_irqrestore(&bitmap->lock, flags);
break;
- }
page = filemap_get_page(bitmap, j);
@@ -1122,6 +1119,8 @@ void bitmap_daemon_work(struct bitmap *bitmap)
write_page(bitmap, page, 0);
bitmap->allclean = 0;
}
+ spin_lock_irqsave(&bitmap->lock, flags);
+ j |= (PAGE_BITS - 1);
continue;
}
@@ -1154,8 +1153,9 @@ void bitmap_daemon_work(struct bitmap *bitmap)
spin_lock_irqsave(&bitmap->lock, flags);
clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
}
- bmc = bitmap_get_counter(bitmap, j << CHUNK_BLOCK_SHIFT(bitmap),
- &blocks, 0);
+ bmc = bitmap_get_counter(bitmap,
+ (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
+ &blocks, 0);
if (bmc) {
/*
if (j < 100) printk("bitmap: j=%lu, *bmc = 0x%x\n", j, *bmc);
@@ -1169,7 +1169,8 @@ void bitmap_daemon_work(struct bitmap *bitmap)
} else if (*bmc == 1) {
/* we can clear the bit */
*bmc = 0;
- bitmap_count_page(bitmap, j << CHUNK_BLOCK_SHIFT(bitmap),
+ bitmap_count_page(bitmap,
+ (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
-1);
/* clear the bit */
@@ -1180,9 +1181,10 @@ void bitmap_daemon_work(struct bitmap *bitmap)
ext2_clear_bit(file_page_offset(j), paddr);
kunmap_atomic(paddr, KM_USER0);
}
- }
- spin_unlock_irqrestore(&bitmap->lock, flags);
+ } else
+ j |= PAGE_COUNTER_MASK;
}
+ spin_unlock_irqrestore(&bitmap->lock, flags);
/* now sync the final page */
if (lastpage != NULL) {
@@ -1514,7 +1516,7 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
unsigned long chunk;
for (chunk = s; chunk <= e; chunk++) {
- sector_t sec = chunk << CHUNK_BLOCK_SHIFT(bitmap);
+ sector_t sec = (sector_t)chunk << CHUNK_BLOCK_SHIFT(bitmap);
bitmap_set_memory_bits(bitmap, sec, 1);
bitmap_file_set_bit(bitmap, sec);
}
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index a2e26c242141..75d8081a9041 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -178,7 +178,7 @@ static int set_chunk_size(struct dm_exception_store *store,
}
/* Validate the chunk size against the device block size */
- if (chunk_size_ulong % (bdev_hardsect_size(store->cow->bdev) >> 9)) {
+ if (chunk_size_ulong % (bdev_logical_block_size(store->cow->bdev) >> 9)) {
*error = "Chunk size is not a multiple of device blocksize";
return -EINVAL;
}
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index be233bc4d917..6fa8ccf91c70 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -413,7 +413,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
* Buffer holds both header and bitset.
*/
buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) +
- bitset_size, ti->limits.hardsect_size);
+ bitset_size,
+ ti->limits.logical_block_size);
if (buf_size > dev->bdev->bd_inode->i_size) {
DMWARN("log device %s too small: need %llu bytes",
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index e75c6dd76a9a..2662a41337e7 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -282,7 +282,7 @@ static int read_header(struct pstore *ps, int *new_snapshot)
*/
if (!ps->store->chunk_size) {
ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
- bdev_hardsect_size(ps->store->cow->bdev) >> 9);
+ bdev_logical_block_size(ps->store->cow->bdev) >> 9);
ps->store->chunk_mask = ps->store->chunk_size - 1;
ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
chunk_size_supplied = 0;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 429b50b975d5..e9a73bb242b0 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -108,7 +108,8 @@ static void combine_restrictions_low(struct io_restrictions *lhs,
lhs->max_hw_segments =
min_not_zero(lhs->max_hw_segments, rhs->max_hw_segments);
- lhs->hardsect_size = max(lhs->hardsect_size, rhs->hardsect_size);
+ lhs->logical_block_size = max(lhs->logical_block_size,
+ rhs->logical_block_size);
lhs->max_segment_size =
min_not_zero(lhs->max_segment_size, rhs->max_segment_size);
@@ -509,7 +510,7 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
* combine_restrictions_low()
*/
rs->max_sectors =
- min_not_zero(rs->max_sectors, q->max_sectors);
+ min_not_zero(rs->max_sectors, queue_max_sectors(q));
/*
* Check if merge fn is supported.
@@ -524,24 +525,25 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
rs->max_phys_segments =
min_not_zero(rs->max_phys_segments,
- q->max_phys_segments);
+ queue_max_phys_segments(q));
rs->max_hw_segments =
- min_not_zero(rs->max_hw_segments, q->max_hw_segments);
+ min_not_zero(rs->max_hw_segments, queue_max_hw_segments(q));
- rs->hardsect_size = max(rs->hardsect_size, q->hardsect_size);
+ rs->logical_block_size = max(rs->logical_block_size,
+ queue_logical_block_size(q));
rs->max_segment_size =
- min_not_zero(rs->max_segment_size, q->max_segment_size);
+ min_not_zero(rs->max_segment_size, queue_max_segment_size(q));
rs->max_hw_sectors =
- min_not_zero(rs->max_hw_sectors, q->max_hw_sectors);
+ min_not_zero(rs->max_hw_sectors, queue_max_hw_sectors(q));
rs->seg_boundary_mask =
min_not_zero(rs->seg_boundary_mask,
- q->seg_boundary_mask);
+ queue_segment_boundary(q));
- rs->bounce_pfn = min_not_zero(rs->bounce_pfn, q->bounce_pfn);
+ rs->bounce_pfn = min_not_zero(rs->bounce_pfn, queue_bounce_pfn(q));
rs->no_cluster |= !test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
}
@@ -683,8 +685,8 @@ static void check_for_valid_limits(struct io_restrictions *rs)
rs->max_phys_segments = MAX_PHYS_SEGMENTS;
if (!rs->max_hw_segments)
rs->max_hw_segments = MAX_HW_SEGMENTS;
- if (!rs->hardsect_size)
- rs->hardsect_size = 1 << SECTOR_SHIFT;
+ if (!rs->logical_block_size)
+ rs->logical_block_size = 1 << SECTOR_SHIFT;
if (!rs->max_segment_size)
rs->max_segment_size = MAX_SEGMENT_SIZE;
if (!rs->seg_boundary_mask)
@@ -912,13 +914,13 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q)
* restrictions.
*/
blk_queue_max_sectors(q, t->limits.max_sectors);
- q->max_phys_segments = t->limits.max_phys_segments;
- q->max_hw_segments = t->limits.max_hw_segments;
- q->hardsect_size = t->limits.hardsect_size;
- q->max_segment_size = t->limits.max_segment_size;
- q->max_hw_sectors = t->limits.max_hw_sectors;
- q->seg_boundary_mask = t->limits.seg_boundary_mask;
- q->bounce_pfn = t->limits.bounce_pfn;
+ blk_queue_max_phys_segments(q, t->limits.max_phys_segments);
+ blk_queue_max_hw_segments(q, t->limits.max_hw_segments);
+ blk_queue_logical_block_size(q, t->limits.logical_block_size);
+ blk_queue_max_segment_size(q, t->limits.max_segment_size);
+ blk_queue_max_hw_sectors(q, t->limits.max_hw_sectors);
+ blk_queue_segment_boundary(q, t->limits.seg_boundary_mask);
+ blk_queue_bounce_limit(q, t->limits.bounce_pfn);
if (t->limits.no_cluster)
queue_flag_clear_unlocked(QUEUE_FLAG_CLUSTER, q);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 424f7b048c30..3fd8b1e65483 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -20,7 +20,8 @@
#include <linux/idr.h>
#include <linux/hdreg.h>
#include <linux/blktrace_api.h>
-#include <trace/block.h>
+
+#include <trace/events/block.h>
#define DM_MSG_PREFIX "core"
@@ -53,8 +54,6 @@ struct dm_target_io {
union map_info info;
};
-DEFINE_TRACE(block_bio_complete);
-
/*
* For request-based dm.
* One of these is allocated per request.
@@ -656,8 +655,7 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
/* the bio has been remapped so dispatch it */
trace_block_remap(bdev_get_queue(clone->bi_bdev), clone,
- tio->io->bio->bi_bdev->bd_dev,
- clone->bi_sector, sector);
+ tio->io->bio->bi_bdev->bd_dev, sector);
generic_make_request(clone);
} else if (r < 0 || r == DM_MAPIO_REQUEUE) {
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 7a36e38393a1..64f1f3e046e0 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -146,7 +146,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
* a one page request is never in violation.
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
- mddev->queue->max_sectors > (PAGE_SIZE>>9))
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->num_sectors = rdev->sectors;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 612343fdde94..20f6ac338349 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1202,7 +1202,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
- bmask = queue_hardsect_size(rdev->bdev->bd_disk->queue)-1;
+ bmask = queue_logical_block_size(rdev->bdev->bd_disk->queue)-1;
if (rdev->sb_size & bmask)
rdev->sb_size = (rdev->sb_size | bmask) + 1;
@@ -1375,6 +1375,9 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->raid_disks = cpu_to_le32(mddev->raid_disks);
sb->size = cpu_to_le64(mddev->dev_sectors);
+ sb->chunksize = cpu_to_le32(mddev->chunk_size >> 9);
+ sb->level = cpu_to_le32(mddev->level);
+ sb->layout = cpu_to_le32(mddev->layout);
if (mddev->bitmap && mddev->bitmap_file == NULL) {
sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
@@ -3066,11 +3069,8 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
} else
err = -EBUSY;
spin_unlock_irq(&mddev->write_lock);
- } else {
- mddev->ro = 0;
- mddev->recovery_cp = MaxSector;
- err = do_md_run(mddev);
- }
+ } else
+ err = -EINVAL;
break;
case active:
if (mddev->pers) {
@@ -3306,7 +3306,9 @@ static ssize_t
action_show(mddev_t *mddev, char *page)
{
char *type = "idle";
- if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
+ if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
+ type = "frozen";
+ else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
(!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))) {
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
type = "reshape";
@@ -3329,7 +3331,12 @@ action_store(mddev_t *mddev, const char *page, size_t len)
if (!mddev->pers || !mddev->pers->sync_request)
return -EINVAL;
- if (cmd_match(page, "idle")) {
+ if (cmd_match(page, "frozen"))
+ set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ else
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+
+ if (cmd_match(page, "idle") || cmd_match(page, "frozen")) {
if (mddev->sync_thread) {
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
md_unregister_thread(mddev->sync_thread);
@@ -3683,7 +3690,7 @@ array_size_store(mddev_t *mddev, const char *buf, size_t len)
if (strict_blocks_to_sectors(buf, &sectors) < 0)
return -EINVAL;
if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors)
- return -EINVAL;
+ return -E2BIG;
mddev->external_size = 1;
}
@@ -4297,6 +4304,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
{
int err = 0;
struct gendisk *disk = mddev->gendisk;
+ mdk_rdev_t *rdev;
if (atomic_read(&mddev->openers) > is_open) {
printk("md: %s still in use.\n",mdname(mddev));
@@ -4339,6 +4347,13 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
/* tell userspace to handle 'inactive' */
sysfs_notify_dirent(mddev->sysfs_state);
+ list_for_each_entry(rdev, &mddev->disks, same_set)
+ if (rdev->raid_disk >= 0) {
+ char nm[20];
+ sprintf(nm, "rd%d", rdev->raid_disk);
+ sysfs_remove_link(&mddev->kobj, nm);
+ }
+
set_capacity(disk, 0);
mddev->changed = 1;
@@ -4359,7 +4374,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
* Free resources if final stop
*/
if (mode == 0) {
- mdk_rdev_t *rdev;
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
@@ -4371,13 +4385,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
}
mddev->bitmap_offset = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set)
- if (rdev->raid_disk >= 0) {
- char nm[20];
- sprintf(nm, "rd%d", rdev->raid_disk);
- sysfs_remove_link(&mddev->kobj, nm);
- }
-
/* make sure all md_delayed_delete calls have finished */
flush_scheduled_work();
@@ -5560,7 +5567,7 @@ static struct block_device_operations md_fops =
.owner = THIS_MODULE,
.open = md_open,
.release = md_release,
- .locked_ioctl = md_ioctl,
+ .ioctl = md_ioctl,
.getgeo = md_getgeo,
.media_changed = md_media_changed,
.revalidate_disk= md_revalidate,
@@ -5705,37 +5712,38 @@ static void status_unused(struct seq_file *seq)
static void status_resync(struct seq_file *seq, mddev_t * mddev)
{
- sector_t max_blocks, resync, res;
- unsigned long dt, db, rt;
+ sector_t max_sectors, resync, res;
+ unsigned long dt, db;
+ sector_t rt;
int scale;
unsigned int per_milli;
- resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2;
+ resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
- max_blocks = mddev->resync_max_sectors >> 1;
+ max_sectors = mddev->resync_max_sectors;
else
- max_blocks = mddev->dev_sectors / 2;
+ max_sectors = mddev->dev_sectors;
/*
* Should not happen.
*/
- if (!max_blocks) {
+ if (!max_sectors) {
MD_BUG();
return;
}
/* Pick 'scale' such that (resync>>scale)*1000 will fit
- * in a sector_t, and (max_blocks>>scale) will fit in a
+ * in a sector_t, and (max_sectors>>scale) will fit in a
* u32, as those are the requirements for sector_div.
* Thus 'scale' must be at least 10
*/
scale = 10;
if (sizeof(sector_t) > sizeof(unsigned long)) {
- while ( max_blocks/2 > (1ULL<<(scale+32)))
+ while ( max_sectors/2 > (1ULL<<(scale+32)))
scale++;
}
res = (resync>>scale)*1000;
- sector_div(res, (u32)((max_blocks>>scale)+1));
+ sector_div(res, (u32)((max_sectors>>scale)+1));
per_milli = res;
{
@@ -5756,25 +5764,35 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
(test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
"resync" : "recovery"))),
per_milli/10, per_milli % 10,
- (unsigned long long) resync,
- (unsigned long long) max_blocks);
+ (unsigned long long) resync/2,
+ (unsigned long long) max_sectors/2);
/*
- * We do not want to overflow, so the order of operands and
- * the * 100 / 100 trick are important. We do a +1 to be
- * safe against division by zero. We only estimate anyway.
- *
* dt: time from mark until now
* db: blocks written from mark until now
* rt: remaining time
+ *
+ * rt is a sector_t, so could be 32bit or 64bit.
+ * So we divide before multiply in case it is 32bit and close
+ * to the limit.
+ * We scale the divisor (db) by 32 to avoid loosing precision
+ * near the end of resync when the number of remaining sectors
+ * is close to 'db'.
+ * We then divide rt by 32 after multiplying by db to compensate.
+ * The '+1' avoids division by zero if db is very small.
*/
dt = ((jiffies - mddev->resync_mark) / HZ);
if (!dt) dt++;
db = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active))
- mddev->resync_mark_cnt;
- rt = (dt * ((unsigned long)(max_blocks-resync) / (db/2/100+1)))/100;
- seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
+ rt = max_sectors - resync; /* number of remaining sectors */
+ sector_div(rt, db/32+1);
+ rt *= dt;
+ rt >>= 5;
+
+ seq_printf(seq, " finish=%lu.%lumin", (unsigned long)rt / 60,
+ ((unsigned long)rt % 60)/6);
seq_printf(seq, " speed=%ldK/sec", db/2/dt);
}
@@ -5965,7 +5983,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
return 0;
}
-static struct seq_operations md_seq_ops = {
+static const struct seq_operations md_seq_ops = {
.start = md_seq_start,
.next = md_seq_next,
.stop = md_seq_stop,
@@ -6344,12 +6362,13 @@ void md_do_sync(mddev_t *mddev)
skipped = 0;
- if ((mddev->curr_resync > mddev->curr_resync_completed &&
- (mddev->curr_resync - mddev->curr_resync_completed)
- > (max_sectors >> 4)) ||
- (j - mddev->curr_resync_completed)*2
- >= mddev->resync_max - mddev->curr_resync_completed
- ) {
+ if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+ ((mddev->curr_resync > mddev->curr_resync_completed &&
+ (mddev->curr_resync - mddev->curr_resync_completed)
+ > (max_sectors >> 4)) ||
+ (j - mddev->curr_resync_completed)*2
+ >= mddev->resync_max - mddev->curr_resync_completed
+ )) {
/* time to update curr_resync_completed */
blk_unplug(mddev->queue);
wait_event(mddev->recovery_wait,
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 41ced0cbe823..4ee31aa13c40 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -303,7 +303,7 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
* merge_bvec_fn will be involved in multipath.)
*/
if (q->merge_bvec_fn &&
- mddev->queue->max_sectors > (PAGE_SIZE>>9))
+ queue_max_sectors(q) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
conf->working_disks++;
@@ -467,7 +467,7 @@ static int multipath_run (mddev_t *mddev)
* violating it, not that we ever expect a device with
* a merge_bvec_fn to be involved in multipath */
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
- mddev->queue->max_sectors > (PAGE_SIZE>>9))
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
if (!test_bit(Faulty, &rdev->flags))
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index c08d7559be55..925507e7d673 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -144,7 +144,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))
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
if (!smallest || (rdev1->sectors < smallest->sectors))
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 36df9109cde1..e23758b4a34e 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1130,7 +1130,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
* a one page request is never in violation.
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
- mddev->queue->max_sectors > (PAGE_SIZE>>9))
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
p->head_position = 0;
@@ -1996,7 +1996,7 @@ static int run(mddev_t *mddev)
* a one page request is never in violation.
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
- mddev->queue->max_sectors > (PAGE_SIZE>>9))
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->head_position = 0;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 81a54f17417e..750550c1166f 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1158,8 +1158,8 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
* 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);
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
+ blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
p->head_position = 0;
rdev->raid_disk = mirror;
@@ -1809,17 +1809,17 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
r10_bio->sector = sect;
raid10_find_phys(conf, r10_bio);
- /* Need to check if this section will still be
+
+ /* Need to check if the array will still be
* degraded
*/
- for (j=0; j<conf->copies;j++) {
- int d = r10_bio->devs[j].devnum;
- if (conf->mirrors[d].rdev == NULL ||
- test_bit(Faulty, &conf->mirrors[d].rdev->flags)) {
+ for (j=0; j<conf->raid_disks; j++)
+ if (conf->mirrors[j].rdev == NULL ||
+ test_bit(Faulty, &conf->mirrors[j].rdev->flags)) {
still_degraded = 1;
break;
}
- }
+
must_sync = bitmap_start_sync(mddev->bitmap, sect,
&sync_blocks, still_degraded);
@@ -2145,8 +2145,8 @@ static int run(mddev_t *mddev)
* 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);
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
+ blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->head_position = 0;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4616bc3a6e71..bef876698232 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -362,7 +362,7 @@ static void raid5_unplug_device(struct request_queue *q);
static struct stripe_head *
get_active_stripe(raid5_conf_t *conf, sector_t sector,
- int previous, int noblock)
+ int previous, int noblock, int noquiesce)
{
struct stripe_head *sh;
@@ -372,7 +372,7 @@ get_active_stripe(raid5_conf_t *conf, sector_t sector,
do {
wait_event_lock_irq(conf->wait_for_stripe,
- conf->quiesce == 0,
+ conf->quiesce == 0 || noquiesce,
conf->device_lock, /* nothing */);
sh = __find_stripe(conf, sector, conf->generation - previous);
if (!sh) {
@@ -2671,7 +2671,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
sector_t bn = compute_blocknr(sh, i, 1);
sector_t s = raid5_compute_sector(conf, bn, 0,
&dd_idx, NULL);
- sh2 = get_active_stripe(conf, s, 0, 1);
+ sh2 = get_active_stripe(conf, s, 0, 1, 1);
if (sh2 == NULL)
/* so far only the early blocks of this stripe
* have been requested. When later blocks
@@ -2944,7 +2944,7 @@ static bool handle_stripe5(struct stripe_head *sh)
/* Finish reconstruct operations initiated by the expansion process */
if (sh->reconstruct_state == reconstruct_state_result) {
struct stripe_head *sh2
- = get_active_stripe(conf, sh->sector, 1, 1);
+ = get_active_stripe(conf, sh->sector, 1, 1, 1);
if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
/* sh cannot be written until sh2 has been read.
* so arrange for sh to be delayed a little
@@ -3189,7 +3189,7 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state)) {
struct stripe_head *sh2
- = get_active_stripe(conf, sh->sector, 1, 1);
+ = get_active_stripe(conf, sh->sector, 1, 1, 1);
if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
/* sh cannot be written until sh2 has been read.
* so arrange for sh to be delayed a little
@@ -3288,7 +3288,7 @@ static void unplug_slaves(mddev_t *mddev)
int i;
rcu_read_lock();
- for (i=0; i<mddev->raid_disks; i++) {
+ for (i = 0; i < conf->raid_disks; i++) {
mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) {
struct request_queue *r_queue = bdev_get_queue(rdev->bdev);
@@ -3463,10 +3463,10 @@ static int bio_fits_rdev(struct bio *bi)
{
struct request_queue *q = bdev_get_queue(bi->bi_bdev);
- if ((bi->bi_size>>9) > q->max_sectors)
+ if ((bi->bi_size>>9) > queue_max_sectors(q))
return 0;
blk_recount_segments(q, bi);
- if (bi->bi_phys_segments > q->max_phys_segments)
+ if (bi->bi_phys_segments > queue_max_phys_segments(q))
return 0;
if (q->merge_bvec_fn)
@@ -3675,7 +3675,7 @@ static int make_request(struct request_queue *q, struct bio * bi)
(unsigned long long)logical_sector);
sh = get_active_stripe(conf, new_sector, previous,
- (bi->bi_rw&RWA_MASK));
+ (bi->bi_rw&RWA_MASK), 0);
if (sh) {
if (unlikely(previous)) {
/* expansion might have moved on while waiting for a
@@ -3811,13 +3811,13 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
safepos = conf->reshape_safe;
sector_div(safepos, data_disks);
if (mddev->delta_disks < 0) {
- writepos -= reshape_sectors;
+ writepos -= min_t(sector_t, reshape_sectors, writepos);
readpos += reshape_sectors;
safepos += reshape_sectors;
} else {
writepos += reshape_sectors;
- readpos -= reshape_sectors;
- safepos -= reshape_sectors;
+ readpos -= min_t(sector_t, reshape_sectors, readpos);
+ safepos -= min_t(sector_t, reshape_sectors, safepos);
}
/* 'writepos' is the most advanced device address we might write.
@@ -3873,7 +3873,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
for (i = 0; i < reshape_sectors; i += STRIPE_SECTORS) {
int j;
int skipped = 0;
- sh = get_active_stripe(conf, stripe_addr+i, 0, 0);
+ sh = get_active_stripe(conf, stripe_addr+i, 0, 0, 1);
set_bit(STRIPE_EXPANDING, &sh->state);
atomic_inc(&conf->reshape_stripes);
/* If any of this stripe is beyond the end of the old
@@ -3916,13 +3916,13 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
raid5_compute_sector(conf, stripe_addr*(new_data_disks),
1, &dd_idx, NULL);
last_sector =
- raid5_compute_sector(conf, ((stripe_addr+conf->chunk_size/512)
+ raid5_compute_sector(conf, ((stripe_addr+reshape_sectors)
*(new_data_disks) - 1),
1, &dd_idx, NULL);
if (last_sector >= mddev->dev_sectors)
last_sector = mddev->dev_sectors - 1;
while (first_sector <= last_sector) {
- sh = get_active_stripe(conf, first_sector, 1, 0);
+ sh = get_active_stripe(conf, first_sector, 1, 0, 1);
set_bit(STRIPE_EXPAND_SOURCE, &sh->state);
set_bit(STRIPE_HANDLE, &sh->state);
release_stripe(sh);
@@ -4022,9 +4022,9 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
bitmap_cond_end_sync(mddev->bitmap, sector_nr);
- sh = get_active_stripe(conf, sector_nr, 0, 1);
+ sh = get_active_stripe(conf, sector_nr, 0, 1, 0);
if (sh == NULL) {
- sh = get_active_stripe(conf, sector_nr, 0, 0);
+ sh = get_active_stripe(conf, sector_nr, 0, 0, 0);
/* make sure we don't swamp the stripe cache if someone else
* is trying to get access
*/
@@ -4034,7 +4034,7 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
* We don't need to check the 'failed' flag as when that gets set,
* recovery aborts.
*/
- for (i=0; i<mddev->raid_disks; i++)
+ for (i = 0; i < conf->raid_disks; i++)
if (conf->disks[i].rdev == NULL)
still_degraded = 1;
@@ -4086,7 +4086,7 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio)
/* already done this stripe */
continue;
- sh = get_active_stripe(conf, sector, 0, 1);
+ sh = get_active_stripe(conf, sector, 0, 1, 0);
if (!sh) {
/* failed to get a stripe - must wait */
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 1dba8f0832a0..5cf6c45b91fe 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -153,7 +153,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
v->rangelow = RSF16_MINFREQ / mult;
v->rangehigh = RSF16_MAXFREQ / mult;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
v->capability = fmi->flags & V4L2_TUNER_CAP_LOW;
v->audmode = V4L2_TUNER_MODE_STEREO;
v->signal = fmi_getsigstr(fmi);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index c09ca8600ea1..935ff9bcdfcc 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -233,7 +233,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
v->rangelow = RSF16_MINFREQ / mult;
v->rangehigh = RSF16_MAXFREQ / mult;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
v->capability = fmr2->flags&V4L2_TUNER_CAP_LOW;
v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO:
V4L2_TUNER_MODE_MONO;
diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
index 4cee0b92eeee..a1e4c0d769a6 100644
--- a/drivers/media/video/au0828/au0828-core.c
+++ b/drivers/media/video/au0828/au0828-core.c
@@ -192,8 +192,6 @@ static int au0828_usb_probe(struct usb_interface *interface,
dev->usbdev = usbdev;
dev->boardnr = id->driver_info;
- usb_set_intfdata(interface, dev);
-
/* Create the v4l2_device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
@@ -222,6 +220,10 @@ static int au0828_usb_probe(struct usb_interface *interface,
/* Digital TV */
au0828_dvb_register(dev);
+ /* Store the pointer to the au0828_dev so it can be accessed in
+ au0828_usb_disconnect */
+ usb_set_intfdata(interface, dev);
+
printk(KERN_INFO "Registered device AU0828 [%s]\n",
dev->board.name == NULL ? "Unset" : dev->board.name);
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 5f582726985d..c4d181dde1ca 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -774,6 +774,7 @@ static int cafe_cam_init(struct cafe_camera *cam)
ret = __cafe_cam_reset(cam);
if (ret)
goto out;
+ chip.ident = V4L2_IDENT_NONE;
chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
chip.match.addr = cam->sensor_addr;
ret = sensor_call(cam, core, g_chip_ident, &chip);
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
index 1519e91c677a..7a8ad5963de8 100644
--- a/drivers/media/video/cx18/cx18-audio.c
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -44,7 +44,7 @@ int cx18_audio_set_io(struct cx18 *cx)
/* handle muxer chips */
v4l2_subdev_call(cx->sd_extmux, audio, s_routing,
- in->audio_input, 0, 0);
+ (u32) in->muxer_input, 0, 0);
err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
audio, s_routing, in->audio_input, 0, 0);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index b9b7064a2be8..8591e4fc359f 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -211,7 +211,7 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
/* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx)
{
- int i;
+ int i, err;
CX18_DEBUG_I2C("i2c init\n");
for (i = 0; i < 2; i++) {
@@ -268,8 +268,18 @@ int init_cx18_i2c(struct cx18 *cx)
cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
core, reset, (u32) CX18_GPIO_RESET_I2C);
- return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
- i2c_bit_add_bus(&cx->i2c_adap[1]);
+ err = i2c_bit_add_bus(&cx->i2c_adap[0]);
+ if (err)
+ goto err;
+ err = i2c_bit_add_bus(&cx->i2c_adap[1]);
+ if (err)
+ goto err_del_bus_0;
+ return 0;
+
+ err_del_bus_0:
+ i2c_del_adapter(&cx->i2c_adap[0]);
+ err:
+ return err;
}
void exit_cx18_i2c(struct cx18 *cx)
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
index 91156546a07a..477d4ab5e9ac 100644
--- a/drivers/media/video/cx231xx/Kconfig
+++ b/drivers/media/video/cx231xx/Kconfig
@@ -1,12 +1,11 @@
config VIDEO_CX231XX
- tristate "Conexant cx231xx USB video capture support"
- depends on VIDEO_DEV && I2C && INPUT
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_IR
- select VIDEOBUF_VMALLOC
- select VIDEO_CX25840
- select VIDEO_CX231XX_ALSA
+ tristate "Conexant cx231xx USB video capture support"
+ depends on VIDEO_DEV && I2C && INPUT
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_IR
+ select VIDEOBUF_VMALLOC
+ select VIDEO_CX25840
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
@@ -15,21 +14,22 @@ config VIDEO_CX231XX
module will be called cx231xx
config VIDEO_CX231XX_ALSA
- tristate "Conexant Cx231xx ALSA audio module"
- depends on VIDEO_CX231XX && SND
- select SND_PCM
+ tristate "Conexant Cx231xx ALSA audio module"
+ depends on VIDEO_CX231XX && SND
+ select SND_PCM
- ---help---
- This is an ALSA driver for Cx231xx USB based TV cards.
+ ---help---
+ This is an ALSA driver for Cx231xx USB based TV cards.
- To compile this driver as a module, choose M here: the
- module will be called cx231xx-alsa
+ To compile this driver as a module, choose M here: the
+ module will be called cx231xx-alsa
config VIDEO_CX231XX_DVB
- tristate "DVB/ATSC Support for Cx231xx based TV cards"
- depends on VIDEO_CX231XX && DVB_CORE
- select VIDEOBUF_DVB
- select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
- ---help---
- This adds support for DVB cards based on the
- Conexant cx231xx chips.
+ tristate "DVB/ATSC Support for Cx231xx based TV cards"
+ depends on VIDEO_CX231XX && DVB_CORE
+ select VIDEOBUF_DVB
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
+
+ ---help---
+ This adds support for DVB cards based on the
+ Conexant cx231xx chips.
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index a3c0565be1a9..6d6293f7d428 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -441,9 +441,9 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
/* Two identical tuners on two different i2c buses,
* we need to reset the correct gpio. */
- if (port->nr == 0)
+ if (port->nr == 1)
bitmask = 0x01;
- else if (port->nr == 1)
+ else if (port->nr == 2)
bitmask = 0x04;
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index f48454ab3900..1dc070da8652 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -314,6 +314,7 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = {
.demod_address = 0x0f,
.if2 = 45600,
.no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
};
static struct stv0900_config netup_stv0900_config = {
@@ -471,7 +472,7 @@ static int dvb_register(struct cx23885_tsport *port)
static struct xc2028_ctrl ctl = {
.fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
- .scode_table = XC3028_FE_OREN538,
+ .demod = XC3028_FE_OREN538,
};
fe = dvb_attach(xc2028_attach,
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index b0195e8ee4d1..db2ac9a99acd 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -305,14 +305,17 @@ int ivtv_waitq(wait_queue_head_t *waitq)
/* Generic utility functions */
int ivtv_msleep_timeout(unsigned int msecs, int intr)
{
- int ret;
int timeout = msecs_to_jiffies(msecs);
do {
set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout(timeout);
- if (intr && (ret = signal_pending(current)))
- return ret;
+ if (intr) {
+ int ret = signal_pending(current);
+
+ if (ret)
+ return ret;
+ }
} while (timeout);
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index ceb05bdcaf62..85ac707228e7 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -190,8 +190,8 @@ static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
mask = itv->card->gpio_audio_detect.mask;
if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
- vt->rxsubchans = V4L2_TUNER_MODE_STEREO |
- V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
else
vt->rxsubchans = V4L2_TUNER_SUB_MONO;
return 0;
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 4a2d464f055e..c342a9fe983e 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -180,7 +180,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
/* Wait for any DMA to finish */
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
- while (itv->i_flags & IVTV_F_I_DMA) {
+ while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
got_sig = signal_pending(current);
if (got_sig)
break;
@@ -1710,7 +1710,8 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
we are waiting unlock first and later lock again. */
mutex_unlock(&itv->serialize_lock);
prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
- if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0)
+ if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) &&
+ !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags))
schedule();
finish_wait(&itv->event_waitq, &wait);
mutex_lock(&itv->serialize_lock);
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index 01c14d2b381a..cd9db0bf33bf 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -196,7 +196,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
bytes_needed, s->name);
return -1;
}
- if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) {
+ if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) {
IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
IVTV_WARN("Cause: the application is not reading fast enough.\n");
}
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
index ff7b7deded4f..7fde36e6d227 100644
--- a/drivers/media/video/ivtv/ivtv-queue.c
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -230,7 +230,8 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
return -ENOMEM;
}
if (ivtv_might_use_dma(s)) {
- s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma);
+ s->sg_handle = pci_map_single(itv->pdev, s->sg_dma,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
ivtv_stream_sync_for_cpu(s);
}
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index 7912ed6b72ee..c0875378acc2 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -1063,7 +1063,8 @@ static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
/* if no UDMA is pending and no UDMA is in progress, then the DMA
is finished */
- while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
+ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
/* don't interrupt if the DMA is in progress but break off
a still pending DMA. */
got_sig = signal_pending(current);
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 66e6eb513076..fa6bb85cb4b0 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -298,7 +298,8 @@ static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
/* if no UDMA is pending and no UDMA is in progress, then the DMA
is finished */
- while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
+ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
/* don't interrupt if the DMA is in progress but break off
a still pending DMA. */
got_sig = signal_pending(current);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c462b811e994..2d0781118eb0 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -1063,10 +1063,6 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = mx3_camera_add_device,
.remove = mx3_camera_remove_device,
-#ifdef CONFIG_PM
- .suspend = mx3_camera_suspend,
- .resume = mx3_camera_resume,
-#endif
.set_crop = mx3_camera_set_crop,
.set_fmt = mx3_camera_set_fmt,
.try_fmt = mx3_camera_try_fmt,
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 5202cadb2aae..30f4698be90a 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -1237,6 +1237,7 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
buffer[1] = (u32) chn_rev;
buffer[2] = CMD_SET_MODE;
memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode));
+ dev->setmode_ready[chn] = 0;
res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
if (debug)
dump_verify_mode(dev, mode);
@@ -1245,7 +1246,6 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
/* wait at least 3 frames before continuing */
if (mode->restart) {
- dev->setmode_ready[chn] = 0;
wait_event_timeout(dev->wait_setmode[chn],
(dev->setmode_ready[chn] != 0),
msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index da47b2f05288..155804b061e9 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -1092,9 +1092,8 @@ static int saa5246a_probe(struct i2c_client *client,
/* Register it */
err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
if (err < 0) {
- kfree(t);
video_device_release(t->vdev);
- t->vdev = NULL;
+ kfree(t);
return err;
}
return 0;
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index 48b27fe48087..271d6e931b75 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -598,6 +598,7 @@ static int saa5249_probe(struct i2c_client *client,
/* Now create a video4linux device */
t->vdev = video_device_alloc();
if (t->vdev == NULL) {
+ kfree(t);
kfree(client);
return -ENOMEM;
}
@@ -617,9 +618,8 @@ static int saa5249_probe(struct i2c_client *client,
/* Register it */
err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
if (err < 0) {
- kfree(t);
video_device_release(t->vdev);
- t->vdev = NULL;
+ kfree(t);
return err;
}
return 0;
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 399412d7f020..507dc85646b2 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1726,14 +1726,17 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
static int __uvc_resume(struct usb_interface *intf, int reset)
{
struct uvc_device *dev = usb_get_intfdata(intf);
- int ret;
uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) {
- if (reset && (ret = uvc_ctrl_resume_device(dev)) < 0)
- return ret;
+ if (reset) {
+ int ret = uvc_ctrl_resume_device(dev);
+
+ if (ret < 0)
+ return ret;
+ }
return uvc_status_resume(dev);
}
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index a95e17329c5b..6ce974d7362f 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -742,7 +742,7 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
/* Buffers are already allocated, bail out. */
if (video->urb_size)
- return 0;
+ return video->urb_size / psize;
/* Compute the number of packets. Bulk endpoints might transfer UVC
* payloads accross multiple URBs.
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 88f10d6cbc92..be64a502ea27 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -42,6 +42,12 @@
printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\
} while (0)
+/* Zero out the end of the struct pointed to by p. Everthing after, but
+ * not including, the specified field is cleared. */
+#define CLEAR_AFTER_FIELD(p, field) \
+ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
+ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+
struct std_descr {
v4l2_std_id std;
const char *descr;
@@ -544,39 +550,39 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_try_fmt_vid_cap)
+ if (ops->vidioc_g_fmt_vid_cap)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (ops->vidioc_try_fmt_vid_overlay)
+ if (ops->vidioc_g_fmt_vid_overlay)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_try_fmt_vid_out)
+ if (ops->vidioc_g_fmt_vid_out)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (ops->vidioc_try_fmt_vid_out_overlay)
+ if (ops->vidioc_g_fmt_vid_out_overlay)
return 0;
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (ops->vidioc_try_fmt_vbi_cap)
+ if (ops->vidioc_g_fmt_vbi_cap)
return 0;
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (ops->vidioc_try_fmt_vbi_out)
+ if (ops->vidioc_g_fmt_vbi_out)
return 0;
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (ops->vidioc_try_fmt_sliced_vbi_cap)
+ if (ops->vidioc_g_fmt_sliced_vbi_cap)
return 0;
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (ops->vidioc_try_fmt_sliced_vbi_out)
+ if (ops->vidioc_g_fmt_sliced_vbi_out)
return 0;
break;
case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_try_fmt_type_private)
+ if (ops->vidioc_g_fmt_type_private)
return 0;
break;
}
@@ -782,44 +788,53 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
if (ops->vidioc_s_fmt_vid_cap)
ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_s_fmt_vid_overlay)
ret = ops->vidioc_s_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
if (ops->vidioc_s_fmt_vid_out)
ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_s_fmt_vid_out_overlay)
ret = ops->vidioc_s_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
if (ops->vidioc_s_fmt_vbi_cap)
ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
if (ops->vidioc_s_fmt_vbi_out)
ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
if (ops->vidioc_s_fmt_sliced_vbi_cap)
ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
if (ops->vidioc_s_fmt_sliced_vbi_out)
ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
+ /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
if (ops->vidioc_s_fmt_type_private)
ret = ops->vidioc_s_fmt_type_private(file,
fh, f);
@@ -836,46 +851,55 @@ static long __video_do_ioctl(struct file *file,
v4l2_type_names));
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
if (ops->vidioc_try_fmt_vid_cap)
ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_try_fmt_vid_overlay)
ret = ops->vidioc_try_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
if (ops->vidioc_try_fmt_vid_out)
ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_try_fmt_vid_out_overlay)
ret = ops->vidioc_try_fmt_vid_out_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
if (ops->vidioc_try_fmt_vbi_cap)
ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
if (ops->vidioc_try_fmt_vbi_out)
ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
if (ops->vidioc_try_fmt_sliced_vbi_cap)
ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
if (ops->vidioc_try_fmt_sliced_vbi_out)
ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
+ /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
if (ops->vidioc_try_fmt_type_private)
ret = ops->vidioc_try_fmt_type_private(file,
fh, f);
@@ -898,6 +922,9 @@ static long __video_do_ioctl(struct file *file,
if (ret)
break;
+ if (p->type < V4L2_BUF_TYPE_PRIVATE)
+ CLEAR_AFTER_FIELD(p, memory);
+
ret = ops->vidioc_reqbufs(file, fh, p);
dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
p->count,
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 092333b1c34f..643cccaa1aab 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -1863,22 +1863,20 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability
static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
{
- int num = -1, i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (zoran_formats[i].flags & flag)
- num++;
- if (num == fmt->index)
- break;
+ unsigned int num, i;
+
+ for (num = i = 0; i < NUM_FORMATS; i++) {
+ if (zoran_formats[i].flags & flag && num++ == fmt->index) {
+ strncpy(fmt->description, zoran_formats[i].name,
+ sizeof(fmt->description) - 1);
+ /* fmt struct pre-zeroed, so adding '\0' not neeed */
+ fmt->pixelformat = zoran_formats[i].fourcc;
+ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+ }
}
- if (fmt->index < 0 /* late, but not too late */ || i == NUM_FORMATS)
- return -EINVAL;
-
- strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1);
- fmt->pixelformat = zoran_formats[i].fourcc;
- if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
- fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
+ return -EINVAL;
}
static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index c0bebc6a2f2c..7847bbc1440d 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -1242,7 +1242,7 @@ static int mspro_block_init_disk(struct memstick_dev *card)
sprintf(msb->disk->disk_name, "mspblk%d", disk_id);
- blk_queue_hardsect_size(msb->queue, msb->page_size);
+ blk_queue_logical_block_size(msb->queue, msb->page_size);
capacity = be16_to_cpu(sys_info->user_block_count);
capacity *= be16_to_cpu(sys_info->block_size);
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 5d496a99e034..44b931504457 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -146,7 +146,6 @@ static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];
static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS];
static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS];
-static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq);
/*
* Driver Callback Index's
@@ -159,7 +158,8 @@ static u8 last_drv_idx;
* Forward protos...
*/
static irqreturn_t mpt_interrupt(int irq, void *bus_id);
-static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
+static int mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+ MPT_FRAME_HDR *reply);
static int mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes,
u32 *req, int replyBytes, u16 *u16reply, int maxwait,
int sleepFlag);
@@ -190,9 +190,9 @@ static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);
static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);
static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc);
static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc);
-static void mpt_timer_expired(unsigned long data);
static void mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc);
-static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch);
+static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch,
+ int sleepFlag);
static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);
static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag);
static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init);
@@ -207,8 +207,8 @@ static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
#endif
static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
-//int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
-static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers);
+static int ProcessEventNotification(MPT_ADAPTER *ioc,
+ EventNotificationReply_t *evReply, int *evHandlers);
static void mpt_iocstatus_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf);
static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);
static void mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info);
@@ -277,6 +277,56 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
}
/**
+ * mpt_is_discovery_complete - determine if discovery has completed
+ * @ioc: per adatper instance
+ *
+ * Returns 1 when discovery completed, else zero.
+ */
+static int
+mpt_is_discovery_complete(MPT_ADAPTER *ioc)
+{
+ ConfigExtendedPageHeader_t hdr;
+ CONFIGPARMS cfg;
+ SasIOUnitPage0_t *buffer;
+ dma_addr_t dma_handle;
+ int rc = 0;
+
+ memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t));
+ memset(&cfg, 0, sizeof(CONFIGPARMS));
+ hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
+ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ cfg.cfghdr.ehdr = &hdr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+ if ((mpt_config(ioc, &cfg)))
+ goto out;
+ if (!hdr.ExtPageLength)
+ goto out;
+
+ buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+ &dma_handle);
+ if (!buffer)
+ goto out;
+
+ cfg.physAddr = dma_handle;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ if ((mpt_config(ioc, &cfg)))
+ goto out_free_consistent;
+
+ if (!(buffer->PhyData[0].PortFlags &
+ MPI_SAS_IOUNIT0_PORT_FLAGS_DISCOVERY_IN_PROGRESS))
+ rc = 1;
+
+ out_free_consistent:
+ pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+ buffer, dma_handle);
+ out:
+ return rc;
+}
+
+/**
* mpt_fault_reset_work - work performed on workq after ioc fault
* @work: input argument, used to derive ioc
*
@@ -290,7 +340,7 @@ mpt_fault_reset_work(struct work_struct *work)
int rc;
unsigned long flags;
- if (ioc->diagPending || !ioc->active)
+ if (ioc->ioc_reset_in_progress || !ioc->active)
goto out;
ioc_raw_state = mpt_GetIocState(ioc, 0);
@@ -307,6 +357,12 @@ mpt_fault_reset_work(struct work_struct *work)
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
"reset (%04xh)\n", ioc->name, ioc_raw_state &
MPI_DOORBELL_DATA_MASK);
+ } else if (ioc->bus_type == SAS && ioc->sas_discovery_quiesce_io) {
+ if ((mpt_is_discovery_complete(ioc))) {
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "clearing "
+ "discovery_quiesce_io flag\n", ioc->name));
+ ioc->sas_discovery_quiesce_io = 0;
+ }
}
out:
@@ -317,11 +373,11 @@ mpt_fault_reset_work(struct work_struct *work)
ioc = ioc->alt_ioc;
/* rearm the timer */
- spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
if (ioc->reset_work_q)
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
msecs_to_jiffies(MPT_POLLING_INTERVAL));
- spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
}
@@ -501,9 +557,9 @@ mpt_interrupt(int irq, void *bus_id)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mpt_base_reply - MPT base driver's callback routine
+ * mptbase_reply - MPT base driver's callback routine
* @ioc: Pointer to MPT_ADAPTER structure
- * @mf: Pointer to original MPT request frame
+ * @req: Pointer to original MPT request frame
* @reply: Pointer to MPT reply frame (NULL if TurboReply)
*
* MPT base driver's callback routine; all base driver
@@ -514,122 +570,49 @@ mpt_interrupt(int irq, void *bus_id)
* should be freed, or 0 if it shouldn't.
*/
static int
-mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
+mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
{
+ EventNotificationReply_t *pEventReply;
+ u8 event;
+ int evHandlers;
int freereq = 1;
- u8 func;
- dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply() called\n", ioc->name));
-#ifdef CONFIG_FUSION_LOGGING
- if ((ioc->debug_level & MPT_DEBUG_MSG_FRAME) &&
- !(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) {
- dmfprintk(ioc, printk(MYIOC_s_INFO_FMT ": Original request frame (@%p) header\n",
- ioc->name, mf));
- DBG_DUMP_REQUEST_FRAME_HDR(ioc, (u32 *)mf);
- }
-#endif
-
- func = reply->u.hdr.Function;
- dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply, Function=%02Xh\n",
- ioc->name, func));
-
- if (func == MPI_FUNCTION_EVENT_NOTIFICATION) {
- EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply;
- int evHandlers = 0;
- int results;
-
- results = ProcessEventNotification(ioc, pEvReply, &evHandlers);
- if (results != evHandlers) {
- /* CHECKME! Any special handling needed here? */
- devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n",
- ioc->name, evHandlers, results));
- }
-
- /*
- * Hmmm... It seems that EventNotificationReply is an exception
- * to the rule of one reply per request.
- */
- if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY) {
+ switch (reply->u.hdr.Function) {
+ case MPI_FUNCTION_EVENT_NOTIFICATION:
+ pEventReply = (EventNotificationReply_t *)reply;
+ evHandlers = 0;
+ ProcessEventNotification(ioc, pEventReply, &evHandlers);
+ event = le32_to_cpu(pEventReply->Event) & 0xFF;
+ if (pEventReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)
freereq = 0;
- } else {
- devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "EVENT_NOTIFICATION reply %p returns Request frame\n",
- ioc->name, pEvReply));
- }
-
-#ifdef CONFIG_PROC_FS
-// LogEvent(ioc, pEvReply);
-#endif
-
- } else if (func == MPI_FUNCTION_EVENT_ACK) {
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply, EventAck reply received\n",
- ioc->name));
- } else if (func == MPI_FUNCTION_CONFIG) {
- CONFIGPARMS *pCfg;
- unsigned long flags;
-
- dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "config_complete (mf=%p,mr=%p)\n",
- ioc->name, mf, reply));
-
- pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
-
- if (pCfg) {
- /* disable timer and remove from linked list */
- del_timer(&pCfg->timer);
-
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- list_del(&pCfg->linkage);
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
- /*
- * If IOC Status is SUCCESS, save the header
- * and set the status code to GOOD.
- */
- pCfg->status = MPT_CONFIG_ERROR;
- if (reply) {
- ConfigReply_t *pReply = (ConfigReply_t *)reply;
- u16 status;
-
- status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- dcprintk(ioc, printk(MYIOC_s_NOTE_FMT " IOCStatus=%04xh, IOCLogInfo=%08xh\n",
- ioc->name, status, le32_to_cpu(pReply->IOCLogInfo)));
-
- pCfg->status = status;
- if (status == MPI_IOCSTATUS_SUCCESS) {
- if ((pReply->Header.PageType &
- MPI_CONFIG_PAGETYPE_MASK) ==
- MPI_CONFIG_PAGETYPE_EXTENDED) {
- pCfg->cfghdr.ehdr->ExtPageLength =
- le16_to_cpu(pReply->ExtPageLength);
- pCfg->cfghdr.ehdr->ExtPageType =
- pReply->ExtPageType;
- }
- pCfg->cfghdr.hdr->PageVersion = pReply->Header.PageVersion;
-
- /* If this is a regular header, save PageLength. */
- /* LMP Do this better so not using a reserved field! */
- pCfg->cfghdr.hdr->PageLength = pReply->Header.PageLength;
- pCfg->cfghdr.hdr->PageNumber = pReply->Header.PageNumber;
- pCfg->cfghdr.hdr->PageType = pReply->Header.PageType;
- }
- }
-
- /*
- * Wake up the original calling thread
- */
- pCfg->wait_done = 1;
- wake_up(&mpt_waitq);
+ if (event != MPI_EVENT_EVENT_CHANGE)
+ break;
+ case MPI_FUNCTION_CONFIG:
+ case MPI_FUNCTION_SAS_IO_UNIT_CONTROL:
+ ioc->mptbase_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+ if (reply) {
+ ioc->mptbase_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ memcpy(ioc->mptbase_cmds.reply, reply,
+ min(MPT_DEFAULT_FRAME_SIZE,
+ 4 * reply->u.reply.MsgLength));
}
- } else if (func == MPI_FUNCTION_SAS_IO_UNIT_CONTROL) {
- /* we should be always getting a reply frame */
- memcpy(ioc->persist_reply_frame, reply,
- min(MPT_DEFAULT_FRAME_SIZE,
- 4*reply->u.reply.MsgLength));
- del_timer(&ioc->persist_timer);
- ioc->persist_wait_done = 1;
- wake_up(&mpt_waitq);
- } else {
- printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n",
- ioc->name, func);
+ if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->mptbase_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->mptbase_cmds.done);
+ } else
+ freereq = 0;
+ if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_FREE_MF)
+ freereq = 1;
+ break;
+ case MPI_FUNCTION_EVENT_ACK:
+ devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "EventAck reply received\n", ioc->name));
+ break;
+ default:
+ printk(MYIOC_s_ERR_FMT
+ "Unexpected msg function (=%02Xh) reply received!\n",
+ ioc->name, reply->u.hdr.Function);
+ break;
}
/*
@@ -988,17 +971,21 @@ mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
/* Put Request back on FreeQ! */
spin_lock_irqsave(&ioc->FreeQlock, flags);
- mf->u.frame.linkage.arg1 = 0xdeadbeaf; /* signature to know if this mf is freed */
+ if (cpu_to_le32(mf->u.frame.linkage.arg1) == 0xdeadbeaf)
+ goto out;
+ /* signature to know if this mf is freed */
+ mf->u.frame.linkage.arg1 = cpu_to_le32(0xdeadbeaf);
list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ);
#ifdef MFCNT
ioc->mfcnt--;
#endif
+ out:
spin_unlock_irqrestore(&ioc->FreeQlock, flags);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mpt_add_sge - Place a simple SGE at address pAddr.
+ * mpt_add_sge - Place a simple 32 bit SGE at address pAddr.
* @pAddr: virtual address for SGE
* @flagslength: SGE flags and data transfer length
* @dma_addr: Physical address
@@ -1006,23 +993,117 @@ mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
* This routine places a MPT request frame back on the MPT adapter's
* FreeQ.
*/
-void
-mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
+static void
+mpt_add_sge(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
{
- if (sizeof(dma_addr_t) == sizeof(u64)) {
- SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address = cpu_to_le32(dma_addr);
+}
+
+/**
+ * mpt_add_sge_64bit - Place a simple 64 bit SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @flagslength: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ **/
+static void
+mpt_add_sge_64bit(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+ SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ pSge->Address.Low = cpu_to_le32
+ (lower_32_bits((unsigned long)(dma_addr)));
+ pSge->Address.High = cpu_to_le32
+ (upper_32_bits((unsigned long)dma_addr));
+ pSge->FlagsLength = cpu_to_le32
+ ((flagslength | MPT_SGE_FLAGS_64_BIT_ADDRESSING));
+}
+
+/**
+ * mpt_add_sge_64bit_1078 - Place a simple 64 bit SGE at address pAddr
+ * (1078 workaround).
+ * @pAddr: virtual address for SGE
+ * @flagslength: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ **/
+static void
+mpt_add_sge_64bit_1078(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+ SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ u32 tmp;
+
+ pSge->Address.Low = cpu_to_le32
+ (lower_32_bits((unsigned long)(dma_addr)));
+ tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+
+ /*
+ * 1078 errata workaround for the 36GB limitation
+ */
+ if ((((u64)dma_addr + MPI_SGE_LENGTH(flagslength)) >> 32) == 9) {
+ flagslength |=
+ MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LOCAL_ADDRESS);
+ tmp |= (1<<31);
+ if (mpt_debug_level & MPT_DEBUG_36GB_MEM)
+ printk(KERN_DEBUG "1078 P0M2 addressing for "
+ "addr = 0x%llx len = %d\n",
+ (unsigned long long)dma_addr,
+ MPI_SGE_LENGTH(flagslength));
+ }
+
+ pSge->Address.High = cpu_to_le32(tmp);
+ pSge->FlagsLength = cpu_to_le32(
+ (flagslength | MPT_SGE_FLAGS_64_BIT_ADDRESSING));
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_add_chain - Place a 32 bit chain SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @next: nextChainOffset value (u32's)
+ * @length: length of next SGL segment
+ * @dma_addr: Physical address
+ *
+ */
+static void
+mpt_add_chain(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+ SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT;
+ pChain->NextChainOffset = next;
+ pChain->Address = cpu_to_le32(dma_addr);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_add_chain_64bit - Place a 64 bit chain SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @next: nextChainOffset value (u32's)
+ * @length: length of next SGL segment
+ * @dma_addr: Physical address
+ *
+ */
+static void
+mpt_add_chain_64bit(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+ SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
u32 tmp = dma_addr & 0xFFFFFFFF;
- pSge->FlagsLength = cpu_to_le32(flagslength);
- pSge->Address.Low = cpu_to_le32(tmp);
- tmp = (u32) ((u64)dma_addr >> 32);
- pSge->Address.High = cpu_to_le32(tmp);
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = (MPI_SGE_FLAGS_CHAIN_ELEMENT |
+ MPI_SGE_FLAGS_64_BIT_ADDRESSING);
- } else {
- SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
- pSge->FlagsLength = cpu_to_le32(flagslength);
- pSge->Address = cpu_to_le32(dma_addr);
- }
+ pChain->NextChainOffset = next;
+
+ pChain->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+ pChain->Address.High = cpu_to_le32(tmp);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -1225,7 +1306,7 @@ mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init)
}
flags_length = flags_length << MPI_SGE_FLAGS_SHIFT;
flags_length |= ioc->HostPageBuffer_sz;
- mpt_add_sge(psge, flags_length, ioc->HostPageBuffer_dma);
+ ioc->add_sge(psge, flags_length, ioc->HostPageBuffer_dma);
ioc->facts.HostPageBufferSGE = ioc_init->HostPageBufferSGE;
return 0;
@@ -1534,21 +1615,42 @@ mpt_mapresources(MPT_ADAPTER *ioc)
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
- dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
- ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
- ioc->name));
- } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
- ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
- ioc->name));
+ if (sizeof(dma_addr_t) > 4) {
+ const uint64_t required_mask = dma_get_required_mask
+ (&pdev->dev);
+ if (required_mask > DMA_BIT_MASK(32)
+ && !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+ && !pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(64))) {
+ ioc->dma_mask = DMA_BIT_MASK(64);
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+ ioc->name));
+ } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ && !pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32))) {
+ ioc->dma_mask = DMA_BIT_MASK(32);
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+ ioc->name));
+ } else {
+ printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
+ ioc->name, pci_name(pdev));
+ return r;
+ }
} else {
- printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
- ioc->name, pci_name(pdev));
- pci_release_selected_regions(pdev, ioc->bars);
- return r;
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ && !pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32))) {
+ ioc->dma_mask = DMA_BIT_MASK(32);
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+ ioc->name));
+ } else {
+ printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
+ ioc->name, pci_name(pdev));
+ return r;
+ }
}
mem_phys = msize = 0;
@@ -1632,6 +1734,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->id = mpt_ids++;
sprintf(ioc->name, "ioc%d", ioc->id);
+ dinitprintk(ioc, printk(KERN_WARNING MYNAM ": mpt_adapter_install\n"));
/*
* set initial debug level
@@ -1650,14 +1753,36 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
return r;
}
+ /*
+ * Setting up proper handlers for scatter gather handling
+ */
+ if (ioc->dma_mask == DMA_BIT_MASK(64)) {
+ if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1078)
+ ioc->add_sge = &mpt_add_sge_64bit_1078;
+ else
+ ioc->add_sge = &mpt_add_sge_64bit;
+ ioc->add_chain = &mpt_add_chain_64bit;
+ ioc->sg_addr_size = 8;
+ } else {
+ ioc->add_sge = &mpt_add_sge;
+ ioc->add_chain = &mpt_add_chain;
+ ioc->sg_addr_size = 4;
+ }
+ ioc->SGE_size = sizeof(u32) + ioc->sg_addr_size;
+
ioc->alloc_total = sizeof(MPT_ADAPTER);
ioc->req_sz = MPT_DEFAULT_FRAME_SIZE; /* avoid div by zero! */
ioc->reply_sz = MPT_REPLY_FRAME_SIZE;
ioc->pcidev = pdev;
- ioc->diagPending = 0;
- spin_lock_init(&ioc->diagLock);
- spin_lock_init(&ioc->initializing_hba_lock);
+
+ spin_lock_init(&ioc->taskmgmt_lock);
+ mutex_init(&ioc->internal_cmds.mutex);
+ init_completion(&ioc->internal_cmds.done);
+ mutex_init(&ioc->mptbase_cmds.mutex);
+ init_completion(&ioc->mptbase_cmds.done);
+ mutex_init(&ioc->taskmgmt_cmds.mutex);
+ init_completion(&ioc->taskmgmt_cmds.done);
/* Initialize the event logging.
*/
@@ -1670,16 +1795,13 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->mfcnt = 0;
#endif
+ ioc->sh = NULL;
ioc->cached_fw = NULL;
/* Initilize SCSI Config Data structure
*/
memset(&ioc->spi_data, 0, sizeof(SpiCfgData));
- /* Initialize the running configQ head.
- */
- INIT_LIST_HEAD(&ioc->configQ);
-
/* Initialize the fc rport list head.
*/
INIT_LIST_HEAD(&ioc->fc_rports);
@@ -1690,9 +1812,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
/* Initialize workqueue */
INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
- spin_lock_init(&ioc->fault_reset_work_lock);
- snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name),
+ snprintf(ioc->reset_work_q_name, MPT_KOBJ_NAME_LEN,
"mpt_poll_%d", ioc->id);
ioc->reset_work_q =
create_singlethread_workqueue(ioc->reset_work_q_name);
@@ -1767,11 +1888,14 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
case MPI_MANUFACTPAGE_DEVID_SAS1064:
case MPI_MANUFACTPAGE_DEVID_SAS1068:
ioc->errata_flag_1064 = 1;
+ ioc->bus_type = SAS;
+ break;
case MPI_MANUFACTPAGE_DEVID_SAS1064E:
case MPI_MANUFACTPAGE_DEVID_SAS1068E:
case MPI_MANUFACTPAGE_DEVID_SAS1078:
ioc->bus_type = SAS;
+ break;
}
@@ -1813,6 +1937,11 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
*/
mpt_detect_bound_ports(ioc, pdev);
+ INIT_LIST_HEAD(&ioc->fw_event_list);
+ spin_lock_init(&ioc->fw_event_lock);
+ snprintf(ioc->fw_event_q_name, MPT_KOBJ_NAME_LEN, "mpt/%d", ioc->id);
+ ioc->fw_event_q = create_singlethread_workqueue(ioc->fw_event_q_name);
+
if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP,
CAN_SLEEP)) != 0){
printk(MYIOC_s_ERR_FMT "didn't initialize properly! (%d)\n",
@@ -1885,13 +2014,18 @@ mpt_detach(struct pci_dev *pdev)
/*
* Stop polling ioc for fault condition
*/
- spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
wq = ioc->reset_work_q;
ioc->reset_work_q = NULL;
- spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
cancel_delayed_work(&ioc->fault_reset_work);
destroy_workqueue(wq);
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->fw_event_q;
+ ioc->fw_event_q = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ destroy_workqueue(wq);
sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
remove_proc_entry(pname, NULL);
@@ -1994,6 +2128,21 @@ mpt_resume(struct pci_dev *pdev)
if (err)
return err;
+ if (ioc->dma_mask == DMA_BIT_MASK(64)) {
+ if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1078)
+ ioc->add_sge = &mpt_add_sge_64bit_1078;
+ else
+ ioc->add_sge = &mpt_add_sge_64bit;
+ ioc->add_chain = &mpt_add_chain_64bit;
+ ioc->sg_addr_size = 8;
+ } else {
+
+ ioc->add_sge = &mpt_add_sge;
+ ioc->add_chain = &mpt_add_chain;
+ ioc->sg_addr_size = 4;
+ }
+ ioc->SGE_size = sizeof(u32) + ioc->sg_addr_size;
+
printk(MYIOC_s_INFO_FMT "pci-resume: ioc-state=0x%x,doorbell=0x%x\n",
ioc->name, (mpt_GetIocState(ioc, 1) >> MPI_IOC_STATE_SHIFT),
CHIPREG_READ32(&ioc->chip->Doorbell));
@@ -2091,12 +2240,16 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
ioc->active = 0;
if (ioc->alt_ioc) {
- if (ioc->alt_ioc->active)
+ if (ioc->alt_ioc->active ||
+ reason == MPT_HOSTEVENT_IOC_RECOVER) {
reset_alt_ioc_active = 1;
-
- /* Disable alt-IOC's reply interrupts (and FreeQ) for a bit ... */
- CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF);
- ioc->alt_ioc->active = 0;
+ /* Disable alt-IOC's reply interrupts
+ * (and FreeQ) for a bit
+ **/
+ CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask,
+ 0xFFFFFFFF);
+ ioc->alt_ioc->active = 0;
+ }
}
hard = 1;
@@ -2117,9 +2270,11 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
}
} else {
- printk(MYIOC_s_WARN_FMT "NOT READY!\n", ioc->name);
+ printk(MYIOC_s_WARN_FMT
+ "NOT READY WARNING!\n", ioc->name);
}
- return -1;
+ ret = -1;
+ goto out;
}
/* hard_reset_done = 0 if a soft reset was performed
@@ -2129,7 +2284,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
if ((rc = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0)
alt_ioc_ready = 1;
else
- printk(MYIOC_s_WARN_FMT "alt_ioc not ready!\n", ioc->alt_ioc->name);
+ printk(MYIOC_s_WARN_FMT
+ ": alt-ioc Not ready WARNING!\n",
+ ioc->alt_ioc->name);
}
for (ii=0; ii<5; ii++) {
@@ -2150,7 +2307,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
if (alt_ioc_ready) {
if ((rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) {
dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "Initial Alt IocFacts failed rc=%x\n", ioc->name, rc));
+ "Initial Alt IocFacts failed rc=%x\n",
+ ioc->name, rc));
/* Retry - alt IOC was initialized once
*/
rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason);
@@ -2194,16 +2352,20 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
IRQF_SHARED, ioc->name, ioc);
if (rc < 0) {
printk(MYIOC_s_ERR_FMT "Unable to allocate "
- "interrupt %d!\n", ioc->name, ioc->pcidev->irq);
+ "interrupt %d!\n",
+ ioc->name, ioc->pcidev->irq);
if (ioc->msi_enable)
pci_disable_msi(ioc->pcidev);
- return -EBUSY;
+ ret = -EBUSY;
+ goto out;
}
irq_allocated = 1;
ioc->pci_irq = ioc->pcidev->irq;
pci_set_master(ioc->pcidev); /* ?? */
- dprintk(ioc, printk(MYIOC_s_INFO_FMT "installed at interrupt "
- "%d\n", ioc->name, ioc->pcidev->irq));
+ pci_set_drvdata(ioc->pcidev, ioc);
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ "installed at interrupt %d\n", ioc->name,
+ ioc->pcidev->irq));
}
}
@@ -2212,17 +2374,22 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
* init as upper addresses are needed for init.
* If fails, continue with alt-ioc processing
*/
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "PrimeIocFifos\n",
+ ioc->name));
if ((ret == 0) && ((rc = PrimeIocFifos(ioc)) != 0))
ret = -3;
/* May need to check/upload firmware & data here!
* If fails, continue with alt-ioc processing
*/
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "SendIocInit\n",
+ ioc->name));
if ((ret == 0) && ((rc = SendIocInit(ioc, sleepFlag)) != 0))
ret = -4;
// NEW!
if (alt_ioc_ready && ((rc = PrimeIocFifos(ioc->alt_ioc)) != 0)) {
- printk(MYIOC_s_WARN_FMT ": alt_ioc (%d) FIFO mgmt alloc!\n",
+ printk(MYIOC_s_WARN_FMT
+ ": alt-ioc (%d) FIFO mgmt alloc WARNING!\n",
ioc->alt_ioc->name, rc);
alt_ioc_ready = 0;
reset_alt_ioc_active = 0;
@@ -2232,8 +2399,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
if ((rc = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) {
alt_ioc_ready = 0;
reset_alt_ioc_active = 0;
- printk(MYIOC_s_WARN_FMT "alt_ioc (%d) init failure!\n",
- ioc->alt_ioc->name, rc);
+ printk(MYIOC_s_WARN_FMT
+ ": alt-ioc: (%d) init failure WARNING!\n",
+ ioc->alt_ioc->name, rc);
}
}
@@ -2269,28 +2437,36 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
}
}
+ /* Enable MPT base driver management of EventNotification
+ * and EventAck handling.
+ */
+ if ((ret == 0) && (!ioc->facts.EventState)) {
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ "SendEventNotification\n",
+ ioc->name));
+ ret = SendEventNotification(ioc, 1, sleepFlag); /* 1=Enable */
+ }
+
+ if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
+ rc = SendEventNotification(ioc->alt_ioc, 1, sleepFlag);
+
if (ret == 0) {
/* Enable! (reply interrupt) */
CHIPREG_WRITE32(&ioc->chip->IntMask, MPI_HIM_DIM);
ioc->active = 1;
}
-
- if (reset_alt_ioc_active && ioc->alt_ioc) {
- /* (re)Enable alt-IOC! (reply interrupt) */
- dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "alt_ioc reply irq re-enabled\n",
- ioc->alt_ioc->name));
- CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, MPI_HIM_DIM);
- ioc->alt_ioc->active = 1;
+ if (rc == 0) { /* alt ioc */
+ if (reset_alt_ioc_active && ioc->alt_ioc) {
+ /* (re)Enable alt-IOC! (reply interrupt) */
+ dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "alt-ioc"
+ "reply irq re-enabled\n",
+ ioc->alt_ioc->name));
+ CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask,
+ MPI_HIM_DIM);
+ ioc->alt_ioc->active = 1;
+ }
}
- /* Enable MPT base driver management of EventNotification
- * and EventAck handling.
- */
- if ((ret == 0) && (!ioc->facts.EventState))
- (void) SendEventNotification(ioc, 1); /* 1=Enable EventNotification */
-
- if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
- (void) SendEventNotification(ioc->alt_ioc, 1); /* 1=Enable EventNotification */
/* Add additional "reason" check before call to GetLanConfigPages
* (combined with GetIoUnitPage2 call). This prevents a somewhat
@@ -2306,8 +2482,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
mutex_init(&ioc->raid_data.inactive_list_mutex);
INIT_LIST_HEAD(&ioc->raid_data.inactive_list);
- if (ioc->bus_type == SAS) {
+ switch (ioc->bus_type) {
+ case SAS:
/* clear persistency table */
if(ioc->facts.IOCExceptions &
MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL) {
@@ -2321,8 +2498,15 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
*/
mpt_findImVolumes(ioc);
- } else if (ioc->bus_type == FC) {
- if ((ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) &&
+ /* Check, and possibly reset, the coalescing value
+ */
+ mpt_read_ioc_pg_1(ioc);
+
+ break;
+
+ case FC:
+ if ((ioc->pfacts[0].ProtocolFlags &
+ MPI_PORTFACTS_PROTOCOL_LAN) &&
(ioc->lan_cnfg_page0.Header.PageLength == 0)) {
/*
* Pre-fetch the ports LAN MAC address!
@@ -2331,11 +2515,14 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
(void) GetLanConfigPages(ioc);
a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
- ioc->name, a[5], a[4], a[3], a[2], a[1], a[0]));
-
+ "LanAddr = %02X:%02X:%02X"
+ ":%02X:%02X:%02X\n",
+ ioc->name, a[5], a[4],
+ a[3], a[2], a[1], a[0]));
}
- } else {
+ break;
+
+ case SPI:
/* Get NVRAM and adapter maximums from SPP 0 and 2
*/
mpt_GetScsiPortSettings(ioc, 0);
@@ -2354,6 +2541,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
mpt_read_ioc_pg_1(ioc);
mpt_read_ioc_pg_4(ioc);
+
+ break;
}
GetIoUnitPage2(ioc);
@@ -2435,16 +2624,20 @@ mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev)
if (_pcidev == peer) {
/* Paranoia checks */
if (ioc->alt_ioc != NULL) {
- printk(MYIOC_s_WARN_FMT "Oops, already bound to %s!\n",
- ioc->name, ioc->alt_ioc->name);
+ printk(MYIOC_s_WARN_FMT
+ "Oops, already bound (%s <==> %s)!\n",
+ ioc->name, ioc->name, ioc->alt_ioc->name);
break;
} else if (ioc_srch->alt_ioc != NULL) {
- printk(MYIOC_s_WARN_FMT "Oops, already bound to %s!\n",
- ioc_srch->name, ioc_srch->alt_ioc->name);
+ printk(MYIOC_s_WARN_FMT
+ "Oops, already bound (%s <==> %s)!\n",
+ ioc_srch->name, ioc_srch->name,
+ ioc_srch->alt_ioc->name);
break;
}
- dprintk(ioc, printk(MYIOC_s_INFO_FMT "FOUND! binding to %s\n",
- ioc->name, ioc_srch->name));
+ dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "FOUND! binding %s <==> %s\n",
+ ioc->name, ioc->name, ioc_srch->name));
ioc_srch->alt_ioc = ioc;
ioc->alt_ioc = ioc_srch;
}
@@ -2464,8 +2657,8 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
int ret;
if (ioc->cached_fw != NULL) {
- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: Pushing FW onto "
- "adapter\n", __func__, ioc->name));
+ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: Pushing FW onto adapter\n", __func__, ioc->name));
if ((ret = mpt_downloadboot(ioc, (MpiFwHeader_t *)
ioc->cached_fw, CAN_SLEEP)) < 0) {
printk(MYIOC_s_WARN_FMT
@@ -2474,11 +2667,30 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
}
}
+ /*
+ * Put the controller into ready state (if its not already)
+ */
+ if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_READY) {
+ if (!SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET,
+ CAN_SLEEP)) {
+ if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_READY)
+ printk(MYIOC_s_ERR_FMT "%s: IOC msg unit "
+ "reset failed to put ioc in ready state!\n",
+ ioc->name, __func__);
+ } else
+ printk(MYIOC_s_ERR_FMT "%s: IOC msg unit reset "
+ "failed!\n", ioc->name, __func__);
+ }
+
+
/* Disable adapter interrupts! */
+ synchronize_irq(ioc->pcidev->irq);
CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF);
ioc->active = 0;
+
/* Clear any lingering interrupt */
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+ CHIPREG_READ32(&ioc->chip->IntStatus);
if (ioc->alloc != NULL) {
sz = ioc->alloc_sz;
@@ -2538,19 +2750,22 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
if((ret = mpt_host_page_access_control(ioc,
MPI_DB_HPBAC_FREE_BUFFER, NO_SLEEP)) != 0) {
printk(MYIOC_s_ERR_FMT
- "host page buffers free failed (%d)!\n",
- ioc->name, ret);
+ ": %s: host page buffers free failed (%d)!\n",
+ ioc->name, __func__, ret);
}
- dexitprintk(ioc, printk(MYIOC_s_INFO_FMT "HostPageBuffer free @ %p, sz=%d bytes\n",
- ioc->name, ioc->HostPageBuffer, ioc->HostPageBuffer_sz));
+ dexitprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "HostPageBuffer free @ %p, sz=%d bytes\n",
+ ioc->name, ioc->HostPageBuffer,
+ ioc->HostPageBuffer_sz));
pci_free_consistent(ioc->pcidev, ioc->HostPageBuffer_sz,
ioc->HostPageBuffer, ioc->HostPageBuffer_dma);
ioc->HostPageBuffer = NULL;
ioc->HostPageBuffer_sz = 0;
ioc->alloc_total -= ioc->HostPageBuffer_sz;
}
-}
+ pci_set_drvdata(ioc->pcidev, NULL);
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mpt_adapter_dispose - Free all resources associated with an MPT adapter
@@ -2690,8 +2905,12 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
}
/* Is it already READY? */
- if (!statefault && (ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY)
+ if (!statefault &&
+ ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY)) {
+ dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+ "IOC is in READY state\n", ioc->name));
return 0;
+ }
/*
* Check to see if IOC is in FAULT state.
@@ -2764,8 +2983,9 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
ii++; cntdn--;
if (!cntdn) {
- printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n",
- ioc->name, (int)((ii+5)/HZ));
+ printk(MYIOC_s_ERR_FMT
+ "Wait IOC_READY state (0x%x) timeout(%d)!\n",
+ ioc->name, ioc_state, (int)((ii+5)/HZ));
return -ETIME;
}
@@ -2778,9 +2998,8 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
}
if (statefault < 3) {
- printk(MYIOC_s_INFO_FMT "Recovered from %s\n",
- ioc->name,
- statefault==1 ? "stuck handshake" : "IOC FAULT");
+ printk(MYIOC_s_INFO_FMT "Recovered from %s\n", ioc->name,
+ statefault == 1 ? "stuck handshake" : "IOC FAULT");
}
return hard_reset_done;
@@ -2833,8 +3052,9 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
/* IOC *must* NOT be in RESET state! */
if (ioc->last_state == MPI_IOC_STATE_RESET) {
- printk(MYIOC_s_ERR_FMT "Can't get IOCFacts NOT READY! (%08x)\n",
- ioc->name, ioc->last_state );
+ printk(KERN_ERR MYNAM
+ ": ERROR - Can't get IOCFacts, %s NOT READY! (%08x)\n",
+ ioc->name, ioc->last_state);
return -44;
}
@@ -2896,7 +3116,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
* Old: u16{Major(4),Minor(4),SubMinor(8)}
* New: u32{Major(8),Minor(8),Unit(8),Dev(8)}
*/
- if (facts->MsgVersion < 0x0102) {
+ if (facts->MsgVersion < MPI_VERSION_01_02) {
/*
* Handle old FC f/w style, convert to new...
*/
@@ -2908,9 +3128,11 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
facts->FWVersion.Word = le32_to_cpu(facts->FWVersion.Word);
facts->ProductID = le16_to_cpu(facts->ProductID);
+
if ((ioc->facts.ProductID & MPI_FW_HEADER_PID_PROD_MASK)
> MPI_FW_HEADER_PID_PROD_TARGET_SCSI)
ioc->ir_firmware = 1;
+
facts->CurrentHostMfaHighAddr =
le32_to_cpu(facts->CurrentHostMfaHighAddr);
facts->GlobalCredits = le16_to_cpu(facts->GlobalCredits);
@@ -2926,7 +3148,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
* to 14 in MPI-1.01.0x.
*/
if (facts->MsgLength >= (offsetof(IOCFactsReply_t,FWImageSize) + 7)/4 &&
- facts->MsgVersion > 0x0100) {
+ facts->MsgVersion > MPI_VERSION_01_00) {
facts->FWImageSize = le32_to_cpu(facts->FWImageSize);
}
@@ -3108,6 +3330,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
ioc_init.MaxDevices = (U8)ioc->devices_per_bus;
ioc_init.MaxBuses = (U8)ioc->number_of_buses;
+
dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "facts.MsgVersion=%x\n",
ioc->name, ioc->facts.MsgVersion));
if (ioc->facts.MsgVersion >= MPI_VERSION_01_05) {
@@ -3122,7 +3345,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
}
ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */
- if (sizeof(dma_addr_t) == sizeof(u64)) {
+ if (ioc->sg_addr_size == sizeof(u64)) {
/* Save the upper 32-bits of the request
* (reply) and sense buffers.
*/
@@ -3325,11 +3548,10 @@ mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag)
FWUpload_t *prequest;
FWUploadReply_t *preply;
FWUploadTCSGE_t *ptcsge;
- int sgeoffset;
u32 flagsLength;
int ii, sz, reply_sz;
int cmdStatus;
-
+ int request_size;
/* If the image size is 0, we are done.
*/
if ((sz = ioc->facts.FWImageSize) == 0)
@@ -3364,42 +3586,41 @@ mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag)
ptcsge->ImageSize = cpu_to_le32(sz);
ptcsge++;
- sgeoffset = sizeof(FWUpload_t) - sizeof(SGE_MPI_UNION) + sizeof(FWUploadTCSGE_t);
-
flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ | sz;
- mpt_add_sge((char *)ptcsge, flagsLength, ioc->cached_fw_dma);
-
- sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
- dinitprintk(ioc, printk(MYIOC_s_INFO_FMT ": Sending FW Upload (req @ %p) sgeoffset=%d \n",
- ioc->name, prequest, sgeoffset));
+ ioc->add_sge((char *)ptcsge, flagsLength, ioc->cached_fw_dma);
+ request_size = offsetof(FWUpload_t, SGL) + sizeof(FWUploadTCSGE_t) +
+ ioc->SGE_size;
+ dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending FW Upload "
+ " (req @ %p) fw_size=%d mf_request_size=%d\n", ioc->name, prequest,
+ ioc->facts.FWImageSize, request_size));
DBG_DUMP_FW_REQUEST_FRAME(ioc, (u32 *)prequest);
- ii = mpt_handshake_req_reply_wait(ioc, sgeoffset, (u32*)prequest,
- reply_sz, (u16*)preply, 65 /*seconds*/, sleepFlag);
+ ii = mpt_handshake_req_reply_wait(ioc, request_size, (u32 *)prequest,
+ reply_sz, (u16 *)preply, 65 /*seconds*/, sleepFlag);
- dinitprintk(ioc, printk(MYIOC_s_INFO_FMT ": FW Upload completed rc=%x \n", ioc->name, ii));
+ dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "FW Upload completed "
+ "rc=%x \n", ioc->name, ii));
cmdStatus = -EFAULT;
if (ii == 0) {
/* Handshake transfer was complete and successful.
* Check the Reply Frame.
*/
- int status, transfer_sz;
- status = le16_to_cpu(preply->IOCStatus);
- if (status == MPI_IOCSTATUS_SUCCESS) {
- transfer_sz = le32_to_cpu(preply->ActualImageSize);
- if (transfer_sz == sz)
+ int status;
+ status = le16_to_cpu(preply->IOCStatus) &
+ MPI_IOCSTATUS_MASK;
+ if (status == MPI_IOCSTATUS_SUCCESS &&
+ ioc->facts.FWImageSize ==
+ le32_to_cpu(preply->ActualImageSize))
cmdStatus = 0;
- }
}
dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": do_upload cmdStatus=%d \n",
ioc->name, cmdStatus));
if (cmdStatus) {
-
- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": fw upload failed, freeing image \n",
- ioc->name));
+ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "fw upload failed, "
+ "freeing image \n", ioc->name));
mpt_free_fw_memory(ioc);
}
kfree(prequest);
@@ -3723,6 +3944,10 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078) {
+
+ if (!ignore)
+ return 0;
+
drsprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: Doorbell=%p; 1078 reset "
"address=%p\n", ioc->name, __func__,
&ioc->chip->Doorbell, &ioc->chip->Reset_1078));
@@ -3740,6 +3965,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
"looking for READY STATE: doorbell=%x"
" count=%d\n",
ioc->name, doorbell, count));
+
if (doorbell == MPI_IOC_STATE_READY) {
return 1;
}
@@ -3890,6 +4116,10 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
doorbell = CHIPREG_READ32(&ioc->chip->Doorbell);
doorbell &= MPI_IOC_STATE_MASK;
+ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "looking for READY STATE: doorbell=%x"
+ " count=%d\n", ioc->name, doorbell, count));
+
if (doorbell == MPI_IOC_STATE_READY) {
break;
}
@@ -3901,6 +4131,11 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
mdelay (1000);
}
}
+
+ if (doorbell != MPI_IOC_STATE_READY)
+ printk(MYIOC_s_ERR_FMT "Failed to come READY "
+ "after reset! IocState=%x", ioc->name,
+ doorbell);
}
}
@@ -4019,8 +4254,9 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag)
if (sleepFlag != CAN_SLEEP)
count *= 10;
- printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n",
- ioc->name, (int)((count+5)/HZ));
+ printk(MYIOC_s_ERR_FMT
+ "Wait IOC_READY state (0x%x) timeout(%d)!\n",
+ ioc->name, state, (int)((count+5)/HZ));
return -ETIME;
}
@@ -4090,24 +4326,29 @@ initChainBuffers(MPT_ADAPTER *ioc)
* num_sge = num sge in request frame + last chain buffer
* scale = num sge per chain buffer if no chain element
*/
- scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
- if (sizeof(dma_addr_t) == sizeof(u64))
- num_sge = scale + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+ scale = ioc->req_sz / ioc->SGE_size;
+ if (ioc->sg_addr_size == sizeof(u64))
+ num_sge = scale + (ioc->req_sz - 60) / ioc->SGE_size;
else
- num_sge = 1+ scale + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+ num_sge = 1 + scale + (ioc->req_sz - 64) / ioc->SGE_size;
- if (sizeof(dma_addr_t) == sizeof(u64)) {
+ if (ioc->sg_addr_size == sizeof(u64)) {
numSGE = (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+ (ioc->req_sz - 60) / ioc->SGE_size;
} else {
- numSGE = 1 + (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+ numSGE = 1 + (scale - 1) * (ioc->facts.MaxChainDepth-1) +
+ scale + (ioc->req_sz - 64) / ioc->SGE_size;
}
dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "num_sge=%d numSGE=%d\n",
ioc->name, num_sge, numSGE));
- if ( numSGE > MPT_SCSI_SG_DEPTH )
- numSGE = MPT_SCSI_SG_DEPTH;
+ if (ioc->bus_type == FC) {
+ if (numSGE > MPT_SCSI_FC_SG_DEPTH)
+ numSGE = MPT_SCSI_FC_SG_DEPTH;
+ } else {
+ if (numSGE > MPT_SCSI_SG_DEPTH)
+ numSGE = MPT_SCSI_SG_DEPTH;
+ }
num_chain = 1;
while (numSGE - num_sge > 0) {
@@ -4161,12 +4402,42 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
dma_addr_t alloc_dma;
u8 *mem;
int i, reply_sz, sz, total_size, num_chain;
+ u64 dma_mask;
+
+ dma_mask = 0;
/* Prime reply FIFO... */
if (ioc->reply_frames == NULL) {
if ( (num_chain = initChainBuffers(ioc)) < 0)
return -1;
+ /*
+ * 1078 errata workaround for the 36GB limitation
+ */
+ if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078 &&
+ ioc->dma_mask > DMA_35BIT_MASK) {
+ if (!pci_set_dma_mask(ioc->pcidev, DMA_BIT_MASK(32))
+ && !pci_set_consistent_dma_mask(ioc->pcidev,
+ DMA_BIT_MASK(32))) {
+ dma_mask = DMA_35BIT_MASK;
+ d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "setting 35 bit addressing for "
+ "Request/Reply/Chain and Sense Buffers\n",
+ ioc->name));
+ } else {
+ /*Reseting DMA mask to 64 bit*/
+ pci_set_dma_mask(ioc->pcidev,
+ DMA_BIT_MASK(64));
+ pci_set_consistent_dma_mask(ioc->pcidev,
+ DMA_BIT_MASK(64));
+
+ printk(MYIOC_s_ERR_FMT
+ "failed setting 35 bit addressing for "
+ "Request/Reply/Chain and Sense Buffers\n",
+ ioc->name);
+ return -1;
+ }
+ }
total_size = reply_sz = (ioc->reply_sz * ioc->reply_depth);
dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffer sz=%d bytes, ReplyDepth=%d\n",
@@ -4305,9 +4576,16 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
alloc_dma += ioc->reply_sz;
}
+ if (dma_mask == DMA_35BIT_MASK && !pci_set_dma_mask(ioc->pcidev,
+ ioc->dma_mask) && !pci_set_consistent_dma_mask(ioc->pcidev,
+ ioc->dma_mask))
+ d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "restoring 64 bit addressing\n", ioc->name));
+
return 0;
out_fail:
+
if (ioc->alloc != NULL) {
sz = ioc->alloc_sz;
pci_free_consistent(ioc->pcidev,
@@ -4324,6 +4602,13 @@ out_fail:
ioc->sense_buf_pool, ioc->sense_buf_pool_dma);
ioc->sense_buf_pool = NULL;
}
+
+ if (dma_mask == DMA_35BIT_MASK && !pci_set_dma_mask(ioc->pcidev,
+ DMA_BIT_MASK(64)) && !pci_set_consistent_dma_mask(ioc->pcidev,
+ DMA_BIT_MASK(64)))
+ d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "restoring 64 bit addressing\n", ioc->name));
+
return -1;
}
@@ -4759,7 +5044,14 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
SasIoUnitControlReply_t *sasIoUnitCntrReply;
MPT_FRAME_HDR *mf = NULL;
MPIHeader_t *mpi_hdr;
+ int ret = 0;
+ unsigned long timeleft;
+
+ mutex_lock(&ioc->mptbase_cmds.mutex);
+ /* init the internal cmd struct */
+ memset(ioc->mptbase_cmds.reply, 0 , MPT_DEFAULT_FRAME_SIZE);
+ INITIALIZE_MGMT_STATUS(ioc->mptbase_cmds.status)
/* insure garbage is not sent to fw */
switch(persist_opcode) {
@@ -4769,17 +5061,19 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
break;
default:
- return -1;
- break;
+ ret = -1;
+ goto out;
}
- printk("%s: persist_opcode=%x\n",__func__, persist_opcode);
+ printk(KERN_DEBUG "%s: persist_opcode=%x\n",
+ __func__, persist_opcode);
/* Get a MF for this command.
*/
if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
- printk("%s: no msg frames!\n",__func__);
- return -1;
+ printk(KERN_DEBUG "%s: no msg frames!\n", __func__);
+ ret = -1;
+ goto out;
}
mpi_hdr = (MPIHeader_t *) mf;
@@ -4789,27 +5083,42 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
sasIoUnitCntrReq->MsgContext = mpi_hdr->MsgContext;
sasIoUnitCntrReq->Operation = persist_opcode;
- init_timer(&ioc->persist_timer);
- ioc->persist_timer.data = (unsigned long) ioc;
- ioc->persist_timer.function = mpt_timer_expired;
- ioc->persist_timer.expires = jiffies + HZ*10 /* 10 sec */;
- ioc->persist_wait_done=0;
- add_timer(&ioc->persist_timer);
mpt_put_msg_frame(mpt_base_index, ioc, mf);
- wait_event(mpt_waitq, ioc->persist_wait_done);
+ timeleft = wait_for_completion_timeout(&ioc->mptbase_cmds.done, 10*HZ);
+ if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = -ETIME;
+ printk(KERN_DEBUG "%s: failed\n", __func__);
+ if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out;
+ if (!timeleft) {
+ printk(KERN_DEBUG "%s: Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+ }
+ goto out;
+ }
+
+ if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ ret = -1;
+ goto out;
+ }
sasIoUnitCntrReply =
- (SasIoUnitControlReply_t *)ioc->persist_reply_frame;
+ (SasIoUnitControlReply_t *)ioc->mptbase_cmds.reply;
if (le16_to_cpu(sasIoUnitCntrReply->IOCStatus) != MPI_IOCSTATUS_SUCCESS) {
- printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
- __func__,
- sasIoUnitCntrReply->IOCStatus,
+ printk(KERN_DEBUG "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
+ __func__, sasIoUnitCntrReply->IOCStatus,
sasIoUnitCntrReply->IOCLogInfo);
- return -1;
- }
+ printk(KERN_DEBUG "%s: failed\n", __func__);
+ ret = -1;
+ } else
+ printk(KERN_DEBUG "%s: success\n", __func__);
+ out:
- printk("%s: success\n",__func__);
- return 0;
+ CLEAR_MGMT_STATUS(ioc->mptbase_cmds.status)
+ mutex_unlock(&ioc->mptbase_cmds.mutex);
+ return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5394,17 +5703,20 @@ mpt_inactive_raid_volumes(MPT_ADAPTER *ioc, u8 channel, u8 id)
* -ENOMEM if pci_alloc failed
**/
int
-mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk)
+mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num,
+ RaidPhysDiskPage0_t *phys_disk)
{
- CONFIGPARMS cfg;
- ConfigPageHeader_t hdr;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
dma_addr_t dma_handle;
pRaidPhysDiskPage0_t buffer = NULL;
int rc;
memset(&cfg, 0 , sizeof(CONFIGPARMS));
memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+ memset(phys_disk, 0, sizeof(RaidPhysDiskPage0_t));
+ hdr.PageVersion = MPI_RAIDPHYSDISKPAGE0_PAGEVERSION;
hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
cfg.cfghdr.hdr = &hdr;
cfg.physAddr = -1;
@@ -5451,6 +5763,161 @@ mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t
}
/**
+ * mpt_raid_phys_disk_get_num_paths - returns number paths associated to this phys_num
+ * @ioc: Pointer to a Adapter Structure
+ * @phys_disk_num: io unit unique phys disk num generated by the ioc
+ *
+ * Return:
+ * returns number paths
+ **/
+int
+mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc, u8 phys_disk_num)
+{
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
+ dma_addr_t dma_handle;
+ pRaidPhysDiskPage1_t buffer = NULL;
+ int rc;
+
+ memset(&cfg, 0 , sizeof(CONFIGPARMS));
+ memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+
+ hdr.PageVersion = MPI_RAIDPHYSDISKPAGE1_PAGEVERSION;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ hdr.PageNumber = 1;
+ cfg.cfghdr.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+ if (mpt_config(ioc, &cfg) != 0) {
+ rc = 0;
+ goto out;
+ }
+
+ if (!hdr.PageLength) {
+ rc = 0;
+ goto out;
+ }
+
+ buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+ &dma_handle);
+
+ if (!buffer) {
+ rc = 0;
+ goto out;
+ }
+
+ cfg.physAddr = dma_handle;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.pageAddr = phys_disk_num;
+
+ if (mpt_config(ioc, &cfg) != 0) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = buffer->NumPhysDiskPaths;
+ out:
+
+ if (buffer)
+ pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+ dma_handle);
+
+ return rc;
+}
+EXPORT_SYMBOL(mpt_raid_phys_disk_get_num_paths);
+
+/**
+ * mpt_raid_phys_disk_pg1 - returns phys disk page 1
+ * @ioc: Pointer to a Adapter Structure
+ * @phys_disk_num: io unit unique phys disk num generated by the ioc
+ * @phys_disk: requested payload data returned
+ *
+ * Return:
+ * 0 on success
+ * -EFAULT if read of config page header fails or data pointer not NULL
+ * -ENOMEM if pci_alloc failed
+ **/
+int
+mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num,
+ RaidPhysDiskPage1_t *phys_disk)
+{
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
+ dma_addr_t dma_handle;
+ pRaidPhysDiskPage1_t buffer = NULL;
+ int rc;
+ int i;
+ __le64 sas_address;
+
+ memset(&cfg, 0 , sizeof(CONFIGPARMS));
+ memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+ rc = 0;
+
+ hdr.PageVersion = MPI_RAIDPHYSDISKPAGE1_PAGEVERSION;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ hdr.PageNumber = 1;
+ cfg.cfghdr.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+ if (mpt_config(ioc, &cfg) != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (!hdr.PageLength) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+ &dma_handle);
+
+ if (!buffer) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cfg.physAddr = dma_handle;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.pageAddr = phys_disk_num;
+
+ if (mpt_config(ioc, &cfg) != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ phys_disk->NumPhysDiskPaths = buffer->NumPhysDiskPaths;
+ phys_disk->PhysDiskNum = phys_disk_num;
+ for (i = 0; i < phys_disk->NumPhysDiskPaths; i++) {
+ phys_disk->Path[i].PhysDiskID = buffer->Path[i].PhysDiskID;
+ phys_disk->Path[i].PhysDiskBus = buffer->Path[i].PhysDiskBus;
+ phys_disk->Path[i].OwnerIdentifier =
+ buffer->Path[i].OwnerIdentifier;
+ phys_disk->Path[i].Flags = le16_to_cpu(buffer->Path[i].Flags);
+ memcpy(&sas_address, &buffer->Path[i].WWID, sizeof(__le64));
+ sas_address = le64_to_cpu(sas_address);
+ memcpy(&phys_disk->Path[i].WWID, &sas_address, sizeof(__le64));
+ memcpy(&sas_address,
+ &buffer->Path[i].OwnerWWID, sizeof(__le64));
+ sas_address = le64_to_cpu(sas_address);
+ memcpy(&phys_disk->Path[i].OwnerWWID,
+ &sas_address, sizeof(__le64));
+ }
+
+ out:
+
+ if (buffer)
+ pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+ dma_handle);
+
+ return rc;
+}
+EXPORT_SYMBOL(mpt_raid_phys_disk_pg1);
+
+
+/**
* mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
* @ioc: Pointer to a Adapter Strucutre
*
@@ -5775,30 +6242,28 @@ mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc)
* SendEventNotification - Send EventNotification (on or off) request to adapter
* @ioc: Pointer to MPT_ADAPTER structure
* @EvSwitch: Event switch flags
+ * @sleepFlag: Specifies whether the process can sleep
*/
static int
-SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch)
+SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch, int sleepFlag)
{
- EventNotification_t *evnp;
+ EventNotification_t evn;
+ MPIDefaultReply_t reply_buf;
- evnp = (EventNotification_t *) mpt_get_msg_frame(mpt_base_index, ioc);
- if (evnp == NULL) {
- devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "Unable to allocate event request frame!\n",
- ioc->name));
- return 0;
- }
- memset(evnp, 0, sizeof(*evnp));
-
- devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending EventNotification (%d) request %p\n", ioc->name, EvSwitch, evnp));
+ memset(&evn, 0, sizeof(EventNotification_t));
+ memset(&reply_buf, 0, sizeof(MPIDefaultReply_t));
- evnp->Function = MPI_FUNCTION_EVENT_NOTIFICATION;
- evnp->ChainOffset = 0;
- evnp->MsgFlags = 0;
- evnp->Switch = EvSwitch;
+ evn.Function = MPI_FUNCTION_EVENT_NOTIFICATION;
+ evn.Switch = EvSwitch;
+ evn.MsgContext = cpu_to_le32(mpt_base_index << 16);
- mpt_put_msg_frame(mpt_base_index, ioc, (MPT_FRAME_HDR *)evnp);
+ devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "Sending EventNotification (%d) request %p\n",
+ ioc->name, EvSwitch, &evn));
- return 0;
+ return mpt_handshake_req_reply_wait(ioc, sizeof(EventNotification_t),
+ (u32 *)&evn, sizeof(MPIDefaultReply_t), (u16 *)&reply_buf, 30,
+ sleepFlag);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5814,7 +6279,7 @@ SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp)
if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
- ioc->name,__func__));
+ ioc->name, __func__));
return -1;
}
@@ -5851,12 +6316,19 @@ int
mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
{
Config_t *pReq;
+ ConfigReply_t *pReply;
ConfigExtendedPageHeader_t *pExtHdr = NULL;
MPT_FRAME_HDR *mf;
- unsigned long flags;
- int ii, rc;
+ int ii;
int flagsLength;
- int in_isr;
+ long timeout;
+ int ret;
+ u8 page_type = 0, extend_page;
+ unsigned long timeleft;
+ unsigned long flags;
+ int in_isr;
+ u8 issue_hard_reset = 0;
+ u8 retry_count = 0;
/* Prevent calling wait_event() (below), if caller happens
* to be in ISR context, because that is fatal!
@@ -5866,15 +6338,43 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
dcprintk(ioc, printk(MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
ioc->name));
return -EPERM;
+ }
+
+ /* don't send a config page during diag reset */
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: busy with host reset\n", ioc->name, __func__));
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+ /* don't send if no chance of success */
+ if (!ioc->active ||
+ mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: ioc not operational, %d, %xh\n",
+ ioc->name, __func__, ioc->active,
+ mpt_GetIocState(ioc, 0)));
+ return -EFAULT;
}
+ retry_config:
+ mutex_lock(&ioc->mptbase_cmds.mutex);
+ /* init the internal cmd struct */
+ memset(ioc->mptbase_cmds.reply, 0 , MPT_DEFAULT_FRAME_SIZE);
+ INITIALIZE_MGMT_STATUS(ioc->mptbase_cmds.status)
+
/* Get and Populate a free Frame
*/
if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
- dcprintk(ioc, printk(MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
- ioc->name));
- return -EAGAIN;
+ dcprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "mpt_config: no msg frames!\n", ioc->name));
+ ret = -EAGAIN;
+ goto out;
}
+
pReq = (Config_t *)mf;
pReq->Action = pCfg->action;
pReq->Reserved = 0;
@@ -5900,7 +6400,9 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
pReq->ExtPageType = pExtHdr->ExtPageType;
pReq->Header.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
- /* Page Length must be treated as a reserved field for the extended header. */
+ /* Page Length must be treated as a reserved field for the
+ * extended header.
+ */
pReq->Header.PageLength = 0;
}
@@ -5913,78 +6415,91 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
else
flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
- if ((pCfg->cfghdr.hdr->PageType & MPI_CONFIG_PAGETYPE_MASK) == MPI_CONFIG_PAGETYPE_EXTENDED) {
+ if ((pCfg->cfghdr.hdr->PageType & MPI_CONFIG_PAGETYPE_MASK) ==
+ MPI_CONFIG_PAGETYPE_EXTENDED) {
flagsLength |= pExtHdr->ExtPageLength * 4;
-
- dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Config request type %d, page %d and action %d\n",
- ioc->name, pReq->ExtPageType, pReq->Header.PageNumber, pReq->Action));
- }
- else {
+ page_type = pReq->ExtPageType;
+ extend_page = 1;
+ } else {
flagsLength |= pCfg->cfghdr.hdr->PageLength * 4;
-
- dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Config request type %d, page %d and action %d\n",
- ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action));
+ page_type = pReq->Header.PageType;
+ extend_page = 0;
}
- mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
-
- /* Append pCfg pointer to end of mf
- */
- *((void **) (((u8 *) mf) + (ioc->req_sz - sizeof(void *)))) = (void *) pCfg;
-
- /* Initalize the timer
- */
- init_timer_on_stack(&pCfg->timer);
- pCfg->timer.data = (unsigned long) ioc;
- pCfg->timer.function = mpt_timer_expired;
- pCfg->wait_done = 0;
-
- /* Set the timer; ensure 10 second minimum */
- if (pCfg->timeout < 10)
- pCfg->timer.expires = jiffies + HZ*10;
- else
- pCfg->timer.expires = jiffies + HZ*pCfg->timeout;
-
- /* Add to end of Q, set timer and then issue this command */
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- list_add_tail(&pCfg->linkage, &ioc->configQ);
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "Sending Config request type 0x%x, page 0x%x and action %d\n",
+ ioc->name, page_type, pReq->Header.PageNumber, pReq->Action));
- add_timer(&pCfg->timer);
+ ioc->add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
+ timeout = (pCfg->timeout < 15) ? HZ*15 : HZ*pCfg->timeout;
mpt_put_msg_frame(mpt_base_index, ioc, mf);
- wait_event(mpt_waitq, pCfg->wait_done);
+ timeleft = wait_for_completion_timeout(&ioc->mptbase_cmds.done,
+ timeout);
+ if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = -ETIME;
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "Failed Sending Config request type 0x%x, page 0x%x,"
+ " action %d, status %xh, time left %ld\n\n",
+ ioc->name, page_type, pReq->Header.PageNumber,
+ pReq->Action, ioc->mptbase_cmds.status, timeleft));
+ if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out;
+ if (!timeleft)
+ issue_hard_reset = 1;
+ goto out;
+ }
- /* mf has been freed - do not access */
+ if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ ret = -1;
+ goto out;
+ }
+ pReply = (ConfigReply_t *)ioc->mptbase_cmds.reply;
+ ret = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ if (ret == MPI_IOCSTATUS_SUCCESS) {
+ if (extend_page) {
+ pCfg->cfghdr.ehdr->ExtPageLength =
+ le16_to_cpu(pReply->ExtPageLength);
+ pCfg->cfghdr.ehdr->ExtPageType =
+ pReply->ExtPageType;
+ }
+ pCfg->cfghdr.hdr->PageVersion = pReply->Header.PageVersion;
+ pCfg->cfghdr.hdr->PageLength = pReply->Header.PageLength;
+ pCfg->cfghdr.hdr->PageNumber = pReply->Header.PageNumber;
+ pCfg->cfghdr.hdr->PageType = pReply->Header.PageType;
- rc = pCfg->status;
+ }
- return rc;
-}
+ if (retry_count)
+ printk(MYIOC_s_INFO_FMT "Retry completed "
+ "ret=0x%x timeleft=%ld\n",
+ ioc->name, ret, timeleft);
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mpt_timer_expired - Callback for timer process.
- * Used only internal config functionality.
- * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
- */
-static void
-mpt_timer_expired(unsigned long data)
-{
- MPT_ADAPTER *ioc = (MPT_ADAPTER *) data;
-
- dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_timer_expired! \n", ioc->name));
+ dcprintk(ioc, printk(KERN_DEBUG "IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+ ret, le32_to_cpu(pReply->IOCLogInfo)));
- /* Perform a FW reload */
- if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
- printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", ioc->name);
+out:
- /* No more processing.
- * Hard reset clean-up will wake up
- * process and free all resources.
- */
- dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_timer_expired complete!\n", ioc->name));
+ CLEAR_MGMT_STATUS(ioc->mptbase_cmds.status)
+ mutex_unlock(&ioc->mptbase_cmds.mutex);
+ if (issue_hard_reset) {
+ issue_hard_reset = 0;
+ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+ /* attempt one retry for a timed out command */
+ if (!retry_count) {
+ printk(MYIOC_s_INFO_FMT
+ "Attempting Retry Config request"
+ " type 0x%x, page 0x%x,"
+ " action %d\n", ioc->name, page_type,
+ pCfg->cfghdr.hdr->PageNumber, pCfg->action);
+ retry_count++;
+ goto retry_config;
+ }
+ }
+ return ret;
- return;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5998,41 +6513,34 @@ mpt_timer_expired(unsigned long data)
static int
mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
{
- CONFIGPARMS *pCfg;
- unsigned long flags;
-
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- ": IOC %s_reset routed to MPT base driver!\n",
- ioc->name, reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
- reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
- if (reset_phase == MPT_IOC_SETUP_RESET) {
- ;
- } else if (reset_phase == MPT_IOC_PRE_RESET) {
- /* If the internal config Q is not empty -
- * delete timer. MF resources will be freed when
- * the FIFO's are primed.
- */
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- list_for_each_entry(pCfg, &ioc->configQ, linkage)
- del_timer(&pCfg->timer);
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
- } else {
- CONFIGPARMS *pNext;
-
- /* Search the configQ for internal commands.
- * Flush the Q, and wake up all suspended threads.
- */
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- list_for_each_entry_safe(pCfg, pNext, &ioc->configQ, linkage) {
- list_del(&pCfg->linkage);
-
- pCfg->status = MPT_CONFIG_ERROR;
- pCfg->wait_done = 1;
- wake_up(&mpt_waitq);
+ switch (reset_phase) {
+ case MPT_IOC_SETUP_RESET:
+ ioc->taskmgmt_quiesce_io = 1;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+ break;
+ case MPT_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT_IOC_POST_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+/* wake up mptbase_cmds */
+ if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->mptbase_cmds.status |=
+ MPT_MGMT_STATUS_DID_IOCRESET;
+ complete(&ioc->mptbase_cmds.done);
}
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+/* wake up taskmgmt_cmds */
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->taskmgmt_cmds.status |=
+ MPT_MGMT_STATUS_DID_IOCRESET;
+ complete(&ioc->taskmgmt_cmds.done);
+ }
+ break;
+ default:
+ break;
}
return 1; /* currently means nothing really */
@@ -6344,6 +6852,59 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int sh
*size = y;
}
+/**
+ * mpt_set_taskmgmt_in_progress_flag - set flags associated with task managment
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ * Returns 0 for SUCCESS or -1 if FAILED.
+ *
+ * If -1 is return, then it was not possible to set the flags
+ **/
+int
+mpt_set_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress || ioc->taskmgmt_in_progress ||
+ (ioc->alt_ioc && ioc->alt_ioc->taskmgmt_in_progress)) {
+ retval = -1;
+ goto out;
+ }
+ retval = 0;
+ ioc->taskmgmt_in_progress = 1;
+ ioc->taskmgmt_quiesce_io = 1;
+ if (ioc->alt_ioc) {
+ ioc->alt_ioc->taskmgmt_in_progress = 1;
+ ioc->alt_ioc->taskmgmt_quiesce_io = 1;
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL(mpt_set_taskmgmt_in_progress_flag);
+
+/**
+ * mpt_clear_taskmgmt_in_progress_flag - clear flags associated with task managment
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+void
+mpt_clear_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ ioc->taskmgmt_in_progress = 0;
+ ioc->taskmgmt_quiesce_io = 0;
+ if (ioc->alt_ioc) {
+ ioc->alt_ioc->taskmgmt_in_progress = 0;
+ ioc->alt_ioc->taskmgmt_quiesce_io = 0;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+}
+EXPORT_SYMBOL(mpt_clear_taskmgmt_in_progress_flag);
/**
@@ -6397,7 +6958,9 @@ int
mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
{
int rc;
+ u8 cb_idx;
unsigned long flags;
+ unsigned long time_count;
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "HardResetHandler Entered!\n", ioc->name));
#ifdef MFCNT
@@ -6410,14 +6973,15 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
/* Reset the adapter. Prevent more than 1 call to
* mpt_do_ioc_recovery at any instant in time.
*/
- spin_lock_irqsave(&ioc->diagLock, flags);
- if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)){
- spin_unlock_irqrestore(&ioc->diagLock, flags);
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
return 0;
- } else {
- ioc->diagPending = 1;
}
- spin_unlock_irqrestore(&ioc->diagLock, flags);
+ ioc->ioc_reset_in_progress = 1;
+ if (ioc->alt_ioc)
+ ioc->alt_ioc->ioc_reset_in_progress = 1;
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
/* FIXME: If do_ioc_recovery fails, repeat....
*/
@@ -6427,47 +6991,57 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
* Prevents timeouts occurring during a diagnostic reset...very bad.
* For all other protocol drivers, this is a no-op.
*/
- {
- u8 cb_idx;
- int r = 0;
-
- for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
- if (MptResetHandlers[cb_idx]) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling IOC reset_setup handler #%d\n",
- ioc->name, cb_idx));
- r += mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET);
- if (ioc->alt_ioc) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling alt-%s setup reset handler #%d\n",
- ioc->name, ioc->alt_ioc->name, cb_idx));
- r += mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_SETUP_RESET);
- }
- }
+ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
+ if (MptResetHandlers[cb_idx]) {
+ mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET);
+ if (ioc->alt_ioc)
+ mpt_signal_reset(cb_idx, ioc->alt_ioc,
+ MPT_IOC_SETUP_RESET);
}
}
- if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) {
- printk(MYIOC_s_WARN_FMT "Cannot recover rc = %d!\n", ioc->name, rc);
+ time_count = jiffies;
+ rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag);
+ if (rc != 0) {
+ printk(KERN_WARNING MYNAM
+ ": WARNING - (%d) Cannot recover %s\n", rc, ioc->name);
+ } else {
+ if (ioc->hard_resets < -1)
+ ioc->hard_resets++;
}
- ioc->reload_fw = 0;
- if (ioc->alt_ioc)
- ioc->alt_ioc->reload_fw = 0;
- spin_lock_irqsave(&ioc->diagLock, flags);
- ioc->diagPending = 0;
- if (ioc->alt_ioc)
- ioc->alt_ioc->diagPending = 0;
- spin_unlock_irqrestore(&ioc->diagLock, flags);
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ ioc->ioc_reset_in_progress = 0;
+ ioc->taskmgmt_quiesce_io = 0;
+ ioc->taskmgmt_in_progress = 0;
+ if (ioc->alt_ioc) {
+ ioc->alt_ioc->ioc_reset_in_progress = 0;
+ ioc->alt_ioc->taskmgmt_quiesce_io = 0;
+ ioc->alt_ioc->taskmgmt_in_progress = 0;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "HardResetHandler rc = %d!\n", ioc->name, rc));
+ dtmprintk(ioc,
+ printk(MYIOC_s_DEBUG_FMT
+ "HardResetHandler: completed (%d seconds): %s\n", ioc->name,
+ jiffies_to_msecs(jiffies - time_count)/1000, ((rc == 0) ?
+ "SUCCESS" : "FAILED")));
return rc;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#ifdef CONFIG_FUSION_LOGGING
static void
-EventDescriptionStr(u8 event, u32 evData0, char *evStr)
+mpt_display_event_info(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply)
{
char *ds = NULL;
+ u32 evData0;
+ int ii;
+ u8 event;
+ char *evStr = ioc->evStr;
+
+ event = le32_to_cpu(pEventReply->Event) & 0xFF;
+ evData0 = le32_to_cpu(pEventReply->Data[0]);
switch(event) {
case MPI_EVENT_NONE:
@@ -6501,9 +7075,9 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LIP)
ds = "Loop State(LIP) Change";
else if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LPE)
- ds = "Loop State(LPE) Change"; /* ??? */
+ ds = "Loop State(LPE) Change";
else
- ds = "Loop State(LPB) Change"; /* ??? */
+ ds = "Loop State(LPB) Change";
break;
case MPI_EVENT_LOGOUT:
ds = "Logout";
@@ -6703,28 +7277,65 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
}
case MPI_EVENT_IR2:
{
+ u8 id = (u8)(evData0);
+ u8 channel = (u8)(evData0 >> 8);
+ u8 phys_num = (u8)(evData0 >> 24);
u8 ReasonCode = (u8)(evData0 >> 16);
+
switch (ReasonCode) {
case MPI_EVENT_IR2_RC_LD_STATE_CHANGED:
- ds = "IR2: LD State Changed";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: LD State Changed: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_PD_STATE_CHANGED:
- ds = "IR2: PD State Changed";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: PD State Changed "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_BAD_BLOCK_TABLE_FULL:
- ds = "IR2: Bad Block Table Full";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: Bad Block Table Full: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_PD_INSERTED:
- ds = "IR2: PD Inserted";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: PD Inserted: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_PD_REMOVED:
- ds = "IR2: PD Removed";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: PD Removed: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
- ds = "IR2: Foreign CFG Detected";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: Foreign CFG Detected: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
case MPI_EVENT_IR2_RC_REBUILD_MEDIUM_ERROR:
- ds = "IR2: Rebuild Medium Error";
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: Rebuild Medium Error: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
+ break;
+ case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: Dual Port Added: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
+ break;
+ case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
+ snprintf(evStr, EVENT_DESCR_STR_SZ,
+ "IR2: Dual Port Removed: "
+ "id=%d channel=%d phys_num=%d",
+ id, channel, phys_num);
break;
default:
ds = "IR2";
@@ -6760,13 +7371,18 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
case MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
{
u8 reason = (u8)(evData0);
- u8 port_num = (u8)(evData0 >> 8);
- u16 handle = le16_to_cpu(evData0 >> 16);
- snprintf(evStr, EVENT_DESCR_STR_SZ,
- "SAS Initiator Device Status Change: reason=0x%02x "
- "port=%d handle=0x%04x",
- reason, port_num, handle);
+ switch (reason) {
+ case MPI_EVENT_SAS_INIT_RC_ADDED:
+ ds = "SAS Initiator Status Change: Added";
+ break;
+ case MPI_EVENT_SAS_INIT_RC_REMOVED:
+ ds = "SAS Initiator Status Change: Deleted";
+ break;
+ default:
+ ds = "SAS Initiator Status Change";
+ break;
+ }
break;
}
@@ -6814,6 +7430,24 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
break;
}
+ case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+ {
+ u8 reason = (u8)(evData0);
+
+ switch (reason) {
+ case MPI_EVENT_SAS_EXP_RC_ADDED:
+ ds = "Expander Status Change: Added";
+ break;
+ case MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING:
+ ds = "Expander Status Change: Deleted";
+ break;
+ default:
+ ds = "Expander Status Change";
+ break;
+ }
+ break;
+ }
+
/*
* MPT base "custom" events may be added here...
*/
@@ -6823,8 +7457,20 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
}
if (ds)
strncpy(evStr, ds, EVENT_DESCR_STR_SZ);
-}
+
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "MPT event:(%02Xh) : %s\n",
+ ioc->name, event, evStr));
+
+ devtverboseprintk(ioc, printk(KERN_DEBUG MYNAM
+ ": Event data:\n"));
+ for (ii = 0; ii < le16_to_cpu(pEventReply->EventDataLength); ii++)
+ devtverboseprintk(ioc, printk(" %08x",
+ le32_to_cpu(pEventReply->Data[ii])));
+ devtverboseprintk(ioc, printk(KERN_DEBUG "\n"));
+}
+#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* ProcessEventNotification - Route EventNotificationReply to all event handlers
@@ -6841,37 +7487,24 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply
{
u16 evDataLen;
u32 evData0 = 0;
-// u32 evCtx;
int ii;
u8 cb_idx;
int r = 0;
int handlers = 0;
- char evStr[EVENT_DESCR_STR_SZ];
u8 event;
/*
* Do platform normalization of values
*/
event = le32_to_cpu(pEventReply->Event) & 0xFF;
-// evCtx = le32_to_cpu(pEventReply->EventContext);
evDataLen = le16_to_cpu(pEventReply->EventDataLength);
if (evDataLen) {
evData0 = le32_to_cpu(pEventReply->Data[0]);
}
- EventDescriptionStr(event, evData0, evStr);
- devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event:(%02Xh) : %s\n",
- ioc->name,
- event,
- evStr));
-
#ifdef CONFIG_FUSION_LOGGING
- devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- ": Event data:\n", ioc->name));
- for (ii = 0; ii < evDataLen; ii++)
- devtverboseprintk(ioc, printk(" %08x",
- le32_to_cpu(pEventReply->Data[ii])));
- devtverboseprintk(ioc, printk("\n"));
+ if (evDataLen)
+ mpt_display_event_info(ioc, pEventReply);
#endif
/*
@@ -6926,8 +7559,9 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply
*/
for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
if (MptEvHandlers[cb_idx]) {
- devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Routing Event to event handler #%d\n",
- ioc->name, cb_idx));
+ devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "Routing Event to event handler #%d\n",
+ ioc->name, cb_idx));
r += (*(MptEvHandlers[cb_idx]))(ioc, pEventReply);
handlers++;
}
@@ -7011,8 +7645,6 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info)
switch (info) {
case 0x00010000:
desc = "bug! MID not found";
- if (ioc->reload_fw == 0)
- ioc->reload_fw++;
break;
case 0x00020000:
@@ -7613,7 +8245,6 @@ EXPORT_SYMBOL(mpt_get_msg_frame);
EXPORT_SYMBOL(mpt_put_msg_frame);
EXPORT_SYMBOL(mpt_put_msg_frame_hi_pri);
EXPORT_SYMBOL(mpt_free_msg_frame);
-EXPORT_SYMBOL(mpt_add_sge);
EXPORT_SYMBOL(mpt_send_handshake_request);
EXPORT_SYMBOL(mpt_verify_adapter);
EXPORT_SYMBOL(mpt_GetIocState);
@@ -7650,7 +8281,7 @@ fusion_init(void)
/* Register ourselves (mptbase) in order to facilitate
* EventNotification handling.
*/
- mpt_base_index = mpt_register(mpt_base_reply, MPTBASE_DRIVER);
+ mpt_base_index = mpt_register(mptbase_reply, MPTBASE_DRIVER);
/* Register for hard reset handling callbacks.
*/
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index b3e981d2a506..1c8514dc31ca 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -76,8 +76,8 @@
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.07"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.07"
+#define MPT_LINUX_VERSION_COMMON "3.04.10"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.09"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
@@ -104,6 +104,7 @@
#endif
#define MPT_NAME_LENGTH 32
+#define MPT_KOBJ_NAME_LEN 20
#define MPT_PROCFS_MPTBASEDIR "mpt"
/* chg it to "driver/fusion" ? */
@@ -134,6 +135,7 @@
#define MPT_COALESCING_TIMEOUT 0x10
+
/*
* SCSI transfer rate defines.
*/
@@ -161,10 +163,10 @@
/*
* Set the MAX_SGE value based on user input.
*/
-#ifdef CONFIG_FUSION_MAX_SGE
-#if CONFIG_FUSION_MAX_SGE < 16
+#ifdef CONFIG_FUSION_MAX_SGE
+#if CONFIG_FUSION_MAX_SGE < 16
#define MPT_SCSI_SG_DEPTH 16
-#elif CONFIG_FUSION_MAX_SGE > 128
+#elif CONFIG_FUSION_MAX_SGE > 128
#define MPT_SCSI_SG_DEPTH 128
#else
#define MPT_SCSI_SG_DEPTH CONFIG_FUSION_MAX_SGE
@@ -173,6 +175,18 @@
#define MPT_SCSI_SG_DEPTH 40
#endif
+#ifdef CONFIG_FUSION_MAX_FC_SGE
+#if CONFIG_FUSION_MAX_FC_SGE < 16
+#define MPT_SCSI_FC_SG_DEPTH 16
+#elif CONFIG_FUSION_MAX_FC_SGE > 256
+#define MPT_SCSI_FC_SG_DEPTH 256
+#else
+#define MPT_SCSI_FC_SG_DEPTH CONFIG_FUSION_MAX_FC_SGE
+#endif
+#else
+#define MPT_SCSI_FC_SG_DEPTH 40
+#endif
+
/* debug print string length used for events and iocstatus */
# define EVENT_DESCR_STR_SZ 100
@@ -431,38 +445,36 @@ do { \
* IOCTL structure and associated defines
*/
-#define MPT_IOCTL_STATUS_DID_IOCRESET 0x01 /* IOC Reset occurred on the current*/
-#define MPT_IOCTL_STATUS_RF_VALID 0x02 /* The Reply Frame is VALID */
-#define MPT_IOCTL_STATUS_TIMER_ACTIVE 0x04 /* The timer is running */
-#define MPT_IOCTL_STATUS_SENSE_VALID 0x08 /* Sense data is valid */
-#define MPT_IOCTL_STATUS_COMMAND_GOOD 0x10 /* Command Status GOOD */
-#define MPT_IOCTL_STATUS_TMTIMER_ACTIVE 0x20 /* The TM timer is running */
-#define MPT_IOCTL_STATUS_TM_FAILED 0x40 /* User TM request failed */
-
#define MPTCTL_RESET_OK 0x01 /* Issue Bus Reset */
-typedef struct _MPT_IOCTL {
- struct _MPT_ADAPTER *ioc;
- u8 ReplyFrame[MPT_DEFAULT_FRAME_SIZE]; /* reply frame data */
- u8 sense[MPT_SENSE_BUFFER_ALLOC];
- int wait_done; /* wake-up value for this ioc */
- u8 rsvd;
- u8 status; /* current command status */
- u8 reset; /* 1 if bus reset allowed */
- u8 id; /* target for reset */
- struct mutex ioctl_mutex;
-} MPT_IOCTL;
-
-#define MPT_SAS_MGMT_STATUS_RF_VALID 0x02 /* The Reply Frame is VALID */
-#define MPT_SAS_MGMT_STATUS_COMMAND_GOOD 0x10 /* Command Status GOOD */
-#define MPT_SAS_MGMT_STATUS_TM_FAILED 0x40 /* User TM request failed */
-
-typedef struct _MPT_SAS_MGMT {
+#define MPT_MGMT_STATUS_RF_VALID 0x01 /* The Reply Frame is VALID */
+#define MPT_MGMT_STATUS_COMMAND_GOOD 0x02 /* Command Status GOOD */
+#define MPT_MGMT_STATUS_PENDING 0x04 /* command is pending */
+#define MPT_MGMT_STATUS_DID_IOCRESET 0x08 /* IOC Reset occurred
+ on the current*/
+#define MPT_MGMT_STATUS_SENSE_VALID 0x10 /* valid sense info */
+#define MPT_MGMT_STATUS_TIMER_ACTIVE 0x20 /* obsolete */
+#define MPT_MGMT_STATUS_FREE_MF 0x40 /* free the mf from
+ complete routine */
+
+#define INITIALIZE_MGMT_STATUS(status) \
+ status = MPT_MGMT_STATUS_PENDING;
+#define CLEAR_MGMT_STATUS(status) \
+ status = 0;
+#define CLEAR_MGMT_PENDING_STATUS(status) \
+ status &= ~MPT_MGMT_STATUS_PENDING;
+#define SET_MGMT_MSG_CONTEXT(msg_context, value) \
+ msg_context = value;
+
+typedef struct _MPT_MGMT {
struct mutex mutex;
struct completion done;
u8 reply[MPT_DEFAULT_FRAME_SIZE]; /* reply frame data */
+ u8 sense[MPT_SENSE_BUFFER_ALLOC];
u8 status; /* current command status */
-}MPT_SAS_MGMT;
+ int completion_code;
+ u32 msg_context;
+} MPT_MGMT;
/*
* Event Structure and define
@@ -564,6 +576,10 @@ struct mptfc_rport_info
u8 flags;
};
+typedef void (*MPT_ADD_SGE)(void *pAddr, u32 flagslength, dma_addr_t dma_addr);
+typedef void (*MPT_ADD_CHAIN)(void *pAddr, u8 next, u16 length,
+ dma_addr_t dma_addr);
+
/*
* Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS
*/
@@ -573,6 +589,10 @@ typedef struct _MPT_ADAPTER
int pci_irq; /* This irq */
char name[MPT_NAME_LENGTH]; /* "iocN" */
char prod_name[MPT_NAME_LENGTH]; /* "LSIFC9x9" */
+#ifdef CONFIG_FUSION_LOGGING
+ /* used in mpt_display_event_info */
+ char evStr[EVENT_DESCR_STR_SZ];
+#endif
char board_name[16];
char board_assembly[16];
char board_tracer[16];
@@ -600,6 +620,10 @@ typedef struct _MPT_ADAPTER
int reply_depth; /* Num Allocated reply frames */
int reply_sz; /* Reply frame size */
int num_chain; /* Number of chain buffers */
+ MPT_ADD_SGE add_sge; /* Pointer to add_sge
+ function */
+ MPT_ADD_CHAIN add_chain; /* Pointer to add_chain
+ function */
/* Pool of buffers for chaining. ReqToChain
* and ChainToChain track index of chain buffers.
* ChainBuffer (DMA) virt/phys addresses.
@@ -640,11 +664,8 @@ typedef struct _MPT_ADAPTER
RaidCfgData raid_data; /* Raid config. data */
SasCfgData sas_data; /* Sas config. data */
FcCfgData fc_data; /* Fc config. data */
- MPT_IOCTL *ioctl; /* ioctl data pointer */
struct proc_dir_entry *ioc_dentry;
struct _MPT_ADAPTER *alt_ioc; /* ptr to 929 bound adapter port */
- spinlock_t diagLock; /* diagnostic reset lock */
- int diagPending;
u32 biosVersion; /* BIOS version from IO Unit Page 2 */
int eventTypes; /* Event logging parameters */
int eventContext; /* Next event context */
@@ -652,7 +673,6 @@ typedef struct _MPT_ADAPTER
struct _mpt_ioctl_events *events; /* pointer to event log */
u8 *cached_fw; /* Pointer to FW */
dma_addr_t cached_fw_dma;
- struct list_head configQ; /* linked list of config. requests */
int hs_reply_idx;
#ifndef MFCNT
u32 pad0;
@@ -665,9 +685,6 @@ typedef struct _MPT_ADAPTER
IOCFactsReply_t facts;
PortFactsReply_t pfacts[2];
FCPortPage0_t fc_port_page0[2];
- struct timer_list persist_timer; /* persist table timer */
- int persist_wait_done; /* persist completion flag */
- u8 persist_reply_frame[MPT_DEFAULT_FRAME_SIZE]; /* persist reply */
LANPage0_t lan_cnfg_page0;
LANPage1_t lan_cnfg_page1;
@@ -682,23 +699,44 @@ typedef struct _MPT_ADAPTER
int aen_event_read_flag; /* flag to indicate event log was read*/
u8 FirstWhoInit;
u8 upload_fw; /* If set, do a fw upload */
- u8 reload_fw; /* Force a FW Reload on next reset */
u8 NBShiftFactor; /* NB Shift Factor based on Block Size (Facts) */
u8 pad1[4];
u8 DoneCtx;
u8 TaskCtx;
u8 InternalCtx;
- spinlock_t initializing_hba_lock;
- int initializing_hba_lock_flag;
struct list_head list;
struct net_device *netdev;
struct list_head sas_topology;
struct mutex sas_topology_mutex;
+
+ struct workqueue_struct *fw_event_q;
+ struct list_head fw_event_list;
+ spinlock_t fw_event_lock;
+ u8 fw_events_off; /* if '1', then ignore events */
+ char fw_event_q_name[MPT_KOBJ_NAME_LEN];
+
struct mutex sas_discovery_mutex;
u8 sas_discovery_runtime;
u8 sas_discovery_ignore_events;
+
+ /* port_info object for the host */
+ struct mptsas_portinfo *hba_port_info;
+ u64 hba_port_sas_addr;
+ u16 hba_port_num_phy;
+ struct list_head sas_device_info_list;
+ struct mutex sas_device_info_mutex;
+ u8 old_sas_discovery_protocal;
+ u8 sas_discovery_quiesce_io;
int sas_index; /* index refrencing */
- MPT_SAS_MGMT sas_mgmt;
+ MPT_MGMT sas_mgmt;
+ MPT_MGMT mptbase_cmds; /* for sending config pages */
+ MPT_MGMT internal_cmds;
+ MPT_MGMT taskmgmt_cmds;
+ MPT_MGMT ioctl_cmds;
+ spinlock_t taskmgmt_lock; /* diagnostic reset lock */
+ int taskmgmt_in_progress;
+ u8 taskmgmt_quiesce_io;
+ u8 ioc_reset_in_progress;
struct work_struct sas_persist_task;
struct work_struct fc_setup_reset_work;
@@ -707,15 +745,27 @@ typedef struct _MPT_ADAPTER
u8 fc_link_speed[2];
spinlock_t fc_rescan_work_lock;
struct work_struct fc_rescan_work;
- char fc_rescan_work_q_name[20];
+ char fc_rescan_work_q_name[MPT_KOBJ_NAME_LEN];
struct workqueue_struct *fc_rescan_work_q;
+
+ /* driver forced bus resets count */
+ unsigned long hard_resets;
+ /* fw/external bus resets count */
+ unsigned long soft_resets;
+ /* cmd timeouts */
+ unsigned long timeouts;
+
struct scsi_cmnd **ScsiLookup;
spinlock_t scsi_lookup_lock;
-
- char reset_work_q_name[20];
+ u64 dma_mask;
+ u32 broadcast_aen_busy;
+ char reset_work_q_name[MPT_KOBJ_NAME_LEN];
struct workqueue_struct *reset_work_q;
struct delayed_work fault_reset_work;
- spinlock_t fault_reset_work_lock;
+
+ u8 sg_addr_size;
+ u8 in_rescan;
+ u8 SGE_size;
} MPT_ADAPTER;
@@ -753,13 +803,14 @@ typedef struct _mpt_sge {
dma_addr_t Address;
} MptSge_t;
-#define mpt_addr_size() \
- ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SGE_FLAGS_64_BIT_ADDRESSING : \
- MPI_SGE_FLAGS_32_BIT_ADDRESSING)
-#define mpt_msg_flags() \
- ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \
- MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32)
+#define mpt_msg_flags(ioc) \
+ (ioc->sg_addr_size == sizeof(u64)) ? \
+ MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \
+ MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32
+
+#define MPT_SGE_FLAGS_64_BIT_ADDRESSING \
+ (MPI_SGE_FLAGS_64_BIT_ADDRESSING << MPI_SGE_FLAGS_SHIFT)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
@@ -835,22 +886,14 @@ typedef struct _MPT_SCSI_HOST {
/* Pool of memory for holding SCpnts before doing
* OS callbacks. freeQ is the free pool.
*/
- u8 tmPending;
- u8 resetPending;
u8 negoNvram; /* DV disabled, nego NVRAM */
u8 pad1;
- u8 tmState;
u8 rsvd[2];
MPT_FRAME_HDR *cmdPtr; /* Ptr to nonOS request */
struct scsi_cmnd *abortSCpnt;
MPT_LOCAL_REPLY localReply; /* internal cmd reply struct */
- unsigned long hard_resets; /* driver forced bus resets count */
- unsigned long soft_resets; /* fw/external bus resets count */
- unsigned long timeouts; /* cmd timeouts */
ushort sel_timeout[MPT_MAX_FC_DEVICES];
char *info_kbuf;
- wait_queue_head_t scandv_waitq;
- int scandv_wait_done;
long last_queue_full;
u16 tm_iocstatus;
u16 spi_pending;
@@ -870,21 +913,16 @@ struct scsi_cmnd;
* Generic structure passed to the base mpt_config function.
*/
typedef struct _x_config_parms {
- struct list_head linkage; /* linked list */
- struct timer_list timer; /* timer function for this request */
union {
ConfigExtendedPageHeader_t *ehdr;
ConfigPageHeader_t *hdr;
} cfghdr;
dma_addr_t physAddr;
- int wait_done; /* wait for this request */
u32 pageAddr; /* properly formatted */
+ u16 status;
u8 action;
u8 dir;
u8 timeout; /* seconds */
- u8 pad1;
- u16 status;
- u16 pad2;
} CONFIGPARMS;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -909,7 +947,6 @@ extern MPT_FRAME_HDR *mpt_get_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc);
extern void mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
extern void mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
extern void mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
-extern void mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr);
extern int mpt_send_handshake_request(u8 cb_idx, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag);
extern int mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp);
@@ -922,6 +959,12 @@ extern void mpt_free_fw_memory(MPT_ADAPTER *ioc);
extern int mpt_findImVolumes(MPT_ADAPTER *ioc);
extern int mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode);
extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk);
+extern int mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num,
+ pRaidPhysDiskPage1_t phys_disk);
+extern int mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc,
+ u8 phys_disk_num);
+extern int mpt_set_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc);
+extern void mpt_clear_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc);
extern void mpt_halt_firmware(MPT_ADAPTER *ioc);
@@ -959,7 +1002,6 @@ extern int mpt_fwfault_debug;
#define MPT_SGE_FLAGS_END_OF_BUFFER (0x40000000)
#define MPT_SGE_FLAGS_LOCAL_ADDRESS (0x08000000)
#define MPT_SGE_FLAGS_DIRECTION (0x04000000)
-#define MPT_SGE_FLAGS_ADDRESSING (mpt_addr_size() << MPI_SGE_FLAGS_SHIFT)
#define MPT_SGE_FLAGS_END_OF_LIST (0x01000000)
#define MPT_SGE_FLAGS_TRANSACTION_ELEMENT (0x00000000)
@@ -972,14 +1014,12 @@ extern int mpt_fwfault_debug;
MPT_SGE_FLAGS_END_OF_BUFFER | \
MPT_SGE_FLAGS_END_OF_LIST | \
MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
- MPT_SGE_FLAGS_ADDRESSING | \
MPT_TRANSFER_IOC_TO_HOST)
#define MPT_SGE_FLAGS_SSIMPLE_WRITE \
(MPT_SGE_FLAGS_LAST_ELEMENT | \
MPT_SGE_FLAGS_END_OF_BUFFER | \
MPT_SGE_FLAGS_END_OF_LIST | \
MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
- MPT_SGE_FLAGS_ADDRESSING | \
MPT_TRANSFER_HOST_TO_IOC)
/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index c63817117c0a..9b2e2198aee9 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -84,6 +84,7 @@ MODULE_VERSION(my_VERSION);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static u8 mptctl_id = MPT_MAX_PROTOCOL_DRIVERS;
+static u8 mptctl_taskmgmt_id = MPT_MAX_PROTOCOL_DRIVERS;
static DECLARE_WAIT_QUEUE_HEAD ( mptctl_wait );
@@ -127,10 +128,7 @@ static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags
struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma,
struct buflist *buflist, MPT_ADAPTER *ioc);
-static void mptctl_timeout_expired (MPT_IOCTL *ioctl);
-static int mptctl_bus_reset(MPT_IOCTL *ioctl);
-static int mptctl_set_tm_flags(MPT_SCSI_HOST *hd);
-static void mptctl_free_tm_flags(MPT_ADAPTER *ioc);
+static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function);
/*
* Reset Handler cleanup function
@@ -183,10 +181,10 @@ mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
int rc = 0;
if (nonblock) {
- if (!mutex_trylock(&ioc->ioctl->ioctl_mutex))
+ if (!mutex_trylock(&ioc->ioctl_cmds.mutex))
rc = -EAGAIN;
} else {
- if (mutex_lock_interruptible(&ioc->ioctl->ioctl_mutex))
+ if (mutex_lock_interruptible(&ioc->ioctl_cmds.mutex))
rc = -ERESTARTSYS;
}
return rc;
@@ -202,99 +200,78 @@ mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
static int
mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
{
- char *sense_data;
- int sz, req_index;
- u16 iocStatus;
- u8 cmd;
+ char *sense_data;
+ int req_index;
+ int sz;
- if (req)
- cmd = req->u.hdr.Function;
- else
- return 1;
- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\tcompleting mpi function (0x%02X), req=%p, "
- "reply=%p\n", ioc->name, req->u.hdr.Function, req, reply));
-
- if (ioc->ioctl) {
-
- if (reply==NULL) {
-
- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_reply() NULL Reply "
- "Function=%x!\n", ioc->name, cmd));
+ if (!req)
+ return 0;
- ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
- ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
+ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "completing mpi function "
+ "(0x%02X), req=%p, reply=%p\n", ioc->name, req->u.hdr.Function,
+ req, reply));
- /* We are done, issue wake up
- */
- ioc->ioctl->wait_done = 1;
- wake_up (&mptctl_wait);
- return 1;
+ /*
+ * Handling continuation of the same reply. Processing the first
+ * reply, and eating the other replys that come later.
+ */
+ if (ioc->ioctl_cmds.msg_context != req->u.hdr.MsgContext)
+ goto out_continuation;
- }
+ ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
- /* Copy the reply frame (which much exist
- * for non-SCSI I/O) to the IOC structure.
- */
- memcpy(ioc->ioctl->ReplyFrame, reply,
- min(ioc->reply_sz, 4*reply->u.reply.MsgLength));
- ioc->ioctl->status |= MPT_IOCTL_STATUS_RF_VALID;
+ if (!reply)
+ goto out;
- /* Set the command status to GOOD if IOC Status is GOOD
- * OR if SCSI I/O cmd and data underrun or recovered error.
- */
- iocStatus = le16_to_cpu(reply->u.reply.IOCStatus) & MPI_IOCSTATUS_MASK;
- if (iocStatus == MPI_IOCSTATUS_SUCCESS)
- ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
-
- if (iocStatus || reply->u.reply.IOCLogInfo)
- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\tiocstatus (0x%04X), "
- "loginfo (0x%08X)\n", ioc->name,
- iocStatus,
- le32_to_cpu(reply->u.reply.IOCLogInfo)));
-
- if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
- (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
-
- if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState)
- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "\tscsi_status (0x%02x), scsi_state (0x%02x), "
- "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name,
- reply->u.sreply.SCSIStatus,
- reply->u.sreply.SCSIState,
- le16_to_cpu(reply->u.sreply.TaskTag),
- le32_to_cpu(reply->u.sreply.TransferCount)));
-
- ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
-
- if ((iocStatus == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN) ||
- (iocStatus == MPI_IOCSTATUS_SCSI_RECOVERED_ERROR)) {
- ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
- }
- }
+ ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ sz = min(ioc->reply_sz, 4*reply->u.reply.MsgLength);
+ memcpy(ioc->ioctl_cmds.reply, reply, sz);
- /* Copy the sense data - if present
- */
- if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) &&
- (reply->u.sreply.SCSIState &
- MPI_SCSI_STATE_AUTOSENSE_VALID)){
+ if (reply->u.reply.IOCStatus || reply->u.reply.IOCLogInfo)
+ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "iocstatus (0x%04X), loginfo (0x%08X)\n", ioc->name,
+ le16_to_cpu(reply->u.reply.IOCStatus),
+ le32_to_cpu(reply->u.reply.IOCLogInfo)));
+
+ if ((req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+ (req->u.hdr.Function ==
+ MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+
+ if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState)
+ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "scsi_status (0x%02x), scsi_state (0x%02x), "
+ "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name,
+ reply->u.sreply.SCSIStatus,
+ reply->u.sreply.SCSIState,
+ le16_to_cpu(reply->u.sreply.TaskTag),
+ le32_to_cpu(reply->u.sreply.TransferCount)));
+
+ if (reply->u.sreply.SCSIState &
+ MPI_SCSI_STATE_AUTOSENSE_VALID) {
sz = req->u.scsireq.SenseBufferLength;
req_index =
le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx);
- sense_data =
- ((u8 *)ioc->sense_buf_pool +
+ sense_data = ((u8 *)ioc->sense_buf_pool +
(req_index * MPT_SENSE_BUFFER_ALLOC));
- memcpy(ioc->ioctl->sense, sense_data, sz);
- ioc->ioctl->status |= MPT_IOCTL_STATUS_SENSE_VALID;
+ memcpy(ioc->ioctl_cmds.sense, sense_data, sz);
+ ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_SENSE_VALID;
}
+ }
- if (cmd == MPI_FUNCTION_SCSI_TASK_MGMT)
- mptctl_free_tm_flags(ioc);
-
- /* We are done, issue wake up
- */
- ioc->ioctl->wait_done = 1;
- wake_up (&mptctl_wait);
+ out:
+ /* We are done, issue wake up
+ */
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT)
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->ioctl_cmds.done);
}
+
+ out_continuation:
+ if (reply && (reply->u.reply.MsgFlags &
+ MPI_MSGFLAGS_CONTINUATION_REPLY))
+ return 0;
return 1;
}
@@ -304,30 +281,66 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
* Expecting an interrupt, however timed out.
*
*/
-static void mptctl_timeout_expired (MPT_IOCTL *ioctl)
+static void
+mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
{
- int rc = 1;
+ unsigned long flags;
- if (ioctl == NULL)
- return;
- dctlprintk(ioctl->ioc,
- printk(MYIOC_s_DEBUG_FMT ": Timeout Expired! Host %d\n",
- ioctl->ioc->name, ioctl->ioc->id));
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n",
+ ioc->name, __func__));
- ioctl->wait_done = 0;
- if (ioctl->reset & MPTCTL_RESET_OK)
- rc = mptctl_bus_reset(ioctl);
+ if (mpt_fwfault_debug)
+ mpt_halt_firmware(ioc);
- if (rc) {
- /* Issue a reset for this device.
- * The IOC is not responding.
- */
- dctlprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
- ioctl->ioc->name));
- mpt_HardResetHandler(ioctl->ioc, CAN_SLEEP);
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status)
+ mpt_free_msg_frame(ioc, mf);
+ return;
}
- return;
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+ if (!mptctl_bus_reset(ioc, mf->u.hdr.Function))
+ return;
+
+ /* Issue a reset for this device.
+ * The IOC is not responding.
+ */
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
+ ioc->name));
+ CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status)
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+}
+
+static int
+mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+{
+ if (!mf)
+ return 0;
+
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt completed (mf=%p, mr=%p)\n",
+ ioc->name, mf, mr));
+
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+
+ if (!mr)
+ goto out;
+
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ memcpy(ioc->taskmgmt_cmds.reply, mr,
+ min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
+ out:
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->taskmgmt_cmds.done);
+ return 1;
+ }
+ return 0;
}
/* mptctl_bus_reset
@@ -335,133 +348,150 @@ static void mptctl_timeout_expired (MPT_IOCTL *ioctl)
* Bus reset code.
*
*/
-static int mptctl_bus_reset(MPT_IOCTL *ioctl)
+static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function)
{
MPT_FRAME_HDR *mf;
SCSITaskMgmt_t *pScsiTm;
- MPT_SCSI_HOST *hd;
+ SCSITaskMgmtReply_t *pScsiTmReply;
int ii;
- int retval=0;
-
-
- ioctl->reset &= ~MPTCTL_RESET_OK;
-
- if (ioctl->ioc->sh == NULL)
+ int retval;
+ unsigned long timeout;
+ unsigned long time_count;
+ u16 iocstatus;
+
+ /* bus reset is only good for SCSI IO, RAID PASSTHRU */
+ if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ||
+ (function == MPI_FUNCTION_SCSI_IO_REQUEST)) {
+ dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "TaskMgmt, not SCSI_IO!!\n", ioc->name));
return -EPERM;
+ }
- hd = shost_priv(ioctl->ioc->sh);
- if (hd == NULL)
+ mutex_lock(&ioc->taskmgmt_cmds.mutex);
+ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
return -EPERM;
+ }
- /* Single threading ....
- */
- if (mptctl_set_tm_flags(hd) != 0)
- return -EPERM;
+ retval = 0;
/* Send request
*/
- if ((mf = mpt_get_msg_frame(mptctl_id, ioctl->ioc)) == NULL) {
- dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt, no msg frames!!\n",
- ioctl->ioc->name));
-
- mptctl_free_tm_flags(ioctl->ioc);
- return -ENOMEM;
+ mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc);
+ if (mf == NULL) {
+ dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "TaskMgmt, no msg frames!!\n", ioc->name));
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ retval = -ENOMEM;
+ goto mptctl_bus_reset_done;
}
- dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt request @ %p\n",
- ioctl->ioc->name, mf));
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
+ ioc->name, mf));
pScsiTm = (SCSITaskMgmt_t *) mf;
- pScsiTm->TargetID = ioctl->id;
- pScsiTm->Bus = hd->port; /* 0 */
- pScsiTm->ChainOffset = 0;
+ memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
- pScsiTm->Reserved = 0;
pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
- pScsiTm->Reserved1 = 0;
pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
-
+ pScsiTm->TargetID = 0;
+ pScsiTm->Bus = 0;
+ pScsiTm->ChainOffset = 0;
+ pScsiTm->Reserved = 0;
+ pScsiTm->Reserved1 = 0;
+ pScsiTm->TaskMsgContext = 0;
for (ii= 0; ii < 8; ii++)
pScsiTm->LUN[ii] = 0;
-
for (ii=0; ii < 7; ii++)
pScsiTm->Reserved2[ii] = 0;
- pScsiTm->TaskMsgContext = 0;
- dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT
- "mptctl_bus_reset: issued.\n", ioctl->ioc->name));
-
- DBG_DUMP_TM_REQUEST_FRAME(ioctl->ioc, (u32 *)mf);
+ switch (ioc->bus_type) {
+ case FC:
+ timeout = 40;
+ break;
+ case SAS:
+ timeout = 30;
+ break;
+ case SPI:
+ default:
+ timeout = 2;
+ break;
+ }
- ioctl->wait_done=0;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt type=%d timeout=%ld\n",
+ ioc->name, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, timeout));
- if ((ioctl->ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
- (ioctl->ioc->facts.MsgVersion >= MPI_VERSION_01_05))
- mpt_put_msg_frame_hi_pri(mptctl_id, ioctl->ioc, mf);
+ INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ time_count = jiffies;
+ if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
+ (ioc->facts.MsgVersion >= MPI_VERSION_01_05))
+ mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf);
else {
- retval = mpt_send_handshake_request(mptctl_id, ioctl->ioc,
- sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP);
+ retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc,
+ sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP);
if (retval != 0) {
- dfailprintk(ioctl->ioc, printk(MYIOC_s_ERR_FMT "_send_handshake FAILED!"
- " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
- hd->ioc, mf));
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "TaskMgmt send_handshake FAILED!"
+ " (ioc %p, mf %p, rc=%d) \n", ioc->name,
+ ioc, mf, retval));
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
goto mptctl_bus_reset_done;
}
}
/* Now wait for the command to complete */
- ii = wait_event_timeout(mptctl_wait,
- ioctl->wait_done == 1,
- HZ*5 /* 5 second timeout */);
+ ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ);
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt failed\n", ioc->name));
+ mpt_free_msg_frame(ioc, mf);
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ retval = 0;
+ else
+ retval = -1; /* return failure */
+ goto mptctl_bus_reset_done;
+ }
- if(ii <=0 && (ioctl->wait_done != 1 )) {
- mpt_free_msg_frame(hd->ioc, mf);
- ioctl->wait_done = 0;
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt failed\n", ioc->name));
+ retval = -1; /* return failure */
+ goto mptctl_bus_reset_done;
+ }
+
+ pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, "
+ "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, "
+ "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus,
+ pScsiTmReply->TargetID, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+ le16_to_cpu(pScsiTmReply->IOCStatus),
+ le32_to_cpu(pScsiTmReply->IOCLogInfo),
+ pScsiTmReply->ResponseCode,
+ le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+ iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+
+ if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
+ iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED ||
+ iocstatus == MPI_IOCSTATUS_SUCCESS)
+ retval = 0;
+ else {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt failed\n", ioc->name));
retval = -1; /* return failure */
}
-mptctl_bus_reset_done:
- mptctl_free_tm_flags(ioctl->ioc);
+ mptctl_bus_reset_done:
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
return retval;
}
-static int
-mptctl_set_tm_flags(MPT_SCSI_HOST *hd) {
- unsigned long flags;
-
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
-
- if (hd->tmState == TM_STATE_NONE) {
- hd->tmState = TM_STATE_IN_PROGRESS;
- hd->tmPending = 1;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- } else {
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- return -EBUSY;
- }
-
- return 0;
-}
-
-static void
-mptctl_free_tm_flags(MPT_ADAPTER *ioc)
-{
- MPT_SCSI_HOST * hd;
- unsigned long flags;
-
- hd = shost_priv(ioc->sh);
- if (hd == NULL)
- return;
-
- spin_lock_irqsave(&ioc->FreeQlock, flags);
-
- hd->tmState = TM_STATE_NONE;
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
- return;
-}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* mptctl_ioc_reset
@@ -473,22 +503,23 @@ mptctl_free_tm_flags(MPT_ADAPTER *ioc)
static int
mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
{
- MPT_IOCTL *ioctl = ioc->ioctl;
- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IOC %s_reset routed to IOCTL driver!\n", ioc->name,
- reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
- reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
- if(ioctl == NULL)
- return 1;
-
switch(reset_phase) {
case MPT_IOC_SETUP_RESET:
- ioctl->status |= MPT_IOCTL_STATUS_DID_IOCRESET;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+ break;
+ case MPT_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
break;
case MPT_IOC_POST_RESET:
- ioctl->status &= ~MPT_IOCTL_STATUS_DID_IOCRESET;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_DID_IOCRESET;
+ complete(&ioc->ioctl_cmds.done);
+ }
break;
- case MPT_IOC_PRE_RESET:
default:
break;
}
@@ -642,7 +673,7 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
else
ret = -EINVAL;
- mutex_unlock(&iocp->ioctl->ioctl_mutex);
+ mutex_unlock(&iocp->ioctl_cmds.mutex);
return ret;
}
@@ -758,6 +789,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
int sge_offset = 0;
u16 iocstat;
pFWDownloadReply_t ReplyMsg = NULL;
+ unsigned long timeleft;
if (mpt_verify_adapter(ioc, &iocp) < 0) {
printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n",
@@ -841,8 +873,9 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
* 96 8
* 64 4
*/
- maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t))
- / (sizeof(dma_addr_t) + sizeof(u32));
+ maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) -
+ sizeof(FWDownloadTCSGE_t))
+ / iocp->SGE_size;
if (numfrags > maxfrags) {
ret = -EMLINK;
goto fwdl_out;
@@ -870,7 +903,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
if (nib == 0 || nib == 3) {
;
} else if (sgIn->Address) {
- mpt_add_sge(sgOut, sgIn->FlagsLength, sgIn->Address);
+ iocp->add_sge(sgOut, sgIn->FlagsLength, sgIn->Address);
n++;
if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) {
printk(MYIOC_s_ERR_FMT "%s@%d::_ioctl_fwdl - "
@@ -882,7 +915,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
}
sgIn++;
bl++;
- sgOut += (sizeof(dma_addr_t) + sizeof(u32));
+ sgOut += iocp->SGE_size;
}
DBG_DUMP_FW_DOWNLOAD(iocp, (u32 *)mf, numfrags);
@@ -891,16 +924,30 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
* Finally, perform firmware download.
*/
ReplyMsg = NULL;
+ SET_MGMT_MSG_CONTEXT(iocp->ioctl_cmds.msg_context, dlmsg->MsgContext);
+ INITIALIZE_MGMT_STATUS(iocp->ioctl_cmds.status)
mpt_put_msg_frame(mptctl_id, iocp, mf);
/* Now wait for the command to complete */
- ret = wait_event_timeout(mptctl_wait,
- iocp->ioctl->wait_done == 1,
- HZ*60);
+retry_wait:
+ timeleft = wait_for_completion_timeout(&iocp->ioctl_cmds.done, HZ*60);
+ if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = -ETIME;
+ printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__);
+ if (iocp->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+ mpt_free_msg_frame(iocp, mf);
+ goto fwdl_out;
+ }
+ if (!timeleft)
+ mptctl_timeout_expired(iocp, mf);
+ else
+ goto retry_wait;
+ goto fwdl_out;
+ }
- if(ret <=0 && (iocp->ioctl->wait_done != 1 )) {
- /* Now we need to reset the board */
- mptctl_timeout_expired(iocp->ioctl);
+ if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__);
+ mpt_free_msg_frame(iocp, mf);
ret = -ENODATA;
goto fwdl_out;
}
@@ -908,7 +955,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
if (sgl)
kfree_sgl(sgl, sgl_dma, buflist, iocp);
- ReplyMsg = (pFWDownloadReply_t)iocp->ioctl->ReplyFrame;
+ ReplyMsg = (pFWDownloadReply_t)iocp->ioctl_cmds.reply;
iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK;
if (iocstat == MPI_IOCSTATUS_SUCCESS) {
printk(MYIOC_s_INFO_FMT "F/W update successfull!\n", iocp->name);
@@ -932,6 +979,9 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
return 0;
fwdl_out:
+
+ CLEAR_MGMT_STATUS(iocp->ioctl_cmds.status);
+ SET_MGMT_MSG_CONTEXT(iocp->ioctl_cmds.msg_context, 0);
kfree_sgl(sgl, sgl_dma, buflist, iocp);
return ret;
}
@@ -1003,7 +1053,7 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags,
*
*/
sgl = sglbuf;
- sg_spill = ((ioc->req_sz - sge_offset)/(sizeof(dma_addr_t) + sizeof(u32))) - 1;
+ sg_spill = ((ioc->req_sz - sge_offset)/ioc->SGE_size) - 1;
while (bytes_allocd < bytes) {
this_alloc = min(alloc_sz, bytes-bytes_allocd);
buflist[buflist_ent].len = this_alloc;
@@ -1024,8 +1074,9 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags,
dma_addr_t dma_addr;
bytes_allocd += this_alloc;
- sgl->FlagsLength = (0x10000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|this_alloc);
- dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir);
+ sgl->FlagsLength = (0x10000000|sgdir|this_alloc);
+ dma_addr = pci_map_single(ioc->pcidev,
+ buflist[buflist_ent].kptr, this_alloc, dir);
sgl->Address = dma_addr;
fragcnt++;
@@ -1771,7 +1822,10 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
int msgContext;
u16 req_idx;
ulong timeout;
+ unsigned long timeleft;
struct scsi_device *sdev;
+ unsigned long flags;
+ u8 function;
/* bufIn and bufOut are used for user to kernel space transfers
*/
@@ -1784,24 +1838,23 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
__FILE__, __LINE__, iocnum);
return -ENODEV;
}
- if (!ioc->ioctl) {
- printk(KERN_ERR MYNAM "%s@%d::mptctl_do_mpt_command - "
- "No memory available during driver init.\n",
- __FILE__, __LINE__);
- return -ENOMEM;
- } else if (ioc->ioctl->status & MPT_IOCTL_STATUS_DID_IOCRESET) {
+
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
printk(KERN_ERR MYNAM "%s@%d::mptctl_do_mpt_command - "
- "Busy with IOC Reset \n", __FILE__, __LINE__);
+ "Busy with diagnostic reset\n", __FILE__, __LINE__);
return -EBUSY;
}
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
/* Verify that the final request frame will not be too large.
*/
sz = karg.dataSgeOffset * 4;
if (karg.dataInSize > 0)
- sz += sizeof(dma_addr_t) + sizeof(u32);
+ sz += ioc->SGE_size;
if (karg.dataOutSize > 0)
- sz += sizeof(dma_addr_t) + sizeof(u32);
+ sz += ioc->SGE_size;
if (sz > ioc->req_sz) {
printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
@@ -1827,10 +1880,12 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
"Unable to read MF from mpt_ioctl_command struct @ %p\n",
ioc->name, __FILE__, __LINE__, mfPtr);
+ function = -1;
rc = -EFAULT;
goto done_free_mem;
}
hdr->MsgContext = cpu_to_le32(msgContext);
+ function = hdr->Function;
/* Verify that this request is allowed.
@@ -1838,7 +1893,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sending mpi function (0x%02X), req=%p\n",
ioc->name, hdr->Function, mf));
- switch (hdr->Function) {
+ switch (function) {
case MPI_FUNCTION_IOC_FACTS:
case MPI_FUNCTION_PORT_FACTS:
karg.dataOutSize = karg.dataInSize = 0;
@@ -1893,7 +1948,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
}
pScsiReq->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH;
- pScsiReq->MsgFlags |= mpt_msg_flags();
+ pScsiReq->MsgFlags |= mpt_msg_flags(ioc);
/* verify that app has not requested
@@ -1935,8 +1990,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
pScsiReq->Control = cpu_to_le32(scsidir | qtag);
pScsiReq->DataLength = cpu_to_le32(dataSize);
- ioc->ioctl->reset = MPTCTL_RESET_OK;
- ioc->ioctl->id = pScsiReq->TargetID;
} else {
printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
@@ -1979,7 +2032,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
int dataSize;
pScsiReq->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH;
- pScsiReq->MsgFlags |= mpt_msg_flags();
+ pScsiReq->MsgFlags |= mpt_msg_flags(ioc);
/* verify that app has not requested
@@ -2014,8 +2067,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
pScsiReq->Control = cpu_to_le32(scsidir | qtag);
pScsiReq->DataLength = cpu_to_le32(dataSize);
- ioc->ioctl->reset = MPTCTL_RESET_OK;
- ioc->ioctl->id = pScsiReq->TargetID;
} else {
printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
"SCSI driver is not loaded. \n",
@@ -2026,20 +2077,17 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
break;
case MPI_FUNCTION_SCSI_TASK_MGMT:
- {
- MPT_SCSI_HOST *hd = NULL;
- if ((ioc->sh == NULL) || ((hd = shost_priv(ioc->sh)) == NULL)) {
- printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
- "SCSI driver not loaded or SCSI host not found. \n",
- ioc->name, __FILE__, __LINE__);
- rc = -EFAULT;
- goto done_free_mem;
- } else if (mptctl_set_tm_flags(hd) != 0) {
- rc = -EPERM;
- goto done_free_mem;
- }
- }
+ {
+ SCSITaskMgmt_t *pScsiTm;
+ pScsiTm = (SCSITaskMgmt_t *)mf;
+ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "\tTaskType=0x%x MsgFlags=0x%x "
+ "TaskMsgContext=0x%x id=%d channel=%d\n",
+ ioc->name, pScsiTm->TaskType, le32_to_cpu
+ (pScsiTm->TaskMsgContext), pScsiTm->MsgFlags,
+ pScsiTm->TargetID, pScsiTm->Bus));
break;
+ }
case MPI_FUNCTION_IOC_INIT:
{
@@ -2123,8 +2171,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
if (karg.dataInSize > 0) {
flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
- MPI_SGE_FLAGS_DIRECTION |
- mpt_addr_size() )
+ MPI_SGE_FLAGS_DIRECTION)
<< MPI_SGE_FLAGS_SHIFT;
} else {
flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE;
@@ -2141,8 +2188,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
/* Set up this SGE.
* Copy to MF and to sglbuf
*/
- mpt_add_sge(psge, flagsLength, dma_addr_out);
- psge += (sizeof(u32) + sizeof(dma_addr_t));
+ ioc->add_sge(psge, flagsLength, dma_addr_out);
+ psge += ioc->SGE_size;
/* Copy user data to kernel space.
*/
@@ -2175,18 +2222,25 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
/* Set up this SGE
* Copy to MF and to sglbuf
*/
- mpt_add_sge(psge, flagsLength, dma_addr_in);
+ ioc->add_sge(psge, flagsLength, dma_addr_in);
}
}
} else {
/* Add a NULL SGE
*/
- mpt_add_sge(psge, flagsLength, (dma_addr_t) -1);
+ ioc->add_sge(psge, flagsLength, (dma_addr_t) -1);
}
- ioc->ioctl->wait_done = 0;
+ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, hdr->MsgContext);
+ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status)
if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) {
+ mutex_lock(&ioc->taskmgmt_cmds.mutex);
+ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ goto done_free_mem;
+ }
+
DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
@@ -2197,10 +2251,11 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP);
if (rc != 0) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "_send_handshake FAILED! (ioc %p, mf %p)\n",
+ "send_handshake FAILED! (ioc %p, mf %p)\n",
ioc->name, ioc, mf));
- mptctl_free_tm_flags(ioc);
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
rc = -ENODATA;
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
goto done_free_mem;
}
}
@@ -2210,36 +2265,47 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
/* Now wait for the command to complete */
timeout = (karg.timeout > 0) ? karg.timeout : MPT_IOCTL_DEFAULT_TIMEOUT;
- timeout = wait_event_timeout(mptctl_wait,
- ioc->ioctl->wait_done == 1,
- HZ*timeout);
-
- if(timeout <=0 && (ioc->ioctl->wait_done != 1 )) {
- /* Now we need to reset the board */
-
- if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT)
- mptctl_free_tm_flags(ioc);
-
- mptctl_timeout_expired(ioc->ioctl);
- rc = -ENODATA;
+retry_wait:
+ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done,
+ HZ*timeout);
+ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ rc = -ETIME;
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: TIMED OUT!\n",
+ ioc->name, __func__));
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+ if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ goto done_free_mem;
+ }
+ if (!timeleft) {
+ if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ mptctl_timeout_expired(ioc, mf);
+ mf = NULL;
+ } else
+ goto retry_wait;
goto done_free_mem;
}
+ if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+
+
mf = NULL;
/* If a valid reply frame, copy to the user.
* Offset 2: reply length in U32's
*/
- if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID) {
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) {
if (karg.maxReplyBytes < ioc->reply_sz) {
- sz = min(karg.maxReplyBytes, 4*ioc->ioctl->ReplyFrame[2]);
+ sz = min(karg.maxReplyBytes,
+ 4*ioc->ioctl_cmds.reply[2]);
} else {
- sz = min(ioc->reply_sz, 4*ioc->ioctl->ReplyFrame[2]);
+ sz = min(ioc->reply_sz, 4*ioc->ioctl_cmds.reply[2]);
}
-
if (sz > 0) {
if (copy_to_user(karg.replyFrameBufPtr,
- &ioc->ioctl->ReplyFrame, sz)){
+ ioc->ioctl_cmds.reply, sz)){
printk(MYIOC_s_ERR_FMT
"%s@%d::mptctl_do_mpt_command - "
"Unable to write out reply frame %p\n",
@@ -2252,10 +2318,11 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
/* If valid sense data, copy to user.
*/
- if (ioc->ioctl->status & MPT_IOCTL_STATUS_SENSE_VALID) {
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_SENSE_VALID) {
sz = min(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE);
if (sz > 0) {
- if (copy_to_user(karg.senseDataPtr, ioc->ioctl->sense, sz)) {
+ if (copy_to_user(karg.senseDataPtr,
+ ioc->ioctl_cmds.sense, sz)) {
printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
"Unable to write sense data to user %p\n",
ioc->name, __FILE__, __LINE__,
@@ -2269,7 +2336,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
/* If the overall status is _GOOD and data in, copy data
* to user.
*/
- if ((ioc->ioctl->status & MPT_IOCTL_STATUS_COMMAND_GOOD) &&
+ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD) &&
(karg.dataInSize > 0) && (bufIn.kptr)) {
if (copy_to_user(karg.dataInBufPtr,
@@ -2284,9 +2351,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
done_free_mem:
- ioc->ioctl->status &= ~(MPT_IOCTL_STATUS_COMMAND_GOOD |
- MPT_IOCTL_STATUS_SENSE_VALID |
- MPT_IOCTL_STATUS_RF_VALID );
+ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status)
+ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0);
/* Free the allocated memory.
*/
@@ -2336,6 +2402,8 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
ToolboxIstwiReadWriteRequest_t *IstwiRWRequest;
MPT_FRAME_HDR *mf = NULL;
MPIHeader_t *mpi_hdr;
+ unsigned long timeleft;
+ int retval;
/* Reset long to int. Should affect IA64 and SPARC only
*/
@@ -2466,9 +2534,9 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
if (hd && (cim_rev == 1)) {
- karg.hard_resets = hd->hard_resets;
- karg.soft_resets = hd->soft_resets;
- karg.timeouts = hd->timeouts;
+ karg.hard_resets = ioc->hard_resets;
+ karg.soft_resets = ioc->soft_resets;
+ karg.timeouts = ioc->timeouts;
}
}
@@ -2476,8 +2544,8 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
* Gather ISTWI(Industry Standard Two Wire Interface) Data
*/
if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
- ioc->name,__func__));
+ dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "%s, no msg frames!!\n", ioc->name, __func__));
goto out;
}
@@ -2498,22 +2566,29 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
pbuf = pci_alloc_consistent(ioc->pcidev, 4, &buf_dma);
if (!pbuf)
goto out;
- mpt_add_sge((char *)&IstwiRWRequest->SGL,
+ ioc->add_sge((char *)&IstwiRWRequest->SGL,
(MPT_SGE_FLAGS_SSIMPLE_READ|4), buf_dma);
- ioc->ioctl->wait_done = 0;
+ retval = 0;
+ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context,
+ IstwiRWRequest->MsgContext);
+ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status)
mpt_put_msg_frame(mptctl_id, ioc, mf);
- rc = wait_event_timeout(mptctl_wait,
- ioc->ioctl->wait_done == 1,
- HZ*MPT_IOCTL_DEFAULT_TIMEOUT /* 10 sec */);
-
- if(rc <=0 && (ioc->ioctl->wait_done != 1 )) {
- /*
- * Now we need to reset the board
- */
- mpt_free_msg_frame(ioc, mf);
- mptctl_timeout_expired(ioc->ioctl);
+retry_wait:
+ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done,
+ HZ*MPT_IOCTL_DEFAULT_TIMEOUT);
+ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ retval = -ETIME;
+ printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, __func__);
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+ mpt_free_msg_frame(ioc, mf);
+ goto out;
+ }
+ if (!timeleft)
+ mptctl_timeout_expired(ioc, mf);
+ else
+ goto retry_wait;
goto out;
}
@@ -2526,10 +2601,13 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
* bays have drives in them
* pbuf[3] = Checksum (0x100 = (byte0 + byte2 + byte3)
*/
- if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID)
+ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID)
karg.rsvd = *(u32 *)pbuf;
out:
+ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status)
+ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0);
+
if (pbuf)
pci_free_consistent(ioc->pcidev, 4, pbuf, buf_dma);
@@ -2753,7 +2831,7 @@ compat_mptfwxfer_ioctl(struct file *filp, unsigned int cmd,
ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
- mutex_unlock(&iocp->ioctl->ioctl_mutex);
+ mutex_unlock(&iocp->ioctl_cmds.mutex);
return ret;
}
@@ -2807,7 +2885,7 @@ compat_mpt_command(struct file *filp, unsigned int cmd,
*/
ret = mptctl_do_mpt_command (karg, &uarg->MF);
- mutex_unlock(&iocp->ioctl->ioctl_mutex);
+ mutex_unlock(&iocp->ioctl_cmds.mutex);
return ret;
}
@@ -2859,21 +2937,10 @@ static long compat_mpctl_ioctl(struct file *f, unsigned int cmd, unsigned long a
static int
mptctl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
- MPT_IOCTL *mem;
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
- /*
- * Allocate and inite a MPT_IOCTL structure
- */
- mem = kzalloc(sizeof(MPT_IOCTL), GFP_KERNEL);
- if (!mem) {
- mptctl_remove(pdev);
- return -ENOMEM;
- }
-
- ioc->ioctl = mem;
- ioc->ioctl->ioc = ioc;
- mutex_init(&ioc->ioctl->ioctl_mutex);
+ mutex_init(&ioc->ioctl_cmds.mutex);
+ init_completion(&ioc->ioctl_cmds.done);
return 0;
}
@@ -2887,9 +2954,6 @@ mptctl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void
mptctl_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
-
- kfree ( ioc->ioctl );
}
static struct mpt_pci_driver mptctl_driver = {
@@ -2929,6 +2993,7 @@ static int __init mptctl_init(void)
goto out_fail;
}
+ mptctl_taskmgmt_id = mpt_register(mptctl_taskmgmt_reply, MPTCTL_DRIVER);
mpt_reset_register(mptctl_id, mptctl_ioc_reset);
mpt_event_register(mptctl_id, mptctl_event_process);
@@ -2953,6 +3018,7 @@ static void mptctl_exit(void)
/* De-register callback handler from base module */
mpt_deregister(mptctl_id);
+ mpt_reset_deregister(mptctl_taskmgmt_id);
mpt_device_driver_deregister(MPTCTL_DRIVER);
diff --git a/drivers/message/fusion/mptdebug.h b/drivers/message/fusion/mptdebug.h
index 510b9f492093..28e478879284 100644
--- a/drivers/message/fusion/mptdebug.h
+++ b/drivers/message/fusion/mptdebug.h
@@ -58,6 +58,7 @@
#define MPT_DEBUG_FC 0x00080000
#define MPT_DEBUG_SAS 0x00100000
#define MPT_DEBUG_SAS_WIDE 0x00200000
+#define MPT_DEBUG_36GB_MEM 0x00400000
/*
* CONFIG_FUSION_LOGGING - enabled in Kconfig
@@ -135,6 +136,8 @@
#define dsaswideprintk(IOC, CMD) \
MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE)
+#define d36memprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_36GB_MEM)
/*
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index c3c24fdf9fb6..e61df133a59e 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -1251,17 +1251,15 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* A slightly different algorithm is required for
* 64bit SGEs.
*/
- scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
- if (sizeof(dma_addr_t) == sizeof(u64)) {
+ scale = ioc->req_sz/ioc->SGE_size;
+ if (ioc->sg_addr_size == sizeof(u64)) {
numSGE = (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 60) / ioc->SGE_size;
} else {
numSGE = 1 + (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 64) / ioc->SGE_size;
}
if (numSGE < sh->sg_tablesize) {
@@ -1292,9 +1290,6 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Clear the TM flags
*/
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- hd->resetPending = 0;
hd->abortSCpnt = NULL;
/* Clear the pointer used to store
@@ -1312,8 +1307,6 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hd->timer.data = (unsigned long) hd;
hd->timer.function = mptscsih_timer_expired;
- init_waitqueue_head(&hd->scandv_waitq);
- hd->scandv_wait_done = 0;
hd->last_queue_full = 0;
sh->transportt = mptfc_transport_template;
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 4e6fcf06a6f2..20e0b447e8e8 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -93,8 +93,37 @@ static u8 mptsasDoneCtx = MPT_MAX_PROTOCOL_DRIVERS;
static u8 mptsasTaskCtx = MPT_MAX_PROTOCOL_DRIVERS;
static u8 mptsasInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */
static u8 mptsasMgmtCtx = MPT_MAX_PROTOCOL_DRIVERS;
-
-static void mptsas_hotplug_work(struct work_struct *work);
+static u8 mptsasDeviceResetCtx = MPT_MAX_PROTOCOL_DRIVERS;
+
+static void mptsas_firmware_event_work(struct work_struct *work);
+static void mptsas_send_sas_event(struct fw_event_work *fw_event);
+static void mptsas_send_raid_event(struct fw_event_work *fw_event);
+static void mptsas_send_ir2_event(struct fw_event_work *fw_event);
+static void mptsas_parse_device_info(struct sas_identify *identify,
+ struct mptsas_devinfo *device_info);
+static inline void mptsas_set_rphy(MPT_ADAPTER *ioc,
+ struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy);
+static struct mptsas_phyinfo *mptsas_find_phyinfo_by_sas_address
+ (MPT_ADAPTER *ioc, u64 sas_address);
+static int mptsas_sas_device_pg0(MPT_ADAPTER *ioc,
+ struct mptsas_devinfo *device_info, u32 form, u32 form_specific);
+static int mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc,
+ struct mptsas_enclosure *enclosure, u32 form, u32 form_specific);
+static int mptsas_add_end_device(MPT_ADAPTER *ioc,
+ struct mptsas_phyinfo *phy_info);
+static void mptsas_del_end_device(MPT_ADAPTER *ioc,
+ struct mptsas_phyinfo *phy_info);
+static void mptsas_send_link_status_event(struct fw_event_work *fw_event);
+static struct mptsas_portinfo *mptsas_find_portinfo_by_sas_address
+ (MPT_ADAPTER *ioc, u64 sas_address);
+static void mptsas_expander_delete(MPT_ADAPTER *ioc,
+ struct mptsas_portinfo *port_info, u8 force);
+static void mptsas_send_expander_event(struct fw_event_work *fw_event);
+static void mptsas_not_responding_devices(MPT_ADAPTER *ioc);
+static void mptsas_scan_sas_topology(MPT_ADAPTER *ioc);
+static void mptsas_broadcast_primative_work(struct fw_event_work *fw_event);
+static void mptsas_handle_queue_full_event(struct fw_event_work *fw_event);
+static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id);
static void mptsas_print_phy_data(MPT_ADAPTER *ioc,
MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
@@ -218,30 +247,125 @@ static void mptsas_print_expander_pg1(MPT_ADAPTER *ioc, SasExpanderPage1_t *pg1)
le16_to_cpu(pg1->AttachedDevHandle)));
}
-static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
+/* inhibit sas firmware event handling */
+static void
+mptsas_fw_event_off(MPT_ADAPTER *ioc)
{
- struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
- return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ ioc->fw_events_off = 1;
+ ioc->sas_discovery_quiesce_io = 0;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
}
-static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
+/* enable sas firmware event handling */
+static void
+mptsas_fw_event_on(MPT_ADAPTER *ioc)
{
- struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
- return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ ioc->fw_events_off = 0;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
-static struct mptsas_portinfo *
-mptsas_get_hba_portinfo(MPT_ADAPTER *ioc)
+/* queue a sas firmware event */
+static void
+mptsas_add_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+ unsigned long delay)
{
- struct list_head *head = &ioc->sas_topology;
- struct mptsas_portinfo *pi = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_add_tail(&fw_event->list, &ioc->fw_event_list);
+ INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work);
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)\n",
+ ioc->name, __func__, fw_event));
+ queue_delayed_work(ioc->fw_event_q, &fw_event->work,
+ delay);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* requeue a sas firmware event */
+static void
+mptsas_requeue_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+ unsigned long delay)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task "
+ "(fw_event=0x%p)\n", ioc->name, __func__, fw_event));
+ fw_event->retries++;
+ queue_delayed_work(ioc->fw_event_q, &fw_event->work,
+ msecs_to_jiffies(delay));
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* free memory assoicated to a sas firmware event */
+static void
+mptsas_free_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: kfree (fw_event=0x%p)\n",
+ ioc->name, __func__, fw_event));
+ list_del(&fw_event->list);
+ kfree(fw_event);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* walk the firmware event queue, and either stop or wait for
+ * outstanding events to complete */
+static void
+mptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event, *next;
+ struct mptsas_target_reset_event *target_reset_list, *n;
+ u8 flush_q;
+ MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
+
+ /* flush the target_reset_list */
+ if (!list_empty(&hd->target_reset_list)) {
+ list_for_each_entry_safe(target_reset_list, n,
+ &hd->target_reset_list, list) {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: removing target reset for id=%d\n",
+ ioc->name, __func__,
+ target_reset_list->sas_event_data.TargetID));
+ list_del(&target_reset_list->list);
+ kfree(target_reset_list);
+ }
+ }
+
+ if (list_empty(&ioc->fw_event_list) ||
+ !ioc->fw_event_q || in_interrupt())
+ return;
- /* always the first entry on sas_topology list */
+ flush_q = 0;
+ list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
+ if (cancel_delayed_work(&fw_event->work))
+ mptsas_free_fw_event(ioc, fw_event);
+ else
+ flush_q = 1;
+ }
+ if (flush_q)
+ flush_workqueue(ioc->fw_event_q);
+}
- if (!list_empty(head))
- pi = list_entry(head->next, struct mptsas_portinfo, list);
- return pi;
+static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+}
+
+static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
+{
+ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
+ return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
}
/*
@@ -265,6 +389,38 @@ mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
return rc;
}
+/**
+ * mptsas_find_portinfo_by_sas_address -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @handle:
+ *
+ * This function should be called with the sas_topology_mutex already held
+ *
+ **/
+static struct mptsas_portinfo *
+mptsas_find_portinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
+{
+ struct mptsas_portinfo *port_info, *rc = NULL;
+ int i;
+
+ if (sas_address >= ioc->hba_port_sas_addr &&
+ sas_address < (ioc->hba_port_sas_addr +
+ ioc->hba_port_num_phy))
+ return ioc->hba_port_info;
+
+ mutex_lock(&ioc->sas_topology_mutex);
+ list_for_each_entry(port_info, &ioc->sas_topology, list)
+ for (i = 0; i < port_info->num_phys; i++)
+ if (port_info->phy_info[i].identify.sas_address ==
+ sas_address) {
+ rc = port_info;
+ goto out;
+ }
+ out:
+ mutex_unlock(&ioc->sas_topology_mutex);
+ return rc;
+}
+
/*
* Returns true if there is a scsi end device
*/
@@ -308,6 +464,7 @@ mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_detai
if(phy_info->port_details != port_details)
continue;
memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
+ mptsas_set_rphy(ioc, phy_info, NULL);
phy_info->port_details = NULL;
}
kfree(port_details);
@@ -379,6 +536,285 @@ starget)
phy_info->port_details->starget = starget;
}
+/**
+ * mptsas_add_device_component -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @channel: fw mapped id's
+ * @id:
+ * @sas_address:
+ * @device_info:
+ *
+ **/
+static void
+mptsas_add_device_component(MPT_ADAPTER *ioc, u8 channel, u8 id,
+ u64 sas_address, u32 device_info, u16 slot, u64 enclosure_logical_id)
+{
+ struct mptsas_device_info *sas_info, *next;
+ struct scsi_device *sdev;
+ struct scsi_target *starget;
+ struct sas_rphy *rphy;
+
+ /*
+ * Delete all matching devices out of the list
+ */
+ mutex_lock(&ioc->sas_device_info_mutex);
+ list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+ list) {
+ if (!sas_info->is_logical_volume &&
+ (sas_info->sas_address == sas_address ||
+ (sas_info->fw.channel == channel &&
+ sas_info->fw.id == id))) {
+ list_del(&sas_info->list);
+ kfree(sas_info);
+ }
+ }
+
+ sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
+ if (!sas_info)
+ goto out;
+
+ /*
+ * Set Firmware mapping
+ */
+ sas_info->fw.id = id;
+ sas_info->fw.channel = channel;
+
+ sas_info->sas_address = sas_address;
+ sas_info->device_info = device_info;
+ sas_info->slot = slot;
+ sas_info->enclosure_logical_id = enclosure_logical_id;
+ INIT_LIST_HEAD(&sas_info->list);
+ list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
+
+ /*
+ * Set OS mapping
+ */
+ shost_for_each_device(sdev, ioc->sh) {
+ starget = scsi_target(sdev);
+ rphy = dev_to_rphy(starget->dev.parent);
+ if (rphy->identify.sas_address == sas_address) {
+ sas_info->os.id = starget->id;
+ sas_info->os.channel = starget->channel;
+ }
+ }
+
+ out:
+ mutex_unlock(&ioc->sas_device_info_mutex);
+ return;
+}
+
+/**
+ * mptsas_add_device_component_by_fw -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @channel: fw mapped id's
+ * @id:
+ *
+ **/
+static void
+mptsas_add_device_component_by_fw(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+ struct mptsas_devinfo sas_device;
+ struct mptsas_enclosure enclosure_info;
+ int rc;
+
+ rc = mptsas_sas_device_pg0(ioc, &sas_device,
+ (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ (channel << 8) + id);
+ if (rc)
+ return;
+
+ memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
+ mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
+ (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
+ MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
+ sas_device.handle_enclosure);
+
+ mptsas_add_device_component(ioc, sas_device.channel,
+ sas_device.id, sas_device.sas_address, sas_device.device_info,
+ sas_device.slot, enclosure_info.enclosure_logical_id);
+}
+
+/**
+ * mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding each individual device to list
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @channel: fw mapped id's
+ * @id:
+ *
+ **/
+static void
+mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc,
+ struct scsi_target *starget)
+{
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
+ dma_addr_t dma_handle;
+ pRaidVolumePage0_t buffer = NULL;
+ int i;
+ RaidPhysDiskPage0_t phys_disk;
+ struct mptsas_device_info *sas_info, *next;
+
+ memset(&cfg, 0 , sizeof(CONFIGPARMS));
+ memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+ hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
+ /* assumption that all volumes on channel = 0 */
+ cfg.pageAddr = starget->id;
+ cfg.cfghdr.hdr = &hdr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.timeout = 10;
+
+ if (mpt_config(ioc, &cfg) != 0)
+ goto out;
+
+ if (!hdr.PageLength)
+ goto out;
+
+ buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+ &dma_handle);
+
+ if (!buffer)
+ goto out;
+
+ cfg.physAddr = dma_handle;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ if (mpt_config(ioc, &cfg) != 0)
+ goto out;
+
+ if (!buffer->NumPhysDisks)
+ goto out;
+
+ /*
+ * Adding entry for hidden components
+ */
+ for (i = 0; i < buffer->NumPhysDisks; i++) {
+
+ if (mpt_raid_phys_disk_pg0(ioc,
+ buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
+ continue;
+
+ mptsas_add_device_component_by_fw(ioc, phys_disk.PhysDiskBus,
+ phys_disk.PhysDiskID);
+
+ mutex_lock(&ioc->sas_device_info_mutex);
+ list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+ list) {
+ if (!sas_info->is_logical_volume &&
+ (sas_info->fw.channel == phys_disk.PhysDiskBus &&
+ sas_info->fw.id == phys_disk.PhysDiskID)) {
+ sas_info->is_hidden_raid_component = 1;
+ sas_info->volume_id = starget->id;
+ }
+ }
+ mutex_unlock(&ioc->sas_device_info_mutex);
+
+ }
+
+ /*
+ * Delete all matching devices out of the list
+ */
+ mutex_lock(&ioc->sas_device_info_mutex);
+ list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+ list) {
+ if (sas_info->is_logical_volume && sas_info->fw.id ==
+ starget->id) {
+ list_del(&sas_info->list);
+ kfree(sas_info);
+ }
+ }
+
+ sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
+ if (sas_info) {
+ sas_info->fw.id = starget->id;
+ sas_info->os.id = starget->id;
+ sas_info->os.channel = starget->channel;
+ sas_info->is_logical_volume = 1;
+ INIT_LIST_HEAD(&sas_info->list);
+ list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
+ }
+ mutex_unlock(&ioc->sas_device_info_mutex);
+
+ out:
+ if (buffer)
+ pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+ dma_handle);
+}
+
+/**
+ * mptsas_add_device_component_starget -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @starget:
+ *
+ **/
+static void
+mptsas_add_device_component_starget(MPT_ADAPTER *ioc,
+ struct scsi_target *starget)
+{
+ VirtTarget *vtarget;
+ struct sas_rphy *rphy;
+ struct mptsas_phyinfo *phy_info = NULL;
+ struct mptsas_enclosure enclosure_info;
+
+ rphy = dev_to_rphy(starget->dev.parent);
+ vtarget = starget->hostdata;
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (!phy_info)
+ return;
+
+ memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
+ mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
+ (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
+ MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
+ phy_info->attached.handle_enclosure);
+
+ mptsas_add_device_component(ioc, phy_info->attached.channel,
+ phy_info->attached.id, phy_info->attached.sas_address,
+ phy_info->attached.device_info,
+ phy_info->attached.slot, enclosure_info.enclosure_logical_id);
+}
+
+/**
+ * mptsas_del_device_component_by_os - Once a device has been removed, we mark the entry in the list as being cached
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @channel: os mapped id's
+ * @id:
+ *
+ **/
+static void
+mptsas_del_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+ struct mptsas_device_info *sas_info, *next;
+
+ /*
+ * Set is_cached flag
+ */
+ list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+ list) {
+ if (sas_info->os.channel == channel && sas_info->os.id == id)
+ sas_info->is_cached = 1;
+ }
+}
+
+/**
+ * mptsas_del_device_components - Cleaning the list
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+static void
+mptsas_del_device_components(MPT_ADAPTER *ioc)
+{
+ struct mptsas_device_info *sas_info, *next;
+
+ mutex_lock(&ioc->sas_device_info_mutex);
+ list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+ list) {
+ list_del(&sas_info->list);
+ kfree(sas_info);
+ }
+ mutex_unlock(&ioc->sas_device_info_mutex);
+}
+
/*
* mptsas_setup_wide_ports
@@ -434,8 +870,8 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
* Forming a port
*/
if (!port_details) {
- port_details = kzalloc(sizeof(*port_details),
- GFP_KERNEL);
+ port_details = kzalloc(sizeof(struct
+ mptsas_portinfo_details), GFP_KERNEL);
if (!port_details)
goto out;
port_details->num_phys = 1;
@@ -523,15 +959,62 @@ mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id)
VirtTarget *vtarget = NULL;
shost_for_each_device(sdev, ioc->sh) {
- if ((vdevice = sdev->hostdata) == NULL)
+ vdevice = sdev->hostdata;
+ if ((vdevice == NULL) ||
+ (vdevice->vtarget == NULL))
+ continue;
+ if ((vdevice->vtarget->tflags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT ||
+ vdevice->vtarget->raidVolume))
continue;
if (vdevice->vtarget->id == id &&
- vdevice->vtarget->channel == channel)
+ vdevice->vtarget->channel == channel)
vtarget = vdevice->vtarget;
}
return vtarget;
}
+static void
+mptsas_queue_device_delete(MPT_ADAPTER *ioc,
+ MpiEventDataSasDeviceStatusChange_t *sas_event_data)
+{
+ struct fw_event_work *fw_event;
+ int sz;
+
+ sz = offsetof(struct fw_event_work, event_data) +
+ sizeof(MpiEventDataSasDeviceStatusChange_t);
+ fw_event = kzalloc(sz, GFP_ATOMIC);
+ if (!fw_event) {
+ printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
+ ioc->name, __func__, __LINE__);
+ return;
+ }
+ memcpy(fw_event->event_data, sas_event_data,
+ sizeof(MpiEventDataSasDeviceStatusChange_t));
+ fw_event->event = MPI_EVENT_SAS_DEVICE_STATUS_CHANGE;
+ fw_event->ioc = ioc;
+ mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
+}
+
+static void
+mptsas_queue_rescan(MPT_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+ int sz;
+
+ sz = offsetof(struct fw_event_work, event_data);
+ fw_event = kzalloc(sz, GFP_ATOMIC);
+ if (!fw_event) {
+ printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
+ ioc->name, __func__, __LINE__);
+ return;
+ }
+ fw_event->event = -1;
+ fw_event->ioc = ioc;
+ mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
+}
+
+
/**
* mptsas_target_reset
*
@@ -550,13 +1033,21 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
{
MPT_FRAME_HDR *mf;
SCSITaskMgmt_t *pScsiTm;
-
- if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n",
- ioc->name,__func__, __LINE__));
+ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0)
return 0;
+
+
+ mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
+ if (mf == NULL) {
+ dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "%s, no msg frames @%d!!\n", ioc->name,
+ __func__, __LINE__));
+ goto out_fail;
}
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
+ ioc->name, mf));
+
/* Format the Request
*/
pScsiTm = (SCSITaskMgmt_t *) mf;
@@ -569,9 +1060,18 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
- mpt_put_msg_frame_hi_pri(ioc->TaskCtx, ioc, mf);
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt type=%d (sas device delete) fw_channel = %d fw_id = %d)\n",
+ ioc->name, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, channel, id));
+
+ mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
return 1;
+
+ out_fail:
+
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ return 0;
}
/**
@@ -602,11 +1102,12 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
vtarget->deleted = 1; /* block IO */
- target_reset_list = kzalloc(sizeof(*target_reset_list),
+ target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event),
GFP_ATOMIC);
if (!target_reset_list) {
- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
- ioc->name,__func__, __LINE__));
+ dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+ "%s, failed to allocate mem @%d..!!\n",
+ ioc->name, __func__, __LINE__));
return;
}
@@ -614,84 +1115,101 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
sizeof(*sas_event_data));
list_add_tail(&target_reset_list->list, &hd->target_reset_list);
- if (hd->resetPending)
- return;
+ target_reset_list->time_count = jiffies;
if (mptsas_target_reset(ioc, channel, id)) {
target_reset_list->target_reset_issued = 1;
- hd->resetPending = 1;
}
}
/**
- * mptsas_dev_reset_complete
- *
- * Completion for TARGET_RESET after NOT_RESPONDING_EVENT,
- * enable work queue to finish off removing device from upper layers.
- * then send next TARGET_RESET in the queue.
- *
- * @ioc
+ * mptsas_taskmgmt_complete - complete SAS task management function
+ * @ioc: Pointer to MPT_ADAPTER structure
*
+ * Completion for TARGET_RESET after NOT_RESPONDING_EVENT, enable work
+ * queue to finish off removing device from upper layers. then send next
+ * TARGET_RESET in the queue.
**/
-static void
-mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
+static int
+mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
{
MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
struct list_head *head = &hd->target_reset_list;
- struct mptsas_target_reset_event *target_reset_list;
- struct mptsas_hotplug_event *ev;
- EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
u8 id, channel;
- __le64 sas_address;
+ struct mptsas_target_reset_event *target_reset_list;
+ SCSITaskMgmtReply_t *pScsiTmReply;
+
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed: "
+ "(mf = %p, mr = %p)\n", ioc->name, mf, mr));
+
+ pScsiTmReply = (SCSITaskMgmtReply_t *)mr;
+ if (pScsiTmReply) {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "\tTaskMgmt completed: fw_channel = %d, fw_id = %d,\n"
+ "\ttask_type = 0x%02X, iocstatus = 0x%04X "
+ "loginfo = 0x%08X,\n\tresponse_code = 0x%02X, "
+ "term_cmnds = %d\n", ioc->name,
+ pScsiTmReply->Bus, pScsiTmReply->TargetID,
+ pScsiTmReply->TaskType,
+ le16_to_cpu(pScsiTmReply->IOCStatus),
+ le32_to_cpu(pScsiTmReply->IOCLogInfo),
+ pScsiTmReply->ResponseCode,
+ le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+ if (pScsiTmReply->ResponseCode)
+ mptscsih_taskmgmt_response_code(ioc,
+ pScsiTmReply->ResponseCode);
+ }
+
+ if (pScsiTmReply && (pScsiTmReply->TaskType ==
+ MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK || pScsiTmReply->TaskType ==
+ MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET)) {
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ memcpy(ioc->taskmgmt_cmds.reply, mr,
+ min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->taskmgmt_cmds.done);
+ return 1;
+ }
+ return 0;
+ }
+
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
if (list_empty(head))
- return;
+ return 1;
- target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, list);
+ target_reset_list = list_entry(head->next,
+ struct mptsas_target_reset_event, list);
- sas_event_data = &target_reset_list->sas_event_data;
- id = sas_event_data->TargetID;
- channel = sas_event_data->Bus;
- hd->resetPending = 0;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt: completed (%d seconds)\n",
+ ioc->name, jiffies_to_msecs(jiffies -
+ target_reset_list->time_count)/1000));
+
+ id = pScsiTmReply->TargetID;
+ channel = pScsiTmReply->Bus;
+ target_reset_list->time_count = jiffies;
/*
* retry target reset
*/
if (!target_reset_list->target_reset_issued) {
- if (mptsas_target_reset(ioc, channel, id)) {
+ if (mptsas_target_reset(ioc, channel, id))
target_reset_list->target_reset_issued = 1;
- hd->resetPending = 1;
- }
- return;
+ return 1;
}
/*
* enable work queue to remove device from upper layers
*/
list_del(&target_reset_list->list);
+ if ((mptsas_find_vtarget(ioc, channel, id)) && !ioc->fw_events_off)
+ mptsas_queue_device_delete(ioc,
+ &target_reset_list->sas_event_data);
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev) {
- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
- ioc->name,__func__, __LINE__));
- return;
- }
-
- INIT_WORK(&ev->work, mptsas_hotplug_work);
- ev->ioc = ioc;
- ev->handle = le16_to_cpu(sas_event_data->DevHandle);
- ev->parent_handle =
- le16_to_cpu(sas_event_data->ParentDevHandle);
- ev->channel = channel;
- ev->id =id;
- ev->phy_id = sas_event_data->PhyNum;
- memcpy(&sas_address, &sas_event_data->SASAddress,
- sizeof(__le64));
- ev->sas_address = le64_to_cpu(sas_address);
- ev->device_info = le32_to_cpu(sas_event_data->DeviceInfo);
- ev->event_type = MPTSAS_DEL_DEVICE;
- schedule_work(&ev->work);
- kfree(target_reset_list);
/*
* issue target reset to next device in the queue
@@ -699,34 +1217,19 @@ mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
head = &hd->target_reset_list;
if (list_empty(head))
- return;
+ return 1;
target_reset_list = list_entry(head->next, struct mptsas_target_reset_event,
list);
- sas_event_data = &target_reset_list->sas_event_data;
- id = sas_event_data->TargetID;
- channel = sas_event_data->Bus;
+ id = target_reset_list->sas_event_data.TargetID;
+ channel = target_reset_list->sas_event_data.Bus;
+ target_reset_list->time_count = jiffies;
- if (mptsas_target_reset(ioc, channel, id)) {
+ if (mptsas_target_reset(ioc, channel, id))
target_reset_list->target_reset_issued = 1;
- hd->resetPending = 1;
- }
-}
-/**
- * mptsas_taskmgmt_complete
- *
- * @ioc
- * @mf
- * @mr
- *
- **/
-static int
-mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
-{
- mptsas_dev_reset_complete(ioc);
- return mptscsih_taskmgmt_complete(ioc, mf, mr);
+ return 1;
}
/**
@@ -740,37 +1243,59 @@ static int
mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
{
MPT_SCSI_HOST *hd;
- struct mptsas_target_reset_event *target_reset_list, *n;
int rc;
rc = mptscsih_ioc_reset(ioc, reset_phase);
+ if ((ioc->bus_type != SAS) || (!rc))
+ return rc;
- if (ioc->bus_type != SAS)
- goto out;
-
- if (reset_phase != MPT_IOC_POST_RESET)
- goto out;
-
- if (!ioc->sh || !ioc->sh->hostdata)
- goto out;
hd = shost_priv(ioc->sh);
if (!hd->ioc)
goto out;
- if (list_empty(&hd->target_reset_list))
- goto out;
-
- /* flush the target_reset_list */
- list_for_each_entry_safe(target_reset_list, n,
- &hd->target_reset_list, list) {
- list_del(&target_reset_list->list);
- kfree(target_reset_list);
+ switch (reset_phase) {
+ case MPT_IOC_SETUP_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+ mptsas_fw_event_off(ioc);
+ break;
+ case MPT_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT_IOC_POST_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->sas_mgmt.status |= MPT_MGMT_STATUS_DID_IOCRESET;
+ complete(&ioc->sas_mgmt.done);
+ }
+ mptsas_cleanup_fw_event_q(ioc);
+ mptsas_queue_rescan(ioc);
+ mptsas_fw_event_on(ioc);
+ break;
+ default:
+ break;
}
out:
return rc;
}
+
+/**
+ * enum device_state -
+ * @DEVICE_RETRY: need to retry the TUR
+ * @DEVICE_ERROR: TUR return error, don't add device
+ * @DEVICE_READY: device can be added
+ *
+ */
+enum device_state{
+ DEVICE_RETRY,
+ DEVICE_ERROR,
+ DEVICE_READY,
+};
+
static int
mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
u32 form, u32 form_specific)
@@ -836,15 +1361,308 @@ mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
return error;
}
+/**
+ * mptsas_add_end_device - report a new end device to sas transport layer
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @phy_info: decribes attached device
+ *
+ * return (0) success (1) failure
+ *
+ **/
+static int
+mptsas_add_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
+{
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct sas_identify identify;
+ char *ds = NULL;
+ u8 fw_id;
+
+ if (!phy_info) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: exit at line=%d\n", ioc->name,
+ __func__, __LINE__));
+ return 1;
+ }
+
+ fw_id = phy_info->attached.id;
+
+ if (mptsas_get_rphy(phy_info)) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return 2;
+ }
+
+ port = mptsas_get_port(phy_info);
+ if (!port) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return 3;
+ }
+
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_SSP_TARGET)
+ ds = "ssp";
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_STP_TARGET)
+ ds = "stp";
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "sata";
+
+ printk(MYIOC_s_INFO_FMT "attaching %s device: fw_channel %d, fw_id %d,"
+ " phy %d, sas_addr 0x%llx\n", ioc->name, ds,
+ phy_info->attached.channel, phy_info->attached.id,
+ phy_info->attached.phy_id, (unsigned long long)
+ phy_info->attached.sas_address);
+
+ mptsas_parse_device_info(&identify, &phy_info->attached);
+ rphy = sas_end_device_alloc(port);
+ if (!rphy) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return 5; /* non-fatal: an rphy can be added later */
+ }
+
+ rphy->identify = identify;
+ if (sas_rphy_add(rphy)) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ sas_rphy_free(rphy);
+ return 6;
+ }
+ mptsas_set_rphy(ioc, phy_info, rphy);
+ return 0;
+}
+
+/**
+ * mptsas_del_end_device - report a deleted end device to sas transport layer
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @phy_info: decribes attached device
+ *
+ **/
+static void
+mptsas_del_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
+{
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct mptsas_portinfo *port_info;
+ struct mptsas_phyinfo *phy_info_parent;
+ int i;
+ char *ds = NULL;
+ u8 fw_id;
+ u64 sas_address;
+
+ if (!phy_info)
+ return;
+
+ fw_id = phy_info->attached.id;
+ sas_address = phy_info->attached.sas_address;
+
+ if (!phy_info->port_details) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return;
+ }
+ rphy = mptsas_get_rphy(phy_info);
+ if (!rphy) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return;
+ }
+
+ if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_INITIATOR
+ || phy_info->attached.device_info
+ & MPI_SAS_DEVICE_INFO_SMP_INITIATOR
+ || phy_info->attached.device_info
+ & MPI_SAS_DEVICE_INFO_STP_INITIATOR)
+ ds = "initiator";
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_SSP_TARGET)
+ ds = "ssp";
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_STP_TARGET)
+ ds = "stp";
+ if (phy_info->attached.device_info &
+ MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "sata";
+
+ dev_printk(KERN_DEBUG, &rphy->dev, MYIOC_s_FMT
+ "removing %s device: fw_channel %d, fw_id %d, phy %d,"
+ "sas_addr 0x%llx\n", ioc->name, ds, phy_info->attached.channel,
+ phy_info->attached.id, phy_info->attached.phy_id,
+ (unsigned long long) sas_address);
+
+ port = mptsas_get_port(phy_info);
+ if (!port) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, fw_id, __LINE__));
+ return;
+ }
+ port_info = phy_info->portinfo;
+ phy_info_parent = port_info->phy_info;
+ for (i = 0; i < port_info->num_phys; i++, phy_info_parent++) {
+ if (!phy_info_parent->phy)
+ continue;
+ if (phy_info_parent->attached.sas_address !=
+ sas_address)
+ continue;
+ dev_printk(KERN_DEBUG, &phy_info_parent->phy->dev,
+ MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n",
+ ioc->name, phy_info_parent->phy_id,
+ phy_info_parent->phy);
+ sas_port_delete_phy(port, phy_info_parent->phy);
+ }
+
+ dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
+ "delete port %d, sas_addr (0x%llx)\n", ioc->name,
+ port->port_identifier, (unsigned long long)sas_address);
+ sas_port_delete(port);
+ mptsas_set_port(ioc, phy_info, NULL);
+ mptsas_port_delete(ioc, phy_info->port_details);
+}
+
+struct mptsas_phyinfo *
+mptsas_refreshing_device_handles(MPT_ADAPTER *ioc,
+ struct mptsas_devinfo *sas_device)
+{
+ struct mptsas_phyinfo *phy_info;
+ struct mptsas_portinfo *port_info;
+ int i;
+
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ sas_device->sas_address);
+ if (!phy_info)
+ goto out;
+ port_info = phy_info->portinfo;
+ if (!port_info)
+ goto out;
+ mutex_lock(&ioc->sas_topology_mutex);
+ for (i = 0; i < port_info->num_phys; i++) {
+ if (port_info->phy_info[i].attached.sas_address !=
+ sas_device->sas_address)
+ continue;
+ port_info->phy_info[i].attached.channel = sas_device->channel;
+ port_info->phy_info[i].attached.id = sas_device->id;
+ port_info->phy_info[i].attached.sas_address =
+ sas_device->sas_address;
+ port_info->phy_info[i].attached.handle = sas_device->handle;
+ port_info->phy_info[i].attached.handle_parent =
+ sas_device->handle_parent;
+ port_info->phy_info[i].attached.handle_enclosure =
+ sas_device->handle_enclosure;
+ }
+ mutex_unlock(&ioc->sas_topology_mutex);
+ out:
+ return phy_info;
+}
+
+/**
+ * mptsas_firmware_event_work - work thread for processing fw events
+ * @work: work queue payload containing info describing the event
+ * Context: user
+ *
+ */
+static void
+mptsas_firmware_event_work(struct work_struct *work)
+{
+ struct fw_event_work *fw_event =
+ container_of(work, struct fw_event_work, work.work);
+ MPT_ADAPTER *ioc = fw_event->ioc;
+
+ /* special rescan topology handling */
+ if (fw_event->event == -1) {
+ if (ioc->in_rescan) {
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: rescan ignored as it is in progress\n",
+ ioc->name, __func__));
+ return;
+ }
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: rescan after "
+ "reset\n", ioc->name, __func__));
+ ioc->in_rescan = 1;
+ mptsas_not_responding_devices(ioc);
+ mptsas_scan_sas_topology(ioc);
+ ioc->in_rescan = 0;
+ mptsas_free_fw_event(ioc, fw_event);
+ return;
+ }
+
+ /* events handling turned off during host reset */
+ if (ioc->fw_events_off) {
+ mptsas_free_fw_event(ioc, fw_event);
+ return;
+ }
+
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: fw_event=(0x%p), "
+ "event = (0x%02x)\n", ioc->name, __func__, fw_event,
+ (fw_event->event & 0xFF)));
+
+ switch (fw_event->event) {
+ case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ mptsas_send_sas_event(fw_event);
+ break;
+ case MPI_EVENT_INTEGRATED_RAID:
+ mptsas_send_raid_event(fw_event);
+ break;
+ case MPI_EVENT_IR2:
+ mptsas_send_ir2_event(fw_event);
+ break;
+ case MPI_EVENT_PERSISTENT_TABLE_FULL:
+ mptbase_sas_persist_operation(ioc,
+ MPI_SAS_OP_CLEAR_NOT_PRESENT);
+ mptsas_free_fw_event(ioc, fw_event);
+ break;
+ case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
+ mptsas_broadcast_primative_work(fw_event);
+ break;
+ case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+ mptsas_send_expander_event(fw_event);
+ break;
+ case MPI_EVENT_SAS_PHY_LINK_STATUS:
+ mptsas_send_link_status_event(fw_event);
+ break;
+ case MPI_EVENT_QUEUE_FULL:
+ mptsas_handle_queue_full_event(fw_event);
+ break;
+ }
+}
+
+
+
static int
mptsas_slave_configure(struct scsi_device *sdev)
{
+ struct Scsi_Host *host = sdev->host;
+ MPT_SCSI_HOST *hd = shost_priv(host);
+ MPT_ADAPTER *ioc = hd->ioc;
+ VirtDevice *vdevice = sdev->hostdata;
- if (sdev->channel == MPTSAS_RAID_CHANNEL)
+ if (vdevice->vtarget->deleted) {
+ sdev_printk(KERN_INFO, sdev, "clearing deleted flag\n");
+ vdevice->vtarget->deleted = 0;
+ }
+
+ /*
+ * RAID volumes placed beyond the last expected port.
+ * Ignore sending sas mode pages in that case..
+ */
+ if (sdev->channel == MPTSAS_RAID_CHANNEL) {
+ mptsas_add_device_component_starget_ir(ioc, scsi_target(sdev));
goto out;
+ }
sas_read_port_mode_page(sdev);
+ mptsas_add_device_component_starget(ioc, scsi_target(sdev));
+
out:
return mptscsih_slave_configure(sdev);
}
@@ -875,9 +1693,18 @@ mptsas_target_alloc(struct scsi_target *starget)
* RAID volumes placed beyond the last expected port.
*/
if (starget->channel == MPTSAS_RAID_CHANNEL) {
- for (i=0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
- if (id == ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID)
- channel = ioc->raid_data.pIocPg2->RaidVolume[i].VolumeBus;
+ if (!ioc->raid_data.pIocPg2) {
+ kfree(vtarget);
+ return -ENXIO;
+ }
+ for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+ if (id == ioc->raid_data.pIocPg2->
+ RaidVolume[i].VolumeID) {
+ channel = ioc->raid_data.pIocPg2->
+ RaidVolume[i].VolumeBus;
+ }
+ }
+ vtarget->raidVolume = 1;
goto out;
}
@@ -926,11 +1753,18 @@ mptsas_target_destroy(struct scsi_target *starget)
struct sas_rphy *rphy;
struct mptsas_portinfo *p;
int i;
- MPT_ADAPTER *ioc = hd->ioc;
+ MPT_ADAPTER *ioc = hd->ioc;
+ VirtTarget *vtarget;
if (!starget->hostdata)
return;
+ vtarget = starget->hostdata;
+
+ mptsas_del_device_component_by_os(ioc, starget->channel,
+ starget->id);
+
+
if (starget->channel == MPTSAS_RAID_CHANNEL)
goto out;
@@ -940,12 +1774,21 @@ mptsas_target_destroy(struct scsi_target *starget)
if (p->phy_info[i].attached.sas_address !=
rphy->identify.sas_address)
continue;
+
+ starget_printk(KERN_INFO, starget, MYIOC_s_FMT
+ "delete device: fw_channel %d, fw_id %d, phy %d, "
+ "sas_addr 0x%llx\n", ioc->name,
+ p->phy_info[i].attached.channel,
+ p->phy_info[i].attached.id,
+ p->phy_info[i].attached.phy_id, (unsigned long long)
+ p->phy_info[i].attached.sas_address);
+
mptsas_set_starget(&p->phy_info[i], NULL);
- goto out;
}
}
out:
+ vtarget->starget = NULL;
kfree(starget->hostdata);
starget->hostdata = NULL;
}
@@ -1008,6 +1851,8 @@ mptsas_slave_alloc(struct scsi_device *sdev)
static int
mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
{
+ MPT_SCSI_HOST *hd;
+ MPT_ADAPTER *ioc;
VirtDevice *vdevice = SCpnt->device->hostdata;
if (!vdevice || !vdevice->vtarget || vdevice->vtarget->deleted) {
@@ -1016,6 +1861,12 @@ mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
return 0;
}
+ hd = shost_priv(SCpnt->device->host);
+ ioc = hd->ioc;
+
+ if (ioc->sas_discovery_quiesce_io)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
// scsi_print_command(SCpnt);
return mptscsih_qcmd(SCpnt,done);
@@ -1114,14 +1965,19 @@ static int mptsas_get_linkerrors(struct sas_phy *phy)
static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
MPT_FRAME_HDR *reply)
{
- ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_COMMAND_GOOD;
+ ioc->sas_mgmt.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
if (reply != NULL) {
- ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_RF_VALID;
+ ioc->sas_mgmt.status |= MPT_MGMT_STATUS_RF_VALID;
memcpy(ioc->sas_mgmt.reply, reply,
min(ioc->reply_sz, 4 * reply->u.reply.MsgLength));
}
- complete(&ioc->sas_mgmt.done);
- return 1;
+
+ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->sas_mgmt.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->sas_mgmt.done);
+ return 1;
+ }
+ return 0;
}
static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
@@ -1160,6 +2016,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET;
req->PhyNum = phy->identify.phy_identifier;
+ INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done,
@@ -1174,7 +2031,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
/* a reply frame is expected */
if ((ioc->sas_mgmt.status &
- MPT_IOCTL_STATUS_RF_VALID) == 0) {
+ MPT_MGMT_STATUS_RF_VALID) == 0) {
error = -ENXIO;
goto out_unlock;
}
@@ -1191,6 +2048,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
error = 0;
out_unlock:
+ CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
mutex_unlock(&ioc->sas_mgmt.mutex);
out:
return error;
@@ -1304,7 +2162,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
struct mptsas_portinfo *port_info;
mutex_lock(&ioc->sas_topology_mutex);
- port_info = mptsas_get_hba_portinfo(ioc);
+ port_info = ioc->hba_port_info;
if (port_info && port_info->phy_info)
sas_address =
port_info->phy_info[0].phy->identify.sas_address;
@@ -1319,26 +2177,32 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
/* request */
flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
- MPI_SGE_FLAGS_DIRECTION |
- mpt_addr_size()) << MPI_SGE_FLAGS_SHIFT;
+ MPI_SGE_FLAGS_DIRECTION)
+ << MPI_SGE_FLAGS_SHIFT;
flagsLength |= (blk_rq_bytes(req) - 4);
dma_addr_out = pci_map_single(ioc->pcidev, bio_data(req->bio),
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_out)
goto put_mf;
- mpt_add_sge(psge, flagsLength, dma_addr_out);
- psge += (sizeof(u32) + sizeof(dma_addr_t));
+ ioc->add_sge(psge, flagsLength, dma_addr_out);
+ psge += ioc->SGE_size;
/* response */
- flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
+ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI_SGE_FLAGS_SYSTEM_ADDRESS |
+ MPI_SGE_FLAGS_IOC_TO_HOST |
+ MPI_SGE_FLAGS_END_OF_BUFFER;
+
+ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
flagsLength |= blk_rq_bytes(rsp) + 4;
dma_addr_in = pci_map_single(ioc->pcidev, bio_data(rsp->bio),
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_in)
goto unmap;
- mpt_add_sge(psge, flagsLength, dma_addr_in);
+ ioc->add_sge(psge, flagsLength, dma_addr_in);
+ INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
@@ -1351,15 +2215,17 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
}
mf = NULL;
- if (ioc->sas_mgmt.status & MPT_IOCTL_STATUS_RF_VALID) {
+ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
SmpPassthroughReply_t *smprep;
smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
memcpy(req->sense, smprep, sizeof(*smprep));
req->sense_len = sizeof(*smprep);
- rsp->resid_len = blk_rq_bytes(rsp) - smprep->ResponseDataLength;
+ req->resid_len = 0;
+ rsp->resid_len -= smprep->ResponseDataLength;
} else {
- printk(MYIOC_s_ERR_FMT "%s: smp passthru reply failed to be returned\n",
+ printk(MYIOC_s_ERR_FMT
+ "%s: smp passthru reply failed to be returned\n",
ioc->name, __func__);
ret = -ENXIO;
}
@@ -1374,6 +2240,7 @@ put_mf:
if (mf)
mpt_free_msg_frame(ioc, mf);
out_unlock:
+ CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
mutex_unlock(&ioc->sas_mgmt.mutex);
out:
return ret;
@@ -1437,7 +2304,7 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
port_info->num_phys = buffer->NumPhys;
port_info->phy_info = kcalloc(port_info->num_phys,
- sizeof(*port_info->phy_info),GFP_KERNEL);
+ sizeof(struct mptsas_phyinfo), GFP_KERNEL);
if (!port_info->phy_info) {
error = -ENOMEM;
goto out_free_consistent;
@@ -1599,10 +2466,6 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
__le64 sas_address;
int error=0;
- if (ioc->sas_discovery_runtime &&
- mptsas_is_end_device(device_info))
- goto out;
-
hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION;
hdr.ExtPageLength = 0;
hdr.PageNumber = 0;
@@ -1643,6 +2506,7 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
mptsas_print_device_pg0(ioc, buffer);
+ memset(device_info, 0, sizeof(struct mptsas_devinfo));
device_info->handle = le16_to_cpu(buffer->DevHandle);
device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle);
device_info->handle_enclosure =
@@ -1674,7 +2538,9 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
SasExpanderPage0_t *buffer;
dma_addr_t dma_handle;
int i, error;
+ __le64 sas_address;
+ memset(port_info, 0, sizeof(struct mptsas_portinfo));
hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
hdr.ExtPageLength = 0;
hdr.PageNumber = 0;
@@ -1720,18 +2586,23 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
}
/* save config data */
- port_info->num_phys = buffer->NumPhys;
+ port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1;
port_info->phy_info = kcalloc(port_info->num_phys,
- sizeof(*port_info->phy_info),GFP_KERNEL);
+ sizeof(struct mptsas_phyinfo), GFP_KERNEL);
if (!port_info->phy_info) {
error = -ENOMEM;
goto out_free_consistent;
}
+ memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
for (i = 0; i < port_info->num_phys; i++) {
port_info->phy_info[i].portinfo = port_info;
port_info->phy_info[i].handle =
le16_to_cpu(buffer->DevHandle);
+ port_info->phy_info[i].identify.sas_address =
+ le64_to_cpu(sas_address);
+ port_info->phy_info[i].identify.handle_parent =
+ le16_to_cpu(buffer->ParentDevHandle);
}
out_free_consistent:
@@ -1751,11 +2622,7 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
dma_addr_t dma_handle;
int error=0;
- if (ioc->sas_discovery_runtime &&
- mptsas_is_end_device(&phy_info->attached))
- goto out;
-
- hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
+ hdr.PageVersion = MPI_SASEXPANDER1_PAGEVERSION;
hdr.ExtPageLength = 0;
hdr.PageNumber = 1;
hdr.Reserved1 = 0;
@@ -1790,6 +2657,12 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
error = mpt_config(ioc, &cfg);
+
+ if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
+ error = -ENODEV;
+ goto out;
+ }
+
if (error)
goto out_free_consistent;
@@ -2009,16 +2882,21 @@ static int mptsas_probe_one_phy(struct device *dev,
goto out;
}
mptsas_set_port(ioc, phy_info, port);
- dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "sas_port_alloc: port=%p dev=%p port_id=%d\n",
- ioc->name, port, dev, port->port_identifier));
+ devtprintk(ioc, dev_printk(KERN_DEBUG, &port->dev,
+ MYIOC_s_FMT "add port %d, sas_addr (0x%llx)\n",
+ ioc->name, port->port_identifier,
+ (unsigned long long)phy_info->
+ attached.sas_address));
}
- dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sas_port_add_phy: phy_id=%d\n",
- ioc->name, phy_info->phy_id));
+ dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "sas_port_add_phy: phy_id=%d\n",
+ ioc->name, phy_info->phy_id));
sas_port_add_phy(port, phy_info->phy);
phy_info->sas_port_add_phy = 0;
+ devtprintk(ioc, dev_printk(KERN_DEBUG, &phy_info->phy->dev,
+ MYIOC_s_FMT "add phy %d, phy-obj (0x%p)\n", ioc->name,
+ phy_info->phy_id, phy_info->phy));
}
-
if (!mptsas_get_rphy(phy_info) && port && !port->rphy) {
struct sas_rphy *rphy;
@@ -2031,18 +2909,17 @@ static int mptsas_probe_one_phy(struct device *dev,
* the adding/removing of devices that occur
* after start of day.
*/
- if (ioc->sas_discovery_runtime &&
- mptsas_is_end_device(&phy_info->attached))
- goto out;
+ if (mptsas_is_end_device(&phy_info->attached) &&
+ phy_info->attached.handle_parent) {
+ goto out;
+ }
mptsas_parse_device_info(&identify, &phy_info->attached);
if (scsi_is_host_device(parent)) {
struct mptsas_portinfo *port_info;
int i;
- mutex_lock(&ioc->sas_topology_mutex);
- port_info = mptsas_get_hba_portinfo(ioc);
- mutex_unlock(&ioc->sas_topology_mutex);
+ port_info = ioc->hba_port_info;
for (i = 0; i < port_info->num_phys; i++)
if (port_info->phy_info[i].identify.sas_address ==
@@ -2101,7 +2978,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
struct mptsas_portinfo *port_info, *hba;
int error = -ENOMEM, i;
- hba = kzalloc(sizeof(*port_info), GFP_KERNEL);
+ hba = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
if (! hba)
goto out;
@@ -2111,9 +2988,10 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
mptsas_sas_io_unit_pg1(ioc);
mutex_lock(&ioc->sas_topology_mutex);
- port_info = mptsas_get_hba_portinfo(ioc);
+ port_info = ioc->hba_port_info;
if (!port_info) {
- port_info = hba;
+ ioc->hba_port_info = port_info = hba;
+ ioc->hba_port_num_phy = port_info->num_phys;
list_add_tail(&port_info->list, &ioc->sas_topology);
} else {
for (i = 0; i < hba->num_phys; i++) {
@@ -2129,15 +3007,22 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
hba = NULL;
}
mutex_unlock(&ioc->sas_topology_mutex);
+#if defined(CPQ_CIM)
+ ioc->num_ports = port_info->num_phys;
+#endif
for (i = 0; i < port_info->num_phys; i++) {
mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
(MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
MPI_SAS_PHY_PGAD_FORM_SHIFT), i);
-
+ port_info->phy_info[i].identify.handle =
+ port_info->phy_info[i].handle;
mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify,
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
- port_info->phy_info[i].handle);
+ port_info->phy_info[i].identify.handle);
+ if (!ioc->hba_port_sas_addr)
+ ioc->hba_port_sas_addr =
+ port_info->phy_info[i].identify.sas_address;
port_info->phy_info[i].identify.phy_id =
port_info->phy_info[i].phy_id = i;
if (port_info->phy_info[i].attached.handle)
@@ -2162,248 +3047,721 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
return error;
}
-static int
-mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle)
+static void
+mptsas_expander_refresh(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
{
- struct mptsas_portinfo *port_info, *p, *ex;
- struct device *parent;
- struct sas_rphy *rphy;
- int error = -ENOMEM, i, j;
-
- ex = kzalloc(sizeof(*port_info), GFP_KERNEL);
- if (!ex)
- goto out;
-
- error = mptsas_sas_expander_pg0(ioc, ex,
- (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
- MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle);
- if (error)
- goto out_free_port_info;
-
- *handle = ex->phy_info[0].handle;
-
- mutex_lock(&ioc->sas_topology_mutex);
- port_info = mptsas_find_portinfo_by_handle(ioc, *handle);
- if (!port_info) {
- port_info = ex;
- list_add_tail(&port_info->list, &ioc->sas_topology);
- } else {
- for (i = 0; i < ex->num_phys; i++) {
- port_info->phy_info[i].handle =
- ex->phy_info[i].handle;
- port_info->phy_info[i].port_id =
- ex->phy_info[i].port_id;
- }
- kfree(ex->phy_info);
- kfree(ex);
- ex = NULL;
- }
- mutex_unlock(&ioc->sas_topology_mutex);
-
+ struct mptsas_portinfo *parent;
+ struct device *parent_dev;
+ struct sas_rphy *rphy;
+ int i;
+ u64 sas_address; /* expander sas address */
+ u32 handle;
+
+ handle = port_info->phy_info[0].handle;
+ sas_address = port_info->phy_info[0].identify.sas_address;
for (i = 0; i < port_info->num_phys; i++) {
mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i],
- (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
- MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + *handle);
-
- if (port_info->phy_info[i].identify.handle) {
- mptsas_sas_device_pg0(ioc,
- &port_info->phy_info[i].identify,
- (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
- MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
- port_info->phy_info[i].identify.handle);
- port_info->phy_info[i].identify.phy_id =
- port_info->phy_info[i].phy_id;
- }
+ (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + handle);
+
+ mptsas_sas_device_pg0(ioc,
+ &port_info->phy_info[i].identify,
+ (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ port_info->phy_info[i].identify.handle);
+ port_info->phy_info[i].identify.phy_id =
+ port_info->phy_info[i].phy_id;
if (port_info->phy_info[i].attached.handle) {
mptsas_sas_device_pg0(ioc,
- &port_info->phy_info[i].attached,
- (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
- MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
- port_info->phy_info[i].attached.handle);
+ &port_info->phy_info[i].attached,
+ (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ port_info->phy_info[i].attached.handle);
port_info->phy_info[i].attached.phy_id =
port_info->phy_info[i].phy_id;
}
}
- parent = &ioc->sh->shost_gendev;
- for (i = 0; i < port_info->num_phys; i++) {
- mutex_lock(&ioc->sas_topology_mutex);
- list_for_each_entry(p, &ioc->sas_topology, list) {
- for (j = 0; j < p->num_phys; j++) {
- if (port_info->phy_info[i].identify.handle !=
- p->phy_info[j].attached.handle)
- continue;
- rphy = mptsas_get_rphy(&p->phy_info[j]);
- parent = &rphy->dev;
- }
- }
+ mutex_lock(&ioc->sas_topology_mutex);
+ parent = mptsas_find_portinfo_by_handle(ioc,
+ port_info->phy_info[0].identify.handle_parent);
+ if (!parent) {
mutex_unlock(&ioc->sas_topology_mutex);
+ return;
}
+ for (i = 0, parent_dev = NULL; i < parent->num_phys && !parent_dev;
+ i++) {
+ if (parent->phy_info[i].attached.sas_address == sas_address) {
+ rphy = mptsas_get_rphy(&parent->phy_info[i]);
+ parent_dev = &rphy->dev;
+ }
+ }
+ mutex_unlock(&ioc->sas_topology_mutex);
mptsas_setup_wide_ports(ioc, port_info);
-
for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
- mptsas_probe_one_phy(parent, &port_info->phy_info[i],
+ mptsas_probe_one_phy(parent_dev, &port_info->phy_info[i],
ioc->sas_index, 0);
+}
- return 0;
+static void
+mptsas_expander_event_add(MPT_ADAPTER *ioc,
+ MpiEventDataSasExpanderStatusChange_t *expander_data)
+{
+ struct mptsas_portinfo *port_info;
+ int i;
+ __le64 sas_address;
- out_free_port_info:
- if (ex) {
- kfree(ex->phy_info);
- kfree(ex);
+ port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
+ if (!port_info)
+ BUG();
+ port_info->num_phys = (expander_data->NumPhys) ?
+ expander_data->NumPhys : 1;
+ port_info->phy_info = kcalloc(port_info->num_phys,
+ sizeof(struct mptsas_phyinfo), GFP_KERNEL);
+ if (!port_info->phy_info)
+ BUG();
+ memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
+ for (i = 0; i < port_info->num_phys; i++) {
+ port_info->phy_info[i].portinfo = port_info;
+ port_info->phy_info[i].handle =
+ le16_to_cpu(expander_data->DevHandle);
+ port_info->phy_info[i].identify.sas_address =
+ le64_to_cpu(sas_address);
+ port_info->phy_info[i].identify.handle_parent =
+ le16_to_cpu(expander_data->ParentDevHandle);
+ }
+
+ mutex_lock(&ioc->sas_topology_mutex);
+ list_add_tail(&port_info->list, &ioc->sas_topology);
+ mutex_unlock(&ioc->sas_topology_mutex);
+
+ printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+ "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+ (unsigned long long)sas_address);
+
+ mptsas_expander_refresh(ioc, port_info);
+}
+
+/**
+ * mptsas_delete_expander_siblings - remove siblings attached to expander
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @parent: the parent port_info object
+ * @expander: the expander port_info object
+ **/
+static void
+mptsas_delete_expander_siblings(MPT_ADAPTER *ioc, struct mptsas_portinfo
+ *parent, struct mptsas_portinfo *expander)
+{
+ struct mptsas_phyinfo *phy_info;
+ struct mptsas_portinfo *port_info;
+ struct sas_rphy *rphy;
+ int i;
+
+ phy_info = expander->phy_info;
+ for (i = 0; i < expander->num_phys; i++, phy_info++) {
+ rphy = mptsas_get_rphy(phy_info);
+ if (!rphy)
+ continue;
+ if (rphy->identify.device_type == SAS_END_DEVICE)
+ mptsas_del_end_device(ioc, phy_info);
+ }
+
+ phy_info = expander->phy_info;
+ for (i = 0; i < expander->num_phys; i++, phy_info++) {
+ rphy = mptsas_get_rphy(phy_info);
+ if (!rphy)
+ continue;
+ if (rphy->identify.device_type ==
+ MPI_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+ rphy->identify.device_type ==
+ MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
+ port_info = mptsas_find_portinfo_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (!port_info)
+ continue;
+ if (port_info == parent) /* backlink rphy */
+ continue;
+ /*
+ Delete this expander even if the expdevpage is exists
+ because the parent expander is already deleted
+ */
+ mptsas_expander_delete(ioc, port_info, 1);
+ }
+ }
+}
+
+
+/**
+ * mptsas_expander_delete - remove this expander
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @port_info: expander port_info struct
+ * @force: Flag to forcefully delete the expander
+ *
+ **/
+
+static void mptsas_expander_delete(MPT_ADAPTER *ioc,
+ struct mptsas_portinfo *port_info, u8 force)
+{
+
+ struct mptsas_portinfo *parent;
+ int i;
+ u64 expander_sas_address;
+ struct mptsas_phyinfo *phy_info;
+ struct mptsas_portinfo buffer;
+ struct mptsas_portinfo_details *port_details;
+ struct sas_port *port;
+
+ if (!port_info)
+ return;
+
+ /* see if expander is still there before deleting */
+ mptsas_sas_expander_pg0(ioc, &buffer,
+ (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
+ port_info->phy_info[0].identify.handle);
+
+ if (buffer.num_phys) {
+ kfree(buffer.phy_info);
+ if (!force)
+ return;
+ }
+
+
+ /*
+ * Obtain the port_info instance to the parent port
+ */
+ port_details = NULL;
+ expander_sas_address =
+ port_info->phy_info[0].identify.sas_address;
+ parent = mptsas_find_portinfo_by_handle(ioc,
+ port_info->phy_info[0].identify.handle_parent);
+ mptsas_delete_expander_siblings(ioc, parent, port_info);
+ if (!parent)
+ goto out;
+
+ /*
+ * Delete rphys in the parent that point
+ * to this expander.
+ */
+ phy_info = parent->phy_info;
+ port = NULL;
+ for (i = 0; i < parent->num_phys; i++, phy_info++) {
+ if (!phy_info->phy)
+ continue;
+ if (phy_info->attached.sas_address !=
+ expander_sas_address)
+ continue;
+ if (!port) {
+ port = mptsas_get_port(phy_info);
+ port_details = phy_info->port_details;
+ }
+ dev_printk(KERN_DEBUG, &phy_info->phy->dev,
+ MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n", ioc->name,
+ phy_info->phy_id, phy_info->phy);
+ sas_port_delete_phy(port, phy_info->phy);
+ }
+ if (port) {
+ dev_printk(KERN_DEBUG, &port->dev,
+ MYIOC_s_FMT "delete port %d, sas_addr (0x%llx)\n",
+ ioc->name, port->port_identifier,
+ (unsigned long long)expander_sas_address);
+ sas_port_delete(port);
+ mptsas_port_delete(ioc, port_details);
}
out:
- return error;
+
+ printk(MYIOC_s_INFO_FMT "delete expander: num_phys %d, "
+ "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+ (unsigned long long)expander_sas_address);
+
+ /*
+ * free link
+ */
+ list_del(&port_info->list);
+ kfree(port_info->phy_info);
+ kfree(port_info);
}
-/*
- * mptsas_delete_expander_phys
+
+/**
+ * mptsas_send_expander_event - expanders events
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @expander_data: event data
*
*
- * This will traverse topology, and remove expanders
- * that are no longer present
+ * This function handles adding, removing, and refreshing
+ * device handles within the expander objects.
*/
static void
-mptsas_delete_expander_phys(MPT_ADAPTER *ioc)
+mptsas_send_expander_event(struct fw_event_work *fw_event)
{
- struct mptsas_portinfo buffer;
- struct mptsas_portinfo *port_info, *n, *parent;
- struct mptsas_phyinfo *phy_info;
- struct sas_port * port;
+ MPT_ADAPTER *ioc;
+ MpiEventDataSasExpanderStatusChange_t *expander_data;
+ struct mptsas_portinfo *port_info;
+ __le64 sas_address;
int i;
- u64 expander_sas_address;
+ ioc = fw_event->ioc;
+ expander_data = (MpiEventDataSasExpanderStatusChange_t *)
+ fw_event->event_data;
+ memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
+ port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
+
+ if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
+ if (port_info) {
+ for (i = 0; i < port_info->num_phys; i++) {
+ port_info->phy_info[i].portinfo = port_info;
+ port_info->phy_info[i].handle =
+ le16_to_cpu(expander_data->DevHandle);
+ port_info->phy_info[i].identify.sas_address =
+ le64_to_cpu(sas_address);
+ port_info->phy_info[i].identify.handle_parent =
+ le16_to_cpu(expander_data->ParentDevHandle);
+ }
+ mptsas_expander_refresh(ioc, port_info);
+ } else if (!port_info && expander_data->NumPhys)
+ mptsas_expander_event_add(ioc, expander_data);
+ } else if (expander_data->ReasonCode ==
+ MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING)
+ mptsas_expander_delete(ioc, port_info, 0);
+
+ mptsas_free_fw_event(ioc, fw_event);
+}
+
+
+/**
+ * mptsas_expander_add -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @handle:
+ *
+ */
+struct mptsas_portinfo *
+mptsas_expander_add(MPT_ADAPTER *ioc, u16 handle)
+{
+ struct mptsas_portinfo buffer, *port_info;
+ int i;
+
+ if ((mptsas_sas_expander_pg0(ioc, &buffer,
+ (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)))
+ return NULL;
+
+ port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_ATOMIC);
+ if (!port_info) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: exit at line=%d\n", ioc->name,
+ __func__, __LINE__));
+ return NULL;
+ }
+ port_info->num_phys = buffer.num_phys;
+ port_info->phy_info = buffer.phy_info;
+ for (i = 0; i < port_info->num_phys; i++)
+ port_info->phy_info[i].portinfo = port_info;
mutex_lock(&ioc->sas_topology_mutex);
- list_for_each_entry_safe(port_info, n, &ioc->sas_topology, list) {
+ list_add_tail(&port_info->list, &ioc->sas_topology);
+ mutex_unlock(&ioc->sas_topology_mutex);
+ printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+ "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+ (unsigned long long)buffer.phy_info[0].identify.sas_address);
+ mptsas_expander_refresh(ioc, port_info);
+ return port_info;
+}
- if (!(port_info->phy_info[0].identify.device_info &
- MPI_SAS_DEVICE_INFO_SMP_TARGET))
+static void
+mptsas_send_link_status_event(struct fw_event_work *fw_event)
+{
+ MPT_ADAPTER *ioc;
+ MpiEventDataSasPhyLinkStatus_t *link_data;
+ struct mptsas_portinfo *port_info;
+ struct mptsas_phyinfo *phy_info = NULL;
+ __le64 sas_address;
+ u8 phy_num;
+ u8 link_rate;
+
+ ioc = fw_event->ioc;
+ link_data = (MpiEventDataSasPhyLinkStatus_t *)fw_event->event_data;
+
+ memcpy(&sas_address, &link_data->SASAddress, sizeof(__le64));
+ sas_address = le64_to_cpu(sas_address);
+ link_rate = link_data->LinkRates >> 4;
+ phy_num = link_data->PhyNum;
+
+ port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
+ if (port_info) {
+ phy_info = &port_info->phy_info[phy_num];
+ if (phy_info)
+ phy_info->negotiated_link_rate = link_rate;
+ }
+
+ if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
+ link_rate == MPI_SAS_IOUNIT0_RATE_3_0) {
+
+ if (!port_info) {
+ if (ioc->old_sas_discovery_protocal) {
+ port_info = mptsas_expander_add(ioc,
+ le16_to_cpu(link_data->DevHandle));
+ if (port_info)
+ goto out;
+ }
+ goto out;
+ }
+
+ if (port_info == ioc->hba_port_info)
+ mptsas_probe_hba_phys(ioc);
+ else
+ mptsas_expander_refresh(ioc, port_info);
+ } else if (phy_info && phy_info->phy) {
+ if (link_rate == MPI_SAS_IOUNIT0_RATE_PHY_DISABLED)
+ phy_info->phy->negotiated_linkrate =
+ SAS_PHY_DISABLED;
+ else if (link_rate ==
+ MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION)
+ phy_info->phy->negotiated_linkrate =
+ SAS_LINK_RATE_FAILED;
+ else
+ phy_info->phy->negotiated_linkrate =
+ SAS_LINK_RATE_UNKNOWN;
+ }
+ out:
+ mptsas_free_fw_event(ioc, fw_event);
+}
+
+static void
+mptsas_not_responding_devices(MPT_ADAPTER *ioc)
+{
+ struct mptsas_portinfo buffer, *port_info;
+ struct mptsas_device_info *sas_info;
+ struct mptsas_devinfo sas_device;
+ u32 handle;
+ VirtTarget *vtarget = NULL;
+ struct mptsas_phyinfo *phy_info;
+ u8 found_expander;
+ int retval, retry_count;
+ unsigned long flags;
+
+ mpt_findImVolumes(ioc);
+
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: exiting due to a parallel reset \n", ioc->name,
+ __func__));
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+ /* devices, logical volumes */
+ mutex_lock(&ioc->sas_device_info_mutex);
+ redo_device_scan:
+ list_for_each_entry(sas_info, &ioc->sas_device_info_list, list) {
+ if (sas_info->is_cached)
continue;
+ if (!sas_info->is_logical_volume) {
+ sas_device.handle = 0;
+ retry_count = 0;
+retry_page:
+ retval = mptsas_sas_device_pg0(ioc, &sas_device,
+ (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID
+ << MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ (sas_info->fw.channel << 8) +
+ sas_info->fw.id);
+
+ if (sas_device.handle)
+ continue;
+ if (retval == -EBUSY) {
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ dfailprintk(ioc,
+ printk(MYIOC_s_DEBUG_FMT
+ "%s: exiting due to reset\n",
+ ioc->name, __func__));
+ spin_unlock_irqrestore
+ (&ioc->taskmgmt_lock, flags);
+ mutex_unlock(&ioc->
+ sas_device_info_mutex);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock,
+ flags);
+ }
- if (mptsas_sas_expander_pg0(ioc, &buffer,
- (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
- MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
- port_info->phy_info[0].handle)) {
+ if (retval && (retval != -ENODEV)) {
+ if (retry_count < 10) {
+ retry_count++;
+ goto retry_page;
+ } else {
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: Config page retry exceeded retry "
+ "count deleting device 0x%llx\n",
+ ioc->name, __func__,
+ sas_info->sas_address));
+ }
+ }
- /*
- * Obtain the port_info instance to the parent port
- */
- parent = mptsas_find_portinfo_by_handle(ioc,
- port_info->phy_info[0].identify.handle_parent);
+ /* delete device */
+ vtarget = mptsas_find_vtarget(ioc,
+ sas_info->fw.channel, sas_info->fw.id);
- if (!parent)
- goto next_port;
+ if (vtarget)
+ vtarget->deleted = 1;
- expander_sas_address =
- port_info->phy_info[0].identify.sas_address;
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ sas_info->sas_address);
- /*
- * Delete rphys in the parent that point
- * to this expander. The transport layer will
- * cleanup all the children.
- */
- phy_info = parent->phy_info;
- for (i = 0; i < parent->num_phys; i++, phy_info++) {
- port = mptsas_get_port(phy_info);
- if (!port)
- continue;
- if (phy_info->attached.sas_address !=
- expander_sas_address)
- continue;
- dsaswideprintk(ioc,
- dev_printk(KERN_DEBUG, &port->dev,
- MYIOC_s_FMT "delete port (%d)\n", ioc->name,
- port->port_identifier));
- sas_port_delete(port);
- mptsas_port_delete(ioc, phy_info->port_details);
+ if (phy_info) {
+ mptsas_del_end_device(ioc, phy_info);
+ goto redo_device_scan;
}
- next_port:
+ } else
+ mptsas_volume_delete(ioc, sas_info->fw.id);
+ }
+ mutex_lock(&ioc->sas_device_info_mutex);
- phy_info = port_info->phy_info;
- for (i = 0; i < port_info->num_phys; i++, phy_info++)
- mptsas_port_delete(ioc, phy_info->port_details);
+ /* expanders */
+ mutex_lock(&ioc->sas_topology_mutex);
+ redo_expander_scan:
+ list_for_each_entry(port_info, &ioc->sas_topology, list) {
- list_del(&port_info->list);
- kfree(port_info->phy_info);
- kfree(port_info);
+ if (port_info->phy_info &&
+ (!(port_info->phy_info[0].identify.device_info &
+ MPI_SAS_DEVICE_INFO_SMP_TARGET)))
+ continue;
+ found_expander = 0;
+ handle = 0xFFFF;
+ while (!mptsas_sas_expander_pg0(ioc, &buffer,
+ (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle) &&
+ !found_expander) {
+
+ handle = buffer.phy_info[0].handle;
+ if (buffer.phy_info[0].identify.sas_address ==
+ port_info->phy_info[0].identify.sas_address) {
+ found_expander = 1;
+ }
+ kfree(buffer.phy_info);
+ }
+
+ if (!found_expander) {
+ mptsas_expander_delete(ioc, port_info, 0);
+ goto redo_expander_scan;
}
- /*
- * Free this memory allocated from inside
- * mptsas_sas_expander_pg0
- */
- kfree(buffer.phy_info);
}
- mutex_unlock(&ioc->sas_topology_mutex);
+ mutex_lock(&ioc->sas_topology_mutex);
+}
+
+/**
+ * mptsas_probe_expanders - adding expanders
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+static void
+mptsas_probe_expanders(MPT_ADAPTER *ioc)
+{
+ struct mptsas_portinfo buffer, *port_info;
+ u32 handle;
+ int i;
+
+ handle = 0xFFFF;
+ while (!mptsas_sas_expander_pg0(ioc, &buffer,
+ (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)) {
+
+ handle = buffer.phy_info[0].handle;
+ port_info = mptsas_find_portinfo_by_sas_address(ioc,
+ buffer.phy_info[0].identify.sas_address);
+
+ if (port_info) {
+ /* refreshing handles */
+ for (i = 0; i < buffer.num_phys; i++) {
+ port_info->phy_info[i].handle = handle;
+ port_info->phy_info[i].identify.handle_parent =
+ buffer.phy_info[0].identify.handle_parent;
+ }
+ mptsas_expander_refresh(ioc, port_info);
+ kfree(buffer.phy_info);
+ continue;
+ }
+
+ port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
+ if (!port_info) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: exit at line=%d\n", ioc->name,
+ __func__, __LINE__));
+ return;
+ }
+ port_info->num_phys = buffer.num_phys;
+ port_info->phy_info = buffer.phy_info;
+ for (i = 0; i < port_info->num_phys; i++)
+ port_info->phy_info[i].portinfo = port_info;
+ mutex_lock(&ioc->sas_topology_mutex);
+ list_add_tail(&port_info->list, &ioc->sas_topology);
+ mutex_unlock(&ioc->sas_topology_mutex);
+ printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+ "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+ (unsigned long long)buffer.phy_info[0].identify.sas_address);
+ mptsas_expander_refresh(ioc, port_info);
+ }
}
-/*
- * Start of day discovery
- */
+static void
+mptsas_probe_devices(MPT_ADAPTER *ioc)
+{
+ u16 handle;
+ struct mptsas_devinfo sas_device;
+ struct mptsas_phyinfo *phy_info;
+
+ handle = 0xFFFF;
+ while (!(mptsas_sas_device_pg0(ioc, &sas_device,
+ MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+
+ handle = sas_device.handle;
+
+ if ((sas_device.device_info &
+ (MPI_SAS_DEVICE_INFO_SSP_TARGET |
+ MPI_SAS_DEVICE_INFO_STP_TARGET |
+ MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0)
+ continue;
+
+ phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
+ if (!phy_info)
+ continue;
+
+ if (mptsas_get_rphy(phy_info))
+ continue;
+
+ mptsas_add_end_device(ioc, phy_info);
+ }
+}
+
+/**
+ * mptsas_scan_sas_topology -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @sas_address:
+ *
+ **/
static void
mptsas_scan_sas_topology(MPT_ADAPTER *ioc)
{
- u32 handle = 0xFFFF;
+ struct scsi_device *sdev;
int i;
- mutex_lock(&ioc->sas_discovery_mutex);
mptsas_probe_hba_phys(ioc);
- while (!mptsas_probe_expander_phys(ioc, &handle))
- ;
+ mptsas_probe_expanders(ioc);
+ mptsas_probe_devices(ioc);
+
/*
Reporting RAID volumes.
*/
- if (!ioc->ir_firmware)
- goto out;
- if (!ioc->raid_data.pIocPg2)
- goto out;
- if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
- goto out;
+ if (!ioc->ir_firmware || !ioc->raid_data.pIocPg2 ||
+ !ioc->raid_data.pIocPg2->NumActiveVolumes)
+ return;
for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+ sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
+ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
+ if (sdev) {
+ scsi_device_put(sdev);
+ continue;
+ }
+ printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
+ "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID);
scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
}
- out:
- mutex_unlock(&ioc->sas_discovery_mutex);
}
-/*
- * Work queue thread to handle Runtime discovery
- * Mere purpose is the hot add/delete of expanders
- *(Mutex UNLOCKED)
- */
+
static void
-__mptsas_discovery_work(MPT_ADAPTER *ioc)
+mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
{
- u32 handle = 0xFFFF;
+ MPT_ADAPTER *ioc;
+ EventDataQueueFull_t *qfull_data;
+ struct mptsas_device_info *sas_info;
+ struct scsi_device *sdev;
+ int depth;
+ int id = -1;
+ int channel = -1;
+ int fw_id, fw_channel;
+ u16 current_depth;
+
+
+ ioc = fw_event->ioc;
+ qfull_data = (EventDataQueueFull_t *)fw_event->event_data;
+ fw_id = qfull_data->TargetID;
+ fw_channel = qfull_data->Bus;
+ current_depth = le16_to_cpu(qfull_data->CurrentDepth);
+
+ /* if hidden raid component, look for the volume id */
+ mutex_lock(&ioc->sas_device_info_mutex);
+ if (mptscsih_is_phys_disk(ioc, fw_channel, fw_id)) {
+ list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+ list) {
+ if (sas_info->is_cached ||
+ sas_info->is_logical_volume)
+ continue;
+ if (sas_info->is_hidden_raid_component &&
+ (sas_info->fw.channel == fw_channel &&
+ sas_info->fw.id == fw_id)) {
+ id = sas_info->volume_id;
+ channel = MPTSAS_RAID_CHANNEL;
+ goto out;
+ }
+ }
+ } else {
+ list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+ list) {
+ if (sas_info->is_cached ||
+ sas_info->is_hidden_raid_component ||
+ sas_info->is_logical_volume)
+ continue;
+ if (sas_info->fw.channel == fw_channel &&
+ sas_info->fw.id == fw_id) {
+ id = sas_info->os.id;
+ channel = sas_info->os.channel;
+ goto out;
+ }
+ }
- ioc->sas_discovery_runtime=1;
- mptsas_delete_expander_phys(ioc);
- mptsas_probe_hba_phys(ioc);
- while (!mptsas_probe_expander_phys(ioc, &handle))
- ;
- ioc->sas_discovery_runtime=0;
-}
+ }
-/*
- * Work queue thread to handle Runtime discovery
- * Mere purpose is the hot add/delete of expanders
- *(Mutex LOCKED)
- */
-static void
-mptsas_discovery_work(struct work_struct *work)
-{
- struct mptsas_discovery_event *ev =
- container_of(work, struct mptsas_discovery_event, work);
- MPT_ADAPTER *ioc = ev->ioc;
+ out:
+ mutex_unlock(&ioc->sas_device_info_mutex);
+
+ if (id != -1) {
+ shost_for_each_device(sdev, ioc->sh) {
+ if (sdev->id == id && sdev->channel == channel) {
+ if (current_depth > sdev->queue_depth) {
+ sdev_printk(KERN_INFO, sdev,
+ "strange observation, the queue "
+ "depth is (%d) meanwhile fw queue "
+ "depth (%d)\n", sdev->queue_depth,
+ current_depth);
+ continue;
+ }
+ depth = scsi_track_queue_full(sdev,
+ current_depth - 1);
+ if (depth > 0)
+ sdev_printk(KERN_INFO, sdev,
+ "Queue depth reduced to (%d)\n",
+ depth);
+ else if (depth < 0)
+ sdev_printk(KERN_INFO, sdev,
+ "Tagged Command Queueing is being "
+ "disabled\n");
+ else if (depth == 0)
+ sdev_printk(KERN_INFO, sdev,
+ "Queue depth not changed yet\n");
+ }
+ }
+ }
- mutex_lock(&ioc->sas_discovery_mutex);
- __mptsas_discovery_work(ioc);
- mutex_unlock(&ioc->sas_discovery_mutex);
- kfree(ev);
+ mptsas_free_fw_event(ioc, fw_event);
}
+
static struct mptsas_phyinfo *
mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
{
@@ -2428,69 +3786,80 @@ mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
return phy_info;
}
+/**
+ * mptsas_find_phyinfo_by_phys_disk_num -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @phys_disk_num:
+ * @channel:
+ * @id:
+ *
+ **/
static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u8 channel, u8 id)
+mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 phys_disk_num,
+ u8 channel, u8 id)
{
- struct mptsas_portinfo *port_info;
struct mptsas_phyinfo *phy_info = NULL;
+ struct mptsas_portinfo *port_info;
+ RaidPhysDiskPage1_t *phys_disk = NULL;
+ int num_paths;
+ u64 sas_address = 0;
int i;
- mutex_lock(&ioc->sas_topology_mutex);
- list_for_each_entry(port_info, &ioc->sas_topology, list) {
- for (i = 0; i < port_info->num_phys; i++) {
- if (!mptsas_is_end_device(
- &port_info->phy_info[i].attached))
- continue;
- if (port_info->phy_info[i].attached.id != id)
- continue;
- if (port_info->phy_info[i].attached.channel != channel)
- continue;
- phy_info = &port_info->phy_info[i];
- break;
+ phy_info = NULL;
+ if (!ioc->raid_data.pIocPg3)
+ return NULL;
+ /* dual port support */
+ num_paths = mpt_raid_phys_disk_get_num_paths(ioc, phys_disk_num);
+ if (!num_paths)
+ goto out;
+ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+ (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+ if (!phys_disk)
+ goto out;
+ mpt_raid_phys_disk_pg1(ioc, phys_disk_num, phys_disk);
+ for (i = 0; i < num_paths; i++) {
+ if ((phys_disk->Path[i].Flags & 1) != 0)
+ /* entry no longer valid */
+ continue;
+ if ((id == phys_disk->Path[i].PhysDiskID) &&
+ (channel == phys_disk->Path[i].PhysDiskBus)) {
+ memcpy(&sas_address, &phys_disk->Path[i].WWID,
+ sizeof(u64));
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ sas_address);
+ goto out;
}
}
- mutex_unlock(&ioc->sas_topology_mutex);
- return phy_info;
-}
-static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
-{
- struct mptsas_portinfo *port_info;
- struct mptsas_phyinfo *phy_info = NULL;
- int i;
+ out:
+ kfree(phys_disk);
+ if (phy_info)
+ return phy_info;
+ /*
+ * Extra code to handle RAID0 case, where the sas_address is not updated
+ * in phys_disk_page_1 when hotswapped
+ */
mutex_lock(&ioc->sas_topology_mutex);
list_for_each_entry(port_info, &ioc->sas_topology, list) {
- for (i = 0; i < port_info->num_phys; i++) {
+ for (i = 0; i < port_info->num_phys && !phy_info; i++) {
if (!mptsas_is_end_device(
&port_info->phy_info[i].attached))
continue;
if (port_info->phy_info[i].attached.phys_disk_num == ~0)
continue;
- if (port_info->phy_info[i].attached.phys_disk_num != id)
- continue;
- if (port_info->phy_info[i].attached.channel != channel)
- continue;
- phy_info = &port_info->phy_info[i];
- break;
+ if ((port_info->phy_info[i].attached.phys_disk_num ==
+ phys_disk_num) &&
+ (port_info->phy_info[i].attached.id == id) &&
+ (port_info->phy_info[i].attached.channel ==
+ channel))
+ phy_info = &port_info->phy_info[i];
}
}
mutex_unlock(&ioc->sas_topology_mutex);
return phy_info;
}
-/*
- * Work queue thread to clear the persitency table
- */
-static void
-mptsas_persist_clear_table(struct work_struct *work)
-{
- MPT_ADAPTER *ioc = container_of(work, MPT_ADAPTER, sas_persist_task);
-
- mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT);
-}
-
static void
mptsas_reprobe_lun(struct scsi_device *sdev, void *data)
{
@@ -2516,7 +3885,8 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
pRaidVolumePage0_t buffer = NULL;
RaidPhysDiskPage0_t phys_disk;
int i;
- struct mptsas_hotplug_event *ev;
+ struct mptsas_phyinfo *phy_info;
+ struct mptsas_devinfo sas_device;
memset(&cfg, 0 , sizeof(CONFIGPARMS));
memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
@@ -2556,20 +3926,16 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
continue;
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev) {
- printk(MYIOC_s_WARN_FMT "mptsas: lost hotplug event\n", ioc->name);
- goto out;
- }
+ if (mptsas_sas_device_pg0(ioc, &sas_device,
+ (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ (phys_disk.PhysDiskBus << 8) +
+ phys_disk.PhysDiskID))
+ continue;
- INIT_WORK(&ev->work, mptsas_hotplug_work);
- ev->ioc = ioc;
- ev->id = phys_disk.PhysDiskID;
- ev->channel = phys_disk.PhysDiskBus;
- ev->phys_disk_num_valid = 1;
- ev->phys_disk_num = phys_disk.PhysDiskNum;
- ev->event_type = MPTSAS_ADD_DEVICE;
- schedule_work(&ev->work);
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ sas_device.sas_address);
+ mptsas_add_end_device(ioc, phy_info);
}
out:
@@ -2581,417 +3947,386 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
* Work queue thread to handle SAS hotplug events
*/
static void
-mptsas_hotplug_work(struct work_struct *work)
+mptsas_hotplug_work(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+ struct mptsas_hotplug_event *hot_plug_info)
{
- struct mptsas_hotplug_event *ev =
- container_of(work, struct mptsas_hotplug_event, work);
-
- MPT_ADAPTER *ioc = ev->ioc;
struct mptsas_phyinfo *phy_info;
- struct sas_rphy *rphy;
- struct sas_port *port;
- struct scsi_device *sdev;
struct scsi_target * starget;
- struct sas_identify identify;
- char *ds = NULL;
struct mptsas_devinfo sas_device;
VirtTarget *vtarget;
- VirtDevice *vdevice;
+ int i;
- mutex_lock(&ioc->sas_discovery_mutex);
- switch (ev->event_type) {
- case MPTSAS_DEL_DEVICE:
+ switch (hot_plug_info->event_type) {
- phy_info = NULL;
- if (ev->phys_disk_num_valid) {
- if (ev->hidden_raid_component){
- if (mptsas_sas_device_pg0(ioc, &sas_device,
- (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
- MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
- (ev->channel << 8) + ev->id)) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- break;
- }
- phy_info = mptsas_find_phyinfo_by_sas_address(
- ioc, sas_device.sas_address);
- }else
- phy_info = mptsas_find_phyinfo_by_phys_disk_num(
- ioc, ev->channel, ev->phys_disk_num);
+ case MPTSAS_ADD_PHYSDISK:
+
+ if (!ioc->raid_data.pIocPg2)
+ break;
+
+ for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+ if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID ==
+ hot_plug_info->id) {
+ printk(MYIOC_s_WARN_FMT "firmware bug: unable "
+ "to add hidden disk - target_id matchs "
+ "volume_id\n", ioc->name);
+ mptsas_free_fw_event(ioc, fw_event);
+ return;
+ }
}
+ mpt_findImVolumes(ioc);
+ case MPTSAS_ADD_DEVICE:
+ memset(&sas_device, 0, sizeof(struct mptsas_devinfo));
+ mptsas_sas_device_pg0(ioc, &sas_device,
+ (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ (hot_plug_info->channel << 8) +
+ hot_plug_info->id);
+
+ if (!sas_device.handle)
+ return;
+
+ phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
if (!phy_info)
- phy_info = mptsas_find_phyinfo_by_target(ioc,
- ev->channel, ev->id);
+ break;
- /*
- * Sanity checks, for non-existing phys and remote rphys.
- */
- if (!phy_info){
+ if (mptsas_get_rphy(phy_info))
+ break;
+
+ mptsas_add_end_device(ioc, phy_info);
+ break;
+
+ case MPTSAS_DEL_DEVICE:
+ phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+ hot_plug_info->sas_address);
+ mptsas_del_end_device(ioc, phy_info);
+ break;
+
+ case MPTSAS_DEL_PHYSDISK:
+
+ mpt_findImVolumes(ioc);
+
+ phy_info = mptsas_find_phyinfo_by_phys_disk_num(
+ ioc, hot_plug_info->phys_disk_num,
+ hot_plug_info->channel,
+ hot_plug_info->id);
+ mptsas_del_end_device(ioc, phy_info);
+ break;
+
+ case MPTSAS_ADD_PHYSDISK_REPROBE:
+
+ if (mptsas_sas_device_pg0(ioc, &sas_device,
+ (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+ (hot_plug_info->channel << 8) + hot_plug_info->id)) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- if (!phy_info->port_details) {
+
+ phy_info = mptsas_find_phyinfo_by_sas_address(
+ ioc, sas_device.sas_address);
+
+ if (!phy_info) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- rphy = mptsas_get_rphy(phy_info);
- if (!rphy) {
+
+ starget = mptsas_get_starget(phy_info);
+ if (!starget) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- port = mptsas_get_port(phy_info);
- if (!port) {
+ vtarget = starget->hostdata;
+ if (!vtarget) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- starget = mptsas_get_starget(phy_info);
- if (starget) {
- vtarget = starget->hostdata;
+ mpt_findImVolumes(ioc);
- if (!vtarget) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- break;
- }
+ starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Hidding: "
+ "fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
+ ioc->name, hot_plug_info->channel, hot_plug_info->id,
+ hot_plug_info->phys_disk_num, (unsigned long long)
+ sas_device.sas_address);
- /*
- * Handling RAID components
- */
- if (ev->phys_disk_num_valid &&
- ev->hidden_raid_component) {
- printk(MYIOC_s_INFO_FMT
- "RAID Hidding: channel=%d, id=%d, "
- "physdsk %d \n", ioc->name, ev->channel,
- ev->id, ev->phys_disk_num);
- vtarget->id = ev->phys_disk_num;
- vtarget->tflags |=
- MPT_TARGET_FLAGS_RAID_COMPONENT;
- mptsas_reprobe_target(starget, 1);
- phy_info->attached.phys_disk_num =
- ev->phys_disk_num;
- break;
- }
- }
-
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_SSP_TARGET)
- ds = "ssp";
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_STP_TARGET)
- ds = "stp";
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_SATA_DEVICE)
- ds = "sata";
-
- printk(MYIOC_s_INFO_FMT
- "removing %s device, channel %d, id %d, phy %d\n",
- ioc->name, ds, ev->channel, ev->id, phy_info->phy_id);
- dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
- "delete port (%d)\n", ioc->name, port->port_identifier);
- sas_port_delete(port);
- mptsas_port_delete(ioc, phy_info->port_details);
+ vtarget->id = hot_plug_info->phys_disk_num;
+ vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
+ phy_info->attached.phys_disk_num = hot_plug_info->phys_disk_num;
+ mptsas_reprobe_target(starget, 1);
break;
- case MPTSAS_ADD_DEVICE:
- if (ev->phys_disk_num_valid)
- mpt_findImVolumes(ioc);
+ case MPTSAS_DEL_PHYSDISK_REPROBE:
- /*
- * Refresh sas device pg0 data
- */
if (mptsas_sas_device_pg0(ioc, &sas_device,
(MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
- (ev->channel << 8) + ev->id)) {
+ (hot_plug_info->channel << 8) + hot_plug_info->id)) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n",
+ ioc->name, __func__,
+ hot_plug_info->id, __LINE__));
break;
}
- __mptsas_discovery_work(ioc);
-
phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
sas_device.sas_address);
-
- if (!phy_info || !phy_info->port_details) {
+ if (!phy_info) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
starget = mptsas_get_starget(phy_info);
- if (starget && (!ev->hidden_raid_component)){
-
- vtarget = starget->hostdata;
-
- if (!vtarget) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- break;
- }
- /*
- * Handling RAID components
- */
- if (vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
- printk(MYIOC_s_INFO_FMT
- "RAID Exposing: channel=%d, id=%d, "
- "physdsk %d \n", ioc->name, ev->channel,
- ev->id, ev->phys_disk_num);
- vtarget->tflags &=
- ~MPT_TARGET_FLAGS_RAID_COMPONENT;
- vtarget->id = ev->id;
- mptsas_reprobe_target(starget, 0);
- phy_info->attached.phys_disk_num = ~0;
- }
+ if (!starget) {
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- if (mptsas_get_rphy(phy_info)) {
+ vtarget = starget->hostdata;
+ if (!vtarget) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- if (ev->channel) printk("%d\n", __LINE__);
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- port = mptsas_get_port(phy_info);
- if (!port) {
+ if (!(vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)) {
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
+ "%s: fw_id=%d exit at line=%d\n", ioc->name,
+ __func__, hot_plug_info->id, __LINE__));
break;
}
- memcpy(&phy_info->attached, &sas_device,
- sizeof(struct mptsas_devinfo));
-
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_SSP_TARGET)
- ds = "ssp";
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_STP_TARGET)
- ds = "stp";
- if (phy_info->attached.device_info &
- MPI_SAS_DEVICE_INFO_SATA_DEVICE)
- ds = "sata";
-
- printk(MYIOC_s_INFO_FMT
- "attaching %s device, channel %d, id %d, phy %d\n",
- ioc->name, ds, ev->channel, ev->id, ev->phy_id);
- mptsas_parse_device_info(&identify, &phy_info->attached);
- rphy = sas_end_device_alloc(port);
- if (!rphy) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- break; /* non-fatal: an rphy can be added later */
- }
+ mpt_findImVolumes(ioc);
- rphy->identify = identify;
- if (sas_rphy_add(rphy)) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
- "%s: exit at line=%d\n", ioc->name,
- __func__, __LINE__));
- sas_rphy_free(rphy);
- break;
- }
- mptsas_set_rphy(ioc, phy_info, rphy);
+ starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Exposing:"
+ " fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
+ ioc->name, hot_plug_info->channel, hot_plug_info->id,
+ hot_plug_info->phys_disk_num, (unsigned long long)
+ sas_device.sas_address);
+
+ vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+ vtarget->id = hot_plug_info->id;
+ phy_info->attached.phys_disk_num = ~0;
+ mptsas_reprobe_target(starget, 0);
+ mptsas_add_device_component_by_fw(ioc,
+ hot_plug_info->channel, hot_plug_info->id);
break;
+
case MPTSAS_ADD_RAID:
- sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
- ev->id, 0);
- if (sdev) {
- scsi_device_put(sdev);
- break;
- }
- printk(MYIOC_s_INFO_FMT
- "attaching raid volume, channel %d, id %d\n",
- ioc->name, MPTSAS_RAID_CHANNEL, ev->id);
- scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL, ev->id, 0);
+
mpt_findImVolumes(ioc);
+ printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
+ "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+ hot_plug_info->id);
+ scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
+ hot_plug_info->id, 0);
break;
+
case MPTSAS_DEL_RAID:
- sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
- ev->id, 0);
- if (!sdev)
- break;
- printk(MYIOC_s_INFO_FMT
- "removing raid volume, channel %d, id %d\n",
- ioc->name, MPTSAS_RAID_CHANNEL, ev->id);
- vdevice = sdev->hostdata;
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
+
mpt_findImVolumes(ioc);
+ printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
+ "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+ hot_plug_info->id);
+ scsi_remove_device(hot_plug_info->sdev);
+ scsi_device_put(hot_plug_info->sdev);
break;
+
case MPTSAS_ADD_INACTIVE_VOLUME:
+
+ mpt_findImVolumes(ioc);
mptsas_adding_inactive_raid_components(ioc,
- ev->channel, ev->id);
+ hot_plug_info->channel, hot_plug_info->id);
break;
- case MPTSAS_IGNORE_EVENT:
+
default:
break;
}
- mutex_unlock(&ioc->sas_discovery_mutex);
- kfree(ev);
+ mptsas_free_fw_event(ioc, fw_event);
}
static void
-mptsas_send_sas_event(MPT_ADAPTER *ioc,
- EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
+mptsas_send_sas_event(struct fw_event_work *fw_event)
{
- struct mptsas_hotplug_event *ev;
- u32 device_info = le32_to_cpu(sas_event_data->DeviceInfo);
- __le64 sas_address;
+ MPT_ADAPTER *ioc;
+ struct mptsas_hotplug_event hot_plug_info;
+ EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
+ u32 device_info;
+ u64 sas_address;
+
+ ioc = fw_event->ioc;
+ sas_event_data = (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)
+ fw_event->event_data;
+ device_info = le32_to_cpu(sas_event_data->DeviceInfo);
if ((device_info &
- (MPI_SAS_DEVICE_INFO_SSP_TARGET |
- MPI_SAS_DEVICE_INFO_STP_TARGET |
- MPI_SAS_DEVICE_INFO_SATA_DEVICE )) == 0)
+ (MPI_SAS_DEVICE_INFO_SSP_TARGET |
+ MPI_SAS_DEVICE_INFO_STP_TARGET |
+ MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0) {
+ mptsas_free_fw_event(ioc, fw_event);
+ return;
+ }
+
+ if (sas_event_data->ReasonCode ==
+ MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED) {
+ mptbase_sas_persist_operation(ioc,
+ MPI_SAS_OP_CLEAR_NOT_PRESENT);
+ mptsas_free_fw_event(ioc, fw_event);
return;
+ }
switch (sas_event_data->ReasonCode) {
case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING:
-
- mptsas_target_reset_queue(ioc, sas_event_data);
- break;
-
case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev) {
- printk(MYIOC_s_WARN_FMT "lost hotplug event\n", ioc->name);
- break;
- }
-
- INIT_WORK(&ev->work, mptsas_hotplug_work);
- ev->ioc = ioc;
- ev->handle = le16_to_cpu(sas_event_data->DevHandle);
- ev->parent_handle =
- le16_to_cpu(sas_event_data->ParentDevHandle);
- ev->channel = sas_event_data->Bus;
- ev->id = sas_event_data->TargetID;
- ev->phy_id = sas_event_data->PhyNum;
+ memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+ hot_plug_info.handle = le16_to_cpu(sas_event_data->DevHandle);
+ hot_plug_info.channel = sas_event_data->Bus;
+ hot_plug_info.id = sas_event_data->TargetID;
+ hot_plug_info.phy_id = sas_event_data->PhyNum;
memcpy(&sas_address, &sas_event_data->SASAddress,
- sizeof(__le64));
- ev->sas_address = le64_to_cpu(sas_address);
- ev->device_info = device_info;
-
+ sizeof(u64));
+ hot_plug_info.sas_address = le64_to_cpu(sas_address);
+ hot_plug_info.device_info = device_info;
if (sas_event_data->ReasonCode &
MPI_EVENT_SAS_DEV_STAT_RC_ADDED)
- ev->event_type = MPTSAS_ADD_DEVICE;
+ hot_plug_info.event_type = MPTSAS_ADD_DEVICE;
else
- ev->event_type = MPTSAS_DEL_DEVICE;
- schedule_work(&ev->work);
+ hot_plug_info.event_type = MPTSAS_DEL_DEVICE;
+ mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
break;
+
case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED:
- /*
- * Persistent table is full.
- */
- INIT_WORK(&ioc->sas_persist_task,
- mptsas_persist_clear_table);
- schedule_work(&ioc->sas_persist_task);
+ mptbase_sas_persist_operation(ioc,
+ MPI_SAS_OP_CLEAR_NOT_PRESENT);
+ mptsas_free_fw_event(ioc, fw_event);
break;
- /*
- * TODO, handle other events
- */
+
case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
- case MPI_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
+ /* TODO */
case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
- case MPI_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
- case MPI_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
- case MPI_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
- case MPI_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
+ /* TODO */
default:
+ mptsas_free_fw_event(ioc, fw_event);
break;
}
}
+
static void
-mptsas_send_raid_event(MPT_ADAPTER *ioc,
- EVENT_DATA_RAID *raid_event_data)
+mptsas_send_raid_event(struct fw_event_work *fw_event)
{
- struct mptsas_hotplug_event *ev;
- int status = le32_to_cpu(raid_event_data->SettingsStatus);
- int state = (status >> 8) & 0xff;
-
- if (ioc->bus_type != SAS)
- return;
-
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev) {
- printk(MYIOC_s_WARN_FMT "lost hotplug event\n", ioc->name);
- return;
+ MPT_ADAPTER *ioc;
+ EVENT_DATA_RAID *raid_event_data;
+ struct mptsas_hotplug_event hot_plug_info;
+ int status;
+ int state;
+ struct scsi_device *sdev = NULL;
+ VirtDevice *vdevice = NULL;
+ RaidPhysDiskPage0_t phys_disk;
+
+ ioc = fw_event->ioc;
+ raid_event_data = (EVENT_DATA_RAID *)fw_event->event_data;
+ status = le32_to_cpu(raid_event_data->SettingsStatus);
+ state = (status >> 8) & 0xff;
+
+ memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+ hot_plug_info.id = raid_event_data->VolumeID;
+ hot_plug_info.channel = raid_event_data->VolumeBus;
+ hot_plug_info.phys_disk_num = raid_event_data->PhysDiskNum;
+
+ if (raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_DELETED ||
+ raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_CREATED ||
+ raid_event_data->ReasonCode ==
+ MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED) {
+ sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
+ hot_plug_info.id, 0);
+ hot_plug_info.sdev = sdev;
+ if (sdev)
+ vdevice = sdev->hostdata;
}
- INIT_WORK(&ev->work, mptsas_hotplug_work);
- ev->ioc = ioc;
- ev->id = raid_event_data->VolumeID;
- ev->channel = raid_event_data->VolumeBus;
- ev->event_type = MPTSAS_IGNORE_EVENT;
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
+ "ReasonCode=%02x\n", ioc->name, __func__,
+ raid_event_data->ReasonCode));
switch (raid_event_data->ReasonCode) {
case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
- ev->phys_disk_num_valid = 1;
- ev->phys_disk_num = raid_event_data->PhysDiskNum;
- ev->event_type = MPTSAS_ADD_DEVICE;
+ hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK_REPROBE;
break;
case MPI_EVENT_RAID_RC_PHYSDISK_CREATED:
- ev->phys_disk_num_valid = 1;
- ev->phys_disk_num = raid_event_data->PhysDiskNum;
- ev->hidden_raid_component = 1;
- ev->event_type = MPTSAS_DEL_DEVICE;
+ hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK_REPROBE;
break;
case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED:
switch (state) {
case MPI_PD_STATE_ONLINE:
case MPI_PD_STATE_NOT_COMPATIBLE:
- ev->phys_disk_num_valid = 1;
- ev->phys_disk_num = raid_event_data->PhysDiskNum;
- ev->hidden_raid_component = 1;
- ev->event_type = MPTSAS_ADD_DEVICE;
+ mpt_raid_phys_disk_pg0(ioc,
+ raid_event_data->PhysDiskNum, &phys_disk);
+ hot_plug_info.id = phys_disk.PhysDiskID;
+ hot_plug_info.channel = phys_disk.PhysDiskBus;
+ hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
break;
+ case MPI_PD_STATE_FAILED:
case MPI_PD_STATE_MISSING:
case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST:
case MPI_PD_STATE_FAILED_AT_HOST_REQUEST:
case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON:
- ev->phys_disk_num_valid = 1;
- ev->phys_disk_num = raid_event_data->PhysDiskNum;
- ev->event_type = MPTSAS_DEL_DEVICE;
+ hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
break;
default:
break;
}
break;
case MPI_EVENT_RAID_RC_VOLUME_DELETED:
- ev->event_type = MPTSAS_DEL_RAID;
+ if (!sdev)
+ break;
+ vdevice->vtarget->deleted = 1; /* block IO */
+ hot_plug_info.event_type = MPTSAS_DEL_RAID;
break;
case MPI_EVENT_RAID_RC_VOLUME_CREATED:
- ev->event_type = MPTSAS_ADD_RAID;
+ if (sdev) {
+ scsi_device_put(sdev);
+ break;
+ }
+ hot_plug_info.event_type = MPTSAS_ADD_RAID;
break;
case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED:
+ if (!(status & MPI_RAIDVOL0_STATUS_FLAG_ENABLED)) {
+ if (!sdev)
+ break;
+ vdevice->vtarget->deleted = 1; /* block IO */
+ hot_plug_info.event_type = MPTSAS_DEL_RAID;
+ break;
+ }
switch (state) {
case MPI_RAIDVOL0_STATUS_STATE_FAILED:
case MPI_RAIDVOL0_STATUS_STATE_MISSING:
- ev->event_type = MPTSAS_DEL_RAID;
+ if (!sdev)
+ break;
+ vdevice->vtarget->deleted = 1; /* block IO */
+ hot_plug_info.event_type = MPTSAS_DEL_RAID;
break;
case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL:
case MPI_RAIDVOL0_STATUS_STATE_DEGRADED:
- ev->event_type = MPTSAS_ADD_RAID;
+ if (sdev) {
+ scsi_device_put(sdev);
+ break;
+ }
+ hot_plug_info.event_type = MPTSAS_ADD_RAID;
break;
default:
break;
@@ -3000,32 +4335,188 @@ mptsas_send_raid_event(MPT_ADAPTER *ioc,
default:
break;
}
- schedule_work(&ev->work);
+
+ if (hot_plug_info.event_type != MPTSAS_IGNORE_EVENT)
+ mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
+ else
+ mptsas_free_fw_event(ioc, fw_event);
}
-static void
-mptsas_send_discovery_event(MPT_ADAPTER *ioc,
- EVENT_DATA_SAS_DISCOVERY *discovery_data)
+/**
+ * mptsas_issue_tm - send mptsas internal tm request
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @type: Task Management type
+ * @channel: channel number for task management
+ * @id: Logical Target ID for reset (if appropriate)
+ * @lun: Logical unit for reset (if appropriate)
+ * @task_context: Context for the task to be aborted
+ * @timeout: timeout for task management control
+ *
+ * return 0 on success and -1 on failure:
+ *
+ */
+static int
+mptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun,
+ int task_context, ulong timeout, u8 *issue_reset)
{
- struct mptsas_discovery_event *ev;
+ MPT_FRAME_HDR *mf;
+ SCSITaskMgmt_t *pScsiTm;
+ int retval;
+ unsigned long timeleft;
+
+ *issue_reset = 0;
+ mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
+ if (mf == NULL) {
+ retval = -1; /* return failure */
+ dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt request: no "
+ "msg frames!!\n", ioc->name));
+ goto out;
+ }
- /*
- * DiscoveryStatus
- *
- * This flag will be non-zero when firmware
- * kicks off discovery, and return to zero
- * once its completed.
- */
- if (discovery_data->DiscoveryStatus)
- return;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request: mr = %p, "
+ "task_type = 0x%02X,\n\t timeout = %ld, fw_channel = %d, "
+ "fw_id = %d, lun = %lld,\n\t task_context = 0x%x\n", ioc->name, mf,
+ type, timeout, channel, id, (unsigned long long)lun,
+ task_context));
+
+ pScsiTm = (SCSITaskMgmt_t *) mf;
+ memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
+ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+ pScsiTm->TaskType = type;
+ pScsiTm->MsgFlags = 0;
+ pScsiTm->TargetID = id;
+ pScsiTm->Bus = channel;
+ pScsiTm->ChainOffset = 0;
+ pScsiTm->Reserved = 0;
+ pScsiTm->Reserved1 = 0;
+ pScsiTm->TaskMsgContext = task_context;
+ int_to_scsilun(lun, (struct scsi_lun *)pScsiTm->LUN);
+
+ INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+ retval = 0;
+ mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
+
+ /* Now wait for the command to complete */
+ timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
+ timeout*HZ);
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ retval = -1; /* return failure */
+ dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "TaskMgmt request: TIMED OUT!(mr=%p)\n", ioc->name, mf));
+ mpt_free_msg_frame(ioc, mf);
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out;
+ *issue_reset = 1;
+ goto out;
+ }
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev)
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ retval = -1; /* return failure */
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt request: failed with no reply\n", ioc->name));
+ goto out;
+ }
+
+ out:
+ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ return retval;
+}
+
+/**
+ * mptsas_broadcast_primative_work - Handle broadcast primitives
+ * @work: work queue payload containing info describing the event
+ *
+ * this will be handled in workqueue context.
+ */
+static void
+mptsas_broadcast_primative_work(struct fw_event_work *fw_event)
+{
+ MPT_ADAPTER *ioc = fw_event->ioc;
+ MPT_FRAME_HDR *mf;
+ VirtDevice *vdevice;
+ int ii;
+ struct scsi_cmnd *sc;
+ SCSITaskMgmtReply_t *pScsiTmReply;
+ u8 issue_reset;
+ int task_context;
+ u8 channel, id;
+ int lun;
+ u32 termination_count;
+ u32 query_count;
+
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s - enter\n", ioc->name, __func__));
+
+ mutex_lock(&ioc->taskmgmt_cmds.mutex);
+ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ mptsas_requeue_fw_event(ioc, fw_event, 1000);
return;
- INIT_WORK(&ev->work, mptsas_discovery_work);
- ev->ioc = ioc;
- schedule_work(&ev->work);
-};
+ }
+
+ issue_reset = 0;
+ termination_count = 0;
+ query_count = 0;
+ mpt_findImVolumes(ioc);
+ pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
+
+ for (ii = 0; ii < ioc->req_depth; ii++) {
+ if (ioc->fw_events_off)
+ goto out;
+ sc = mptscsih_get_scsi_lookup(ioc, ii);
+ if (!sc)
+ continue;
+ mf = MPT_INDEX_2_MFPTR(ioc, ii);
+ if (!mf)
+ continue;
+ task_context = mf->u.frame.hwhdr.msgctxu.MsgContext;
+ vdevice = sc->device->hostdata;
+ if (!vdevice || !vdevice->vtarget)
+ continue;
+ if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
+ continue; /* skip hidden raid components */
+ if (vdevice->vtarget->raidVolume)
+ continue; /* skip hidden raid components */
+ channel = vdevice->vtarget->channel;
+ id = vdevice->vtarget->id;
+ lun = vdevice->lun;
+ if (mptsas_issue_tm(ioc, MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK,
+ channel, id, (u64)lun, task_context, 30, &issue_reset))
+ goto out;
+ query_count++;
+ termination_count +=
+ le32_to_cpu(pScsiTmReply->TerminationCount);
+ if ((pScsiTmReply->IOCStatus == MPI_IOCSTATUS_SUCCESS) &&
+ (pScsiTmReply->ResponseCode ==
+ MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
+ pScsiTmReply->ResponseCode ==
+ MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))
+ continue;
+ if (mptsas_issue_tm(ioc,
+ MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET,
+ channel, id, (u64)lun, 0, 30, &issue_reset))
+ goto out;
+ termination_count +=
+ le32_to_cpu(pScsiTmReply->TerminationCount);
+ }
+
+ out:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s - exit, query_count = %d termination_count = %d\n",
+ ioc->name, __func__, query_count, termination_count));
+
+ ioc->broadcast_aen_busy = 0;
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+
+ if (issue_reset) {
+ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ }
+ mptsas_free_fw_event(ioc, fw_event);
+}
/*
* mptsas_send_ir2_event - handle exposing hidden disk when
@@ -3036,76 +4527,159 @@ mptsas_send_discovery_event(MPT_ADAPTER *ioc,
*
*/
static void
-mptsas_send_ir2_event(MPT_ADAPTER *ioc, PTR_MPI_EVENT_DATA_IR2 ir2_data)
+mptsas_send_ir2_event(struct fw_event_work *fw_event)
{
- struct mptsas_hotplug_event *ev;
-
- if (ir2_data->ReasonCode !=
- MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED)
- return;
-
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev)
+ MPT_ADAPTER *ioc;
+ struct mptsas_hotplug_event hot_plug_info;
+ MPI_EVENT_DATA_IR2 *ir2_data;
+ u8 reasonCode;
+ RaidPhysDiskPage0_t phys_disk;
+
+ ioc = fw_event->ioc;
+ ir2_data = (MPI_EVENT_DATA_IR2 *)fw_event->event_data;
+ reasonCode = ir2_data->ReasonCode;
+
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
+ "ReasonCode=%02x\n", ioc->name, __func__, reasonCode));
+
+ memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+ hot_plug_info.id = ir2_data->TargetID;
+ hot_plug_info.channel = ir2_data->Bus;
+ switch (reasonCode) {
+ case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
+ hot_plug_info.event_type = MPTSAS_ADD_INACTIVE_VOLUME;
+ break;
+ case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
+ hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
+ hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
+ break;
+ case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
+ hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
+ mpt_raid_phys_disk_pg0(ioc,
+ ir2_data->PhysDiskNum, &phys_disk);
+ hot_plug_info.id = phys_disk.PhysDiskID;
+ hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
+ break;
+ default:
+ mptsas_free_fw_event(ioc, fw_event);
return;
-
- INIT_WORK(&ev->work, mptsas_hotplug_work);
- ev->ioc = ioc;
- ev->id = ir2_data->TargetID;
- ev->channel = ir2_data->Bus;
- ev->event_type = MPTSAS_ADD_INACTIVE_VOLUME;
-
- schedule_work(&ev->work);
-};
+ }
+ mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
+}
static int
mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
{
- int rc=1;
- u8 event = le32_to_cpu(reply->Event) & 0xFF;
+ u32 event = le32_to_cpu(reply->Event);
+ int sz, event_data_sz;
+ struct fw_event_work *fw_event;
+ unsigned long delay;
- if (!ioc->sh)
- goto out;
-
- /*
- * sas_discovery_ignore_events
- *
- * This flag is to prevent anymore processing of
- * sas events once mptsas_remove function is called.
- */
- if (ioc->sas_discovery_ignore_events) {
- rc = mptscsih_event_process(ioc, reply);
- goto out;
- }
+ /* events turned off due to host reset or driver unloading */
+ if (ioc->fw_events_off)
+ return 0;
+ delay = msecs_to_jiffies(1);
switch (event) {
+ case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
+ {
+ EVENT_DATA_SAS_BROADCAST_PRIMITIVE *broadcast_event_data =
+ (EVENT_DATA_SAS_BROADCAST_PRIMITIVE *)reply->Data;
+ if (broadcast_event_data->Primitive !=
+ MPI_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
+ return 0;
+ if (ioc->broadcast_aen_busy)
+ return 0;
+ ioc->broadcast_aen_busy = 1;
+ break;
+ }
case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
- mptsas_send_sas_event(ioc,
- (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data);
+ {
+ EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data =
+ (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data;
+
+ if (sas_event_data->ReasonCode ==
+ MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING) {
+ mptsas_target_reset_queue(ioc, sas_event_data);
+ return 0;
+ }
break;
- case MPI_EVENT_INTEGRATED_RAID:
- mptsas_send_raid_event(ioc,
- (EVENT_DATA_RAID *)reply->Data);
+ }
+ case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+ {
+ MpiEventDataSasExpanderStatusChange_t *expander_data =
+ (MpiEventDataSasExpanderStatusChange_t *)reply->Data;
+
+ if (ioc->old_sas_discovery_protocal)
+ return 0;
+
+ if (expander_data->ReasonCode ==
+ MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING &&
+ ioc->device_missing_delay)
+ delay = HZ * ioc->device_missing_delay;
break;
+ }
+ case MPI_EVENT_SAS_DISCOVERY:
+ {
+ u32 discovery_status;
+ EventDataSasDiscovery_t *discovery_data =
+ (EventDataSasDiscovery_t *)reply->Data;
+
+ discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
+ ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
+ if (ioc->old_sas_discovery_protocal && !discovery_status)
+ mptsas_queue_rescan(ioc);
+ return 0;
+ }
+ case MPI_EVENT_INTEGRATED_RAID:
case MPI_EVENT_PERSISTENT_TABLE_FULL:
- INIT_WORK(&ioc->sas_persist_task,
- mptsas_persist_clear_table);
- schedule_work(&ioc->sas_persist_task);
- break;
- case MPI_EVENT_SAS_DISCOVERY:
- mptsas_send_discovery_event(ioc,
- (EVENT_DATA_SAS_DISCOVERY *)reply->Data);
- break;
case MPI_EVENT_IR2:
- mptsas_send_ir2_event(ioc,
- (PTR_MPI_EVENT_DATA_IR2)reply->Data);
+ case MPI_EVENT_SAS_PHY_LINK_STATUS:
+ case MPI_EVENT_QUEUE_FULL:
break;
default:
- rc = mptscsih_event_process(ioc, reply);
- break;
+ return 0;
}
- out:
- return rc;
+ event_data_sz = ((reply->MsgLength * 4) -
+ offsetof(EventNotificationReply_t, Data));
+ sz = offsetof(struct fw_event_work, event_data) + event_data_sz;
+ fw_event = kzalloc(sz, GFP_ATOMIC);
+ if (!fw_event) {
+ printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n", ioc->name,
+ __func__, __LINE__);
+ return 0;
+ }
+ memcpy(fw_event->event_data, reply->Data, event_data_sz);
+ fw_event->event = event;
+ fw_event->ioc = ioc;
+ mptsas_add_fw_event(ioc, fw_event, delay);
+ return 0;
+}
+
+/* Delete a volume when no longer listed in ioc pg2
+ */
+static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id)
+{
+ struct scsi_device *sdev;
+ int i;
+
+ sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, id, 0);
+ if (!sdev)
+ return;
+ if (!ioc->raid_data.pIocPg2)
+ goto out;
+ if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
+ goto out;
+ for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
+ if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID == id)
+ goto release_sdev;
+ out:
+ printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
+ "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL, id);
+ scsi_remove_device(sdev);
+ release_sdev:
+ scsi_device_put(sdev);
}
static int
@@ -3127,6 +4701,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return r;
ioc = pci_get_drvdata(pdev);
+ mptsas_fw_event_off(ioc);
ioc->DoneCtx = mptsasDoneCtx;
ioc->TaskCtx = mptsasTaskCtx;
ioc->InternalCtx = mptsasInternalCtx;
@@ -3210,17 +4785,15 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* A slightly different algorithm is required for
* 64bit SGEs.
*/
- scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
- if (sizeof(dma_addr_t) == sizeof(u64)) {
+ scale = ioc->req_sz/ioc->SGE_size;
+ if (ioc->sg_addr_size == sizeof(u64)) {
numSGE = (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 60) / ioc->SGE_size;
} else {
numSGE = 1 + (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 64) / ioc->SGE_size;
}
if (numSGE < sh->sg_tablesize) {
@@ -3250,9 +4823,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Clear the TM flags
*/
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- hd->resetPending = 0;
hd->abortSCpnt = NULL;
/* Clear the pointer used to store
@@ -3272,10 +4842,11 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->sas_data.ptClear = mpt_pt_clear;
- init_waitqueue_head(&hd->scandv_waitq);
- hd->scandv_wait_done = 0;
hd->last_queue_full = 0;
INIT_LIST_HEAD(&hd->target_reset_list);
+ INIT_LIST_HEAD(&ioc->sas_device_info_list);
+ mutex_init(&ioc->sas_device_info_mutex);
+
spin_unlock_irqrestore(&ioc->FreeQlock, flags);
if (ioc->sas_data.ptClear==1) {
@@ -3290,8 +4861,11 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_mptsas_probe;
}
+ /* older firmware doesn't support expander events */
+ if ((ioc->facts.HeaderVersion >> 8) < 0xE)
+ ioc->old_sas_discovery_protocal = 1;
mptsas_scan_sas_topology(ioc);
-
+ mptsas_fw_event_on(ioc);
return 0;
out_mptsas_probe:
@@ -3300,12 +4874,25 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return error;
}
+void
+mptsas_shutdown(struct pci_dev *pdev)
+{
+ MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+
+ mptsas_fw_event_off(ioc);
+ mptsas_cleanup_fw_event_q(ioc);
+}
+
static void __devexit mptsas_remove(struct pci_dev *pdev)
{
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
struct mptsas_portinfo *p, *n;
int i;
+ mptsas_shutdown(pdev);
+
+ mptsas_del_device_components(ioc);
+
ioc->sas_discovery_ignore_events = 1;
sas_remove_host(ioc->sh);
@@ -3314,11 +4901,12 @@ static void __devexit mptsas_remove(struct pci_dev *pdev)
list_del(&p->list);
for (i = 0 ; i < p->num_phys ; i++)
mptsas_port_delete(ioc, p->phy_info[i].port_details);
+
kfree(p->phy_info);
kfree(p);
}
mutex_unlock(&ioc->sas_topology_mutex);
-
+ ioc->hba_port_info = NULL;
mptscsih_remove(pdev);
}
@@ -3343,7 +4931,7 @@ static struct pci_driver mptsas_driver = {
.id_table = mptsas_pci_table,
.probe = mptsas_probe,
.remove = __devexit_p(mptsas_remove),
- .shutdown = mptscsih_shutdown,
+ .shutdown = mptsas_shutdown,
#ifdef CONFIG_PM
.suspend = mptscsih_suspend,
.resume = mptscsih_resume,
@@ -3363,10 +4951,12 @@ mptsas_init(void)
return -ENODEV;
mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER);
- mptsasTaskCtx = mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER);
+ mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER);
mptsasInternalCtx =
mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER);
mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER);
+ mptsasDeviceResetCtx =
+ mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER);
mpt_event_register(mptsasDoneCtx, mptsas_event_process);
mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset);
@@ -3391,6 +4981,7 @@ mptsas_exit(void)
mpt_deregister(mptsasInternalCtx);
mpt_deregister(mptsasTaskCtx);
mpt_deregister(mptsasDoneCtx);
+ mpt_deregister(mptsasDeviceResetCtx);
}
module_init(mptsas_init);
diff --git a/drivers/message/fusion/mptsas.h b/drivers/message/fusion/mptsas.h
index 2b544e0877e6..953c2bfcf6aa 100644
--- a/drivers/message/fusion/mptsas.h
+++ b/drivers/message/fusion/mptsas.h
@@ -53,6 +53,7 @@ struct mptsas_target_reset_event {
struct list_head list;
EVENT_DATA_SAS_DEVICE_STATUS_CHANGE sas_event_data;
u8 target_reset_issued;
+ unsigned long time_count;
};
enum mptsas_hotplug_action {
@@ -60,12 +61,37 @@ enum mptsas_hotplug_action {
MPTSAS_DEL_DEVICE,
MPTSAS_ADD_RAID,
MPTSAS_DEL_RAID,
+ MPTSAS_ADD_PHYSDISK,
+ MPTSAS_ADD_PHYSDISK_REPROBE,
+ MPTSAS_DEL_PHYSDISK,
+ MPTSAS_DEL_PHYSDISK_REPROBE,
MPTSAS_ADD_INACTIVE_VOLUME,
MPTSAS_IGNORE_EVENT,
};
+struct mptsas_mapping{
+ u8 id;
+ u8 channel;
+};
+
+struct mptsas_device_info {
+ struct list_head list;
+ struct mptsas_mapping os; /* operating system mapping*/
+ struct mptsas_mapping fw; /* firmware mapping */
+ u64 sas_address;
+ u32 device_info; /* specific bits for devices */
+ u16 slot; /* enclosure slot id */
+ u64 enclosure_logical_id; /*enclosure address */
+ u8 is_logical_volume; /* is this logical volume */
+ /* this belongs to volume */
+ u8 is_hidden_raid_component;
+ /* this valid when is_hidden_raid_component set */
+ u8 volume_id;
+ /* cached data for a removed device */
+ u8 is_cached;
+};
+
struct mptsas_hotplug_event {
- struct work_struct work;
MPT_ADAPTER *ioc;
enum mptsas_hotplug_action event_type;
u64 sas_address;
@@ -73,11 +99,18 @@ struct mptsas_hotplug_event {
u8 id;
u32 device_info;
u16 handle;
- u16 parent_handle;
u8 phy_id;
- u8 phys_disk_num_valid; /* hrc (hidden raid component) */
u8 phys_disk_num; /* hrc - unique index*/
- u8 hidden_raid_component; /* hrc - don't expose*/
+ struct scsi_device *sdev;
+};
+
+struct fw_event_work {
+ struct list_head list;
+ struct delayed_work work;
+ MPT_ADAPTER *ioc;
+ u32 event;
+ u8 retries;
+ u8 event_data[1];
};
struct mptsas_discovery_event {
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index e62c6bc4ad33..024e8305bcf2 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -80,7 +80,7 @@ MODULE_VERSION(my_VERSION);
/*
* Other private/forward protos...
*/
-static struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
+struct scsi_cmnd *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i);
static void mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd);
static int SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd);
@@ -92,18 +92,24 @@ static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
SCSIIORequest_t *pReq, int req_idx);
static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
static void mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
-static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
-static int mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
-static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);
+int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id,
+ int lun, int ctx2abort, ulong timeout);
int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
+void
+mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
+static int mptscsih_get_completion_code(MPT_ADAPTER *ioc,
+ MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
static void mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);
+static int
+mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type,
+ SCSITaskMgmtReply_t *pScsiTmReply);
void mptscsih_remove(struct pci_dev *);
void mptscsih_shutdown(struct pci_dev *);
#ifdef CONFIG_PM
@@ -113,69 +119,6 @@ int mptscsih_resume(struct pci_dev *pdev);
#define SNS_LEN(scp) SCSI_SENSE_BUFFERSIZE
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_add_sge - Place a simple SGE at address pAddr.
- * @pAddr: virtual address for SGE
- * @flagslength: SGE flags and data transfer length
- * @dma_addr: Physical address
- *
- * This routine places a MPT request frame back on the MPT adapter's
- * FreeQ.
- */
-static inline void
-mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
-{
- if (sizeof(dma_addr_t) == sizeof(u64)) {
- SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
- u32 tmp = dma_addr & 0xFFFFFFFF;
-
- pSge->FlagsLength = cpu_to_le32(flagslength);
- pSge->Address.Low = cpu_to_le32(tmp);
- tmp = (u32) ((u64)dma_addr >> 32);
- pSge->Address.High = cpu_to_le32(tmp);
-
- } else {
- SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
- pSge->FlagsLength = cpu_to_le32(flagslength);
- pSge->Address = cpu_to_le32(dma_addr);
- }
-} /* mptscsih_add_sge() */
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_add_chain - Place a chain SGE at address pAddr.
- * @pAddr: virtual address for SGE
- * @next: nextChainOffset value (u32's)
- * @length: length of next SGL segment
- * @dma_addr: Physical address
- *
- * This routine places a MPT request frame back on the MPT adapter's
- * FreeQ.
- */
-static inline void
-mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
-{
- if (sizeof(dma_addr_t) == sizeof(u64)) {
- SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
- u32 tmp = dma_addr & 0xFFFFFFFF;
-
- pChain->Length = cpu_to_le16(length);
- pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
-
- pChain->NextChainOffset = next;
-
- pChain->Address.Low = cpu_to_le32(tmp);
- tmp = (u32) ((u64)dma_addr >> 32);
- pChain->Address.High = cpu_to_le32(tmp);
- } else {
- SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
- pChain->Length = cpu_to_le16(length);
- pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
- pChain->NextChainOffset = next;
- pChain->Address = cpu_to_le32(dma_addr);
- }
-} /* mptscsih_add_chain() */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
@@ -281,10 +224,10 @@ mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
*/
nextSGEset:
- numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
+ numSgeSlots = ((frm_sz - sgeOffset) / ioc->SGE_size);
numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
- sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
+ sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | sgdir;
/* Get first (num - 1) SG elements
* Skip any SG entries with a length of 0
@@ -293,17 +236,19 @@ nextSGEset:
for (ii=0; ii < (numSgeThisFrame-1); ii++) {
thisxfer = sg_dma_len(sg);
if (thisxfer == 0) {
- sg = sg_next(sg); /* Get next SG element from the OS */
+ /* Get next SG element from the OS */
+ sg = sg_next(sg);
sg_done++;
continue;
}
v2 = sg_dma_address(sg);
- mptscsih_add_sge(psge, sgflags | thisxfer, v2);
+ ioc->add_sge(psge, sgflags | thisxfer, v2);
- sg = sg_next(sg); /* Get next SG element from the OS */
- psge += (sizeof(u32) + sizeof(dma_addr_t));
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ /* Get next SG element from the OS */
+ sg = sg_next(sg);
+ psge += ioc->SGE_size;
+ sgeOffset += ioc->SGE_size;
sg_done++;
}
@@ -320,12 +265,8 @@ nextSGEset:
thisxfer = sg_dma_len(sg);
v2 = sg_dma_address(sg);
- mptscsih_add_sge(psge, sgflags | thisxfer, v2);
- /*
- sg = sg_next(sg);
- psge += (sizeof(u32) + sizeof(dma_addr_t));
- */
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ ioc->add_sge(psge, sgflags | thisxfer, v2);
+ sgeOffset += ioc->SGE_size;
sg_done++;
if (chainSge) {
@@ -334,7 +275,8 @@ nextSGEset:
* Update the chain element
* Offset and Length fields.
*/
- mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
+ ioc->add_chain((char *)chainSge, 0, sgeOffset,
+ ioc->ChainBufferDMA + chain_dma_off);
} else {
/* The current buffer is the original MF
* and there is no Chain buffer.
@@ -367,7 +309,7 @@ nextSGEset:
* set properly).
*/
if (sg_done) {
- u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
+ u32 *ptmp = (u32 *) (psge - ioc->SGE_size);
sgflags = le32_to_cpu(*ptmp);
sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
*ptmp = cpu_to_le32(sgflags);
@@ -381,8 +323,9 @@ nextSGEset:
* Old chain element is now complete.
*/
u8 nextChain = (u8) (sgeOffset >> 2);
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
- mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
+ sgeOffset += ioc->SGE_size;
+ ioc->add_chain((char *)chainSge, nextChain, sgeOffset,
+ ioc->ChainBufferDMA + chain_dma_off);
} else {
/* The original MF buffer requires a chain buffer -
* set the offset.
@@ -592,14 +535,15 @@ mptscsih_info_scsiio(MPT_ADAPTER *ioc, struct scsi_cmnd *sc, SCSIIOReply_t * pSc
}
scsi_print_command(sc);
- printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d\n",
- ioc->name, pScsiReply->Bus, pScsiReply->TargetID);
+ printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d, lun = %d\n",
+ ioc->name, pScsiReply->Bus, pScsiReply->TargetID, sc->device->lun);
printk(MYIOC_s_DEBUG_FMT "\trequest_len = %d, underflow = %d, "
"resid = %d\n", ioc->name, scsi_bufflen(sc), sc->underflow,
scsi_get_resid(sc));
printk(MYIOC_s_DEBUG_FMT "\ttag = %d, transfer_count = %d, "
"sc->result = %08X\n", ioc->name, le16_to_cpu(pScsiReply->TaskTag),
le32_to_cpu(pScsiReply->TransferCount), sc->result);
+
printk(MYIOC_s_DEBUG_FMT "\tiocstatus = %s (0x%04x), "
"scsi_status = %s (0x%02x), scsi_state = (0x%02x)\n",
ioc->name, desc, ioc_status, desc1, pScsiReply->SCSIStatus,
@@ -654,16 +598,14 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
req_idx_MR = (mr != NULL) ?
le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx) : req_idx;
+
+ /* Special case, where already freed message frame is received from
+ * Firmware. It happens with Resetting IOC.
+ * Return immediately. Do not care
+ */
if ((req_idx != req_idx_MR) ||
- (mf->u.frame.linkage.arg1 == 0xdeadbeaf)) {
- printk(MYIOC_s_ERR_FMT "Received a mf that was already freed\n",
- ioc->name);
- printk (MYIOC_s_ERR_FMT
- "req_idx=%x req_idx_MR=%x mf=%p mr=%p sc=%p\n",
- ioc->name, req_idx, req_idx_MR, mf, mr,
- mptscsih_get_scsi_lookup(ioc, req_idx_MR));
+ (le32_to_cpu(mf->u.frame.linkage.arg1) == 0xdeadbeaf))
return 0;
- }
sc = mptscsih_getclear_scsi_lookup(ioc, req_idx);
if (sc == NULL) {
@@ -810,12 +752,16 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
*/
case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
- case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
/* Linux handles an unsolicited DID_RESET better
* than an unsolicited DID_ABORT.
*/
sc->result = DID_RESET << 16;
+ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+ if (ioc->bus_type == FC)
+ sc->result = DID_ERROR << 16;
+ else
+ sc->result = DID_RESET << 16;
break;
case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
@@ -992,9 +938,9 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
scsi_dma_unmap(sc);
sc->result = DID_RESET << 16;
sc->host_scribble = NULL;
- sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT
- "completing cmds: fw_channel %d, fw_id %d, sc=%p,"
- " mf = %p, idx=%x\n", ioc->name, channel, id, sc, mf, ii);
+ dtmprintk(ioc, sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT
+ "completing cmds: fw_channel %d, fw_id %d, sc=%p, mf = %p, "
+ "idx=%x\n", ioc->name, channel, id, sc, mf, ii));
sc->scsi_done(sc);
}
}
@@ -1053,9 +999,11 @@ mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
scsi_dma_unmap(sc);
sc->host_scribble = NULL;
sc->result = DID_NO_CONNECT << 16;
- sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT "completing cmds: fw_channel %d,"
- "fw_id %d, sc=%p, mf = %p, idx=%x\n", ioc->name, vdevice->vtarget->channel,
- vdevice->vtarget->id, sc, mf, ii);
+ dtmprintk(ioc, sdev_printk(KERN_INFO, sc->device,
+ MYIOC_s_FMT "completing cmds: fw_channel %d, "
+ "fw_id %d, sc=%p, mf = %p, idx=%x\n", ioc->name,
+ vdevice->vtarget->channel, vdevice->vtarget->id,
+ sc, mf, ii));
sc->scsi_done(sc);
spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
}
@@ -1346,7 +1294,6 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
MPT_FRAME_HDR *mf;
SCSIIORequest_t *pScsiReq;
VirtDevice *vdevice = SCpnt->device->hostdata;
- int lun;
u32 datalen;
u32 scsictl;
u32 scsidir;
@@ -1357,13 +1304,12 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
hd = shost_priv(SCpnt->device->host);
ioc = hd->ioc;
- lun = SCpnt->device->lun;
SCpnt->scsi_done = done;
dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "qcmd: SCpnt=%p, done()=%p\n",
ioc->name, SCpnt, done));
- if (hd->resetPending) {
+ if (ioc->taskmgmt_quiesce_io) {
dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
ioc->name, SCpnt));
return SCSI_MLQUEUE_HOST_BUSY;
@@ -1422,7 +1368,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
pScsiReq->CDBLength = SCpnt->cmd_len;
pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
pScsiReq->Reserved = 0;
- pScsiReq->MsgFlags = mpt_msg_flags();
+ pScsiReq->MsgFlags = mpt_msg_flags(ioc);
int_to_scsilun(SCpnt->device->lun, (struct scsi_lun *)pScsiReq->LUN);
pScsiReq->Control = cpu_to_le32(scsictl);
@@ -1448,7 +1394,8 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
*/
if (datalen == 0) {
/* Add a NULL SGE */
- mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
+ ioc->add_sge((char *)&pScsiReq->SGL,
+ MPT_SGE_FLAGS_SSIMPLE_READ | 0,
(dma_addr_t) -1);
} else {
/* Add a 32 or 64 bit SGE */
@@ -1528,8 +1475,8 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_TMHandler - Generic handler for SCSI Task Management.
- * @hd: Pointer to MPT SCSI HOST structure
+ * mptscsih_IssueTaskMgmt - Generic send Task Management function.
+ * @hd: Pointer to MPT_SCSI_HOST structure
* @type: Task Management type
* @channel: channel number for task management
* @id: Logical Target ID for reset (if appropriate)
@@ -1537,145 +1484,68 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
* @ctx2abort: Context for the task to be aborted (if appropriate)
* @timeout: timeout for task management control
*
- * Fall through to mpt_HardResetHandler if: not operational, too many
- * failed TM requests or handshake failure.
+ * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
+ * or a non-interrupt thread. In the former, must not call schedule().
*
- * Remark: Currently invoked from a non-interrupt thread (_bh).
+ * Not all fields are meaningfull for all task types.
*
- * Note: With old EH code, at most 1 SCSI TaskMgmt function per IOC
- * will be active.
+ * Returns 0 for SUCCESS, or FAILED.
*
- * Returns 0 for SUCCESS, or %FAILED.
**/
int
-mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout)
+mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun,
+ int ctx2abort, ulong timeout)
{
- MPT_ADAPTER *ioc;
- int rc = -1;
+ MPT_FRAME_HDR *mf;
+ SCSITaskMgmt_t *pScsiTm;
+ int ii;
+ int retval;
+ MPT_ADAPTER *ioc = hd->ioc;
+ unsigned long timeleft;
+ u8 issue_hard_reset;
u32 ioc_raw_state;
- unsigned long flags;
-
- ioc = hd->ioc;
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler Entered!\n", ioc->name));
-
- // SJR - CHECKME - Can we avoid this here?
- // (mpt_HardResetHandler has this check...)
- spin_lock_irqsave(&ioc->diagLock, flags);
- if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
- spin_unlock_irqrestore(&ioc->diagLock, flags);
- return FAILED;
- }
- spin_unlock_irqrestore(&ioc->diagLock, flags);
-
- /* Wait a fixed amount of time for the TM pending flag to be cleared.
- * If we time out and not bus reset, then we return a FAILED status
- * to the caller.
- * The call to mptscsih_tm_pending_wait() will set the pending flag
- * if we are
- * successful. Otherwise, reload the FW.
- */
- if (mptscsih_tm_pending_wait(hd) == FAILED) {
- if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler abort: "
- "Timed out waiting for last TM (%d) to complete! \n",
- ioc->name, hd->tmPending));
- return FAILED;
- } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler target "
- "reset: Timed out waiting for last TM (%d) "
- "to complete! \n", ioc->name,
- hd->tmPending));
- return FAILED;
- } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler bus reset: "
- "Timed out waiting for last TM (%d) to complete! \n",
- ioc->name, hd->tmPending));
- return FAILED;
- }
- } else {
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending |= (1 << type);
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- }
+ unsigned long time_count;
+ issue_hard_reset = 0;
ioc_raw_state = mpt_GetIocState(ioc, 0);
if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
printk(MYIOC_s_WARN_FMT
- "TM Handler for type=%x: IOC Not operational (0x%x)!\n",
+ "TaskMgmt type=%x: IOC Not operational (0x%x)!\n",
ioc->name, type, ioc_raw_state);
- printk(MYIOC_s_WARN_FMT " Issuing HardReset!!\n", ioc->name);
+ printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
+ ioc->name, __func__);
if (mpt_HardResetHandler(ioc, CAN_SLEEP) < 0)
- printk(MYIOC_s_WARN_FMT "TMHandler: HardReset "
+ printk(MYIOC_s_WARN_FMT "TaskMgmt HardReset "
"FAILED!!\n", ioc->name);
- return FAILED;
+ return 0;
}
if (ioc_raw_state & MPI_DOORBELL_ACTIVE) {
printk(MYIOC_s_WARN_FMT
- "TM Handler for type=%x: ioc_state: "
+ "TaskMgmt type=%x: ioc_state: "
"DOORBELL_ACTIVE (0x%x)!\n",
ioc->name, type, ioc_raw_state);
return FAILED;
}
- /* Isse the Task Mgmt request.
- */
- if (hd->hard_resets < -1)
- hd->hard_resets++;
-
- rc = mptscsih_IssueTaskMgmt(hd, type, channel, id, lun,
- ctx2abort, timeout);
- if (rc)
- printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n",
- ioc->name);
- else
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Issue of TaskMgmt Successful!\n",
- ioc->name));
-
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "TMHandler rc = %d!\n", ioc->name, rc));
-
- return rc;
-}
-
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_IssueTaskMgmt - Generic send Task Management function.
- * @hd: Pointer to MPT_SCSI_HOST structure
- * @type: Task Management type
- * @channel: channel number for task management
- * @id: Logical Target ID for reset (if appropriate)
- * @lun: Logical Unit for reset (if appropriate)
- * @ctx2abort: Context for the task to be aborted (if appropriate)
- * @timeout: timeout for task management control
- *
- * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
- * or a non-interrupt thread. In the former, must not call schedule().
- *
- * Not all fields are meaningfull for all task types.
- *
- * Returns 0 for SUCCESS, or FAILED.
- *
- **/
-static int
-mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout)
-{
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
- int ii;
- int retval;
- MPT_ADAPTER *ioc = hd->ioc;
+ mutex_lock(&ioc->taskmgmt_cmds.mutex);
+ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+ mf = NULL;
+ retval = FAILED;
+ goto out;
+ }
/* Return Fail to calling function if no message frames available.
*/
if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
- ioc->name));
- return FAILED;
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "TaskMgmt no msg frames!!\n", ioc->name));
+ retval = FAILED;
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ goto out;
}
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt request @ %p\n",
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
ioc->name, mf));
/* Format the Request
@@ -1699,11 +1569,14 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
pScsiTm->TaskMsgContext = ctx2abort;
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt: ctx2abort (0x%08x) "
- "type=%d\n", ioc->name, ctx2abort, type));
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt: ctx2abort (0x%08x) "
+ "task_type = 0x%02X, timeout = %ld\n", ioc->name, ctx2abort,
+ type, timeout));
DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)pScsiTm);
+ INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ time_count = jiffies;
if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
(ioc->facts.MsgVersion >= MPI_VERSION_01_05))
mpt_put_msg_frame_hi_pri(ioc->TaskCtx, ioc, mf);
@@ -1711,47 +1584,50 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
retval = mpt_send_handshake_request(ioc->TaskCtx, ioc,
sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP);
if (retval) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "send_handshake FAILED!"
- " (hd %p, ioc %p, mf %p, rc=%d) \n", ioc->name, hd,
- ioc, mf, retval));
- goto fail_out;
+ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "TaskMgmt handshake FAILED!(mf=%p, rc=%d) \n",
+ ioc->name, mf, retval));
+ mpt_free_msg_frame(ioc, mf);
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ goto out;
}
}
- if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "task management request TIMED OUT!"
- " (hd %p, ioc %p, mf %p) \n", ioc->name, hd,
- ioc, mf));
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
- ioc->name));
- retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "rc=%d \n",
- ioc->name, retval));
- goto fail_out;
+ timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
+ timeout*HZ);
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ retval = FAILED;
+ dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
+ "TaskMgmt TIMED OUT!(mf=%p)\n", ioc->name, mf));
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out;
+ issue_hard_reset = 1;
+ goto out;
}
- /*
- * Handle success case, see if theres a non-zero ioc_status.
- */
- if (hd->tm_iocstatus == MPI_IOCSTATUS_SUCCESS ||
- hd->tm_iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
- hd->tm_iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED)
- retval = 0;
- else
- retval = FAILED;
+ retval = mptscsih_taskmgmt_reply(ioc, type,
+ (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply);
- return retval;
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt completed (%d seconds)\n",
+ ioc->name, jiffies_to_msecs(jiffies - time_count)/1000));
- fail_out:
+ out:
- /*
- * Free task management mf, and corresponding tm flags
- */
- mpt_free_msg_frame(ioc, mf);
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- return FAILED;
+ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+ if (issue_hard_reset) {
+ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+ }
+
+ retval = (retval == 0) ? 0 : FAILED;
+ mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+ return retval;
}
+EXPORT_SYMBOL(mptscsih_IssueTaskMgmt);
static int
mptscsih_get_tm_timeout(MPT_ADAPTER *ioc)
@@ -1838,13 +1714,8 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
goto out;
}
- if (hd->resetPending) {
- retval = FAILED;
- goto out;
- }
-
- if (hd->timeouts < -1)
- hd->timeouts++;
+ if (ioc->timeouts < -1)
+ ioc->timeouts++;
if (mpt_fwfault_debug)
mpt_halt_firmware(ioc);
@@ -1861,22 +1732,30 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
hd->abortSCpnt = SCpnt;
- retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
- vdevice->vtarget->channel, vdevice->vtarget->id, vdevice->lun,
- ctx2abort, mptscsih_get_tm_timeout(ioc));
+ retval = mptscsih_IssueTaskMgmt(hd,
+ MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ vdevice->vtarget->channel,
+ vdevice->vtarget->id, vdevice->lun,
+ ctx2abort, mptscsih_get_tm_timeout(ioc));
if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx &&
- SCpnt->serial_number == sn)
+ SCpnt->serial_number == sn) {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "task abort: command still in active list! (sc=%p)\n",
+ ioc->name, SCpnt));
retval = FAILED;
+ } else {
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "task abort: command cleared from active list! (sc=%p)\n",
+ ioc->name, SCpnt));
+ retval = SUCCESS;
+ }
out:
printk(MYIOC_s_INFO_FMT "task abort: %s (sc=%p)\n",
- ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
+ ioc->name, ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), SCpnt);
- if (retval == 0)
- return SUCCESS;
- else
- return FAILED;
+ return retval;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -1909,14 +1788,9 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
ioc->name, SCpnt);
scsi_print_command(SCpnt);
- if (hd->resetPending) {
- retval = FAILED;
- goto out;
- }
-
vdevice = SCpnt->device->hostdata;
if (!vdevice || !vdevice->vtarget) {
- retval = 0;
+ retval = SUCCESS;
goto out;
}
@@ -1927,9 +1801,11 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
goto out;
}
- retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
- vdevice->vtarget->channel, vdevice->vtarget->id, 0, 0,
- mptscsih_get_tm_timeout(ioc));
+ retval = mptscsih_IssueTaskMgmt(hd,
+ MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
+ vdevice->vtarget->channel,
+ vdevice->vtarget->id, 0, 0,
+ mptscsih_get_tm_timeout(ioc));
out:
printk (MYIOC_s_INFO_FMT "target reset: %s (sc=%p)\n",
@@ -1972,12 +1848,16 @@ mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
ioc->name, SCpnt);
scsi_print_command(SCpnt);
- if (hd->timeouts < -1)
- hd->timeouts++;
+ if (ioc->timeouts < -1)
+ ioc->timeouts++;
vdevice = SCpnt->device->hostdata;
- retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
- vdevice->vtarget->channel, 0, 0, 0, mptscsih_get_tm_timeout(ioc));
+ if (!vdevice || !vdevice->vtarget)
+ return SUCCESS;
+ retval = mptscsih_IssueTaskMgmt(hd,
+ MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+ vdevice->vtarget->channel, 0, 0, 0,
+ mptscsih_get_tm_timeout(ioc));
printk(MYIOC_s_INFO_FMT "bus reset: %s (sc=%p)\n",
ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
@@ -2001,8 +1881,9 @@ int
mptscsih_host_reset(struct scsi_cmnd *SCpnt)
{
MPT_SCSI_HOST * hd;
- int retval;
+ int status = SUCCESS;
MPT_ADAPTER *ioc;
+ int retval;
/* If we can't locate the host to reset, then we failed. */
if ((hd = shost_priv(SCpnt->device->host)) == NULL){
@@ -2021,86 +1902,71 @@ mptscsih_host_reset(struct scsi_cmnd *SCpnt)
/* If our attempts to reset the host failed, then return a failed
* status. The host will be taken off line by the SCSI mid-layer.
*/
- if (mpt_HardResetHandler(ioc, CAN_SLEEP) < 0) {
- retval = FAILED;
- } else {
- /* Make sure TM pending is cleared and TM state is set to
- * NONE.
- */
- retval = 0;
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- }
+ retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
+ if (retval < 0)
+ status = FAILED;
+ else
+ status = SUCCESS;
printk(MYIOC_s_INFO_FMT "host reset: %s (sc=%p)\n",
ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
- return retval;
+ return status;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_tm_pending_wait - wait for pending task management request to complete
- * @hd: Pointer to MPT host structure.
- *
- * Returns {SUCCESS,FAILED}.
- */
static int
-mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
+mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type,
+ SCSITaskMgmtReply_t *pScsiTmReply)
{
- unsigned long flags;
- int loop_count = 4 * 10; /* Wait 10 seconds */
- int status = FAILED;
- MPT_ADAPTER *ioc = hd->ioc;
+ u16 iocstatus;
+ u32 termination_count;
+ int retval;
- do {
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- if (hd->tmState == TM_STATE_NONE) {
- hd->tmState = TM_STATE_IN_PROGRESS;
- hd->tmPending = 1;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- status = SUCCESS;
- break;
- }
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- msleep(250);
- } while (--loop_count);
+ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+ retval = FAILED;
+ goto out;
+ }
- return status;
-}
+ DBG_DUMP_TM_REPLY_FRAME(ioc, (u32 *)pScsiTmReply);
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_tm_wait_for_completion - wait for completion of TM task
- * @hd: Pointer to MPT host structure.
- * @timeout: timeout value
- *
- * Returns {SUCCESS,FAILED}.
- */
-static int
-mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
-{
- unsigned long flags;
- int loop_count = 4 * timeout;
- int status = FAILED;
- MPT_ADAPTER *ioc = hd->ioc;
+ iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ termination_count = le32_to_cpu(pScsiTmReply->TerminationCount);
- do {
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- if(hd->tmPending == 0) {
- status = SUCCESS;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- break;
- }
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- msleep(250);
- } while (--loop_count);
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt fw_channel = %d, fw_id = %d, task_type = 0x%02X,\n"
+ "\tiocstatus = 0x%04X, loginfo = 0x%08X, response_code = 0x%02X,\n"
+ "\tterm_cmnds = %d\n", ioc->name, pScsiTmReply->Bus,
+ pScsiTmReply->TargetID, type, le16_to_cpu(pScsiTmReply->IOCStatus),
+ le32_to_cpu(pScsiTmReply->IOCLogInfo), pScsiTmReply->ResponseCode,
+ termination_count));
- return status;
+ if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
+ pScsiTmReply->ResponseCode)
+ mptscsih_taskmgmt_response_code(ioc,
+ pScsiTmReply->ResponseCode);
+
+ if (iocstatus == MPI_IOCSTATUS_SUCCESS) {
+ retval = 0;
+ goto out;
+ }
+
+ retval = FAILED;
+ if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ if (termination_count == 1)
+ retval = 0;
+ goto out;
+ }
+
+ if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
+ iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED)
+ retval = 0;
+
+ out:
+ return retval;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static void
+void
mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
{
char *desc;
@@ -2134,6 +2000,7 @@ mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n",
ioc->name, response_code, desc);
}
+EXPORT_SYMBOL(mptscsih_taskmgmt_response_code);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
@@ -2150,97 +2017,28 @@ mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
* Returns 1 indicating alloc'd request frame ptr should be freed.
**/
int
-mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf,
+ MPT_FRAME_HDR *mr)
{
- SCSITaskMgmtReply_t *pScsiTmReply;
- SCSITaskMgmt_t *pScsiTmReq;
- MPT_SCSI_HOST *hd;
- unsigned long flags;
- u16 iocstatus;
- u8 tmType;
- u32 termination_count;
-
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
- ioc->name, mf, mr));
- if (!ioc->sh) {
- dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
- "TaskMgmt Complete: NULL Scsi Host Ptr\n", ioc->name));
- return 1;
- }
-
- if (mr == NULL) {
- dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
- "ERROR! TaskMgmt Reply: NULL Request %p\n", ioc->name, mf));
- return 1;
- }
-
- hd = shost_priv(ioc->sh);
- pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
- pScsiTmReq = (SCSITaskMgmt_t*)mf;
- tmType = pScsiTmReq->TaskType;
- iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- termination_count = le32_to_cpu(pScsiTmReply->TerminationCount);
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "TaskMgmt completed (mf=%p, mr=%p)\n", ioc->name, mf, mr));
- if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
- pScsiTmReply->ResponseCode)
- mptscsih_taskmgmt_response_code(ioc,
- pScsiTmReply->ResponseCode);
- DBG_DUMP_TM_REPLY_FRAME(ioc, (u32 *)pScsiTmReply);
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
-#ifdef CONFIG_FUSION_LOGGING
- if ((ioc->debug_level & MPT_DEBUG_REPLY) ||
- (ioc->debug_level & MPT_DEBUG_TM ))
- printk("%s: ha=%d [%d:%d:0] task_type=0x%02X "
- "iocstatus=0x%04X\n\tloginfo=0x%08X response_code=0x%02X "
- "term_cmnds=%d\n", __func__, ioc->id, pScsiTmReply->Bus,
- pScsiTmReply->TargetID, pScsiTmReq->TaskType,
- le16_to_cpu(pScsiTmReply->IOCStatus),
- le32_to_cpu(pScsiTmReply->IOCLogInfo),pScsiTmReply->ResponseCode,
- le32_to_cpu(pScsiTmReply->TerminationCount));
-#endif
- if (!iocstatus) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT " TaskMgmt SUCCESS\n", ioc->name));
- hd->abortSCpnt = NULL;
+ if (!mr)
goto out;
- }
-
- /* Error? (anything non-zero?) */
-
- /* clear flags and continue.
- */
- switch (tmType) {
-
- case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
- if (termination_count == 1)
- iocstatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED;
- hd->abortSCpnt = NULL;
- break;
-
- case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS:
-
- /* If an internal command is present
- * or the TM failed - reload the FW.
- * FC FW may respond FAILED to an ABORT
- */
- if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED ||
- hd->cmdPtr)
- if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
- printk(MYIOC_s_WARN_FMT " Firmware Reload FAILED!!\n", ioc->name);
- break;
-
- case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
- default:
- break;
- }
+ ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ memcpy(ioc->taskmgmt_cmds.reply, mr,
+ min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
out:
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- hd->tm_iocstatus = iocstatus;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
- return 1;
+ if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ mpt_clear_taskmgmt_in_progress_flag(ioc);
+ ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->taskmgmt_cmds.done);
+ return 1;
+ }
+ return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -2290,8 +2088,10 @@ int
mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id)
{
struct inactive_raid_component_info *component_info;
- int i;
+ int i, j;
+ RaidPhysDiskPage1_t *phys_disk;
int rc = 0;
+ int num_paths;
if (!ioc->raid_data.pIocPg3)
goto out;
@@ -2303,6 +2103,45 @@ mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id)
}
}
+ if (ioc->bus_type != SAS)
+ goto out;
+
+ /*
+ * Check if dual path
+ */
+ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
+ num_paths = mpt_raid_phys_disk_get_num_paths(ioc,
+ ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum);
+ if (num_paths < 2)
+ continue;
+ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+ (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+ if (!phys_disk)
+ continue;
+ if ((mpt_raid_phys_disk_pg1(ioc,
+ ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum,
+ phys_disk))) {
+ kfree(phys_disk);
+ continue;
+ }
+ for (j = 0; j < num_paths; j++) {
+ if ((phys_disk->Path[j].Flags &
+ MPI_RAID_PHYSDISK1_FLAG_INVALID))
+ continue;
+ if ((phys_disk->Path[j].Flags &
+ MPI_RAID_PHYSDISK1_FLAG_BROKEN))
+ continue;
+ if ((id == phys_disk->Path[j].PhysDiskID) &&
+ (channel == phys_disk->Path[j].PhysDiskBus)) {
+ rc = 1;
+ kfree(phys_disk);
+ goto out;
+ }
+ }
+ kfree(phys_disk);
+ }
+
+
/*
* Check inactive list for matching phys disks
*/
@@ -2327,8 +2166,10 @@ u8
mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
{
struct inactive_raid_component_info *component_info;
- int i;
+ int i, j;
+ RaidPhysDiskPage1_t *phys_disk;
int rc = -ENXIO;
+ int num_paths;
if (!ioc->raid_data.pIocPg3)
goto out;
@@ -2340,6 +2181,44 @@ mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
}
}
+ if (ioc->bus_type != SAS)
+ goto out;
+
+ /*
+ * Check if dual path
+ */
+ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
+ num_paths = mpt_raid_phys_disk_get_num_paths(ioc,
+ ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum);
+ if (num_paths < 2)
+ continue;
+ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+ (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+ if (!phys_disk)
+ continue;
+ if ((mpt_raid_phys_disk_pg1(ioc,
+ ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum,
+ phys_disk))) {
+ kfree(phys_disk);
+ continue;
+ }
+ for (j = 0; j < num_paths; j++) {
+ if ((phys_disk->Path[j].Flags &
+ MPI_RAID_PHYSDISK1_FLAG_INVALID))
+ continue;
+ if ((phys_disk->Path[j].Flags &
+ MPI_RAID_PHYSDISK1_FLAG_BROKEN))
+ continue;
+ if ((id == phys_disk->Path[j].PhysDiskID) &&
+ (channel == phys_disk->Path[j].PhysDiskBus)) {
+ rc = phys_disk->PhysDiskNum;
+ kfree(phys_disk);
+ goto out;
+ }
+ }
+ kfree(phys_disk);
+ }
+
/*
* Check inactive list for matching phys disks
*/
@@ -2457,7 +2336,6 @@ mptscsih_slave_configure(struct scsi_device *sdev)
sdev->ppr, sdev->inquiry_len));
vdevice->configured_lun = 1;
- mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"Queue depth=%d, tflags=%x\n",
@@ -2469,6 +2347,7 @@ mptscsih_slave_configure(struct scsi_device *sdev)
ioc->name, vtarget->negoFlags, vtarget->maxOffset,
vtarget->minSyncFactor));
+ mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"tagged %d, simple %d, ordered %d\n",
ioc->name,sdev->tagged_supported, sdev->simple_tags,
@@ -2542,15 +2421,13 @@ mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR
}
/**
- * mptscsih_get_scsi_lookup
+ * mptscsih_get_scsi_lookup - retrieves scmd entry
* @ioc: Pointer to MPT_ADAPTER structure
* @i: index into the array
*
- * retrieves scmd entry from ScsiLookup[] array list
- *
* Returns the scsi_cmd pointer
- **/
-static struct scsi_cmnd *
+ */
+struct scsi_cmnd *
mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i)
{
unsigned long flags;
@@ -2562,15 +2439,15 @@ mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i)
return scmd;
}
+EXPORT_SYMBOL(mptscsih_get_scsi_lookup);
/**
- * mptscsih_getclear_scsi_lookup
+ * mptscsih_getclear_scsi_lookup - retrieves and clears scmd entry from ScsiLookup[] array list
* @ioc: Pointer to MPT_ADAPTER structure
* @i: index into the array
*
- * retrieves and clears scmd entry from ScsiLookup[] array list
- *
* Returns the scsi_cmd pointer
+ *
**/
static struct scsi_cmnd *
mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i)
@@ -2635,94 +2512,33 @@ int
mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
{
MPT_SCSI_HOST *hd;
- unsigned long flags;
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- ": IOC %s_reset routed to SCSI host driver!\n",
- ioc->name, reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
- reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
- /* If a FW reload request arrives after base installed but
- * before all scsi hosts have been attached, then an alt_ioc
- * may have a NULL sh pointer.
- */
if (ioc->sh == NULL || shost_priv(ioc->sh) == NULL)
return 0;
- else
- hd = shost_priv(ioc->sh);
-
- if (reset_phase == MPT_IOC_SETUP_RESET) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Setup-Diag Reset\n", ioc->name));
-
- /* Clean Up:
- * 1. Set Hard Reset Pending Flag
- * All new commands go to doneQ
- */
- hd->resetPending = 1;
-
- } else if (reset_phase == MPT_IOC_PRE_RESET) {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Pre-Diag Reset\n", ioc->name));
- /* 2. Flush running commands
- * Clean ScsiLookup (and associated memory)
- * AND clean mytaskQ
- */
-
- /* 2b. Reply to OS all known outstanding I/O commands.
- */
+ hd = shost_priv(ioc->sh);
+ switch (reset_phase) {
+ case MPT_IOC_SETUP_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+ break;
+ case MPT_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
mptscsih_flush_running_cmds(hd);
-
- /* 2c. If there was an internal command that
- * has not completed, configuration or io request,
- * free these resources.
- */
- if (hd->cmdPtr) {
- del_timer(&hd->timer);
- mpt_free_msg_frame(ioc, hd->cmdPtr);
- }
-
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Pre-Reset complete.\n", ioc->name));
-
- } else {
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Post-Diag Reset\n", ioc->name));
-
- /* Once a FW reload begins, all new OS commands are
- * redirected to the doneQ w/ a reset status.
- * Init all control structures.
- */
-
- /* 2. Chain Buffer initialization
- */
-
- /* 4. Renegotiate to all devices, if SPI
- */
-
- /* 5. Enable new commands to be posted
- */
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- hd->resetPending = 0;
- hd->tmState = TM_STATE_NONE;
-
- /* 6. If there was an internal command,
- * wake this process up.
- */
- if (hd->cmdPtr) {
- /*
- * Wake up the original calling thread
- */
- hd->pLocal = &hd->localReply;
- hd->pLocal->completion = MPT_SCANDV_DID_RESET;
- hd->scandv_wait_done = 1;
- wake_up(&hd->scandv_waitq);
- hd->cmdPtr = NULL;
+ break;
+ case MPT_IOC_POST_RESET:
+ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+ if (ioc->internal_cmds.status & MPT_MGMT_STATUS_PENDING) {
+ ioc->internal_cmds.status |=
+ MPT_MGMT_STATUS_DID_IOCRESET;
+ complete(&ioc->internal_cmds.done);
}
-
- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Post-Reset complete.\n", ioc->name));
-
+ break;
+ default:
+ break;
}
-
return 1; /* currently means nothing really */
}
@@ -2730,55 +2546,16 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
int
mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
{
- MPT_SCSI_HOST *hd;
u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
- devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
- ioc->name, event));
-
- if (ioc->sh == NULL ||
- ((hd = shost_priv(ioc->sh)) == NULL))
- return 1;
-
- switch (event) {
- case MPI_EVENT_UNIT_ATTENTION: /* 03 */
- /* FIXME! */
- break;
- case MPI_EVENT_IOC_BUS_RESET: /* 04 */
- case MPI_EVENT_EXT_BUS_RESET: /* 05 */
- if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
- hd->soft_resets++;
- break;
- case MPI_EVENT_LOGOUT: /* 09 */
- /* FIXME! */
- break;
-
- case MPI_EVENT_RESCAN: /* 06 */
- break;
-
- /*
- * CHECKME! Don't think we need to do
- * anything for these, but...
- */
- case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
- case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
- /*
- * CHECKME! Falling thru...
- */
- break;
-
- case MPI_EVENT_INTEGRATED_RAID: /* 0B */
- break;
+ devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "MPT event (=%02Xh) routed to SCSI host driver!\n",
+ ioc->name, event));
- case MPI_EVENT_NONE: /* 00 */
- case MPI_EVENT_LOG_DATA: /* 01 */
- case MPI_EVENT_STATE_CHANGE: /* 02 */
- case MPI_EVENT_EVENT_CHANGE: /* 0A */
- default:
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": Ignoring event (=%02Xh)\n",
- ioc->name, event));
- break;
- }
+ if ((event == MPI_EVENT_IOC_BUS_RESET ||
+ event == MPI_EVENT_EXT_BUS_RESET) &&
+ (ioc->bus_type == SPI) && (ioc->soft_resets < -1))
+ ioc->soft_resets++;
return 1; /* currently means nothing really */
}
@@ -2809,153 +2586,44 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
* Used ONLY for DV and other internal commands.
*/
int
-mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+ MPT_FRAME_HDR *reply)
{
- MPT_SCSI_HOST *hd;
SCSIIORequest_t *pReq;
- int completionCode;
+ SCSIIOReply_t *pReply;
+ u8 cmd;
u16 req_idx;
+ u8 *sense_data;
+ int sz;
- hd = shost_priv(ioc->sh);
-
- if ((mf == NULL) ||
- (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
- printk(MYIOC_s_ERR_FMT
- "ScanDvComplete, %s req frame ptr! (=%p)\n",
- ioc->name, mf?"BAD":"NULL", (void *) mf);
- goto wakeup;
- }
-
- del_timer(&hd->timer);
- req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- mptscsih_set_scsi_lookup(ioc, req_idx, NULL);
- pReq = (SCSIIORequest_t *) mf;
+ ioc->internal_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+ ioc->internal_cmds.completion_code = MPT_SCANDV_GOOD;
+ if (!reply)
+ goto out;
- if (mf != hd->cmdPtr) {
- printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
- ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
+ pReply = (SCSIIOReply_t *) reply;
+ pReq = (SCSIIORequest_t *) req;
+ ioc->internal_cmds.completion_code =
+ mptscsih_get_completion_code(ioc, req, reply);
+ ioc->internal_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+ memcpy(ioc->internal_cmds.reply, reply,
+ min(MPT_DEFAULT_FRAME_SIZE, 4 * reply->u.reply.MsgLength));
+ cmd = reply->u.hdr.Function;
+ if (((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+ (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) &&
+ (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID)) {
+ req_idx = le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sense_data = ((u8 *)ioc->sense_buf_pool +
+ (req_idx * MPT_SENSE_BUFFER_ALLOC));
+ sz = min_t(int, pReq->SenseBufferLength,
+ MPT_SENSE_BUFFER_ALLOC);
+ memcpy(ioc->internal_cmds.sense, sense_data, sz);
}
- hd->cmdPtr = NULL;
-
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
- ioc->name, mf, mr, req_idx));
-
- hd->pLocal = &hd->localReply;
- hd->pLocal->scsiStatus = 0;
-
- /* If target struct exists, clear sense valid flag.
- */
- if (mr == NULL) {
- completionCode = MPT_SCANDV_GOOD;
- } else {
- SCSIIOReply_t *pReply;
- u16 status;
- u8 scsi_status;
-
- pReply = (SCSIIOReply_t *) mr;
-
- status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- scsi_status = pReply->SCSIStatus;
-
-
- switch(status) {
-
- case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
- completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
- break;
-
- case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
- case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
- case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
- case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
- completionCode = MPT_SCANDV_DID_RESET;
- break;
-
- case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
- case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
- case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
- if (pReply->Function == MPI_FUNCTION_CONFIG) {
- ConfigReply_t *pr = (ConfigReply_t *)mr;
- completionCode = MPT_SCANDV_GOOD;
- hd->pLocal->header.PageVersion = pr->Header.PageVersion;
- hd->pLocal->header.PageLength = pr->Header.PageLength;
- hd->pLocal->header.PageNumber = pr->Header.PageNumber;
- hd->pLocal->header.PageType = pr->Header.PageType;
-
- } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
- /* If the RAID Volume request is successful,
- * return GOOD, else indicate that
- * some type of error occurred.
- */
- MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
- if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
- completionCode = MPT_SCANDV_GOOD;
- else
- completionCode = MPT_SCANDV_SOME_ERROR;
- memcpy(hd->pLocal->sense, pr, sizeof(hd->pLocal->sense));
-
- } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- u8 *sense_data;
- int sz;
-
- /* save sense data in global structure
- */
- completionCode = MPT_SCANDV_SENSE;
- hd->pLocal->scsiStatus = scsi_status;
- sense_data = ((u8 *)ioc->sense_buf_pool +
- (req_idx * MPT_SENSE_BUFFER_ALLOC));
-
- sz = min_t(int, pReq->SenseBufferLength,
- SCSI_STD_SENSE_BYTES);
- memcpy(hd->pLocal->sense, sense_data, sz);
-
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT " Check Condition, sense ptr %p\n",
- ioc->name, sense_data));
- } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
- if (pReq->CDB[0] == INQUIRY)
- completionCode = MPT_SCANDV_ISSUE_SENSE;
- else
- completionCode = MPT_SCANDV_DID_RESET;
- }
- else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
- completionCode = MPT_SCANDV_DID_RESET;
- else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
- completionCode = MPT_SCANDV_DID_RESET;
- else {
- completionCode = MPT_SCANDV_GOOD;
- hd->pLocal->scsiStatus = scsi_status;
- }
- break;
-
- case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
- if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
- completionCode = MPT_SCANDV_DID_RESET;
- else
- completionCode = MPT_SCANDV_SOME_ERROR;
- break;
-
- default:
- completionCode = MPT_SCANDV_SOME_ERROR;
- break;
-
- } /* switch(status) */
-
- } /* end of address reply case */
-
- hd->pLocal->completion = completionCode;
-
- /* MF and RF are freed in mpt_interrupt
- */
-wakeup:
- /* Free Chain buffers (will never chain) in scan or dv */
- //mptscsih_freeChainBuffers(ioc, req_idx);
-
- /*
- * Wake up the original calling thread
- */
- hd->scandv_wait_done = 1;
- wake_up(&hd->scandv_waitq);
-
+ out:
+ if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_PENDING))
+ return 0;
+ ioc->internal_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+ complete(&ioc->internal_cmds.done);
return 1;
}
@@ -3004,6 +2672,95 @@ mptscsih_timer_expired(unsigned long data)
return;
}
+/**
+ * mptscsih_get_completion_code -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @reply:
+ * @cmd:
+ *
+ **/
+static int
+mptscsih_get_completion_code(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+ MPT_FRAME_HDR *reply)
+{
+ SCSIIOReply_t *pReply;
+ MpiRaidActionReply_t *pr;
+ u8 scsi_status;
+ u16 status;
+ int completion_code;
+
+ pReply = (SCSIIOReply_t *)reply;
+ status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ scsi_status = pReply->SCSIStatus;
+
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh,"
+ "IOCLogInfo=%08xh\n", ioc->name, status, pReply->SCSIState,
+ scsi_status, le32_to_cpu(pReply->IOCLogInfo)));
+
+ switch (status) {
+
+ case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
+ completion_code = MPT_SCANDV_SELECTION_TIMEOUT;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
+ case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
+ case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
+ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+ completion_code = MPT_SCANDV_DID_RESET;
+ break;
+
+ case MPI_IOCSTATUS_BUSY:
+ case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ completion_code = MPT_SCANDV_BUSY;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
+ case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
+ case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
+ if (pReply->Function == MPI_FUNCTION_CONFIG) {
+ completion_code = MPT_SCANDV_GOOD;
+ } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
+ pr = (MpiRaidActionReply_t *)reply;
+ if (le16_to_cpu(pr->ActionStatus) ==
+ MPI_RAID_ACTION_ASTATUS_SUCCESS)
+ completion_code = MPT_SCANDV_GOOD;
+ else
+ completion_code = MPT_SCANDV_SOME_ERROR;
+ } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID)
+ completion_code = MPT_SCANDV_SENSE;
+ else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
+ if (req->u.scsireq.CDB[0] == INQUIRY)
+ completion_code = MPT_SCANDV_ISSUE_SENSE;
+ else
+ completion_code = MPT_SCANDV_DID_RESET;
+ } else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
+ completion_code = MPT_SCANDV_DID_RESET;
+ else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
+ completion_code = MPT_SCANDV_DID_RESET;
+ else if (scsi_status == MPI_SCSI_STATUS_BUSY)
+ completion_code = MPT_SCANDV_BUSY;
+ else
+ completion_code = MPT_SCANDV_GOOD;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
+ if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
+ completion_code = MPT_SCANDV_DID_RESET;
+ else
+ completion_code = MPT_SCANDV_SOME_ERROR;
+ break;
+ default:
+ completion_code = MPT_SCANDV_SOME_ERROR;
+ break;
+
+ } /* switch(status) */
+
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ " completionCode set to %08xh\n", ioc->name, completion_code));
+ return completion_code;
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
@@ -3030,22 +2787,27 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
{
MPT_FRAME_HDR *mf;
SCSIIORequest_t *pScsiReq;
- SCSIIORequest_t ReqCopy;
int my_idx, ii, dir;
- int rc, cmdTimeout;
- int in_isr;
+ int timeout;
char cmdLen;
char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- char cmd = io->cmd;
- MPT_ADAPTER *ioc = hd->ioc;
+ u8 cmd = io->cmd;
+ MPT_ADAPTER *ioc = hd->ioc;
+ int ret = 0;
+ unsigned long timeleft;
+ unsigned long flags;
- in_isr = in_interrupt();
- if (in_isr) {
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Internal SCSI IO request not allowed in ISR context!\n",
- ioc->name));
- return -EPERM;
+ /* don't send internal command during diag reset */
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: busy with host reset\n", ioc->name, __func__));
+ return MPT_SCANDV_BUSY;
}
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ mutex_lock(&ioc->internal_cmds.mutex);
/* Set command specific information
*/
@@ -3055,13 +2817,13 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
dir = MPI_SCSIIO_CONTROL_READ;
CDB[0] = cmd;
CDB[4] = io->size;
- cmdTimeout = 10;
+ timeout = 10;
break;
case TEST_UNIT_READY:
cmdLen = 6;
dir = MPI_SCSIIO_CONTROL_READ;
- cmdTimeout = 10;
+ timeout = 10;
break;
case START_STOP:
@@ -3069,7 +2831,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
dir = MPI_SCSIIO_CONTROL_READ;
CDB[0] = cmd;
CDB[4] = 1; /*Spin up the disk */
- cmdTimeout = 15;
+ timeout = 15;
break;
case REQUEST_SENSE:
@@ -3077,7 +2839,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
CDB[0] = cmd;
CDB[4] = io->size;
dir = MPI_SCSIIO_CONTROL_READ;
- cmdTimeout = 10;
+ timeout = 10;
break;
case READ_BUFFER:
@@ -3096,7 +2858,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
CDB[6] = (io->size >> 16) & 0xFF;
CDB[7] = (io->size >> 8) & 0xFF;
CDB[8] = io->size & 0xFF;
- cmdTimeout = 10;
+ timeout = 10;
break;
case WRITE_BUFFER:
@@ -3111,21 +2873,21 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
CDB[6] = (io->size >> 16) & 0xFF;
CDB[7] = (io->size >> 8) & 0xFF;
CDB[8] = io->size & 0xFF;
- cmdTimeout = 10;
+ timeout = 10;
break;
case RESERVE:
cmdLen = 6;
dir = MPI_SCSIIO_CONTROL_READ;
CDB[0] = cmd;
- cmdTimeout = 10;
+ timeout = 10;
break;
case RELEASE:
cmdLen = 6;
dir = MPI_SCSIIO_CONTROL_READ;
CDB[0] = cmd;
- cmdTimeout = 10;
+ timeout = 10;
break;
case SYNCHRONIZE_CACHE:
@@ -3133,20 +2895,23 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
dir = MPI_SCSIIO_CONTROL_READ;
CDB[0] = cmd;
// CDB[1] = 0x02; /* set immediate bit */
- cmdTimeout = 10;
+ timeout = 10;
break;
default:
/* Error Case */
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
/* Get and Populate a free Frame
+ * MsgContext set in mpt_get_msg_frame call
*/
if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) {
- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "No msg frames!\n",
- ioc->name));
- return -EBUSY;
+ dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: No msg frames!\n",
+ ioc->name, __func__));
+ ret = MPT_SCANDV_BUSY;
+ goto out;
}
pScsiReq = (SCSIIORequest_t *) mf;
@@ -3172,7 +2937,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
pScsiReq->Reserved = 0;
- pScsiReq->MsgFlags = mpt_msg_flags();
+ pScsiReq->MsgFlags = mpt_msg_flags(ioc);
/* MsgContext set in mpt_get_msg_fram call */
int_to_scsilun(io->lun, (struct scsi_lun *)pScsiReq->LUN);
@@ -3184,74 +2949,58 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
if (cmd == REQUEST_SENSE) {
pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Untagged! 0x%2x\n",
- ioc->name, cmd));
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: Untagged! 0x%02x\n", ioc->name, __func__, cmd));
}
- for (ii=0; ii < 16; ii++)
+ for (ii = 0; ii < 16; ii++)
pScsiReq->CDB[ii] = CDB[ii];
pScsiReq->DataLength = cpu_to_le32(io->size);
pScsiReq->SenseBufferLowAddr = cpu_to_le32(ioc->sense_buf_low_dma
+ (my_idx * MPT_SENSE_BUFFER_ALLOC));
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
- ioc->name, cmd, io->channel, io->id, io->lun));
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: Sending Command 0x%02x for fw_channel=%d fw_id=%d lun=%d\n",
+ ioc->name, __func__, cmd, io->channel, io->id, io->lun));
- if (dir == MPI_SCSIIO_CONTROL_READ) {
- mpt_add_sge((char *) &pScsiReq->SGL,
- MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
- io->data_dma);
- } else {
- mpt_add_sge((char *) &pScsiReq->SGL,
- MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
- io->data_dma);
- }
-
- /* The ISR will free the request frame, but we need
- * the information to initialize the target. Duplicate.
- */
- memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
-
- /* Issue this command after:
- * finish init
- * add timer
- * Wait until the reply has been received
- * ScsiScanDvCtx callback function will
- * set hd->pLocal;
- * set scandv_wait_done and call wake_up
- */
- hd->pLocal = NULL;
- hd->timer.expires = jiffies + HZ*cmdTimeout;
- hd->scandv_wait_done = 0;
-
- /* Save cmd pointer, for resource free if timeout or
- * FW reload occurs
- */
- hd->cmdPtr = mf;
+ if (dir == MPI_SCSIIO_CONTROL_READ)
+ ioc->add_sge((char *) &pScsiReq->SGL,
+ MPT_SGE_FLAGS_SSIMPLE_READ | io->size, io->data_dma);
+ else
+ ioc->add_sge((char *) &pScsiReq->SGL,
+ MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size, io->data_dma);
- add_timer(&hd->timer);
+ INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status)
mpt_put_msg_frame(ioc->InternalCtx, ioc, mf);
- wait_event(hd->scandv_waitq, hd->scandv_wait_done);
-
- if (hd->pLocal) {
- rc = hd->pLocal->completion;
- hd->pLocal->skip = 0;
-
- /* Always set fatal error codes in some cases.
- */
- if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
- rc = -ENXIO;
- else if (rc == MPT_SCANDV_SOME_ERROR)
- rc = -rc;
- } else {
- rc = -EFAULT;
- /* This should never happen. */
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "_do_cmd: Null pLocal!!!\n",
- ioc->name));
+ timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done,
+ timeout*HZ);
+ if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = MPT_SCANDV_DID_RESET;
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+ "%s: TIMED OUT for cmd=0x%02x\n", ioc->name, __func__,
+ cmd));
+ if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+ mpt_free_msg_frame(ioc, mf);
+ goto out;
+ }
+ if (!timeleft) {
+ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+ }
+ goto out;
}
- return rc;
+ ret = ioc->internal_cmds.completion_code;
+ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: success, rc=0x%02x\n",
+ ioc->name, __func__, ret));
+
+ out:
+ CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+ mutex_unlock(&ioc->internal_cmds.mutex);
+ return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -3491,6 +3240,7 @@ struct device_attribute *mptscsih_host_attrs[] = {
&dev_attr_debug_level,
NULL,
};
+
EXPORT_SYMBOL(mptscsih_host_attrs);
EXPORT_SYMBOL(mptscsih_remove);
@@ -3516,6 +3266,5 @@ EXPORT_SYMBOL(mptscsih_event_process);
EXPORT_SYMBOL(mptscsih_ioc_reset);
EXPORT_SYMBOL(mptscsih_change_queue_depth);
EXPORT_SYMBOL(mptscsih_timer_expired);
-EXPORT_SYMBOL(mptscsih_TMHandler);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index 319aa3033371..eb3f677528ac 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -60,6 +60,7 @@
#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
#define MPT_SCANDV_ISSUE_SENSE (0x00000010)
#define MPT_SCANDV_FALLBACK (0x00000020)
+#define MPT_SCANDV_BUSY (0x00000040)
#define MPT_SCANDV_MAX_RETRIES (10)
@@ -89,6 +90,7 @@
#endif
+
typedef struct _internal_cmd {
char *data; /* data pointer */
dma_addr_t data_dma; /* data dma address */
@@ -112,6 +114,8 @@ extern int mptscsih_resume(struct pci_dev *pdev);
extern int mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int func);
extern const char * mptscsih_info(struct Scsi_Host *SChost);
extern int mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *));
+extern int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel,
+ u8 id, int lun, int ctx2abort, ulong timeout);
extern void mptscsih_slave_destroy(struct scsi_device *device);
extern int mptscsih_slave_configure(struct scsi_device *device);
extern int mptscsih_abort(struct scsi_cmnd * SCpnt);
@@ -126,7 +130,8 @@ extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pE
extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
extern void mptscsih_timer_expired(unsigned long data);
-extern int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);
extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern struct device_attribute *mptscsih_host_attrs[];
+extern struct scsi_cmnd *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
+extern void mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index 61620144e49c..c5b808fd55ba 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -300,7 +300,7 @@ mptspi_writeIOCPage4(MPT_SCSI_HOST *hd, u8 channel , u8 id)
flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
(IOCPage4Ptr->Header.PageLength + ii) * 4;
- mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
+ ioc->add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
@@ -614,19 +614,24 @@ static void mptspi_read_parameters(struct scsi_target *starget)
spi_width(starget) = (nego & MPI_SCSIDEVPAGE0_NP_WIDE) ? 1 : 0;
}
-static int
+int
mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id)
{
+ MPT_ADAPTER *ioc = hd->ioc;
MpiRaidActionRequest_t *pReq;
MPT_FRAME_HDR *mf;
- MPT_ADAPTER *ioc = hd->ioc;
+ int ret;
+ unsigned long timeleft;
+
+ mutex_lock(&ioc->internal_cmds.mutex);
/* Get and Populate a free Frame
*/
if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) {
- ddvprintk(ioc, printk(MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
- ioc->name));
- return -EAGAIN;
+ dfailprintk(hd->ioc, printk(MYIOC_s_WARN_FMT
+ "%s: no msg frames!\n", ioc->name, __func__));
+ ret = -EAGAIN;
+ goto out;
}
pReq = (MpiRaidActionRequest_t *)mf;
if (quiesce)
@@ -643,29 +648,36 @@ mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id)
pReq->Reserved2 = 0;
pReq->ActionDataWord = 0; /* Reserved for this action */
- mpt_add_sge((char *)&pReq->ActionDataSGE,
+ ioc->add_sge((char *)&pReq->ActionDataSGE,
MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RAID Volume action=%x channel=%d id=%d\n",
ioc->name, pReq->Action, channel, id));
- hd->pLocal = NULL;
- hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
- hd->scandv_wait_done = 0;
-
- /* Save cmd pointer, for resource free if timeout or
- * FW reload occurs
- */
- hd->cmdPtr = mf;
-
- add_timer(&hd->timer);
+ INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status)
mpt_put_msg_frame(ioc->InternalCtx, ioc, mf);
- wait_event(hd->scandv_waitq, hd->scandv_wait_done);
+ timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done, 10*HZ);
+ if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = -ETIME;
+ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: TIMED OUT!\n",
+ ioc->name, __func__));
+ if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out;
+ if (!timeleft) {
+ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+ ioc->name, __func__);
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ mpt_free_msg_frame(ioc, mf);
+ }
+ goto out;
+ }
- if ((hd->pLocal == NULL) || (hd->pLocal->completion != 0))
- return -1;
+ ret = ioc->internal_cmds.completion_code;
- return 0;
+ out:
+ CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+ mutex_unlock(&ioc->internal_cmds.mutex);
+ return ret;
}
static void mptspi_dv_device(struct _MPT_SCSI_HOST *hd,
@@ -1423,17 +1435,15 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* A slightly different algorithm is required for
* 64bit SGEs.
*/
- scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
- if (sizeof(dma_addr_t) == sizeof(u64)) {
+ scale = ioc->req_sz/ioc->SGE_size;
+ if (ioc->sg_addr_size == sizeof(u64)) {
numSGE = (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 60) / ioc->SGE_size;
} else {
numSGE = 1 + (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
- (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
- sizeof(u32));
+ (ioc->req_sz - 64) / ioc->SGE_size;
}
if (numSGE < sh->sg_tablesize) {
@@ -1464,9 +1474,6 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Clear the TM flags
*/
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- hd->resetPending = 0;
hd->abortSCpnt = NULL;
/* Clear the pointer used to store
@@ -1493,8 +1500,6 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mpt_saf_te));
ioc->spi_data.noQas = 0;
- init_waitqueue_head(&hd->scandv_waitq);
- hd->scandv_wait_done = 0;
hd->last_queue_full = 0;
hd->spi_pending = 0;
@@ -1514,7 +1519,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* issue internal bus reset
*/
if (ioc->spi_data.bus_reset)
- mptscsih_TMHandler(hd,
+ mptscsih_IssueTaskMgmt(hd,
MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
0, 0, 0, 0, 5);
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 6573ef4408f1..335d4c78a775 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -794,8 +794,9 @@ static int i2o_block_transfer(struct request *req)
if (c->adaptec) {
u8 cmd[10];
u32 scsi_flags;
- u16 hwsec = queue_hardsect_size(req->q) >> KERNEL_SECTOR_SHIFT;
+ u16 hwsec;
+ hwsec = queue_logical_block_size(req->q) >> KERNEL_SECTOR_SHIFT;
memset(cmd, 0, 10);
sgl_offset = SGL_OFFSET_12;
@@ -1078,7 +1079,7 @@ static int i2o_block_probe(struct device *dev)
*/
if (!i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) ||
!i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4)) {
- blk_queue_hardsect_size(queue, le32_to_cpu(blocksize));
+ blk_queue_logical_block_size(queue, le32_to_cpu(blocksize));
} else
osm_warn("unable to get blocksize of %s\n", gd->disk_name);
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 7793932a513b..11a6248cc1c1 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -443,7 +443,7 @@ static irqreturn_t pcf50633_irq(int irq, void *data)
dev_dbg(pcf->dev, "pcf50633_irq\n");
get_device(pcf->dev);
- disable_irq(pcf->irq);
+ disable_irq_nosync(pcf->irq);
schedule_work(&pcf->irq_work);
return IRQ_HANDLED;
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index c2be3088e2e1..fe24079387c5 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -79,10 +79,6 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
/* Cache is CPU endian */
dest[i - reg] = be16_to_cpu(dest[i - reg]);
- /* Satisfy non-volatile bits from cache */
- dest[i - reg] &= wm8350_reg_io_map[i].vol;
- dest[i - reg] |= wm8350->reg_cache[i];
-
/* Mask out non-readable bits */
dest[i - reg] &= wm8350_reg_io_map[i].readable;
}
@@ -182,9 +178,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
(wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
| src[i - reg];
- /* Don't store volatile bits */
- wm8350->reg_cache[i] &= ~wm8350_reg_io_map[i].vol;
-
src[i - reg] = cpu_to_be16(src[i - reg]);
}
@@ -1261,7 +1254,6 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
(i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
value = be16_to_cpu(wm8350->reg_cache[i]);
value &= wm8350_reg_io_map[i].readable;
- value &= ~wm8350_reg_io_map[i].vol;
wm8350->reg_cache[i] = value;
} else
wm8350->reg_cache[i] = reg_map[i];
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index 3cf61ece71d7..348443bdb23b 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -119,7 +119,7 @@ enclosure_register(struct device *dev, const char *name, int components,
edev->edev.class = &enclosure_class;
edev->edev.parent = get_device(dev);
edev->cb = cb;
- dev_set_name(&edev->edev, name);
+ dev_set_name(&edev->edev, "%s", name);
err = device_register(&edev->edev);
if (err)
goto err;
@@ -255,8 +255,8 @@ enclosure_component_register(struct enclosure_device *edev,
ecomp->number = number;
cdev = &ecomp->cdev;
cdev->parent = get_device(&edev->edev);
- if (name)
- dev_set_name(cdev, name);
+ if (name && name[0])
+ dev_set_name(cdev, "%s", name);
else
dev_set_name(cdev, "%u", number);
diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c
index 2e2a5923d4c2..a71e245801ee 100644
--- a/drivers/misc/isl29003.c
+++ b/drivers/misc/isl29003.c
@@ -64,6 +64,7 @@ struct isl29003_data {
struct i2c_client *client;
struct mutex lock;
u8 reg_cache[ISL29003_NUM_CACHABLE_REGS];
+ u8 power_state_before_suspend;
};
static int gain_range[] = {
@@ -411,6 +412,9 @@ static int __devexit isl29003_remove(struct i2c_client *client)
#ifdef CONFIG_PM
static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg)
{
+ struct isl29003_data *data = i2c_get_clientdata(client);
+
+ data->power_state_before_suspend = isl29003_get_power_state(client);
return isl29003_set_power_state(client, 0);
}
@@ -421,10 +425,11 @@ static int isl29003_resume(struct i2c_client *client)
/* restore registers from cache */
for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)
- if (!i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
+ if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
return -EIO;
- return 0;
+ return isl29003_set_power_state(client,
+ data->power_state_before_suspend);
}
#else
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 949e99770ad6..98ffc41eaf2c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -254,6 +254,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.data.blocks = blk_rq_sectors(req);
/*
+ * The block layer doesn't support all sector count
+ * restrictions, so we need to be prepared for too big
+ * requests.
+ */
+ if (brq.data.blocks > card->host->max_blk_count)
+ brq.data.blocks = card->host->max_blk_count;
+
+ /*
* After a read error, we redo the request one sector at a time
* in order to accurately determine which sectors can be read
* successfully.
@@ -513,7 +521,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
- blk_queue_hardsect_size(md->queue.queue, 512);
+ blk_queue_logical_block_size(md->queue.queue, 512);
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index fa073ab3fa34..264911732756 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -706,7 +706,7 @@ static void mmc_power_up(struct mmc_host *host)
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
- mmc_delay(2);
+ mmc_delay(10);
host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
@@ -716,7 +716,7 @@ static void mmc_power_up(struct mmc_host *host)
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
- mmc_delay(2);
+ mmc_delay(10);
}
static void mmc_power_off(struct mmc_host *host)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index a663429b3d55..7d4febdab286 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -490,7 +490,7 @@ static void mmci_check_status(unsigned long data)
mod_timer(&host->timer, jiffies + HZ);
}
-static int __devinit mmci_probe(struct amba_device *dev, void *id)
+static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
{
struct mmc_platform_data *plat = dev->dev.platform_data;
struct mmci_host *host;
@@ -514,6 +514,7 @@ static int __devinit mmci_probe(struct amba_device *dev, void *id)
}
host = mmc_priv(mmc);
+ host->mmc = mmc;
/* Bits 12 thru 19 is the designer */
host->hw_designer = (dev->periphid >> 12) & 0xff;
/* Bits 20 thru 23 is the revison */
@@ -545,7 +546,6 @@ static int __devinit mmci_probe(struct amba_device *dev, void *id)
host->mclk = clk_get_rate(host->clk);
DBG(host, "eventual mclk rate: %u Hz\n", host->mclk);
}
- host->mmc = mmc;
host->base = ioremap(dev->res.start, SZ_4K);
if (!host->base) {
ret = -ENOMEM;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index b5c375d94ab3..b56d72ff06e9 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -64,6 +64,31 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
unsigned int tmout;
int tmout_index;
+ /*
+ * Hardware weirdness. The FIFO_EMPTY bit of the HW_STATE
+ * register is sometimes not set before a while when some
+ * "unusual" data block sizes are used (such as with the SWITCH
+ * command), even despite the fact that the XFER_DONE interrupt
+ * was raised. And if another data transfer starts before
+ * this bit comes to good sense (which eventually happens by
+ * itself) then the new transfer simply fails with a timeout.
+ */
+ if (!(mvsd_read(MVSD_HW_STATE) & (1 << 13))) {
+ unsigned long t = jiffies + HZ;
+ unsigned int hw_state, count = 0;
+ do {
+ if (time_after(jiffies, t)) {
+ dev_warn(host->dev, "FIFO_EMPTY bit missing\n");
+ break;
+ }
+ hw_state = mvsd_read(MVSD_HW_STATE);
+ count++;
+ } while (!(hw_state & (1 << 13)));
+ dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit "
+ "(hw=0x%04x, count=%d, jiffies=%ld)\n",
+ hw_state, count, jiffies - (t - HZ));
+ }
+
/* If timeout=0 then maximum timeout index is used. */
tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
tmout += data->timeout_clks;
@@ -620,9 +645,18 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS;
+ /*
+ * The HI_SPEED_EN bit is causing trouble with many (but not all)
+ * high speed SD, SDHC and SDIO cards. Not enabling that bit
+ * makes all cards work. So let's just ignore that bit for now
+ * and revisit this issue if problems for not enabling this bit
+ * are ever reported.
+ */
+#if 0
if (ios->timing == MMC_TIMING_MMC_HS ||
ios->timing == MMC_TIMING_SD_HS)
ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN;
+#endif
host->ctrl = ctrl_reg;
mvsd_write(MVSD_HOST_CTRL, ctrl_reg);
@@ -825,24 +859,23 @@ static int __exit mvsd_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int mvsd_suspend(struct platform_device *dev, pm_message_t state,
- u32 level)
+static int mvsd_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
- if (mmc && level == SUSPEND_DISABLE)
+ if (mmc)
ret = mmc_suspend_host(mmc, state);
return ret;
}
-static int mvsd_resume(struct platform_device *dev, u32 level)
+static int mvsd_resume(struct platform_device *dev)
{
- struct mmc_host *mmc = platform_dev_get_drvdata(dev);
+ struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
- if (mmc && level == RESUME_ENABLE)
+ if (mmc)
ret = mmc_resume_host(mmc);
return ret;
@@ -883,3 +916,4 @@ module_param(nodma, int, 0);
MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre");
MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mvsdio");
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index b4a615c55f28..f4cbe473670e 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -140,6 +140,8 @@ struct mxcmci_host {
struct work_struct datawork;
};
+static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
+
static inline int mxcmci_use_dma(struct mxcmci_host *host)
{
return host->do_dma;
@@ -160,7 +162,7 @@ static void mxcmci_softreset(struct mxcmci_host *host)
writew(0xff, host->base + MMC_REG_RES_TO);
}
-static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
+static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
unsigned int blksz = data->blksz;
@@ -168,6 +170,7 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
#ifdef HAS_DMA
struct scatterlist *sg;
int i;
+ int ret;
#endif
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
@@ -183,7 +186,7 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
for_each_sg(data->sg, sg, data->sg_len, i) {
if (sg->offset & 3 || sg->length & 3) {
host->do_dma = 0;
- return;
+ return 0;
}
}
@@ -192,23 +195,30 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
- imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize,
- host->res->start + MMC_REG_BUFFER_ACCESS,
- DMA_MODE_READ);
+ ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
+ datasize,
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_READ);
} else {
host->dma_dir = DMA_TO_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
- imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize,
- host->res->start + MMC_REG_BUFFER_ACCESS,
- DMA_MODE_WRITE);
+ ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
+ datasize,
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_WRITE);
}
+ if (ret) {
+ dev_err(mmc_dev(host->mmc), "failed to setup DMA : %d\n", ret);
+ return ret;
+ }
wmb();
imx_dma_enable(host->dma);
#endif /* HAS_DMA */
+ return 0;
}
static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
@@ -345,8 +355,11 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
stat = readl(host->base + MMC_REG_STATUS);
if (stat & STATUS_ERR_MASK)
return stat;
- if (time_after(jiffies, timeout))
+ if (time_after(jiffies, timeout)) {
+ mxcmci_softreset(host);
+ mxcmci_set_clk_rate(host, host->clock);
return STATUS_TIME_OUT_READ;
+ }
if (stat & mask)
return 0;
cpu_relax();
@@ -531,6 +544,7 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct mxcmci_host *host = mmc_priv(mmc);
unsigned int cmdat = host->cmdat;
+ int error;
WARN_ON(host->req != NULL);
@@ -540,7 +554,12 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
host->do_dma = 1;
#endif
if (req->data) {
- mxcmci_setup_data(host, req->data);
+ error = mxcmci_setup_data(host, req->data);
+ if (error) {
+ req->cmd->error = error;
+ goto out;
+ }
+
cmdat |= CMD_DAT_CONT_DATA_ENABLE;
@@ -548,7 +567,9 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
cmdat |= CMD_DAT_CONT_WRITE;
}
- if (mxcmci_start_cmd(host, req->cmd, cmdat))
+ error = mxcmci_start_cmd(host, req->cmd, cmdat);
+out:
+ if (error)
mxcmci_finish_request(host, req);
}
@@ -724,7 +745,9 @@ static int mxcmci_probe(struct platform_device *pdev)
goto out_clk_put;
}
- mmc->f_min = clk_get_rate(host->clk) >> 7;
+ mmc->f_min = clk_get_rate(host->clk) >> 16;
+ if (mmc->f_min < 400000)
+ mmc->f_min = 400000;
mmc->f_max = clk_get_rate(host->clk) >> 1;
/* recommended in data sheet */
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 5570849188cc..dceb5ee3bda0 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -157,8 +157,6 @@ struct mmc_omap_host {
struct timer_list dma_timer;
unsigned dma_len;
- short power_pin;
-
struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS];
struct mmc_omap_slot *current_slot;
spinlock_t slot_lock;
@@ -824,7 +822,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
del_timer(&host->cmd_abort_timer);
host->abort = 1;
OMAP_MMC_WRITE(host, IE, 0);
- disable_irq(host->irq);
+ disable_irq_nosync(host->irq);
schedule_work(&host->cmd_abort_work);
return IRQ_HANDLED;
}
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e62a22a7f00c..c40cb96255a2 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -680,7 +680,7 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
host->dma_ch = -1;
/*
* DMA Callback: run in interrupt context.
- * mutex_unlock will through a kernel warning if used.
+ * mutex_unlock will throw a kernel warning if used.
*/
up(&host->sem);
}
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
index 3ff4ac3abe8b..128c614d11aa 100644
--- a/drivers/mmc/host/sdhci-of.c
+++ b/drivers/mmc/host/sdhci-of.c
@@ -55,7 +55,13 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg)
static u16 esdhc_readw(struct sdhci_host *host, int reg)
{
- return in_be16(host->ioaddr + (reg ^ 0x2));
+ u16 ret;
+
+ if (unlikely(reg == SDHCI_HOST_VERSION))
+ ret = in_be16(host->ioaddr + reg);
+ else
+ ret = in_be16(host->ioaddr + (reg ^ 0x2));
+ return ret;
}
static u8 esdhc_readb(struct sdhci_host *host, int reg)
@@ -277,6 +283,7 @@ static int __devexit sdhci_of_remove(struct of_device *ofdev)
static const struct of_device_id sdhci_of_match[] = {
{ .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
{ .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+ { .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
{ .compatible = "generic-sdhci", },
{},
};
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index cd37962ec44f..65be27995d5c 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -522,8 +522,8 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- goto unmap;
+ dev_err(&pdev->dev, "cannot allocate host\n");
+ return ERR_PTR(PTR_ERR(host));
}
slot = sdhci_priv(host);
@@ -541,7 +541,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
if (ret) {
dev_err(&pdev->dev, "cannot request region\n");
- return ERR_PTR(ret);
+ goto free;
}
addr = pci_resource_start(pdev, bar);
@@ -572,6 +572,8 @@ unmap:
release:
pci_release_region(pdev, bar);
+
+free:
sdhci_free_host(host);
return ERR_PTR(ret);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index f20a834f4309..65c6f996bbd3 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -125,7 +125,7 @@
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
- SDHCI_INT_DATA_END_BIT)
+ SDHCI_INT_DATA_END_BIT | SDHCI_ADMA_ERROR)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 8185b1f3e5e6..cc6369ea67dd 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -54,7 +54,7 @@
#define SR_SRWD 0x80 /* SR write protect */
/* Define max times to check status register before we give up. */
-#define MAX_READY_WAIT_COUNT 100000
+#define MAX_READY_WAIT_JIFFIES (10 * HZ) /* eg. M25P128 specs 6s max sector erase */
#define CMD_SIZE 4
#ifdef CONFIG_M25PXX_USE_FAST_READ
@@ -139,20 +139,20 @@ static inline int write_enable(struct m25p *flash)
*/
static int wait_till_ready(struct m25p *flash)
{
- int count;
+ unsigned long deadline;
int sr;
- /* one chip guarantees max 5 msec wait here after page writes,
- * but potentially three seconds (!) after page erase.
- */
- for (count = 0; count < MAX_READY_WAIT_COUNT; count++) {
+ deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+
+ do {
if ((sr = read_sr(flash)) < 0)
break;
else if (!(sr & SR_WIP))
return 0;
- /* REVISIT sometimes sleeping would be best */
- }
+ cond_resched();
+
+ } while (!time_after_eq(jiffies, deadline));
return 1;
}
@@ -246,10 +246,12 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
mutex_lock(&flash->lock);
/* whole-chip erase? */
- if (len == flash->mtd.size && erase_chip(flash)) {
- instr->state = MTD_ERASE_FAILED;
- mutex_unlock(&flash->lock);
- return -EIO;
+ if (len == flash->mtd.size) {
+ if (erase_chip(flash)) {
+ instr->state = MTD_ERASE_FAILED;
+ mutex_unlock(&flash->lock);
+ return -EIO;
+ }
/* REVISIT in some cases we could speed up erasing large regions
* by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 62dee54af0a5..43976aa4dbb1 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -178,7 +178,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
/* Calculate flash page address; use block erase (for speed) if
* we're at a block boundary and need to erase the whole block.
*/
- pageaddr = div_u64(instr->len, priv->page_size);
+ pageaddr = div_u64(instr->addr, priv->page_size);
do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
pageaddr = pageaddr << priv->page_offset;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 502622f628bc..aaac3b6800b7 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -378,7 +378,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
}
tr->blkcore_priv->rq->queuedata = tr;
- blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
+ blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize);
if (tr->discard)
blk_queue_set_discard(tr->blkcore_priv->rq,
blktrans_discard_request);
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index fdd6ae859397..bccb4b1ffc46 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -48,11 +48,11 @@ static LIST_HEAD(mtd_notifiers);
*/
static void mtd_release(struct device *dev)
{
- struct mtd_info *mtd = dev_to_mtd(dev);
+ dev_t index = MTD_DEVT(dev_to_mtd(dev)->index);
/* remove /dev/mtdXro node if needed */
- if (MTD_DEVT(mtd->index))
- device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1);
+ if (index)
+ device_destroy(mtd_class, index + 1);
}
static ssize_t mtd_type_show(struct device *dev,
@@ -132,6 +132,17 @@ static ssize_t mtd_writesize_show(struct device *dev,
}
static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
+static ssize_t mtd_subpagesize_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mtd_info *mtd = dev_to_mtd(dev);
+ unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", subpagesize);
+
+}
+static DEVICE_ATTR(subpagesize, S_IRUGO, mtd_subpagesize_show, NULL);
+
static ssize_t mtd_oobsize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -169,6 +180,7 @@ static struct attribute *mtd_attrs[] = {
&dev_attr_size.attr,
&dev_attr_erasesize.attr,
&dev_attr_writesize.attr,
+ &dev_attr_subpagesize.attr,
&dev_attr_oobsize.attr,
&dev_attr_numeraseregions.attr,
&dev_attr_name.attr,
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index 92285d0089c2..af8b42e0a55b 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -74,8 +74,7 @@ static int get_sb_mtd_aux(struct file_system_type *fs_type, int flags,
ret = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
if (ret < 0) {
- up_write(&sb->s_umount);
- deactivate_super(sb);
+ deactivate_locked_super(sb);
return ret;
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 0119220de7d0..02700f769b8a 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -407,16 +407,17 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
}
info->chip.ecc.mode = ecc_mode;
- info->clk = clk_get(&pdev->dev, "AEMIFCLK");
+ info->clk = clk_get(&pdev->dev, "aemif");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
- dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret);
+ dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
goto err_clk;
}
ret = clk_enable(info->clk);
if (ret < 0) {
- dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret);
+ dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
+ ret);
goto err_clk_enable;
}
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index f3548d048014..40c26080ecda 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -831,6 +831,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
break;
case NAND_CMD_READID:
+ host->col_addr = 0;
send_read_id(host);
break;
@@ -867,6 +868,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
mtd->priv = this;
mtd->owner = THIS_MODULE;
mtd->dev.parent = &pdev->dev;
+ mtd->name = "mxc_nand";
/* 50 us command delay time */
this->chip_delay = 5;
@@ -882,8 +884,10 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->verify_buf = mxc_nand_verify_buf;
host->clk = clk_get(&pdev->dev, "nfc");
- if (IS_ERR(host->clk))
+ if (IS_ERR(host->clk)) {
+ err = PTR_ERR(host->clk);
goto eclk;
+ }
clk_enable(host->clk);
host->clk_act = 1;
@@ -896,7 +900,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
host->regs = ioremap(res->start, res->end - res->start + 1);
if (!host->regs) {
- err = -EIO;
+ err = -ENOMEM;
goto eres;
}
@@ -1011,30 +1015,35 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct mtd_info *info = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct nand_chip *nand_chip = mtd->priv;
+ struct mxc_nand_host *host = nand_chip->priv;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n");
- if (info)
- ret = info->suspend(info);
-
- /* Disable the NFC clock */
- clk_disable(nfc_clk); /* FIXME */
+ if (mtd) {
+ ret = mtd->suspend(mtd);
+ /* Disable the NFC clock */
+ clk_disable(host->clk);
+ }
return ret;
}
static int mxcnd_resume(struct platform_device *pdev)
{
- struct mtd_info *info = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct nand_chip *nand_chip = mtd->priv;
+ struct mxc_nand_host *host = nand_chip->priv;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n");
- /* Enable the NFC clock */
- clk_enable(nfc_clk); /* FIXME */
- if (info)
- info->resume(info);
+ if (mtd) {
+ /* Enable the NFC clock */
+ clk_enable(host->clk);
+ mtd->resume(mtd);
+ }
return ret;
}
@@ -1055,13 +1064,7 @@ static struct platform_driver mxcnd_driver = {
static int __init mxc_nd_init(void)
{
- /* Register the device driver structure. */
- pr_info("MXC MTD nand Driver\n");
- if (platform_driver_probe(&mxcnd_driver, mxcnd_probe) != 0) {
- printk(KERN_ERR "Driver register failed for mxcnd_driver\n");
- return -ENODEV;
- }
- return 0;
+ return platform_driver_probe(&mxcnd_driver, mxcnd_probe);
}
static void __exit mxc_nd_cleanup(void)
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index fbb371921991..682aad897081 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -480,9 +480,13 @@ static int pnp_registered;
#ifdef CONFIG_EISA
static struct eisa_device_id el3_eisa_ids[] = {
+ { "TCM5090" },
+ { "TCM5091" },
{ "TCM5092" },
{ "TCM5093" },
+ { "TCM5094" },
{ "TCM5095" },
+ { "TCM5098" },
{ "" }
};
MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 214a92d1ef75..f3c4a3b910bb 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2264,6 +2264,17 @@ config BNX2
To compile this driver as a module, choose M here: the module
will be called bnx2. This is recommended.
+config CNIC
+ tristate "Broadcom CNIC support"
+ depends on BNX2
+ depends on UIO
+ help
+ This driver supports offload features of Broadcom NetXtremeII
+ gigabit Ethernet cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cnic. This is recommended.
+
config SPIDER_NET
tristate "Spider Gigabit Ethernet driver"
depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB)
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1fc4602a6ff2..db30ebd7b262 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_STNIC) += stnic.o 8390.o
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_TIGON3) += tg3.o
obj-$(CONFIG_BNX2) += bnx2.o
+obj-$(CONFIG_CNIC) += cnic.o
obj-$(CONFIG_BNX2X) += bnx2x.o
bnx2x-objs := bnx2x_main.o bnx2x_link.o
spidernet-y += spider_net.o spider_net_ethtool.o
@@ -102,7 +103,7 @@ obj-$(CONFIG_HAMACHI) += hamachi.o
obj-$(CONFIG_NET) += Space.o loopback.o
obj-$(CONFIG_SEEQ8005) += seeq8005.o
obj-$(CONFIG_NET_SB1000) += sb1000.o
-obj-$(CONFIG_MAC8390) += mac8390.o 8390.o
+obj-$(CONFIG_MAC8390) += mac8390.o
obj-$(CONFIG_APNE) += apne.o 8390.o
obj-$(CONFIG_PCMCIA_PCNET) += 8390.o
obj-$(CONFIG_HP100) += hp100.o
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 448487e22fa3..a740053d3af3 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -338,12 +338,12 @@ static int ixp4xx_mdio_register(void)
if (cpu_is_ixp43x()) {
/* IXP43x lacks NPE-B and uses NPE-C for MII PHY access */
if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEC_ETH))
- return -ENOSYS;
+ return -ENODEV;
mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthC_BASE_VIRT;
} else {
/* All MII PHY accesses use NPE-B Ethernet registers */
if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEB_ETH0))
- return -ENOSYS;
+ return -ENODEV;
mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT;
}
@@ -1174,7 +1174,7 @@ static int __devinit eth_init_one(struct platform_device *pdev)
regs_phys = IXP4XX_EthC_BASE_PHYS;
break;
default:
- err = -ENOSYS;
+ err = -ENODEV;
goto err_free;
}
@@ -1189,15 +1189,10 @@ static int __devinit eth_init_one(struct platform_device *pdev)
goto err_free;
}
- if (register_netdev(dev)) {
- err = -EIO;
- goto err_npe_rel;
- }
-
port->mem_res = request_mem_region(regs_phys, REGS_SIZE, dev->name);
if (!port->mem_res) {
err = -EBUSY;
- goto err_unreg;
+ goto err_npe_rel;
}
port->plat = plat;
@@ -1215,20 +1210,25 @@ static int __devinit eth_init_one(struct platform_device *pdev)
snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, "0", plat->phy);
port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0,
PHY_INTERFACE_MODE_MII);
- if (IS_ERR(port->phydev)) {
- printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
- return PTR_ERR(port->phydev);
- }
+ if ((err = IS_ERR(port->phydev)))
+ goto err_free_mem;
port->phydev->irq = PHY_POLL;
+ if ((err = register_netdev(dev)))
+ goto err_phy_dis;
+
printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, plat->phy,
npe_name(port->npe));
return 0;
-err_unreg:
- unregister_netdev(dev);
+err_phy_dis:
+ phy_disconnect(port->phydev);
+err_free_mem:
+ npe_port_tab[NPE_ID(port->id)] = NULL;
+ platform_set_drvdata(pdev, NULL);
+ release_resource(port->mem_res);
err_npe_rel:
npe_release(port->npe);
err_free:
@@ -1242,6 +1242,7 @@ static int __devexit eth_remove_one(struct platform_device *pdev)
struct port *port = netdev_priv(dev);
unregister_netdev(dev);
+ phy_disconnect(port->phydev);
npe_port_tab[NPE_ID(port->id)] = NULL;
platform_set_drvdata(pdev, NULL);
npe_release(port->npe);
diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c
index fb57b750866b..1342418fb209 100644
--- a/drivers/net/atl1e/atl1e_main.c
+++ b/drivers/net/atl1e/atl1e_main.c
@@ -37,6 +37,7 @@ char atl1e_driver_version[] = DRV_VERSION;
*/
static struct pci_device_id atl1e_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)},
+ {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1066)},
/* required last entry */
{ 0 }
};
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 0ab22540bf59..4e817126e280 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -82,6 +82,12 @@
#include "atl1.h"
+#define ATLX_DRIVER_VERSION "2.1.3"
+MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, \
+ Chris Snook <csnook@redhat.com>, Jay Cliburn <jcliburn@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ATLX_DRIVER_VERSION);
+
/* Temporary hack for merging atl1 and atl2 */
#include "atlx.c"
diff --git a/drivers/net/atlx/atlx.h b/drivers/net/atlx/atlx.h
index 297a03da6b7f..14054b75aa62 100644
--- a/drivers/net/atlx/atlx.h
+++ b/drivers/net/atlx/atlx.h
@@ -29,12 +29,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#define ATLX_DRIVER_VERSION "2.1.3"
-MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, \
- Chris Snook <csnook@redhat.com>, Jay Cliburn <jcliburn@gmail.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(ATLX_DRIVER_VERSION);
-
#define ATLX_ERR_PHY 2
#define ATLX_ERR_PHY_SPEED 7
#define ATLX_ERR_PHY_RES 8
diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h
index c49ddd08b2aa..b4bb06fdf307 100644
--- a/drivers/net/benet/be.h
+++ b/drivers/net/benet/be.h
@@ -35,8 +35,22 @@
#define DRV_VER "2.0.348"
#define DRV_NAME "be2net"
#define BE_NAME "ServerEngines BladeEngine2 10Gbps NIC"
+#define OC_NAME "Emulex OneConnect 10Gbps NIC"
#define DRV_DESC BE_NAME "Driver"
+#define BE_VENDOR_ID 0x19a2
+#define BE_DEVICE_ID1 0x211
+#define OC_DEVICE_ID1 0x700
+#define OC_DEVICE_ID2 0x701
+
+static inline char *nic_name(struct pci_dev *pdev)
+{
+ if (pdev->device == OC_DEVICE_ID1 || pdev->device == OC_DEVICE_ID2)
+ return OC_NAME;
+ else
+ return BE_NAME;
+}
+
/* Number of bytes of an RX frame that are copied to skb->data */
#define BE_HDR_LEN 64
#define BE_MAX_JUMBO_FRAME_SIZE 9018
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c
index 30d0c81c989e..5c378b5e8e41 100644
--- a/drivers/net/benet/be_main.c
+++ b/drivers/net/benet/be_main.c
@@ -28,10 +28,10 @@ static unsigned int rx_frag_size = 2048;
module_param(rx_frag_size, uint, S_IRUGO);
MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
-#define BE_VENDOR_ID 0x19a2
-#define BE2_DEVICE_ID_1 0x0211
static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
- { PCI_DEVICE(BE_VENDOR_ID, BE2_DEVICE_ID_1) },
+ { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -1859,7 +1859,7 @@ static int __devinit be_probe(struct pci_dev *pdev,
if (status != 0)
goto stats_clean;
- dev_info(&pdev->dev, BE_NAME " port %d\n", adapter->port_num);
+ dev_info(&pdev->dev, "%s port %d\n", nic_name(pdev), adapter->port_num);
return 0;
stats_clean:
@@ -1873,7 +1873,7 @@ rel_reg:
disable_dev:
pci_disable_device(pdev);
do_none:
- dev_warn(&pdev->dev, BE_NAME " initialization failed\n");
+ dev_err(&pdev->dev, "%s initialization failed\n", nic_name(pdev));
return status;
}
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index 9f971ed6b58d..b4da18213324 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -979,22 +979,7 @@ static int bfin_mac_open(struct net_device *dev)
return 0;
}
-static const struct net_device_ops bfin_mac_netdev_ops = {
- .ndo_open = bfin_mac_open,
- .ndo_stop = bfin_mac_close,
- .ndo_start_xmit = bfin_mac_hard_start_xmit,
- .ndo_set_mac_address = bfin_mac_set_mac_address,
- .ndo_tx_timeout = bfin_mac_timeout,
- .ndo_set_multicast_list = bfin_mac_set_multicast_list,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_change_mtu = eth_change_mtu,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = bfin_mac_poll,
-#endif
-};
-
/*
- *
* this makes the board clean up everything that it can
* and not talk to the outside world. Caused by
* an 'ifconfig ethX down'
@@ -1019,6 +1004,20 @@ static int bfin_mac_close(struct net_device *dev)
return 0;
}
+static const struct net_device_ops bfin_mac_netdev_ops = {
+ .ndo_open = bfin_mac_open,
+ .ndo_stop = bfin_mac_close,
+ .ndo_start_xmit = bfin_mac_hard_start_xmit,
+ .ndo_set_mac_address = bfin_mac_set_mac_address,
+ .ndo_tx_timeout = bfin_mac_timeout,
+ .ndo_set_multicast_list = bfin_mac_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = bfin_mac_poll,
+#endif
+};
+
static int __devinit bfin_mac_probe(struct platform_device *pdev)
{
struct net_device *ndev;
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index d47839184a06..3f5fcb0156a1 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -49,13 +49,17 @@
#include <linux/firmware.h>
#include <linux/log2.h>
+#if defined(CONFIG_CNIC) || defined(CONFIG_CNIC_MODULE)
+#define BCM_CNIC 1
+#include "cnic_if.h"
+#endif
#include "bnx2.h"
#include "bnx2_fw.h"
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "2.0.0"
-#define DRV_MODULE_RELDATE "April 2, 2009"
+#define DRV_MODULE_VERSION "2.0.1"
+#define DRV_MODULE_RELDATE "May 6, 2009"
#define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-4.6.16.fw"
#define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-4.6.16.fw"
#define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-4.6.17.fw"
@@ -315,6 +319,158 @@ bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
spin_unlock_bh(&bp->indirect_lock);
}
+#ifdef BCM_CNIC
+static int
+bnx2_drv_ctl(struct net_device *dev, struct drv_ctl_info *info)
+{
+ struct bnx2 *bp = netdev_priv(dev);
+ struct drv_ctl_io *io = &info->data.io;
+
+ switch (info->cmd) {
+ case DRV_CTL_IO_WR_CMD:
+ bnx2_reg_wr_ind(bp, io->offset, io->data);
+ break;
+ case DRV_CTL_IO_RD_CMD:
+ io->data = bnx2_reg_rd_ind(bp, io->offset);
+ break;
+ case DRV_CTL_CTX_WR_CMD:
+ bnx2_ctx_wr(bp, io->cid_addr, io->offset, io->data);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void bnx2_setup_cnic_irq_info(struct bnx2 *bp)
+{
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+ struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+ int sb_id;
+
+ if (bp->flags & BNX2_FLAG_USING_MSIX) {
+ cp->drv_state |= CNIC_DRV_STATE_USING_MSIX;
+ bnapi->cnic_present = 0;
+ sb_id = bp->irq_nvecs;
+ cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX;
+ } else {
+ cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
+ bnapi->cnic_tag = bnapi->last_status_idx;
+ bnapi->cnic_present = 1;
+ sb_id = 0;
+ cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
+ }
+
+ cp->irq_arr[0].vector = bp->irq_tbl[sb_id].vector;
+ cp->irq_arr[0].status_blk = (void *)
+ ((unsigned long) bnapi->status_blk.msi +
+ (BNX2_SBLK_MSIX_ALIGN_SIZE * sb_id));
+ cp->irq_arr[0].status_blk_num = sb_id;
+ cp->num_irq = 1;
+}
+
+static int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops,
+ void *data)
+{
+ struct bnx2 *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ if (ops == NULL)
+ return -EINVAL;
+
+ if (cp->drv_state & CNIC_DRV_STATE_REGD)
+ return -EBUSY;
+
+ bp->cnic_data = data;
+ rcu_assign_pointer(bp->cnic_ops, ops);
+
+ cp->num_irq = 0;
+ cp->drv_state = CNIC_DRV_STATE_REGD;
+
+ bnx2_setup_cnic_irq_info(bp);
+
+ return 0;
+}
+
+static int bnx2_unregister_cnic(struct net_device *dev)
+{
+ struct bnx2 *bp = netdev_priv(dev);
+ struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ cp->drv_state = 0;
+ bnapi->cnic_present = 0;
+ rcu_assign_pointer(bp->cnic_ops, NULL);
+ synchronize_rcu();
+ return 0;
+}
+
+struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
+{
+ struct bnx2 *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ cp->drv_owner = THIS_MODULE;
+ cp->chip_id = bp->chip_id;
+ cp->pdev = bp->pdev;
+ cp->io_base = bp->regview;
+ cp->drv_ctl = bnx2_drv_ctl;
+ cp->drv_register_cnic = bnx2_register_cnic;
+ cp->drv_unregister_cnic = bnx2_unregister_cnic;
+
+ return cp;
+}
+EXPORT_SYMBOL(bnx2_cnic_probe);
+
+static void
+bnx2_cnic_stop(struct bnx2 *bp)
+{
+ struct cnic_ops *c_ops;
+ struct cnic_ctl_info info;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops) {
+ info.cmd = CNIC_CTL_STOP_CMD;
+ c_ops->cnic_ctl(bp->cnic_data, &info);
+ }
+ rcu_read_unlock();
+}
+
+static void
+bnx2_cnic_start(struct bnx2 *bp)
+{
+ struct cnic_ops *c_ops;
+ struct cnic_ctl_info info;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops) {
+ if (!(bp->flags & BNX2_FLAG_USING_MSIX)) {
+ struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+
+ bnapi->cnic_tag = bnapi->last_status_idx;
+ }
+ info.cmd = CNIC_CTL_START_CMD;
+ c_ops->cnic_ctl(bp->cnic_data, &info);
+ }
+ rcu_read_unlock();
+}
+
+#else
+
+static void
+bnx2_cnic_stop(struct bnx2 *bp)
+{
+}
+
+static void
+bnx2_cnic_start(struct bnx2 *bp)
+{
+}
+
+#endif
+
static int
bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
{
@@ -488,6 +644,7 @@ bnx2_napi_enable(struct bnx2 *bp)
static void
bnx2_netif_stop(struct bnx2 *bp)
{
+ bnx2_cnic_stop(bp);
bnx2_disable_int_sync(bp);
if (netif_running(bp->dev)) {
bnx2_napi_disable(bp);
@@ -504,6 +661,7 @@ bnx2_netif_start(struct bnx2 *bp)
netif_tx_wake_all_queues(bp->dev);
bnx2_napi_enable(bp);
bnx2_enable_int(bp);
+ bnx2_cnic_start(bp);
}
}
}
@@ -2600,6 +2758,7 @@ bnx2_get_hw_tx_cons(struct bnx2_napi *bnapi)
/* Tell compiler that status block fields can change. */
barrier();
cons = *bnapi->hw_tx_cons_ptr;
+ barrier();
if (unlikely((cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT))
cons++;
return cons;
@@ -2879,6 +3038,7 @@ bnx2_get_hw_rx_cons(struct bnx2_napi *bnapi)
/* Tell compiler that status block fields can change. */
barrier();
cons = *bnapi->hw_rx_cons_ptr;
+ barrier();
if (unlikely((cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT))
cons++;
return cons;
@@ -3162,6 +3322,11 @@ bnx2_has_work(struct bnx2_napi *bnapi)
if (bnx2_has_fast_work(bnapi))
return 1;
+#ifdef BCM_CNIC
+ if (bnapi->cnic_present && (bnapi->cnic_tag != sblk->status_idx))
+ return 1;
+#endif
+
if ((sblk->status_attn_bits & STATUS_ATTN_EVENTS) !=
(sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS))
return 1;
@@ -3191,6 +3356,23 @@ bnx2_chk_missed_msi(struct bnx2 *bp)
bp->idle_chk_status_idx = bnapi->last_status_idx;
}
+#ifdef BCM_CNIC
+static void bnx2_poll_cnic(struct bnx2 *bp, struct bnx2_napi *bnapi)
+{
+ struct cnic_ops *c_ops;
+
+ if (!bnapi->cnic_present)
+ return;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops)
+ bnapi->cnic_tag = c_ops->cnic_handler(bp->cnic_data,
+ bnapi->status_blk.msi);
+ rcu_read_unlock();
+}
+#endif
+
static void bnx2_poll_link(struct bnx2 *bp, struct bnx2_napi *bnapi)
{
struct status_block *sblk = bnapi->status_blk.msi;
@@ -3265,6 +3447,10 @@ static int bnx2_poll(struct napi_struct *napi, int budget)
work_done = bnx2_poll_work(bp, bnapi, work_done, budget);
+#ifdef BCM_CNIC
+ bnx2_poll_cnic(bp, bnapi);
+#endif
+
/* bnapi->last_status_idx is used below to tell the hw how
* much work has been processed, so we must read it before
* checking for more work.
@@ -4630,8 +4816,11 @@ bnx2_init_chip(struct bnx2 *bp)
val = REG_RD(bp, BNX2_MQ_CONFIG);
val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256;
- if (CHIP_ID(bp) == CHIP_ID_5709_A0 || CHIP_ID(bp) == CHIP_ID_5709_A1)
- val |= BNX2_MQ_CONFIG_HALT_DIS;
+ if (CHIP_NUM(bp) == CHIP_NUM_5709) {
+ val |= BNX2_MQ_CONFIG_BIN_MQ_MODE;
+ if (CHIP_REV(bp) == CHIP_REV_Ax)
+ val |= BNX2_MQ_CONFIG_HALT_DIS;
+ }
REG_WR(bp, BNX2_MQ_CONFIG, val);
@@ -7469,7 +7658,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
INIT_WORK(&bp->reset_task, bnx2_reset_task);
dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0);
- mem_len = MB_GET_CID_ADDR(TX_TSS_CID + TX_MAX_TSS_RINGS);
+ mem_len = MB_GET_CID_ADDR(TX_TSS_CID + TX_MAX_TSS_RINGS + 1);
dev->mem_end = dev->mem_start + mem_len;
dev->irq = pdev->irq;
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index 5b570e17c839..a1ff739bc9b5 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -361,6 +361,9 @@ struct l2_fhdr {
#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE (1<<28)
#define BNX2_L2CTX_HOST_BDIDX 0x00000004
+#define BNX2_L2CTX_STATUSB_NUM_SHIFT 16
+#define BNX2_L2CTX_STATUSB_NUM(sb_id) \
+ (((sb_id) > 0) ? (((sb_id) + 7) << BNX2_L2CTX_STATUSB_NUM_SHIFT) : 0)
#define BNX2_L2CTX_HOST_BSEQ 0x00000008
#define BNX2_L2CTX_NX_BSEQ 0x0000000c
#define BNX2_L2CTX_NX_BDHADDR_HI 0x00000010
@@ -5900,6 +5903,7 @@ struct l2_fhdr {
#define BNX2_RXP_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
#define BNX2_RXP_SCRATCH 0x000e0000
+#define BNX2_RXP_SCRATCH_RXP_FLOOD 0x000e0024
#define BNX2_RXP_SCRATCH_RSS_TBL_SZ 0x000e0038
#define BNX2_RXP_SCRATCH_RSS_TBL 0x000e003c
#define BNX2_RXP_SCRATCH_RSS_TBL_MAX_ENTRIES 128
@@ -6678,6 +6682,11 @@ struct bnx2_napi {
u32 last_status_idx;
u32 int_num;
+#ifdef BCM_CNIC
+ u32 cnic_tag;
+ int cnic_present;
+#endif
+
struct bnx2_rx_ring_info rx_ring;
struct bnx2_tx_ring_info tx_ring;
};
@@ -6727,6 +6736,11 @@ struct bnx2 {
int tx_ring_size;
u32 tx_wake_thresh;
+#ifdef BCM_CNIC
+ struct cnic_ops *cnic_ops;
+ void *cnic_data;
+#endif
+
/* End of fields used in the performance code paths. */
unsigned int current_interval;
@@ -6885,6 +6899,10 @@ struct bnx2 {
u32 idle_chk_status_idx;
+#ifdef BCM_CNIC
+ struct cnic_eth_dev cnic_eth_dev;
+#endif
+
const struct firmware *mips_firmware;
const struct firmware *rv2p_firmware;
};
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 8c2e5ab51f08..faf094abef7f 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1465,6 +1465,12 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best,
return best;
}
+static int agg_device_up(const struct aggregator *agg)
+{
+ return (netif_running(agg->slave->dev) &&
+ netif_carrier_ok(agg->slave->dev));
+}
+
/**
* ad_agg_selection_logic - select an aggregation group for a team
* @aggregator: the aggregator we're looking at
@@ -1496,14 +1502,13 @@ static void ad_agg_selection_logic(struct aggregator *agg)
struct port *port;
origin = agg;
-
active = __get_active_agg(agg);
- best = active;
+ best = (active && agg_device_up(active)) ? active : NULL;
do {
agg->is_active = 0;
- if (agg->num_of_ports)
+ if (agg->num_of_ports && agg_device_up(agg))
best = ad_agg_selection_test(best, agg);
} while ((agg = __get_next_agg(agg)));
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 553a89919778..46d312bedfb8 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1706,10 +1706,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
* Called with RTNL
*/
int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
- __releases(&bond->curr_slave_lock)
- __releases(&bond->lock)
__acquires(&bond->lock)
- __acquires(&bond->curr_slave_lock)
+ __releases(&bond->lock)
{
struct bonding *bond = netdev_priv(bond_dev);
struct sockaddr *sa = addr;
@@ -1745,9 +1743,6 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
}
}
- write_unlock_bh(&bond->curr_slave_lock);
- read_unlock(&bond->lock);
-
if (swap_slave) {
alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave);
alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave);
@@ -1755,16 +1750,15 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr,
bond->alb_info.rlb_enabled);
+ read_lock(&bond->lock);
alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr);
if (bond->alb_info.rlb_enabled) {
/* inform clients mac address has changed */
rlb_req_update_slave_clients(bond, bond->curr_active_slave);
}
+ read_unlock(&bond->lock);
}
- read_lock(&bond->lock);
- write_lock_bh(&bond->curr_slave_lock);
-
return 0;
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 63369b6b14d4..74824028f85c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2213,33 +2213,24 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
- int i, found = 0;
-
- if (info->slave_id < 0) {
- return -ENODEV;
- }
+ int i, res = -ENODEV;
read_lock(&bond->lock);
bond_for_each_slave(bond, slave, i) {
if (i == (int)info->slave_id) {
- found = 1;
+ res = 0;
+ strcpy(info->slave_name, slave->dev->name);
+ info->link = slave->link;
+ info->state = slave->state;
+ info->link_failure_count = slave->link_failure_count;
break;
}
}
read_unlock(&bond->lock);
- if (found) {
- strcpy(info->slave_name, slave->dev->name);
- info->link = slave->link;
- info->state = slave->state;
- info->link_failure_count = slave->link_failure_count;
- } else {
- return -ENODEV;
- }
-
- return 0;
+ return res;
}
/*-------------------------------- Monitoring -------------------------------*/
@@ -5167,16 +5158,15 @@ int bond_create(char *name, struct bond_params *params)
up_write(&bonding_rwsem);
rtnl_unlock(); /* allows sysfs registration of net device */
res = bond_create_sysfs_entry(netdev_priv(bond_dev));
- if (res < 0) {
- rtnl_lock();
- down_write(&bonding_rwsem);
- bond_deinit(bond_dev);
- unregister_netdevice(bond_dev);
- goto out_rtnl;
- }
+ if (res < 0)
+ goto out_unreg;
return 0;
+out_unreg:
+ rtnl_lock();
+ down_write(&bonding_rwsem);
+ unregister_netdevice(bond_dev);
out_bond:
bond_deinit(bond_dev);
out_netdev:
@@ -5191,7 +5181,6 @@ static int __init bonding_init(void)
{
int i;
int res;
- struct bonding *bond;
printk(KERN_INFO "%s", version);
@@ -5222,13 +5211,6 @@ static int __init bonding_init(void)
goto out;
err:
- list_for_each_entry(bond, &bond_dev_list, bond_list) {
- bond_work_cancel_all(bond);
- destroy_workqueue(bond->wq);
- }
-
- bond_destroy_sysfs();
-
rtnl_lock();
bond_free_all();
rtnl_unlock();
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
new file mode 100644
index 000000000000..8d740376bbd2
--- /dev/null
+++ b/drivers/net/cnic.c
@@ -0,0 +1,2711 @@
+/* cnic.c: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Original skeleton written by: John(Zongxi) Chen (zongxi@broadcom.com)
+ * Modified and maintained by: Michael Chan <mchan@broadcom.com>
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/uio_driver.h>
+#include <linux/in.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define BCM_VLAN 1
+#endif
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/route.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <scsi/iscsi_if.h>
+
+#include "cnic_if.h"
+#include "bnx2.h"
+#include "cnic.h"
+#include "cnic_defs.h"
+
+#define DRV_MODULE_NAME "cnic"
+#define PFX DRV_MODULE_NAME ": "
+
+static char version[] __devinitdata =
+ "Broadcom NetXtreme II CNIC Driver " DRV_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Michael Chan <mchan@broadcom.com> and John(Zongxi) "
+ "Chen (zongxi@broadcom.com");
+MODULE_DESCRIPTION("Broadcom NetXtreme II CNIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CNIC_MODULE_VERSION);
+
+static LIST_HEAD(cnic_dev_list);
+static DEFINE_RWLOCK(cnic_dev_lock);
+static DEFINE_MUTEX(cnic_lock);
+
+static struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE];
+
+static int cnic_service_bnx2(void *, void *);
+static int cnic_ctl(void *, struct cnic_ctl_info *);
+
+static struct cnic_ops cnic_bnx2_ops = {
+ .cnic_owner = THIS_MODULE,
+ .cnic_handler = cnic_service_bnx2,
+ .cnic_ctl = cnic_ctl,
+};
+
+static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *);
+static void cnic_init_bnx2_tx_ring(struct cnic_dev *);
+static void cnic_init_bnx2_rx_ring(struct cnic_dev *);
+static int cnic_cm_set_pg(struct cnic_sock *);
+
+static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode)
+{
+ struct cnic_dev *dev = uinfo->priv;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (cp->uio_dev != -1)
+ return -EBUSY;
+
+ cp->uio_dev = iminor(inode);
+
+ cnic_shutdown_bnx2_rx_ring(dev);
+
+ cnic_init_bnx2_tx_ring(dev);
+ cnic_init_bnx2_rx_ring(dev);
+
+ return 0;
+}
+
+static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode)
+{
+ struct cnic_dev *dev = uinfo->priv;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ cp->uio_dev = -1;
+ return 0;
+}
+
+static inline void cnic_hold(struct cnic_dev *dev)
+{
+ atomic_inc(&dev->ref_count);
+}
+
+static inline void cnic_put(struct cnic_dev *dev)
+{
+ atomic_dec(&dev->ref_count);
+}
+
+static inline void csk_hold(struct cnic_sock *csk)
+{
+ atomic_inc(&csk->ref_count);
+}
+
+static inline void csk_put(struct cnic_sock *csk)
+{
+ atomic_dec(&csk->ref_count);
+}
+
+static struct cnic_dev *cnic_from_netdev(struct net_device *netdev)
+{
+ struct cnic_dev *cdev;
+
+ read_lock(&cnic_dev_lock);
+ list_for_each_entry(cdev, &cnic_dev_list, list) {
+ if (netdev == cdev->netdev) {
+ cnic_hold(cdev);
+ read_unlock(&cnic_dev_lock);
+ return cdev;
+ }
+ }
+ read_unlock(&cnic_dev_lock);
+ return NULL;
+}
+
+static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+ struct drv_ctl_io *io = &info.data.io;
+
+ info.cmd = DRV_CTL_CTX_WR_CMD;
+ io->cid_addr = cid_addr;
+ io->offset = off;
+ io->data = val;
+ ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static void cnic_reg_wr_ind(struct cnic_dev *dev, u32 off, u32 val)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+ struct drv_ctl_io *io = &info.data.io;
+
+ info.cmd = DRV_CTL_IO_WR_CMD;
+ io->offset = off;
+ io->data = val;
+ ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static u32 cnic_reg_rd_ind(struct cnic_dev *dev, u32 off)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+ struct drv_ctl_io *io = &info.data.io;
+
+ info.cmd = DRV_CTL_IO_RD_CMD;
+ io->offset = off;
+ ethdev->drv_ctl(dev->netdev, &info);
+ return io->data;
+}
+
+static int cnic_in_use(struct cnic_sock *csk)
+{
+ return test_bit(SK_F_INUSE, &csk->flags);
+}
+
+static void cnic_kwq_completion(struct cnic_dev *dev, u32 count)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+
+ info.cmd = DRV_CTL_COMPLETION_CMD;
+ info.data.comp.comp_count = count;
+ ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static int cnic_send_nlmsg(struct cnic_local *cp, u32 type,
+ struct cnic_sock *csk)
+{
+ struct iscsi_path path_req;
+ char *buf = NULL;
+ u16 len = 0;
+ u32 msg_type = ISCSI_KEVENT_IF_DOWN;
+ struct cnic_ulp_ops *ulp_ops;
+
+ if (cp->uio_dev == -1)
+ return -ENODEV;
+
+ if (csk) {
+ len = sizeof(path_req);
+ buf = (char *) &path_req;
+ memset(&path_req, 0, len);
+
+ msg_type = ISCSI_KEVENT_PATH_REQ;
+ path_req.handle = (u64) csk->l5_cid;
+ if (test_bit(SK_F_IPV6, &csk->flags)) {
+ memcpy(&path_req.dst.v6_addr, &csk->dst_ip[0],
+ sizeof(struct in6_addr));
+ path_req.ip_addr_len = 16;
+ } else {
+ memcpy(&path_req.dst.v4_addr, &csk->dst_ip[0],
+ sizeof(struct in_addr));
+ path_req.ip_addr_len = 4;
+ }
+ path_req.vlan_id = csk->vlan_id;
+ path_req.pmtu = csk->mtu;
+ }
+
+ rcu_read_lock();
+ ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]);
+ if (ulp_ops)
+ ulp_ops->iscsi_nl_send_msg(cp->dev, msg_type, buf, len);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int cnic_iscsi_nl_msg_recv(struct cnic_dev *dev, u32 msg_type,
+ char *buf, u16 len)
+{
+ int rc = -EINVAL;
+
+ switch (msg_type) {
+ case ISCSI_UEVENT_PATH_UPDATE: {
+ struct cnic_local *cp;
+ u32 l5_cid;
+ struct cnic_sock *csk;
+ struct iscsi_path *path_resp;
+
+ if (len < sizeof(*path_resp))
+ break;
+
+ path_resp = (struct iscsi_path *) buf;
+ cp = dev->cnic_priv;
+ l5_cid = (u32) path_resp->handle;
+ if (l5_cid >= MAX_CM_SK_TBL_SZ)
+ break;
+
+ csk = &cp->csk_tbl[l5_cid];
+ csk_hold(csk);
+ if (cnic_in_use(csk)) {
+ memcpy(csk->ha, path_resp->mac_addr, 6);
+ if (test_bit(SK_F_IPV6, &csk->flags))
+ memcpy(&csk->src_ip[0], &path_resp->src.v6_addr,
+ sizeof(struct in6_addr));
+ else
+ memcpy(&csk->src_ip[0], &path_resp->src.v4_addr,
+ sizeof(struct in_addr));
+ if (is_valid_ether_addr(csk->ha))
+ cnic_cm_set_pg(csk);
+ }
+ csk_put(csk);
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+static int cnic_offld_prep(struct cnic_sock *csk)
+{
+ if (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+ return 0;
+
+ if (!test_bit(SK_F_CONNECT_START, &csk->flags)) {
+ clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int cnic_close_prep(struct cnic_sock *csk)
+{
+ clear_bit(SK_F_CONNECT_START, &csk->flags);
+ smp_mb__after_clear_bit();
+
+ if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags)) {
+ while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+ msleep(1);
+
+ return 1;
+ }
+ return 0;
+}
+
+static int cnic_abort_prep(struct cnic_sock *csk)
+{
+ clear_bit(SK_F_CONNECT_START, &csk->flags);
+ smp_mb__after_clear_bit();
+
+ while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+ msleep(1);
+
+ if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags)) {
+ csk->state = L4_KCQE_OPCODE_VALUE_RESET_COMP;
+ return 1;
+ }
+
+ return 0;
+}
+
+int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
+{
+ struct cnic_dev *dev;
+
+ if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
+ ulp_type);
+ return -EINVAL;
+ }
+ mutex_lock(&cnic_lock);
+ if (cnic_ulp_tbl[ulp_type]) {
+ printk(KERN_ERR PFX "cnic_register_driver: Type %d has already "
+ "been registered\n", ulp_type);
+ mutex_unlock(&cnic_lock);
+ return -EBUSY;
+ }
+
+ read_lock(&cnic_dev_lock);
+ list_for_each_entry(dev, &cnic_dev_list, list) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ clear_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]);
+ }
+ read_unlock(&cnic_dev_lock);
+
+ rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
+ mutex_unlock(&cnic_lock);
+
+ /* Prevent race conditions with netdev_event */
+ rtnl_lock();
+ read_lock(&cnic_dev_lock);
+ list_for_each_entry(dev, &cnic_dev_list, list) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]))
+ ulp_ops->cnic_init(dev);
+ }
+ read_unlock(&cnic_dev_lock);
+ rtnl_unlock();
+
+ return 0;
+}
+
+int cnic_unregister_driver(int ulp_type)
+{
+ struct cnic_dev *dev;
+
+ if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
+ ulp_type);
+ return -EINVAL;
+ }
+ mutex_lock(&cnic_lock);
+ if (!cnic_ulp_tbl[ulp_type]) {
+ printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
+ "been registered\n", ulp_type);
+ goto out_unlock;
+ }
+ read_lock(&cnic_dev_lock);
+ list_for_each_entry(dev, &cnic_dev_list, list) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+ printk(KERN_ERR PFX "cnic_unregister_driver: Type %d "
+ "still has devices registered\n", ulp_type);
+ read_unlock(&cnic_dev_lock);
+ goto out_unlock;
+ }
+ }
+ read_unlock(&cnic_dev_lock);
+
+ rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL);
+
+ mutex_unlock(&cnic_lock);
+ synchronize_rcu();
+ return 0;
+
+out_unlock:
+ mutex_unlock(&cnic_lock);
+ return -EINVAL;
+}
+
+static int cnic_start_hw(struct cnic_dev *);
+static void cnic_stop_hw(struct cnic_dev *);
+
+static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
+ void *ulp_ctx)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_ulp_ops *ulp_ops;
+
+ if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
+ ulp_type);
+ return -EINVAL;
+ }
+ mutex_lock(&cnic_lock);
+ if (cnic_ulp_tbl[ulp_type] == NULL) {
+ printk(KERN_ERR PFX "cnic_register_device: Driver with type %d "
+ "has not been registered\n", ulp_type);
+ mutex_unlock(&cnic_lock);
+ return -EAGAIN;
+ }
+ if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+ printk(KERN_ERR PFX "cnic_register_device: Type %d has already "
+ "been registered to this device\n", ulp_type);
+ mutex_unlock(&cnic_lock);
+ return -EBUSY;
+ }
+
+ clear_bit(ULP_F_START, &cp->ulp_flags[ulp_type]);
+ cp->ulp_handle[ulp_type] = ulp_ctx;
+ ulp_ops = cnic_ulp_tbl[ulp_type];
+ rcu_assign_pointer(cp->ulp_ops[ulp_type], ulp_ops);
+ cnic_hold(dev);
+
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[ulp_type]))
+ ulp_ops->cnic_start(cp->ulp_handle[ulp_type]);
+
+ mutex_unlock(&cnic_lock);
+
+ return 0;
+
+}
+EXPORT_SYMBOL(cnic_register_driver);
+
+static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
+ ulp_type);
+ return -EINVAL;
+ }
+ mutex_lock(&cnic_lock);
+ if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+ rcu_assign_pointer(cp->ulp_ops[ulp_type], NULL);
+ cnic_put(dev);
+ } else {
+ printk(KERN_ERR PFX "cnic_unregister_device: device not "
+ "registered to this ulp type %d\n", ulp_type);
+ mutex_unlock(&cnic_lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&cnic_lock);
+
+ synchronize_rcu();
+
+ return 0;
+}
+EXPORT_SYMBOL(cnic_unregister_driver);
+
+static int cnic_init_id_tbl(struct cnic_id_tbl *id_tbl, u32 size, u32 start_id)
+{
+ id_tbl->start = start_id;
+ id_tbl->max = size;
+ id_tbl->next = 0;
+ spin_lock_init(&id_tbl->lock);
+ id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL);
+ if (!id_tbl->table)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void cnic_free_id_tbl(struct cnic_id_tbl *id_tbl)
+{
+ kfree(id_tbl->table);
+ id_tbl->table = NULL;
+}
+
+static int cnic_alloc_id(struct cnic_id_tbl *id_tbl, u32 id)
+{
+ int ret = -1;
+
+ id -= id_tbl->start;
+ if (id >= id_tbl->max)
+ return ret;
+
+ spin_lock(&id_tbl->lock);
+ if (!test_bit(id, id_tbl->table)) {
+ set_bit(id, id_tbl->table);
+ ret = 0;
+ }
+ spin_unlock(&id_tbl->lock);
+ return ret;
+}
+
+/* Returns -1 if not successful */
+static u32 cnic_alloc_new_id(struct cnic_id_tbl *id_tbl)
+{
+ u32 id;
+
+ spin_lock(&id_tbl->lock);
+ id = find_next_zero_bit(id_tbl->table, id_tbl->max, id_tbl->next);
+ if (id >= id_tbl->max) {
+ id = -1;
+ if (id_tbl->next != 0) {
+ id = find_first_zero_bit(id_tbl->table, id_tbl->next);
+ if (id >= id_tbl->next)
+ id = -1;
+ }
+ }
+
+ if (id < id_tbl->max) {
+ set_bit(id, id_tbl->table);
+ id_tbl->next = (id + 1) & (id_tbl->max - 1);
+ id += id_tbl->start;
+ }
+
+ spin_unlock(&id_tbl->lock);
+
+ return id;
+}
+
+static void cnic_free_id(struct cnic_id_tbl *id_tbl, u32 id)
+{
+ if (id == -1)
+ return;
+
+ id -= id_tbl->start;
+ if (id >= id_tbl->max)
+ return;
+
+ clear_bit(id, id_tbl->table);
+}
+
+static void cnic_free_dma(struct cnic_dev *dev, struct cnic_dma *dma)
+{
+ int i;
+
+ if (!dma->pg_arr)
+ return;
+
+ for (i = 0; i < dma->num_pages; i++) {
+ if (dma->pg_arr[i]) {
+ pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE,
+ dma->pg_arr[i], dma->pg_map_arr[i]);
+ dma->pg_arr[i] = NULL;
+ }
+ }
+ if (dma->pgtbl) {
+ pci_free_consistent(dev->pcidev, dma->pgtbl_size,
+ dma->pgtbl, dma->pgtbl_map);
+ dma->pgtbl = NULL;
+ }
+ kfree(dma->pg_arr);
+ dma->pg_arr = NULL;
+ dma->num_pages = 0;
+}
+
+static void cnic_setup_page_tbl(struct cnic_dev *dev, struct cnic_dma *dma)
+{
+ int i;
+ u32 *page_table = dma->pgtbl;
+
+ for (i = 0; i < dma->num_pages; i++) {
+ /* Each entry needs to be in big endian format. */
+ *page_table = (u32) ((u64) dma->pg_map_arr[i] >> 32);
+ page_table++;
+ *page_table = (u32) dma->pg_map_arr[i];
+ page_table++;
+ }
+}
+
+static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma,
+ int pages, int use_pg_tbl)
+{
+ int i, size;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ size = pages * (sizeof(void *) + sizeof(dma_addr_t));
+ dma->pg_arr = kzalloc(size, GFP_ATOMIC);
+ if (dma->pg_arr == NULL)
+ return -ENOMEM;
+
+ dma->pg_map_arr = (dma_addr_t *) (dma->pg_arr + pages);
+ dma->num_pages = pages;
+
+ for (i = 0; i < pages; i++) {
+ dma->pg_arr[i] = pci_alloc_consistent(dev->pcidev,
+ BCM_PAGE_SIZE,
+ &dma->pg_map_arr[i]);
+ if (dma->pg_arr[i] == NULL)
+ goto error;
+ }
+ if (!use_pg_tbl)
+ return 0;
+
+ dma->pgtbl_size = ((pages * 8) + BCM_PAGE_SIZE - 1) &
+ ~(BCM_PAGE_SIZE - 1);
+ dma->pgtbl = pci_alloc_consistent(dev->pcidev, dma->pgtbl_size,
+ &dma->pgtbl_map);
+ if (dma->pgtbl == NULL)
+ goto error;
+
+ cp->setup_pgtbl(dev, dma);
+
+ return 0;
+
+error:
+ cnic_free_dma(dev, dma);
+ return -ENOMEM;
+}
+
+static void cnic_free_resc(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int i = 0;
+
+ if (cp->cnic_uinfo) {
+ cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+ while (cp->uio_dev != -1 && i < 15) {
+ msleep(100);
+ i++;
+ }
+ uio_unregister_device(cp->cnic_uinfo);
+ kfree(cp->cnic_uinfo);
+ cp->cnic_uinfo = NULL;
+ }
+
+ if (cp->l2_buf) {
+ pci_free_consistent(dev->pcidev, cp->l2_buf_size,
+ cp->l2_buf, cp->l2_buf_map);
+ cp->l2_buf = NULL;
+ }
+
+ if (cp->l2_ring) {
+ pci_free_consistent(dev->pcidev, cp->l2_ring_size,
+ cp->l2_ring, cp->l2_ring_map);
+ cp->l2_ring = NULL;
+ }
+
+ for (i = 0; i < cp->ctx_blks; i++) {
+ if (cp->ctx_arr[i].ctx) {
+ pci_free_consistent(dev->pcidev, cp->ctx_blk_size,
+ cp->ctx_arr[i].ctx,
+ cp->ctx_arr[i].mapping);
+ cp->ctx_arr[i].ctx = NULL;
+ }
+ }
+ kfree(cp->ctx_arr);
+ cp->ctx_arr = NULL;
+ cp->ctx_blks = 0;
+
+ cnic_free_dma(dev, &cp->gbl_buf_info);
+ cnic_free_dma(dev, &cp->conn_buf_info);
+ cnic_free_dma(dev, &cp->kwq_info);
+ cnic_free_dma(dev, &cp->kcq_info);
+ kfree(cp->iscsi_tbl);
+ cp->iscsi_tbl = NULL;
+ kfree(cp->ctx_tbl);
+ cp->ctx_tbl = NULL;
+
+ cnic_free_id_tbl(&cp->cid_tbl);
+}
+
+static int cnic_alloc_context(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (CHIP_NUM(cp) == CHIP_NUM_5709) {
+ int i, k, arr_size;
+
+ cp->ctx_blk_size = BCM_PAGE_SIZE;
+ cp->cids_per_blk = BCM_PAGE_SIZE / 128;
+ arr_size = BNX2_MAX_CID / cp->cids_per_blk *
+ sizeof(struct cnic_ctx);
+ cp->ctx_arr = kzalloc(arr_size, GFP_KERNEL);
+ if (cp->ctx_arr == NULL)
+ return -ENOMEM;
+
+ k = 0;
+ for (i = 0; i < 2; i++) {
+ u32 j, reg, off, lo, hi;
+
+ if (i == 0)
+ off = BNX2_PG_CTX_MAP;
+ else
+ off = BNX2_ISCSI_CTX_MAP;
+
+ reg = cnic_reg_rd_ind(dev, off);
+ lo = reg >> 16;
+ hi = reg & 0xffff;
+ for (j = lo; j < hi; j += cp->cids_per_blk, k++)
+ cp->ctx_arr[k].cid = j;
+ }
+
+ cp->ctx_blks = k;
+ if (cp->ctx_blks >= (BNX2_MAX_CID / cp->cids_per_blk)) {
+ cp->ctx_blks = 0;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < cp->ctx_blks; i++) {
+ cp->ctx_arr[i].ctx =
+ pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE,
+ &cp->ctx_arr[i].mapping);
+ if (cp->ctx_arr[i].ctx == NULL)
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct uio_info *uinfo;
+ int ret;
+
+ ret = cnic_alloc_dma(dev, &cp->kwq_info, KWQ_PAGE_CNT, 1);
+ if (ret)
+ goto error;
+ cp->kwq = (struct kwqe **) cp->kwq_info.pg_arr;
+
+ ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 1);
+ if (ret)
+ goto error;
+ cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr;
+
+ ret = cnic_alloc_context(dev);
+ if (ret)
+ goto error;
+
+ cp->l2_ring_size = 2 * BCM_PAGE_SIZE;
+ cp->l2_ring = pci_alloc_consistent(dev->pcidev, cp->l2_ring_size,
+ &cp->l2_ring_map);
+ if (!cp->l2_ring)
+ goto error;
+
+ cp->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size;
+ cp->l2_buf_size = PAGE_ALIGN(cp->l2_buf_size);
+ cp->l2_buf = pci_alloc_consistent(dev->pcidev, cp->l2_buf_size,
+ &cp->l2_buf_map);
+ if (!cp->l2_buf)
+ goto error;
+
+ uinfo = kzalloc(sizeof(*uinfo), GFP_ATOMIC);
+ if (!uinfo)
+ goto error;
+
+ uinfo->mem[0].addr = dev->netdev->base_addr;
+ uinfo->mem[0].internal_addr = dev->regview;
+ uinfo->mem[0].size = dev->netdev->mem_end - dev->netdev->mem_start;
+ uinfo->mem[0].memtype = UIO_MEM_PHYS;
+
+ uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK;
+ if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX)
+ uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9;
+ else
+ uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE;
+ uinfo->mem[1].memtype = UIO_MEM_LOGICAL;
+
+ uinfo->mem[2].addr = (unsigned long) cp->l2_ring;
+ uinfo->mem[2].size = cp->l2_ring_size;
+ uinfo->mem[2].memtype = UIO_MEM_LOGICAL;
+
+ uinfo->mem[3].addr = (unsigned long) cp->l2_buf;
+ uinfo->mem[3].size = cp->l2_buf_size;
+ uinfo->mem[3].memtype = UIO_MEM_LOGICAL;
+
+ uinfo->name = "bnx2_cnic";
+ uinfo->version = CNIC_MODULE_VERSION;
+ uinfo->irq = UIO_IRQ_CUSTOM;
+
+ uinfo->open = cnic_uio_open;
+ uinfo->release = cnic_uio_close;
+
+ uinfo->priv = dev;
+
+ ret = uio_register_device(&dev->pcidev->dev, uinfo);
+ if (ret) {
+ kfree(uinfo);
+ goto error;
+ }
+
+ cp->cnic_uinfo = uinfo;
+
+ return 0;
+
+error:
+ cnic_free_resc(dev);
+ return ret;
+}
+
+static inline u32 cnic_kwq_avail(struct cnic_local *cp)
+{
+ return cp->max_kwq_idx -
+ ((cp->kwq_prod_idx - cp->kwq_con_idx) & cp->max_kwq_idx);
+}
+
+static int cnic_submit_bnx2_kwqes(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num_wqes)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct kwqe *prod_qe;
+ u16 prod, sw_prod, i;
+
+ if (!test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ return -EAGAIN; /* bnx2 is down */
+
+ spin_lock_bh(&cp->cnic_ulp_lock);
+ if (num_wqes > cnic_kwq_avail(cp) &&
+ !(cp->cnic_local_flags & CNIC_LCL_FL_KWQ_INIT)) {
+ spin_unlock_bh(&cp->cnic_ulp_lock);
+ return -EAGAIN;
+ }
+
+ cp->cnic_local_flags &= ~CNIC_LCL_FL_KWQ_INIT;
+
+ prod = cp->kwq_prod_idx;
+ sw_prod = prod & MAX_KWQ_IDX;
+ for (i = 0; i < num_wqes; i++) {
+ prod_qe = &cp->kwq[KWQ_PG(sw_prod)][KWQ_IDX(sw_prod)];
+ memcpy(prod_qe, wqes[i], sizeof(struct kwqe));
+ prod++;
+ sw_prod = prod & MAX_KWQ_IDX;
+ }
+ cp->kwq_prod_idx = prod;
+
+ CNIC_WR16(dev, cp->kwq_io_addr, cp->kwq_prod_idx);
+
+ spin_unlock_bh(&cp->cnic_ulp_lock);
+ return 0;
+}
+
+static void service_kcqes(struct cnic_dev *dev, int num_cqes)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int i, j;
+
+ i = 0;
+ j = 1;
+ while (num_cqes) {
+ struct cnic_ulp_ops *ulp_ops;
+ int ulp_type;
+ u32 kcqe_op_flag = cp->completed_kcq[i]->kcqe_op_flag;
+ u32 kcqe_layer = kcqe_op_flag & KCQE_FLAGS_LAYER_MASK;
+
+ if (unlikely(kcqe_op_flag & KCQE_RAMROD_COMPLETION))
+ cnic_kwq_completion(dev, 1);
+
+ while (j < num_cqes) {
+ u32 next_op = cp->completed_kcq[i + j]->kcqe_op_flag;
+
+ if ((next_op & KCQE_FLAGS_LAYER_MASK) != kcqe_layer)
+ break;
+
+ if (unlikely(next_op & KCQE_RAMROD_COMPLETION))
+ cnic_kwq_completion(dev, 1);
+ j++;
+ }
+
+ if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L5_RDMA)
+ ulp_type = CNIC_ULP_RDMA;
+ else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L5_ISCSI)
+ ulp_type = CNIC_ULP_ISCSI;
+ else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L4)
+ ulp_type = CNIC_ULP_L4;
+ else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L2)
+ goto end;
+ else {
+ printk(KERN_ERR PFX "%s: Unknown type of KCQE(0x%x)\n",
+ dev->netdev->name, kcqe_op_flag);
+ goto end;
+ }
+
+ rcu_read_lock();
+ ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+ if (likely(ulp_ops)) {
+ ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type],
+ cp->completed_kcq + i, j);
+ }
+ rcu_read_unlock();
+end:
+ num_cqes -= j;
+ i += j;
+ j = 1;
+ }
+ return;
+}
+
+static u16 cnic_bnx2_next_idx(u16 idx)
+{
+ return idx + 1;
+}
+
+static u16 cnic_bnx2_hw_idx(u16 idx)
+{
+ return idx;
+}
+
+static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u16 i, ri, last;
+ struct kcqe *kcqe;
+ int kcqe_cnt = 0, last_cnt = 0;
+
+ i = ri = last = *sw_prod;
+ ri &= MAX_KCQ_IDX;
+
+ while ((i != hw_prod) && (kcqe_cnt < MAX_COMPLETED_KCQE)) {
+ kcqe = &cp->kcq[KCQ_PG(ri)][KCQ_IDX(ri)];
+ cp->completed_kcq[kcqe_cnt++] = kcqe;
+ i = cp->next_idx(i);
+ ri = i & MAX_KCQ_IDX;
+ if (likely(!(kcqe->kcqe_op_flag & KCQE_FLAGS_NEXT))) {
+ last_cnt = kcqe_cnt;
+ last = i;
+ }
+ }
+
+ *sw_prod = last;
+ return last_cnt;
+}
+
+static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp)
+{
+ u16 rx_cons = *cp->rx_cons_ptr;
+ u16 tx_cons = *cp->tx_cons_ptr;
+
+ if (cp->tx_cons != tx_cons || cp->rx_cons != rx_cons) {
+ cp->tx_cons = tx_cons;
+ cp->rx_cons = rx_cons;
+ uio_event_notify(cp->cnic_uinfo);
+ }
+}
+
+static int cnic_service_bnx2(void *data, void *status_blk)
+{
+ struct cnic_dev *dev = data;
+ struct status_block *sblk = status_blk;
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 status_idx = sblk->status_idx;
+ u16 hw_prod, sw_prod;
+ int kcqe_cnt;
+
+ if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+ return status_idx;
+
+ cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
+
+ hw_prod = sblk->status_completion_producer_index;
+ sw_prod = cp->kcq_prod_idx;
+ while (sw_prod != hw_prod) {
+ kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod);
+ if (kcqe_cnt == 0)
+ goto done;
+
+ service_kcqes(dev, kcqe_cnt);
+
+ /* Tell compiler that status_blk fields can change. */
+ barrier();
+ if (status_idx != sblk->status_idx) {
+ status_idx = sblk->status_idx;
+ cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
+ hw_prod = sblk->status_completion_producer_index;
+ } else
+ break;
+ }
+
+done:
+ CNIC_WR16(dev, cp->kcq_io_addr, sw_prod);
+
+ cp->kcq_prod_idx = sw_prod;
+
+ cnic_chk_bnx2_pkt_rings(cp);
+ return status_idx;
+}
+
+static void cnic_service_bnx2_msix(unsigned long data)
+{
+ struct cnic_dev *dev = (struct cnic_dev *) data;
+ struct cnic_local *cp = dev->cnic_priv;
+ struct status_block_msix *status_blk = cp->bnx2_status_blk;
+ u32 status_idx = status_blk->status_idx;
+ u16 hw_prod, sw_prod;
+ int kcqe_cnt;
+
+ cp->kwq_con_idx = status_blk->status_cmd_consumer_index;
+
+ hw_prod = status_blk->status_completion_producer_index;
+ sw_prod = cp->kcq_prod_idx;
+ while (sw_prod != hw_prod) {
+ kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod);
+ if (kcqe_cnt == 0)
+ goto done;
+
+ service_kcqes(dev, kcqe_cnt);
+
+ /* Tell compiler that status_blk fields can change. */
+ barrier();
+ if (status_idx != status_blk->status_idx) {
+ status_idx = status_blk->status_idx;
+ cp->kwq_con_idx = status_blk->status_cmd_consumer_index;
+ hw_prod = status_blk->status_completion_producer_index;
+ } else
+ break;
+ }
+
+done:
+ CNIC_WR16(dev, cp->kcq_io_addr, sw_prod);
+ cp->kcq_prod_idx = sw_prod;
+
+ cnic_chk_bnx2_pkt_rings(cp);
+
+ cp->last_status_idx = status_idx;
+ CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+ BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | cp->last_status_idx);
+}
+
+static irqreturn_t cnic_irq(int irq, void *dev_instance)
+{
+ struct cnic_dev *dev = dev_instance;
+ struct cnic_local *cp = dev->cnic_priv;
+ u16 prod = cp->kcq_prod_idx & MAX_KCQ_IDX;
+
+ if (cp->ack_int)
+ cp->ack_int(dev);
+
+ prefetch(cp->status_blk);
+ prefetch(&cp->kcq[KCQ_PG(prod)][KCQ_IDX(prod)]);
+
+ if (likely(test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+ tasklet_schedule(&cp->cnic_irq_task);
+
+ return IRQ_HANDLED;
+}
+
+static void cnic_ulp_stop(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int if_type;
+
+ rcu_read_lock();
+ for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+ struct cnic_ulp_ops *ulp_ops;
+
+ ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+ if (!ulp_ops)
+ continue;
+
+ if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+ ulp_ops->cnic_stop(cp->ulp_handle[if_type]);
+ }
+ rcu_read_unlock();
+}
+
+static void cnic_ulp_start(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int if_type;
+
+ rcu_read_lock();
+ for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+ struct cnic_ulp_ops *ulp_ops;
+
+ ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+ if (!ulp_ops || !ulp_ops->cnic_start)
+ continue;
+
+ if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+ ulp_ops->cnic_start(cp->ulp_handle[if_type]);
+ }
+ rcu_read_unlock();
+}
+
+static int cnic_ctl(void *data, struct cnic_ctl_info *info)
+{
+ struct cnic_dev *dev = data;
+
+ switch (info->cmd) {
+ case CNIC_CTL_STOP_CMD:
+ cnic_hold(dev);
+ mutex_lock(&cnic_lock);
+
+ cnic_ulp_stop(dev);
+ cnic_stop_hw(dev);
+
+ mutex_unlock(&cnic_lock);
+ cnic_put(dev);
+ break;
+ case CNIC_CTL_START_CMD:
+ cnic_hold(dev);
+ mutex_lock(&cnic_lock);
+
+ if (!cnic_start_hw(dev))
+ cnic_ulp_start(dev);
+
+ mutex_unlock(&cnic_lock);
+ cnic_put(dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void cnic_ulp_init(struct cnic_dev *dev)
+{
+ int i;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ rcu_read_lock();
+ for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+ struct cnic_ulp_ops *ulp_ops;
+
+ ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+ if (!ulp_ops || !ulp_ops->cnic_init)
+ continue;
+
+ if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+ ulp_ops->cnic_init(dev);
+
+ }
+ rcu_read_unlock();
+}
+
+static void cnic_ulp_exit(struct cnic_dev *dev)
+{
+ int i;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ rcu_read_lock();
+ for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+ struct cnic_ulp_ops *ulp_ops;
+
+ ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+ if (!ulp_ops || !ulp_ops->cnic_exit)
+ continue;
+
+ if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+ ulp_ops->cnic_exit(dev);
+
+ }
+ rcu_read_unlock();
+}
+
+static int cnic_cm_offload_pg(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_offload_pg *l4kwqe;
+ struct kwqe *wqes[1];
+
+ l4kwqe = (struct l4_kwq_offload_pg *) &csk->kwqe1;
+ memset(l4kwqe, 0, sizeof(*l4kwqe));
+ wqes[0] = (struct kwqe *) l4kwqe;
+
+ l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_OFFLOAD_PG;
+ l4kwqe->flags =
+ L4_LAYER_CODE << L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT;
+ l4kwqe->l2hdr_nbytes = ETH_HLEN;
+
+ l4kwqe->da0 = csk->ha[0];
+ l4kwqe->da1 = csk->ha[1];
+ l4kwqe->da2 = csk->ha[2];
+ l4kwqe->da3 = csk->ha[3];
+ l4kwqe->da4 = csk->ha[4];
+ l4kwqe->da5 = csk->ha[5];
+
+ l4kwqe->sa0 = dev->mac_addr[0];
+ l4kwqe->sa1 = dev->mac_addr[1];
+ l4kwqe->sa2 = dev->mac_addr[2];
+ l4kwqe->sa3 = dev->mac_addr[3];
+ l4kwqe->sa4 = dev->mac_addr[4];
+ l4kwqe->sa5 = dev->mac_addr[5];
+
+ l4kwqe->etype = ETH_P_IP;
+ l4kwqe->ipid_count = DEF_IPID_COUNT;
+ l4kwqe->host_opaque = csk->l5_cid;
+
+ if (csk->vlan_id) {
+ l4kwqe->pg_flags |= L4_KWQ_OFFLOAD_PG_VLAN_TAGGING;
+ l4kwqe->vlan_tag = csk->vlan_id;
+ l4kwqe->l2hdr_nbytes += 4;
+ }
+
+ return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_update_pg(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_update_pg *l4kwqe;
+ struct kwqe *wqes[1];
+
+ l4kwqe = (struct l4_kwq_update_pg *) &csk->kwqe1;
+ memset(l4kwqe, 0, sizeof(*l4kwqe));
+ wqes[0] = (struct kwqe *) l4kwqe;
+
+ l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPDATE_PG;
+ l4kwqe->flags =
+ L4_LAYER_CODE << L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT;
+ l4kwqe->pg_cid = csk->pg_cid;
+
+ l4kwqe->da0 = csk->ha[0];
+ l4kwqe->da1 = csk->ha[1];
+ l4kwqe->da2 = csk->ha[2];
+ l4kwqe->da3 = csk->ha[3];
+ l4kwqe->da4 = csk->ha[4];
+ l4kwqe->da5 = csk->ha[5];
+
+ l4kwqe->pg_host_opaque = csk->l5_cid;
+ l4kwqe->pg_valids = L4_KWQ_UPDATE_PG_VALIDS_DA;
+
+ return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_upload_pg(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_upload *l4kwqe;
+ struct kwqe *wqes[1];
+
+ l4kwqe = (struct l4_kwq_upload *) &csk->kwqe1;
+ memset(l4kwqe, 0, sizeof(*l4kwqe));
+ wqes[0] = (struct kwqe *) l4kwqe;
+
+ l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPLOAD_PG;
+ l4kwqe->flags =
+ L4_LAYER_CODE << L4_KWQ_UPLOAD_LAYER_CODE_SHIFT;
+ l4kwqe->cid = csk->pg_cid;
+
+ return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_conn_req(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_connect_req1 *l4kwqe1;
+ struct l4_kwq_connect_req2 *l4kwqe2;
+ struct l4_kwq_connect_req3 *l4kwqe3;
+ struct kwqe *wqes[3];
+ u8 tcp_flags = 0;
+ int num_wqes = 2;
+
+ l4kwqe1 = (struct l4_kwq_connect_req1 *) &csk->kwqe1;
+ l4kwqe2 = (struct l4_kwq_connect_req2 *) &csk->kwqe2;
+ l4kwqe3 = (struct l4_kwq_connect_req3 *) &csk->kwqe3;
+ memset(l4kwqe1, 0, sizeof(*l4kwqe1));
+ memset(l4kwqe2, 0, sizeof(*l4kwqe2));
+ memset(l4kwqe3, 0, sizeof(*l4kwqe3));
+
+ l4kwqe3->op_code = L4_KWQE_OPCODE_VALUE_CONNECT3;
+ l4kwqe3->flags =
+ L4_LAYER_CODE << L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT;
+ l4kwqe3->ka_timeout = csk->ka_timeout;
+ l4kwqe3->ka_interval = csk->ka_interval;
+ l4kwqe3->ka_max_probe_count = csk->ka_max_probe_count;
+ l4kwqe3->tos = csk->tos;
+ l4kwqe3->ttl = csk->ttl;
+ l4kwqe3->snd_seq_scale = csk->snd_seq_scale;
+ l4kwqe3->pmtu = csk->mtu;
+ l4kwqe3->rcv_buf = csk->rcv_buf;
+ l4kwqe3->snd_buf = csk->snd_buf;
+ l4kwqe3->seed = csk->seed;
+
+ wqes[0] = (struct kwqe *) l4kwqe1;
+ if (test_bit(SK_F_IPV6, &csk->flags)) {
+ wqes[1] = (struct kwqe *) l4kwqe2;
+ wqes[2] = (struct kwqe *) l4kwqe3;
+ num_wqes = 3;
+
+ l4kwqe1->conn_flags = L4_KWQ_CONNECT_REQ1_IP_V6;
+ l4kwqe2->op_code = L4_KWQE_OPCODE_VALUE_CONNECT2;
+ l4kwqe2->flags =
+ L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT |
+ L4_LAYER_CODE << L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT;
+ l4kwqe2->src_ip_v6_2 = be32_to_cpu(csk->src_ip[1]);
+ l4kwqe2->src_ip_v6_3 = be32_to_cpu(csk->src_ip[2]);
+ l4kwqe2->src_ip_v6_4 = be32_to_cpu(csk->src_ip[3]);
+ l4kwqe2->dst_ip_v6_2 = be32_to_cpu(csk->dst_ip[1]);
+ l4kwqe2->dst_ip_v6_3 = be32_to_cpu(csk->dst_ip[2]);
+ l4kwqe2->dst_ip_v6_4 = be32_to_cpu(csk->dst_ip[3]);
+ l4kwqe3->mss = l4kwqe3->pmtu - sizeof(struct ipv6hdr) -
+ sizeof(struct tcphdr);
+ } else {
+ wqes[1] = (struct kwqe *) l4kwqe3;
+ l4kwqe3->mss = l4kwqe3->pmtu - sizeof(struct iphdr) -
+ sizeof(struct tcphdr);
+ }
+
+ l4kwqe1->op_code = L4_KWQE_OPCODE_VALUE_CONNECT1;
+ l4kwqe1->flags =
+ (L4_LAYER_CODE << L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT) |
+ L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT;
+ l4kwqe1->cid = csk->cid;
+ l4kwqe1->pg_cid = csk->pg_cid;
+ l4kwqe1->src_ip = be32_to_cpu(csk->src_ip[0]);
+ l4kwqe1->dst_ip = be32_to_cpu(csk->dst_ip[0]);
+ l4kwqe1->src_port = be16_to_cpu(csk->src_port);
+ l4kwqe1->dst_port = be16_to_cpu(csk->dst_port);
+ if (csk->tcp_flags & SK_TCP_NO_DELAY_ACK)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK;
+ if (csk->tcp_flags & SK_TCP_KEEP_ALIVE)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_KEEP_ALIVE;
+ if (csk->tcp_flags & SK_TCP_NAGLE)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE;
+ if (csk->tcp_flags & SK_TCP_TIMESTAMP)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_TIME_STAMP;
+ if (csk->tcp_flags & SK_TCP_SACK)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_SACK;
+ if (csk->tcp_flags & SK_TCP_SEG_SCALING)
+ tcp_flags |= L4_KWQ_CONNECT_REQ1_SEG_SCALING;
+
+ l4kwqe1->tcp_flags = tcp_flags;
+
+ return dev->submit_kwqes(dev, wqes, num_wqes);
+}
+
+static int cnic_cm_close_req(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_close_req *l4kwqe;
+ struct kwqe *wqes[1];
+
+ l4kwqe = (struct l4_kwq_close_req *) &csk->kwqe2;
+ memset(l4kwqe, 0, sizeof(*l4kwqe));
+ wqes[0] = (struct kwqe *) l4kwqe;
+
+ l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_CLOSE;
+ l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT;
+ l4kwqe->cid = csk->cid;
+
+ return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_abort_req(struct cnic_sock *csk)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct l4_kwq_reset_req *l4kwqe;
+ struct kwqe *wqes[1];
+
+ l4kwqe = (struct l4_kwq_reset_req *) &csk->kwqe2;
+ memset(l4kwqe, 0, sizeof(*l4kwqe));
+ wqes[0] = (struct kwqe *) l4kwqe;
+
+ l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_RESET;
+ l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT;
+ l4kwqe->cid = csk->cid;
+
+ return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_create(struct cnic_dev *dev, int ulp_type, u32 cid,
+ u32 l5_cid, struct cnic_sock **csk, void *context)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_sock *csk1;
+
+ if (l5_cid >= MAX_CM_SK_TBL_SZ)
+ return -EINVAL;
+
+ csk1 = &cp->csk_tbl[l5_cid];
+ if (atomic_read(&csk1->ref_count))
+ return -EAGAIN;
+
+ if (test_and_set_bit(SK_F_INUSE, &csk1->flags))
+ return -EBUSY;
+
+ csk1->dev = dev;
+ csk1->cid = cid;
+ csk1->l5_cid = l5_cid;
+ csk1->ulp_type = ulp_type;
+ csk1->context = context;
+
+ csk1->ka_timeout = DEF_KA_TIMEOUT;
+ csk1->ka_interval = DEF_KA_INTERVAL;
+ csk1->ka_max_probe_count = DEF_KA_MAX_PROBE_COUNT;
+ csk1->tos = DEF_TOS;
+ csk1->ttl = DEF_TTL;
+ csk1->snd_seq_scale = DEF_SND_SEQ_SCALE;
+ csk1->rcv_buf = DEF_RCV_BUF;
+ csk1->snd_buf = DEF_SND_BUF;
+ csk1->seed = DEF_SEED;
+
+ *csk = csk1;
+ return 0;
+}
+
+static void cnic_cm_cleanup(struct cnic_sock *csk)
+{
+ if (csk->src_port) {
+ struct cnic_dev *dev = csk->dev;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ cnic_free_id(&cp->csk_port_tbl, csk->src_port);
+ csk->src_port = 0;
+ }
+}
+
+static void cnic_close_conn(struct cnic_sock *csk)
+{
+ if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags)) {
+ cnic_cm_upload_pg(csk);
+ clear_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+ }
+ cnic_cm_cleanup(csk);
+}
+
+static int cnic_cm_destroy(struct cnic_sock *csk)
+{
+ if (!cnic_in_use(csk))
+ return -EINVAL;
+
+ csk_hold(csk);
+ clear_bit(SK_F_INUSE, &csk->flags);
+ smp_mb__after_clear_bit();
+ while (atomic_read(&csk->ref_count) != 1)
+ msleep(1);
+ cnic_cm_cleanup(csk);
+
+ csk->flags = 0;
+ csk_put(csk);
+ return 0;
+}
+
+static inline u16 cnic_get_vlan(struct net_device *dev,
+ struct net_device **vlan_dev)
+{
+ if (dev->priv_flags & IFF_802_1Q_VLAN) {
+ *vlan_dev = vlan_dev_real_dev(dev);
+ return vlan_dev_vlan_id(dev);
+ }
+ *vlan_dev = dev;
+ return 0;
+}
+
+static int cnic_get_v4_route(struct sockaddr_in *dst_addr,
+ struct dst_entry **dst)
+{
+ struct flowi fl;
+ int err;
+ struct rtable *rt;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.nl_u.ip4_u.daddr = dst_addr->sin_addr.s_addr;
+
+ err = ip_route_output_key(&init_net, &rt, &fl);
+ if (!err)
+ *dst = &rt->u.dst;
+ return err;
+}
+
+static int cnic_get_v6_route(struct sockaddr_in6 *dst_addr,
+ struct dst_entry **dst)
+{
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+ ipv6_addr_copy(&fl.fl6_dst, &dst_addr->sin6_addr);
+ if (ipv6_addr_type(&fl.fl6_dst) & IPV6_ADDR_LINKLOCAL)
+ fl.oif = dst_addr->sin6_scope_id;
+
+ *dst = ip6_route_output(&init_net, NULL, &fl);
+ if (*dst)
+ return 0;
+#endif
+
+ return -ENETUNREACH;
+}
+
+static struct cnic_dev *cnic_cm_select_dev(struct sockaddr_in *dst_addr,
+ int ulp_type)
+{
+ struct cnic_dev *dev = NULL;
+ struct dst_entry *dst;
+ struct net_device *netdev = NULL;
+ int err = -ENETUNREACH;
+
+ if (dst_addr->sin_family == AF_INET)
+ err = cnic_get_v4_route(dst_addr, &dst);
+ else if (dst_addr->sin_family == AF_INET6) {
+ struct sockaddr_in6 *dst_addr6 =
+ (struct sockaddr_in6 *) dst_addr;
+
+ err = cnic_get_v6_route(dst_addr6, &dst);
+ } else
+ return NULL;
+
+ if (err)
+ return NULL;
+
+ if (!dst->dev)
+ goto done;
+
+ cnic_get_vlan(dst->dev, &netdev);
+
+ dev = cnic_from_netdev(netdev);
+
+done:
+ dst_release(dst);
+ if (dev)
+ cnic_put(dev);
+ return dev;
+}
+
+static int cnic_resolve_addr(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ return cnic_send_nlmsg(cp, ISCSI_KEVENT_PATH_REQ, csk);
+}
+
+static int cnic_get_route(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct cnic_local *cp = dev->cnic_priv;
+ int is_v6, err, rc = -ENETUNREACH;
+ struct dst_entry *dst;
+ struct net_device *realdev;
+ u32 local_port;
+
+ if (saddr->local.v6.sin6_family == AF_INET6 &&
+ saddr->remote.v6.sin6_family == AF_INET6)
+ is_v6 = 1;
+ else if (saddr->local.v4.sin_family == AF_INET &&
+ saddr->remote.v4.sin_family == AF_INET)
+ is_v6 = 0;
+ else
+ return -EINVAL;
+
+ clear_bit(SK_F_IPV6, &csk->flags);
+
+ if (is_v6) {
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ set_bit(SK_F_IPV6, &csk->flags);
+ err = cnic_get_v6_route(&saddr->remote.v6, &dst);
+ if (err)
+ return err;
+
+ if (!dst || dst->error || !dst->dev)
+ goto err_out;
+
+ memcpy(&csk->dst_ip[0], &saddr->remote.v6.sin6_addr,
+ sizeof(struct in6_addr));
+ csk->dst_port = saddr->remote.v6.sin6_port;
+ local_port = saddr->local.v6.sin6_port;
+#else
+ return rc;
+#endif
+
+ } else {
+ err = cnic_get_v4_route(&saddr->remote.v4, &dst);
+ if (err)
+ return err;
+
+ if (!dst || dst->error || !dst->dev)
+ goto err_out;
+
+ csk->dst_ip[0] = saddr->remote.v4.sin_addr.s_addr;
+ csk->dst_port = saddr->remote.v4.sin_port;
+ local_port = saddr->local.v4.sin_port;
+ }
+
+ csk->vlan_id = cnic_get_vlan(dst->dev, &realdev);
+ if (realdev != dev->netdev)
+ goto err_out;
+
+ if (local_port >= CNIC_LOCAL_PORT_MIN &&
+ local_port < CNIC_LOCAL_PORT_MAX) {
+ if (cnic_alloc_id(&cp->csk_port_tbl, local_port))
+ local_port = 0;
+ } else
+ local_port = 0;
+
+ if (!local_port) {
+ local_port = cnic_alloc_new_id(&cp->csk_port_tbl);
+ if (local_port == -1) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ }
+ csk->src_port = local_port;
+
+ csk->mtu = dst_mtu(dst);
+ rc = 0;
+
+err_out:
+ dst_release(dst);
+ return rc;
+}
+
+static void cnic_init_csk_state(struct cnic_sock *csk)
+{
+ csk->state = 0;
+ clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+ clear_bit(SK_F_CLOSING, &csk->flags);
+}
+
+static int cnic_cm_connect(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+ int err = 0;
+
+ if (!cnic_in_use(csk))
+ return -EINVAL;
+
+ if (test_and_set_bit(SK_F_CONNECT_START, &csk->flags))
+ return -EINVAL;
+
+ cnic_init_csk_state(csk);
+
+ err = cnic_get_route(csk, saddr);
+ if (err)
+ goto err_out;
+
+ err = cnic_resolve_addr(csk, saddr);
+ if (!err)
+ return 0;
+
+err_out:
+ clear_bit(SK_F_CONNECT_START, &csk->flags);
+ return err;
+}
+
+static int cnic_cm_abort(struct cnic_sock *csk)
+{
+ struct cnic_local *cp = csk->dev->cnic_priv;
+ u32 opcode;
+
+ if (!cnic_in_use(csk))
+ return -EINVAL;
+
+ if (cnic_abort_prep(csk))
+ return cnic_cm_abort_req(csk);
+
+ /* Getting here means that we haven't started connect, or
+ * connect was not successful.
+ */
+
+ csk->state = L4_KCQE_OPCODE_VALUE_RESET_COMP;
+ if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+ opcode = csk->state;
+ else
+ opcode = L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD;
+ cp->close_conn(csk, opcode);
+
+ return 0;
+}
+
+static int cnic_cm_close(struct cnic_sock *csk)
+{
+ if (!cnic_in_use(csk))
+ return -EINVAL;
+
+ if (cnic_close_prep(csk)) {
+ csk->state = L4_KCQE_OPCODE_VALUE_CLOSE_COMP;
+ return cnic_cm_close_req(csk);
+ }
+ return 0;
+}
+
+static void cnic_cm_upcall(struct cnic_local *cp, struct cnic_sock *csk,
+ u8 opcode)
+{
+ struct cnic_ulp_ops *ulp_ops;
+ int ulp_type = csk->ulp_type;
+
+ rcu_read_lock();
+ ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+ if (ulp_ops) {
+ if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE)
+ ulp_ops->cm_connect_complete(csk);
+ else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_COMP)
+ ulp_ops->cm_close_complete(csk);
+ else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED)
+ ulp_ops->cm_remote_abort(csk);
+ else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP)
+ ulp_ops->cm_abort_complete(csk);
+ else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED)
+ ulp_ops->cm_remote_close(csk);
+ }
+ rcu_read_unlock();
+}
+
+static int cnic_cm_set_pg(struct cnic_sock *csk)
+{
+ if (cnic_offld_prep(csk)) {
+ if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+ cnic_cm_update_pg(csk);
+ else
+ cnic_cm_offload_pg(csk);
+ }
+ return 0;
+}
+
+static void cnic_cm_process_offld_pg(struct cnic_dev *dev, struct l4_kcq *kcqe)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 l5_cid = kcqe->pg_host_opaque;
+ u8 opcode = kcqe->op_code;
+ struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+ csk_hold(csk);
+ if (!cnic_in_use(csk))
+ goto done;
+
+ if (opcode == L4_KCQE_OPCODE_VALUE_UPDATE_PG) {
+ clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+ goto done;
+ }
+ csk->pg_cid = kcqe->pg_cid;
+ set_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+ cnic_cm_conn_req(csk);
+
+done:
+ csk_put(csk);
+}
+
+static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct l4_kcq *l4kcqe = (struct l4_kcq *) kcqe;
+ u8 opcode = l4kcqe->op_code;
+ u32 l5_cid;
+ struct cnic_sock *csk;
+
+ if (opcode == L4_KCQE_OPCODE_VALUE_OFFLOAD_PG ||
+ opcode == L4_KCQE_OPCODE_VALUE_UPDATE_PG) {
+ cnic_cm_process_offld_pg(dev, l4kcqe);
+ return;
+ }
+
+ l5_cid = l4kcqe->conn_id;
+ if (opcode & 0x80)
+ l5_cid = l4kcqe->cid;
+ if (l5_cid >= MAX_CM_SK_TBL_SZ)
+ return;
+
+ csk = &cp->csk_tbl[l5_cid];
+ csk_hold(csk);
+
+ if (!cnic_in_use(csk)) {
+ csk_put(csk);
+ return;
+ }
+
+ switch (opcode) {
+ case L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE:
+ if (l4kcqe->status == 0)
+ set_bit(SK_F_OFFLD_COMPLETE, &csk->flags);
+
+ smp_mb__before_clear_bit();
+ clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+ cnic_cm_upcall(cp, csk, opcode);
+ break;
+
+ case L4_KCQE_OPCODE_VALUE_RESET_RECEIVED:
+ if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags))
+ csk->state = opcode;
+ /* fall through */
+ case L4_KCQE_OPCODE_VALUE_CLOSE_COMP:
+ case L4_KCQE_OPCODE_VALUE_RESET_COMP:
+ cp->close_conn(csk, opcode);
+ break;
+
+ case L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED:
+ cnic_cm_upcall(cp, csk, opcode);
+ break;
+ }
+ csk_put(csk);
+}
+
+static void cnic_cm_indicate_kcqe(void *data, struct kcqe *kcqe[], u32 num)
+{
+ struct cnic_dev *dev = data;
+ int i;
+
+ for (i = 0; i < num; i++)
+ cnic_cm_process_kcqe(dev, kcqe[i]);
+}
+
+static struct cnic_ulp_ops cm_ulp_ops = {
+ .indicate_kcqes = cnic_cm_indicate_kcqe,
+};
+
+static void cnic_cm_free_mem(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+
+ kfree(cp->csk_tbl);
+ cp->csk_tbl = NULL;
+ cnic_free_id_tbl(&cp->csk_port_tbl);
+}
+
+static int cnic_cm_alloc_mem(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+
+ cp->csk_tbl = kzalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ,
+ GFP_KERNEL);
+ if (!cp->csk_tbl)
+ return -ENOMEM;
+
+ if (cnic_init_id_tbl(&cp->csk_port_tbl, CNIC_LOCAL_PORT_RANGE,
+ CNIC_LOCAL_PORT_MIN)) {
+ cnic_cm_free_mem(dev);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int cnic_ready_to_close(struct cnic_sock *csk, u32 opcode)
+{
+ if ((opcode == csk->state) ||
+ (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED &&
+ csk->state == L4_KCQE_OPCODE_VALUE_CLOSE_COMP)) {
+ if (!test_and_set_bit(SK_F_CLOSING, &csk->flags))
+ return 1;
+ }
+ return 0;
+}
+
+static void cnic_close_bnx2_conn(struct cnic_sock *csk, u32 opcode)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ clear_bit(SK_F_CONNECT_START, &csk->flags);
+ if (cnic_ready_to_close(csk, opcode)) {
+ cnic_close_conn(csk);
+ cnic_cm_upcall(cp, csk, opcode);
+ }
+}
+
+static void cnic_cm_stop_bnx2_hw(struct cnic_dev *dev)
+{
+}
+
+static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev)
+{
+ u32 seed;
+
+ get_random_bytes(&seed, 4);
+ cnic_ctx_wr(dev, 45, 0, seed);
+ return 0;
+}
+
+static int cnic_cm_open(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int err;
+
+ err = cnic_cm_alloc_mem(dev);
+ if (err)
+ return err;
+
+ err = cp->start_cm(dev);
+
+ if (err)
+ goto err_out;
+
+ dev->cm_create = cnic_cm_create;
+ dev->cm_destroy = cnic_cm_destroy;
+ dev->cm_connect = cnic_cm_connect;
+ dev->cm_abort = cnic_cm_abort;
+ dev->cm_close = cnic_cm_close;
+ dev->cm_select_dev = cnic_cm_select_dev;
+
+ cp->ulp_handle[CNIC_ULP_L4] = dev;
+ rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], &cm_ulp_ops);
+ return 0;
+
+err_out:
+ cnic_cm_free_mem(dev);
+ return err;
+}
+
+static int cnic_cm_shutdown(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int i;
+
+ cp->stop_cm(dev);
+
+ if (!cp->csk_tbl)
+ return 0;
+
+ for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+ struct cnic_sock *csk = &cp->csk_tbl[i];
+
+ clear_bit(SK_F_INUSE, &csk->flags);
+ cnic_cm_cleanup(csk);
+ }
+ cnic_cm_free_mem(dev);
+
+ return 0;
+}
+
+static void cnic_init_context(struct cnic_dev *dev, u32 cid)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 cid_addr;
+ int i;
+
+ if (CHIP_NUM(cp) == CHIP_NUM_5709)
+ return;
+
+ cid_addr = GET_CID_ADDR(cid);
+
+ for (i = 0; i < CTX_SIZE; i += 4)
+ cnic_ctx_wr(dev, cid_addr, i, 0);
+}
+
+static int cnic_setup_5709_context(struct cnic_dev *dev, int valid)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int ret = 0, i;
+ u32 valid_bit = valid ? BNX2_CTX_HOST_PAGE_TBL_DATA0_VALID : 0;
+
+ if (CHIP_NUM(cp) != CHIP_NUM_5709)
+ return 0;
+
+ for (i = 0; i < cp->ctx_blks; i++) {
+ int j;
+ u32 idx = cp->ctx_arr[i].cid / cp->cids_per_blk;
+ u32 val;
+
+ memset(cp->ctx_arr[i].ctx, 0, BCM_PAGE_SIZE);
+
+ CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_DATA0,
+ (cp->ctx_arr[i].mapping & 0xffffffff) | valid_bit);
+ CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_DATA1,
+ (u64) cp->ctx_arr[i].mapping >> 32);
+ CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_CTRL, idx |
+ BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ);
+ for (j = 0; j < 10; j++) {
+
+ val = CNIC_RD(dev, BNX2_CTX_HOST_PAGE_TBL_CTRL);
+ if (!(val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ))
+ break;
+ udelay(5);
+ }
+ if (val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void cnic_free_irq(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ cp->disable_int_sync(dev);
+ tasklet_disable(&cp->cnic_irq_task);
+ free_irq(ethdev->irq_arr[0].vector, dev);
+ }
+}
+
+static int cnic_init_bnx2_irq(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ int err, i = 0;
+ int sblk_num = cp->status_blk_num;
+ u32 base = ((sblk_num - 1) * BNX2_HC_SB_CONFIG_SIZE) +
+ BNX2_HC_SB_CONFIG_1;
+
+ CNIC_WR(dev, base, BNX2_HC_SB_CONFIG_1_ONE_SHOT);
+
+ CNIC_WR(dev, base + BNX2_HC_COMP_PROD_TRIP_OFF, (2 << 16) | 8);
+ CNIC_WR(dev, base + BNX2_HC_COM_TICKS_OFF, (64 << 16) | 220);
+ CNIC_WR(dev, base + BNX2_HC_CMD_TICKS_OFF, (64 << 16) | 220);
+
+ cp->bnx2_status_blk = cp->status_blk;
+ cp->last_status_idx = cp->bnx2_status_blk->status_idx;
+ tasklet_init(&cp->cnic_irq_task, &cnic_service_bnx2_msix,
+ (unsigned long) dev);
+ err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0,
+ "cnic", dev);
+ if (err) {
+ tasklet_disable(&cp->cnic_irq_task);
+ return err;
+ }
+ while (cp->bnx2_status_blk->status_completion_producer_index &&
+ i < 10) {
+ CNIC_WR(dev, BNX2_HC_COALESCE_NOW,
+ 1 << (11 + sblk_num));
+ udelay(10);
+ i++;
+ barrier();
+ }
+ if (cp->bnx2_status_blk->status_completion_producer_index) {
+ cnic_free_irq(dev);
+ goto failed;
+ }
+
+ } else {
+ struct status_block *sblk = cp->status_blk;
+ u32 hc_cmd = CNIC_RD(dev, BNX2_HC_COMMAND);
+ int i = 0;
+
+ while (sblk->status_completion_producer_index && i < 10) {
+ CNIC_WR(dev, BNX2_HC_COMMAND,
+ hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+ udelay(10);
+ i++;
+ barrier();
+ }
+ if (sblk->status_completion_producer_index)
+ goto failed;
+
+ }
+ return 0;
+
+failed:
+ printk(KERN_ERR PFX "%s: " "KCQ index not resetting to 0.\n",
+ dev->netdev->name);
+ return -EBUSY;
+}
+
+static void cnic_enable_bnx2_int(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (!(ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX))
+ return;
+
+ CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+ BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | cp->last_status_idx);
+}
+
+static void cnic_disable_bnx2_int_sync(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (!(ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX))
+ return;
+
+ CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+ BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+ CNIC_RD(dev, BNX2_PCICFG_INT_ACK_CMD);
+ synchronize_irq(ethdev->irq_arr[0].vector);
+}
+
+static void cnic_init_bnx2_tx_ring(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ u32 cid_addr, tx_cid, sb_id;
+ u32 val, offset0, offset1, offset2, offset3;
+ int i;
+ struct tx_bd *txbd;
+ dma_addr_t buf_map;
+ struct status_block *s_blk = cp->status_blk;
+
+ sb_id = cp->status_blk_num;
+ tx_cid = 20;
+ cnic_init_context(dev, tx_cid);
+ cnic_init_context(dev, tx_cid + 1);
+ cp->tx_cons_ptr = &s_blk->status_tx_quick_consumer_index2;
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ struct status_block_msix *sblk = cp->status_blk;
+
+ tx_cid = TX_TSS_CID + sb_id - 1;
+ cnic_init_context(dev, tx_cid);
+ CNIC_WR(dev, BNX2_TSCH_TSS_CFG, (sb_id << 24) |
+ (TX_TSS_CID << 7));
+ cp->tx_cons_ptr = &sblk->status_tx_quick_consumer_index;
+ }
+ cp->tx_cons = *cp->tx_cons_ptr;
+
+ cid_addr = GET_CID_ADDR(tx_cid);
+ if (CHIP_NUM(cp) == CHIP_NUM_5709) {
+ u32 cid_addr2 = GET_CID_ADDR(tx_cid + 4) + 0x40;
+
+ for (i = 0; i < PHY_CTX_SIZE; i += 4)
+ cnic_ctx_wr(dev, cid_addr2, i, 0);
+
+ offset0 = BNX2_L2CTX_TYPE_XI;
+ offset1 = BNX2_L2CTX_CMD_TYPE_XI;
+ offset2 = BNX2_L2CTX_TBDR_BHADDR_HI_XI;
+ offset3 = BNX2_L2CTX_TBDR_BHADDR_LO_XI;
+ } else {
+ offset0 = BNX2_L2CTX_TYPE;
+ offset1 = BNX2_L2CTX_CMD_TYPE;
+ offset2 = BNX2_L2CTX_TBDR_BHADDR_HI;
+ offset3 = BNX2_L2CTX_TBDR_BHADDR_LO;
+ }
+ val = BNX2_L2CTX_TYPE_TYPE_L2 | BNX2_L2CTX_TYPE_SIZE_L2;
+ cnic_ctx_wr(dev, cid_addr, offset0, val);
+
+ val = BNX2_L2CTX_CMD_TYPE_TYPE_L2 | (8 << 16);
+ cnic_ctx_wr(dev, cid_addr, offset1, val);
+
+ txbd = (struct tx_bd *) cp->l2_ring;
+
+ buf_map = cp->l2_buf_map;
+ for (i = 0; i < MAX_TX_DESC_CNT; i++, txbd++) {
+ txbd->tx_bd_haddr_hi = (u64) buf_map >> 32;
+ txbd->tx_bd_haddr_lo = (u64) buf_map & 0xffffffff;
+ }
+ val = (u64) cp->l2_ring_map >> 32;
+ cnic_ctx_wr(dev, cid_addr, offset2, val);
+ txbd->tx_bd_haddr_hi = val;
+
+ val = (u64) cp->l2_ring_map & 0xffffffff;
+ cnic_ctx_wr(dev, cid_addr, offset3, val);
+ txbd->tx_bd_haddr_lo = val;
+}
+
+static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ u32 cid_addr, sb_id, val, coal_reg, coal_val;
+ int i;
+ struct rx_bd *rxbd;
+ struct status_block *s_blk = cp->status_blk;
+
+ sb_id = cp->status_blk_num;
+ cnic_init_context(dev, 2);
+ cp->rx_cons_ptr = &s_blk->status_rx_quick_consumer_index2;
+ coal_reg = BNX2_HC_COMMAND;
+ coal_val = CNIC_RD(dev, coal_reg);
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ struct status_block_msix *sblk = cp->status_blk;
+
+ cp->rx_cons_ptr = &sblk->status_rx_quick_consumer_index;
+ coal_reg = BNX2_HC_COALESCE_NOW;
+ coal_val = 1 << (11 + sb_id);
+ }
+ i = 0;
+ while (!(*cp->rx_cons_ptr != 0) && i < 10) {
+ CNIC_WR(dev, coal_reg, coal_val);
+ udelay(10);
+ i++;
+ barrier();
+ }
+ cp->rx_cons = *cp->rx_cons_ptr;
+
+ cid_addr = GET_CID_ADDR(2);
+ val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE |
+ BNX2_L2CTX_CTX_TYPE_SIZE_L2 | (0x02 << 8);
+ cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_CTX_TYPE, val);
+
+ if (sb_id == 0)
+ val = 2 << BNX2_L2CTX_STATUSB_NUM_SHIFT;
+ else
+ val = BNX2_L2CTX_STATUSB_NUM(sb_id);
+ cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_HOST_BDIDX, val);
+
+ rxbd = (struct rx_bd *) (cp->l2_ring + BCM_PAGE_SIZE);
+ for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
+ dma_addr_t buf_map;
+ int n = (i % cp->l2_rx_ring_size) + 1;
+
+ buf_map = cp->l2_buf_map + (n * cp->l2_single_buf_size);
+ rxbd->rx_bd_len = cp->l2_single_buf_size;
+ rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END;
+ rxbd->rx_bd_haddr_hi = (u64) buf_map >> 32;
+ rxbd->rx_bd_haddr_lo = (u64) buf_map & 0xffffffff;
+ }
+ val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) >> 32;
+ cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_HI, val);
+ rxbd->rx_bd_haddr_hi = val;
+
+ val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) & 0xffffffff;
+ cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_LO, val);
+ rxbd->rx_bd_haddr_lo = val;
+
+ val = cnic_reg_rd_ind(dev, BNX2_RXP_SCRATCH_RXP_FLOOD);
+ cnic_reg_wr_ind(dev, BNX2_RXP_SCRATCH_RXP_FLOOD, val | (1 << 2));
+}
+
+static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *dev)
+{
+ struct kwqe *wqes[1], l2kwqe;
+
+ memset(&l2kwqe, 0, sizeof(l2kwqe));
+ wqes[0] = &l2kwqe;
+ l2kwqe.kwqe_op_flag = (L2_LAYER_CODE << KWQE_FLAGS_LAYER_SHIFT) |
+ (L2_KWQE_OPCODE_VALUE_FLUSH <<
+ KWQE_OPCODE_SHIFT) | 2;
+ dev->submit_kwqes(dev, wqes, 1);
+}
+
+static void cnic_set_bnx2_mac(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 val;
+
+ val = cp->func << 2;
+
+ cp->shmem_base = cnic_reg_rd_ind(dev, BNX2_SHM_HDR_ADDR_0 + val);
+
+ val = cnic_reg_rd_ind(dev, cp->shmem_base +
+ BNX2_PORT_HW_CFG_ISCSI_MAC_UPPER);
+ dev->mac_addr[0] = (u8) (val >> 8);
+ dev->mac_addr[1] = (u8) val;
+
+ CNIC_WR(dev, BNX2_EMAC_MAC_MATCH4, val);
+
+ val = cnic_reg_rd_ind(dev, cp->shmem_base +
+ BNX2_PORT_HW_CFG_ISCSI_MAC_LOWER);
+ dev->mac_addr[2] = (u8) (val >> 24);
+ dev->mac_addr[3] = (u8) (val >> 16);
+ dev->mac_addr[4] = (u8) (val >> 8);
+ dev->mac_addr[5] = (u8) val;
+
+ CNIC_WR(dev, BNX2_EMAC_MAC_MATCH5, val);
+
+ val = 4 | BNX2_RPM_SORT_USER2_BC_EN;
+ if (CHIP_NUM(cp) != CHIP_NUM_5709)
+ val |= BNX2_RPM_SORT_USER2_PROM_VLAN;
+
+ CNIC_WR(dev, BNX2_RPM_SORT_USER2, 0x0);
+ CNIC_WR(dev, BNX2_RPM_SORT_USER2, val);
+ CNIC_WR(dev, BNX2_RPM_SORT_USER2, val | BNX2_RPM_SORT_USER2_ENA);
+}
+
+static int cnic_start_bnx2_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct status_block *sblk = cp->status_blk;
+ u32 val;
+ int err;
+
+ cnic_set_bnx2_mac(dev);
+
+ val = CNIC_RD(dev, BNX2_MQ_CONFIG);
+ val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
+ if (BCM_PAGE_BITS > 12)
+ val |= (12 - 8) << 4;
+ else
+ val |= (BCM_PAGE_BITS - 8) << 4;
+
+ CNIC_WR(dev, BNX2_MQ_CONFIG, val);
+
+ CNIC_WR(dev, BNX2_HC_COMP_PROD_TRIP, (2 << 16) | 8);
+ CNIC_WR(dev, BNX2_HC_COM_TICKS, (64 << 16) | 220);
+ CNIC_WR(dev, BNX2_HC_CMD_TICKS, (64 << 16) | 220);
+
+ err = cnic_setup_5709_context(dev, 1);
+ if (err)
+ return err;
+
+ cnic_init_context(dev, KWQ_CID);
+ cnic_init_context(dev, KCQ_CID);
+
+ cp->kwq_cid_addr = GET_CID_ADDR(KWQ_CID);
+ cp->kwq_io_addr = MB_GET_CID_ADDR(KWQ_CID) + L5_KRNLQ_HOST_QIDX;
+
+ cp->max_kwq_idx = MAX_KWQ_IDX;
+ cp->kwq_prod_idx = 0;
+ cp->kwq_con_idx = 0;
+ cp->cnic_local_flags |= CNIC_LCL_FL_KWQ_INIT;
+
+ if (CHIP_NUM(cp) == CHIP_NUM_5706 || CHIP_NUM(cp) == CHIP_NUM_5708)
+ cp->kwq_con_idx_ptr = &sblk->status_rx_quick_consumer_index15;
+ else
+ cp->kwq_con_idx_ptr = &sblk->status_cmd_consumer_index;
+
+ /* Initialize the kernel work queue context. */
+ val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+ (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_TYPE, val);
+
+ val = (BCM_PAGE_SIZE / sizeof(struct kwqe) - 1) << 16;
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+
+ val = ((BCM_PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT;
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+
+ val = (u32) ((u64) cp->kwq_info.pgtbl_map >> 32);
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+
+ val = (u32) cp->kwq_info.pgtbl_map;
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+ cp->kcq_cid_addr = GET_CID_ADDR(KCQ_CID);
+ cp->kcq_io_addr = MB_GET_CID_ADDR(KCQ_CID) + L5_KRNLQ_HOST_QIDX;
+
+ cp->kcq_prod_idx = 0;
+
+ /* Initialize the kernel complete queue context. */
+ val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+ (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_TYPE, val);
+
+ val = (BCM_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16;
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+
+ val = ((BCM_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT;
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+
+ val = (u32) ((u64) cp->kcq_info.pgtbl_map >> 32);
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+
+ val = (u32) cp->kcq_info.pgtbl_map;
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+ cp->int_num = 0;
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ u32 sb_id = cp->status_blk_num;
+ u32 sb = BNX2_L2CTX_STATUSB_NUM(sb_id);
+
+ cp->int_num = sb_id << BNX2_PCICFG_INT_ACK_CMD_INT_NUM_SHIFT;
+ cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_HOST_QIDX, sb);
+ cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_HOST_QIDX, sb);
+ }
+
+ /* Enable Commnad Scheduler notification when we write to the
+ * host producer index of the kernel contexts. */
+ CNIC_WR(dev, BNX2_MQ_KNL_CMD_MASK1, 2);
+
+ /* Enable Command Scheduler notification when we write to either
+ * the Send Queue or Receive Queue producer indexes of the kernel
+ * bypass contexts. */
+ CNIC_WR(dev, BNX2_MQ_KNL_BYP_CMD_MASK1, 7);
+ CNIC_WR(dev, BNX2_MQ_KNL_BYP_WRITE_MASK1, 7);
+
+ /* Notify COM when the driver post an application buffer. */
+ CNIC_WR(dev, BNX2_MQ_KNL_RX_V2P_MASK2, 0x2000);
+
+ /* Set the CP and COM doorbells. These two processors polls the
+ * doorbell for a non zero value before running. This must be done
+ * after setting up the kernel queue contexts. */
+ cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 1);
+ cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 1);
+
+ cnic_init_bnx2_tx_ring(dev);
+ cnic_init_bnx2_rx_ring(dev);
+
+ err = cnic_init_bnx2_irq(dev);
+ if (err) {
+ printk(KERN_ERR PFX "%s: cnic_init_irq failed\n",
+ dev->netdev->name);
+ cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
+ cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 0);
+ return err;
+ }
+
+ return 0;
+}
+
+static int cnic_start_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int err;
+
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ return -EALREADY;
+
+ err = ethdev->drv_register_cnic(dev->netdev, cp->cnic_ops, dev);
+ if (err) {
+ printk(KERN_ERR PFX "%s: register_cnic failed\n",
+ dev->netdev->name);
+ goto err2;
+ }
+
+ dev->regview = ethdev->io_base;
+ cp->chip_id = ethdev->chip_id;
+ pci_dev_get(dev->pcidev);
+ cp->func = PCI_FUNC(dev->pcidev->devfn);
+ cp->status_blk = ethdev->irq_arr[0].status_blk;
+ cp->status_blk_num = ethdev->irq_arr[0].status_blk_num;
+
+ err = cp->alloc_resc(dev);
+ if (err) {
+ printk(KERN_ERR PFX "%s: allocate resource failure\n",
+ dev->netdev->name);
+ goto err1;
+ }
+
+ err = cp->start_hw(dev);
+ if (err)
+ goto err1;
+
+ err = cnic_cm_open(dev);
+ if (err)
+ goto err1;
+
+ set_bit(CNIC_F_CNIC_UP, &dev->flags);
+
+ cp->enable_int(dev);
+
+ return 0;
+
+err1:
+ ethdev->drv_unregister_cnic(dev->netdev);
+ cp->free_resc(dev);
+ pci_dev_put(dev->pcidev);
+err2:
+ return err;
+}
+
+static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ cnic_disable_bnx2_int_sync(dev);
+
+ cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
+ cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 0);
+
+ cnic_init_context(dev, KWQ_CID);
+ cnic_init_context(dev, KCQ_CID);
+
+ cnic_setup_5709_context(dev, 0);
+ cnic_free_irq(dev);
+
+ ethdev->drv_unregister_cnic(dev->netdev);
+
+ cnic_free_resc(dev);
+}
+
+static void cnic_stop_hw(struct cnic_dev *dev)
+{
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ clear_bit(CNIC_F_CNIC_UP, &dev->flags);
+ rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], NULL);
+ synchronize_rcu();
+ cnic_cm_shutdown(dev);
+ cp->stop_hw(dev);
+ pci_dev_put(dev->pcidev);
+ }
+}
+
+static void cnic_free_dev(struct cnic_dev *dev)
+{
+ int i = 0;
+
+ while ((atomic_read(&dev->ref_count) != 0) && i < 10) {
+ msleep(100);
+ i++;
+ }
+ if (atomic_read(&dev->ref_count) != 0)
+ printk(KERN_ERR PFX "%s: Failed waiting for ref count to go"
+ " to zero.\n", dev->netdev->name);
+
+ printk(KERN_INFO PFX "Removed CNIC device: %s\n", dev->netdev->name);
+ dev_put(dev->netdev);
+ kfree(dev);
+}
+
+static struct cnic_dev *cnic_alloc_dev(struct net_device *dev,
+ struct pci_dev *pdev)
+{
+ struct cnic_dev *cdev;
+ struct cnic_local *cp;
+ int alloc_size;
+
+ alloc_size = sizeof(struct cnic_dev) + sizeof(struct cnic_local);
+
+ cdev = kzalloc(alloc_size , GFP_KERNEL);
+ if (cdev == NULL) {
+ printk(KERN_ERR PFX "%s: allocate dev struct failure\n",
+ dev->name);
+ return NULL;
+ }
+
+ cdev->netdev = dev;
+ cdev->cnic_priv = (char *)cdev + sizeof(struct cnic_dev);
+ cdev->register_device = cnic_register_device;
+ cdev->unregister_device = cnic_unregister_device;
+ cdev->iscsi_nl_msg_recv = cnic_iscsi_nl_msg_recv;
+
+ cp = cdev->cnic_priv;
+ cp->dev = cdev;
+ cp->uio_dev = -1;
+ cp->l2_single_buf_size = 0x400;
+ cp->l2_rx_ring_size = 3;
+
+ spin_lock_init(&cp->cnic_ulp_lock);
+
+ printk(KERN_INFO PFX "Added CNIC device: %s\n", dev->name);
+
+ return cdev;
+}
+
+static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
+{
+ struct pci_dev *pdev;
+ struct cnic_dev *cdev;
+ struct cnic_local *cp;
+ struct cnic_eth_dev *ethdev = NULL;
+ struct cnic_eth_dev *(*probe)(void *) = NULL;
+
+ probe = __symbol_get("bnx2_cnic_probe");
+ if (probe) {
+ ethdev = (*probe)(dev);
+ symbol_put_addr(probe);
+ }
+ if (!ethdev)
+ return NULL;
+
+ pdev = ethdev->pdev;
+ if (!pdev)
+ return NULL;
+
+ dev_hold(dev);
+ pci_dev_get(pdev);
+ if (pdev->device == PCI_DEVICE_ID_NX2_5709 ||
+ pdev->device == PCI_DEVICE_ID_NX2_5709S) {
+ u8 rev;
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+ if (rev < 0x10) {
+ pci_dev_put(pdev);
+ goto cnic_err;
+ }
+ }
+ pci_dev_put(pdev);
+
+ cdev = cnic_alloc_dev(dev, pdev);
+ if (cdev == NULL)
+ goto cnic_err;
+
+ set_bit(CNIC_F_BNX2_CLASS, &cdev->flags);
+ cdev->submit_kwqes = cnic_submit_bnx2_kwqes;
+
+ cp = cdev->cnic_priv;
+ cp->ethdev = ethdev;
+ cdev->pcidev = pdev;
+
+ cp->cnic_ops = &cnic_bnx2_ops;
+ cp->start_hw = cnic_start_bnx2_hw;
+ cp->stop_hw = cnic_stop_bnx2_hw;
+ cp->setup_pgtbl = cnic_setup_page_tbl;
+ cp->alloc_resc = cnic_alloc_bnx2_resc;
+ cp->free_resc = cnic_free_resc;
+ cp->start_cm = cnic_cm_init_bnx2_hw;
+ cp->stop_cm = cnic_cm_stop_bnx2_hw;
+ cp->enable_int = cnic_enable_bnx2_int;
+ cp->disable_int_sync = cnic_disable_bnx2_int_sync;
+ cp->close_conn = cnic_close_bnx2_conn;
+ cp->next_idx = cnic_bnx2_next_idx;
+ cp->hw_idx = cnic_bnx2_hw_idx;
+ return cdev;
+
+cnic_err:
+ dev_put(dev);
+ return NULL;
+}
+
+static struct cnic_dev *is_cnic_dev(struct net_device *dev)
+{
+ struct ethtool_drvinfo drvinfo;
+ struct cnic_dev *cdev = NULL;
+
+ if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) {
+ memset(&drvinfo, 0, sizeof(drvinfo));
+ dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
+
+ if (!strcmp(drvinfo.driver, "bnx2"))
+ cdev = init_bnx2_cnic(dev);
+ if (cdev) {
+ write_lock(&cnic_dev_lock);
+ list_add(&cdev->list, &cnic_dev_list);
+ write_unlock(&cnic_dev_lock);
+ }
+ }
+ return cdev;
+}
+
+/**
+ * netdev event handler
+ */
+static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct net_device *netdev = ptr;
+ struct cnic_dev *dev;
+ int if_type;
+ int new_dev = 0;
+
+ dev = cnic_from_netdev(netdev);
+
+ if (!dev && (event == NETDEV_REGISTER || event == NETDEV_UP)) {
+ /* Check for the hot-plug device */
+ dev = is_cnic_dev(netdev);
+ if (dev) {
+ new_dev = 1;
+ cnic_hold(dev);
+ }
+ }
+ if (dev) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (new_dev)
+ cnic_ulp_init(dev);
+ else if (event == NETDEV_UNREGISTER)
+ cnic_ulp_exit(dev);
+ else if (event == NETDEV_UP) {
+ mutex_lock(&cnic_lock);
+ if (!cnic_start_hw(dev))
+ cnic_ulp_start(dev);
+ mutex_unlock(&cnic_lock);
+ }
+
+ rcu_read_lock();
+ for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+ struct cnic_ulp_ops *ulp_ops;
+ void *ctx;
+
+ ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+ if (!ulp_ops || !ulp_ops->indicate_netevent)
+ continue;
+
+ ctx = cp->ulp_handle[if_type];
+
+ ulp_ops->indicate_netevent(ctx, event);
+ }
+ rcu_read_unlock();
+
+ if (event == NETDEV_GOING_DOWN) {
+ mutex_lock(&cnic_lock);
+ cnic_ulp_stop(dev);
+ cnic_stop_hw(dev);
+ mutex_unlock(&cnic_lock);
+ } else if (event == NETDEV_UNREGISTER) {
+ write_lock(&cnic_dev_lock);
+ list_del_init(&dev->list);
+ write_unlock(&cnic_dev_lock);
+
+ cnic_put(dev);
+ cnic_free_dev(dev);
+ goto done;
+ }
+ cnic_put(dev);
+ }
+done:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block cnic_netdev_notifier = {
+ .notifier_call = cnic_netdev_event
+};
+
+static void cnic_release(void)
+{
+ struct cnic_dev *dev;
+
+ while (!list_empty(&cnic_dev_list)) {
+ dev = list_entry(cnic_dev_list.next, struct cnic_dev, list);
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+ cnic_ulp_stop(dev);
+ cnic_stop_hw(dev);
+ }
+
+ cnic_ulp_exit(dev);
+ list_del_init(&dev->list);
+ cnic_free_dev(dev);
+ }
+}
+
+static int __init cnic_init(void)
+{
+ int rc = 0;
+
+ printk(KERN_INFO "%s", version);
+
+ rc = register_netdevice_notifier(&cnic_netdev_notifier);
+ if (rc) {
+ cnic_release();
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit cnic_exit(void)
+{
+ unregister_netdevice_notifier(&cnic_netdev_notifier);
+ cnic_release();
+ return;
+}
+
+module_init(cnic_init);
+module_exit(cnic_exit);
diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h
new file mode 100644
index 000000000000..5192d4a9df5a
--- /dev/null
+++ b/drivers/net/cnic.h
@@ -0,0 +1,299 @@
+/* cnic.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
+
+#ifndef CNIC_H
+#define CNIC_H
+
+#define KWQ_PAGE_CNT 4
+#define KCQ_PAGE_CNT 16
+
+#define KWQ_CID 24
+#define KCQ_CID 25
+
+/*
+ * krnlq_context definition
+ */
+#define L5_KRNLQ_FLAGS 0x00000000
+#define L5_KRNLQ_SIZE 0x00000000
+#define L5_KRNLQ_TYPE 0x00000000
+#define KRNLQ_FLAGS_PG_SZ (0xf<<0)
+#define KRNLQ_FLAGS_PG_SZ_256 (0<<0)
+#define KRNLQ_FLAGS_PG_SZ_512 (1<<0)
+#define KRNLQ_FLAGS_PG_SZ_1K (2<<0)
+#define KRNLQ_FLAGS_PG_SZ_2K (3<<0)
+#define KRNLQ_FLAGS_PG_SZ_4K (4<<0)
+#define KRNLQ_FLAGS_PG_SZ_8K (5<<0)
+#define KRNLQ_FLAGS_PG_SZ_16K (6<<0)
+#define KRNLQ_FLAGS_PG_SZ_32K (7<<0)
+#define KRNLQ_FLAGS_PG_SZ_64K (8<<0)
+#define KRNLQ_FLAGS_PG_SZ_128K (9<<0)
+#define KRNLQ_FLAGS_PG_SZ_256K (10<<0)
+#define KRNLQ_FLAGS_PG_SZ_512K (11<<0)
+#define KRNLQ_FLAGS_PG_SZ_1M (12<<0)
+#define KRNLQ_FLAGS_PG_SZ_2M (13<<0)
+#define KRNLQ_FLAGS_QE_SELF_SEQ (1<<15)
+#define KRNLQ_SIZE_TYPE_SIZE ((((0x28 + 0x1f) & ~0x1f) / 0x20) << 16)
+#define KRNLQ_TYPE_TYPE (0xf<<28)
+#define KRNLQ_TYPE_TYPE_EMPTY (0<<28)
+#define KRNLQ_TYPE_TYPE_KRNLQ (6<<28)
+
+#define L5_KRNLQ_HOST_QIDX 0x00000004
+#define L5_KRNLQ_HOST_FW_QIDX 0x00000008
+#define L5_KRNLQ_NX_QE_SELF_SEQ 0x0000000c
+#define L5_KRNLQ_QE_SELF_SEQ_MAX 0x0000000c
+#define L5_KRNLQ_NX_QE_HADDR_HI 0x00000010
+#define L5_KRNLQ_NX_QE_HADDR_LO 0x00000014
+#define L5_KRNLQ_PGTBL_PGIDX 0x00000018
+#define L5_KRNLQ_NX_PG_QIDX 0x00000018
+#define L5_KRNLQ_PGTBL_NPAGES 0x0000001c
+#define L5_KRNLQ_QIDX_INCR 0x0000001c
+#define L5_KRNLQ_PGTBL_HADDR_HI 0x00000020
+#define L5_KRNLQ_PGTBL_HADDR_LO 0x00000024
+
+#define BNX2_PG_CTX_MAP 0x1a0034
+#define BNX2_ISCSI_CTX_MAP 0x1a0074
+
+struct cnic_redirect_entry {
+ struct dst_entry *old_dst;
+ struct dst_entry *new_dst;
+};
+
+#define MAX_COMPLETED_KCQE 64
+
+#define MAX_CNIC_L5_CONTEXT 256
+
+#define MAX_CM_SK_TBL_SZ MAX_CNIC_L5_CONTEXT
+
+#define MAX_ISCSI_TBL_SZ 256
+
+#define CNIC_LOCAL_PORT_MIN 60000
+#define CNIC_LOCAL_PORT_MAX 61000
+#define CNIC_LOCAL_PORT_RANGE (CNIC_LOCAL_PORT_MAX - CNIC_LOCAL_PORT_MIN)
+
+#define KWQE_CNT (BCM_PAGE_SIZE / sizeof(struct kwqe))
+#define KCQE_CNT (BCM_PAGE_SIZE / sizeof(struct kcqe))
+#define MAX_KWQE_CNT (KWQE_CNT - 1)
+#define MAX_KCQE_CNT (KCQE_CNT - 1)
+
+#define MAX_KWQ_IDX ((KWQ_PAGE_CNT * KWQE_CNT) - 1)
+#define MAX_KCQ_IDX ((KCQ_PAGE_CNT * KCQE_CNT) - 1)
+
+#define KWQ_PG(x) (((x) & ~MAX_KWQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KWQ_IDX(x) ((x) & MAX_KWQE_CNT)
+
+#define KCQ_PG(x) (((x) & ~MAX_KCQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KCQ_IDX(x) ((x) & MAX_KCQE_CNT)
+
+#define BNX2X_NEXT_KCQE(x) (((x) & (MAX_KCQE_CNT - 1)) == \
+ (MAX_KCQE_CNT - 1)) ? \
+ (x) + 2 : (x) + 1
+
+#define BNX2X_KWQ_DATA_PG(cp, x) ((x) / (cp)->kwq_16_data_pp)
+#define BNX2X_KWQ_DATA_IDX(cp, x) ((x) % (cp)->kwq_16_data_pp)
+#define BNX2X_KWQ_DATA(cp, x) \
+ &(cp)->kwq_16_data[BNX2X_KWQ_DATA_PG(cp, x)][BNX2X_KWQ_DATA_IDX(cp, x)]
+
+#define DEF_IPID_COUNT 0xc001
+
+#define DEF_KA_TIMEOUT 10000
+#define DEF_KA_INTERVAL 300000
+#define DEF_KA_MAX_PROBE_COUNT 3
+#define DEF_TOS 0
+#define DEF_TTL 0xfe
+#define DEF_SND_SEQ_SCALE 0
+#define DEF_RCV_BUF 0xffff
+#define DEF_SND_BUF 0xffff
+#define DEF_SEED 0
+#define DEF_MAX_RT_TIME 500
+#define DEF_MAX_DA_COUNT 2
+#define DEF_SWS_TIMER 1000
+#define DEF_MAX_CWND 0xffff
+
+struct cnic_ctx {
+ u32 cid;
+ void *ctx;
+ dma_addr_t mapping;
+};
+
+#define BNX2_MAX_CID 0x2000
+
+struct cnic_dma {
+ int num_pages;
+ void **pg_arr;
+ dma_addr_t *pg_map_arr;
+ int pgtbl_size;
+ u32 *pgtbl;
+ dma_addr_t pgtbl_map;
+};
+
+struct cnic_id_tbl {
+ spinlock_t lock;
+ u32 start;
+ u32 max;
+ u32 next;
+ unsigned long *table;
+};
+
+#define CNIC_KWQ16_DATA_SIZE 128
+
+struct kwqe_16_data {
+ u8 data[CNIC_KWQ16_DATA_SIZE];
+};
+
+struct cnic_iscsi {
+ struct cnic_dma task_array_info;
+ struct cnic_dma r2tq_info;
+ struct cnic_dma hq_info;
+};
+
+struct cnic_context {
+ u32 cid;
+ struct kwqe_16_data *kwqe_data;
+ dma_addr_t kwqe_data_mapping;
+ wait_queue_head_t waitq;
+ int wait_cond;
+ unsigned long timestamp;
+ u32 ctx_flags;
+#define CTX_FL_OFFLD_START 0x00000001
+ u8 ulp_proto_id;
+ union {
+ struct cnic_iscsi *iscsi;
+ } proto;
+};
+
+struct cnic_local {
+
+ spinlock_t cnic_ulp_lock;
+ void *ulp_handle[MAX_CNIC_ULP_TYPE];
+ unsigned long ulp_flags[MAX_CNIC_ULP_TYPE];
+#define ULP_F_INIT 0
+#define ULP_F_START 1
+ struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE];
+
+ /* protected by ulp_lock */
+ u32 cnic_local_flags;
+#define CNIC_LCL_FL_KWQ_INIT 0x00000001
+
+ struct cnic_dev *dev;
+
+ struct cnic_eth_dev *ethdev;
+
+ void *l2_ring;
+ dma_addr_t l2_ring_map;
+ int l2_ring_size;
+ int l2_rx_ring_size;
+
+ void *l2_buf;
+ dma_addr_t l2_buf_map;
+ int l2_buf_size;
+ int l2_single_buf_size;
+
+ u16 *rx_cons_ptr;
+ u16 *tx_cons_ptr;
+ u16 rx_cons;
+ u16 tx_cons;
+
+ u32 kwq_cid_addr;
+ u32 kcq_cid_addr;
+
+ struct cnic_dma kwq_info;
+ struct kwqe **kwq;
+
+ struct cnic_dma kwq_16_data_info;
+
+ u16 max_kwq_idx;
+
+ u16 kwq_prod_idx;
+ u32 kwq_io_addr;
+
+ u16 *kwq_con_idx_ptr;
+ u16 kwq_con_idx;
+
+ struct cnic_dma kcq_info;
+ struct kcqe **kcq;
+
+ u16 kcq_prod_idx;
+ u32 kcq_io_addr;
+
+ void *status_blk;
+ struct status_block_msix *bnx2_status_blk;
+ struct host_status_block *bnx2x_status_blk;
+
+ u32 status_blk_num;
+ u32 int_num;
+ u32 last_status_idx;
+ struct tasklet_struct cnic_irq_task;
+
+ struct kcqe *completed_kcq[MAX_COMPLETED_KCQE];
+
+ struct cnic_sock *csk_tbl;
+ struct cnic_id_tbl csk_port_tbl;
+
+ struct cnic_dma conn_buf_info;
+ struct cnic_dma gbl_buf_info;
+
+ struct cnic_iscsi *iscsi_tbl;
+ struct cnic_context *ctx_tbl;
+ struct cnic_id_tbl cid_tbl;
+ int max_iscsi_conn;
+ atomic_t iscsi_conn;
+
+ /* per connection parameters */
+ int num_iscsi_tasks;
+ int num_ccells;
+ int task_array_size;
+ int r2tq_size;
+ int hq_size;
+ int num_cqs;
+
+ struct cnic_ctx *ctx_arr;
+ int ctx_blks;
+ int ctx_blk_size;
+ int cids_per_blk;
+
+ u32 chip_id;
+ int func;
+ u32 shmem_base;
+
+ u32 uio_dev;
+ struct uio_info *cnic_uinfo;
+
+ struct cnic_ops *cnic_ops;
+ int (*start_hw)(struct cnic_dev *);
+ void (*stop_hw)(struct cnic_dev *);
+ void (*setup_pgtbl)(struct cnic_dev *,
+ struct cnic_dma *);
+ int (*alloc_resc)(struct cnic_dev *);
+ void (*free_resc)(struct cnic_dev *);
+ int (*start_cm)(struct cnic_dev *);
+ void (*stop_cm)(struct cnic_dev *);
+ void (*enable_int)(struct cnic_dev *);
+ void (*disable_int_sync)(struct cnic_dev *);
+ void (*ack_int)(struct cnic_dev *);
+ void (*close_conn)(struct cnic_sock *, u32 opcode);
+ u16 (*next_idx)(u16);
+ u16 (*hw_idx)(u16);
+};
+
+struct bnx2x_bd_chain_next {
+ u32 addr_lo;
+ u32 addr_hi;
+ u8 reserved[8];
+};
+
+#define ISCSI_RAMROD_CMD_ID_UPDATE_CONN (ISCSI_KCQE_OPCODE_UPDATE_CONN)
+#define ISCSI_RAMROD_CMD_ID_INIT (ISCSI_KCQE_OPCODE_INIT)
+
+#define CDU_REGION_NUMBER_XCM_AG 2
+#define CDU_REGION_NUMBER_UCM_AG 4
+
+#endif
+
diff --git a/drivers/net/cnic_defs.h b/drivers/net/cnic_defs.h
new file mode 100644
index 000000000000..cee80f694457
--- /dev/null
+++ b/drivers/net/cnic_defs.h
@@ -0,0 +1,580 @@
+
+/* cnic.c: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#ifndef CNIC_DEFS_H
+#define CNIC_DEFS_H
+
+/* KWQ (kernel work queue) request op codes */
+#define L2_KWQE_OPCODE_VALUE_FLUSH (4)
+
+#define L4_KWQE_OPCODE_VALUE_CONNECT1 (50)
+#define L4_KWQE_OPCODE_VALUE_CONNECT2 (51)
+#define L4_KWQE_OPCODE_VALUE_CONNECT3 (52)
+#define L4_KWQE_OPCODE_VALUE_RESET (53)
+#define L4_KWQE_OPCODE_VALUE_CLOSE (54)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_SECRET (60)
+#define L4_KWQE_OPCODE_VALUE_INIT_ULP (61)
+
+#define L4_KWQE_OPCODE_VALUE_OFFLOAD_PG (1)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_PG (9)
+#define L4_KWQE_OPCODE_VALUE_UPLOAD_PG (14)
+
+#define L5CM_RAMROD_CMD_ID_BASE (0x80)
+#define L5CM_RAMROD_CMD_ID_TCP_CONNECT (L5CM_RAMROD_CMD_ID_BASE + 3)
+#define L5CM_RAMROD_CMD_ID_CLOSE (L5CM_RAMROD_CMD_ID_BASE + 12)
+#define L5CM_RAMROD_CMD_ID_ABORT (L5CM_RAMROD_CMD_ID_BASE + 13)
+#define L5CM_RAMROD_CMD_ID_SEARCHER_DELETE (L5CM_RAMROD_CMD_ID_BASE + 14)
+#define L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD (L5CM_RAMROD_CMD_ID_BASE + 15)
+
+/* KCQ (kernel completion queue) response op codes */
+#define L4_KCQE_OPCODE_VALUE_CLOSE_COMP (53)
+#define L4_KCQE_OPCODE_VALUE_RESET_COMP (54)
+#define L4_KCQE_OPCODE_VALUE_FW_TCP_UPDATE (55)
+#define L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE (56)
+#define L4_KCQE_OPCODE_VALUE_RESET_RECEIVED (57)
+#define L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED (58)
+#define L4_KCQE_OPCODE_VALUE_INIT_ULP (61)
+
+#define L4_KCQE_OPCODE_VALUE_OFFLOAD_PG (1)
+#define L4_KCQE_OPCODE_VALUE_UPDATE_PG (9)
+#define L4_KCQE_OPCODE_VALUE_UPLOAD_PG (14)
+
+/* KCQ (kernel completion queue) completion status */
+#define L4_KCQE_COMPLETION_STATUS_SUCCESS (0)
+#define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93)
+
+#define L4_LAYER_CODE (4)
+#define L2_LAYER_CODE (2)
+
+/*
+ * L4 KCQ CQE
+ */
+struct l4_kcq {
+ u32 cid;
+ u32 pg_cid;
+ u32 conn_id;
+ u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+ u16 status;
+ u16 reserved1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved1;
+ u16 status;
+#endif
+ u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KCQ_RESERVED3 (0x7<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_RAMROD_COMPLETION (0x1<<3) /* Everest only */
+#define L4_KCQ_RAMROD_COMPLETION_SHIFT 3
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+ u8 op_code;
+ u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+ u16 qe_self_seq;
+ u8 op_code;
+ u8 flags;
+#define L4_KCQ_RESERVED3 (0xF<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_RAMROD_COMPLETION (0x1<<3) /* Everest only */
+#define L4_KCQ_RAMROD_COMPLETION_SHIFT 3
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * L4 KCQ CQE PG upload
+ */
+struct l4_kcq_upload_pg {
+ u32 pg_cid;
+#if defined(__BIG_ENDIAN)
+ u16 pg_status;
+ u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+ u16 pg_ipid_count;
+ u16 pg_status;
+#endif
+ u32 reserved1[5];
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+ u8 op_code;
+ u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+ u16 qe_self_seq;
+ u8 op_code;
+ u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * Gracefully close the connection request
+ */
+struct l4_kwq_close_req {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 cid;
+ u32 reserved2[6];
+};
+
+
+/*
+ * The first request to be passed in order to establish connection in option2
+ */
+struct l4_kwq_connect_req1 {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u8 reserved0;
+ u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+ u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+ u8 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 cid;
+ u32 pg_cid;
+ u32 src_ip;
+ u32 dst_ip;
+#if defined(__BIG_ENDIAN)
+ u16 dst_port;
+ u16 src_port;
+#elif defined(__LITTLE_ENDIAN)
+ u16 src_port;
+ u16 dst_port;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 rsrv1[3];
+ u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+ u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+ u8 rsrv1[3];
+#endif
+ u32 rsrv2;
+};
+
+
+/*
+ * The second ( optional )request to be passed in order to establish
+ * connection in option2 - for IPv6 only
+ */
+struct l4_kwq_connect_req2 {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u8 reserved0;
+ u8 rsrv;
+#elif defined(__LITTLE_ENDIAN)
+ u8 rsrv;
+ u8 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 reserved2;
+ u32 src_ip_v6_2;
+ u32 src_ip_v6_3;
+ u32 src_ip_v6_4;
+ u32 dst_ip_v6_2;
+ u32 dst_ip_v6_3;
+ u32 dst_ip_v6_4;
+};
+
+
+/*
+ * The third ( and last )request to be passed in order to establish
+ * connection in option2
+ */
+struct l4_kwq_connect_req3 {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 ka_timeout;
+ u32 ka_interval ;
+#if defined(__BIG_ENDIAN)
+ u8 snd_seq_scale;
+ u8 ttl;
+ u8 tos;
+ u8 ka_max_probe_count;
+#elif defined(__LITTLE_ENDIAN)
+ u8 ka_max_probe_count;
+ u8 tos;
+ u8 ttl;
+ u8 snd_seq_scale;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 pmtu;
+ u16 mss;
+#elif defined(__LITTLE_ENDIAN)
+ u16 mss;
+ u16 pmtu;
+#endif
+ u32 rcv_buf;
+ u32 snd_buf;
+ u32 seed;
+};
+
+
+/*
+ * a KWQE request to offload a PG connection
+ */
+struct l4_kwq_offload_pg {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 l2hdr_nbytes;
+ u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+ u8 da0;
+ u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+ u8 da1;
+ u8 da0;
+ u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+ u8 l2hdr_nbytes;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 da2;
+ u8 da3;
+ u8 da4;
+ u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+ u8 da5;
+ u8 da4;
+ u8 da3;
+ u8 da2;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 sa0;
+ u8 sa1;
+ u8 sa2;
+ u8 sa3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 sa3;
+ u8 sa2;
+ u8 sa1;
+ u8 sa0;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 sa4;
+ u8 sa5;
+ u16 etype;
+#elif defined(__LITTLE_ENDIAN)
+ u16 etype;
+ u8 sa5;
+ u8 sa4;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 vlan_tag;
+ u16 ipid_start;
+#elif defined(__LITTLE_ENDIAN)
+ u16 ipid_start;
+ u16 vlan_tag;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 ipid_count;
+ u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved3;
+ u16 ipid_count;
+#endif
+ u32 host_opaque;
+};
+
+
+/*
+ * Abortively close the connection request
+ */
+struct l4_kwq_reset_req {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+ u8 op_code;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_code;
+ u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 cid;
+ u32 reserved2[6];
+};
+
+
+/*
+ * a KWQE request to update a PG connection
+ */
+struct l4_kwq_update_pg {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+ u8 opcode;
+ u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+ u16 oper16;
+ u8 opcode;
+ u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 pg_cid;
+ u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+ u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+ u8 pg_unused_a;
+ u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+ u16 pg_ipid_count;
+ u8 pg_unused_a;
+ u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserverd3;
+ u8 da0;
+ u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+ u8 da1;
+ u8 da0;
+ u16 reserverd3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 da2;
+ u8 da3;
+ u8 da4;
+ u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+ u8 da5;
+ u8 da4;
+ u8 da3;
+ u8 da2;
+#endif
+ u32 reserved4;
+ u32 reserved5;
+};
+
+
+/*
+ * a KWQE request to upload a PG or L4 context
+ */
+struct l4_kwq_upload {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+ u8 opcode;
+ u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+ u16 oper16;
+ u8 opcode;
+ u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+#endif
+ u32 cid;
+ u32 reserved2[6];
+};
+
+#endif /* CNIC_DEFS_H */
diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h
new file mode 100644
index 000000000000..06380963a34e
--- /dev/null
+++ b/drivers/net/cnic_if.h
@@ -0,0 +1,299 @@
+/* cnic_if.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
+
+#ifndef CNIC_IF_H
+#define CNIC_IF_H
+
+#define CNIC_MODULE_VERSION "2.0.0"
+#define CNIC_MODULE_RELDATE "May 21, 2009"
+
+#define CNIC_ULP_RDMA 0
+#define CNIC_ULP_ISCSI 1
+#define CNIC_ULP_L4 2
+#define MAX_CNIC_ULP_TYPE_EXT 2
+#define MAX_CNIC_ULP_TYPE 3
+
+struct kwqe {
+ u32 kwqe_op_flag;
+
+#define KWQE_OPCODE_MASK 0x00ff0000
+#define KWQE_OPCODE_SHIFT 16
+#define KWQE_FLAGS_LAYER_SHIFT 28
+#define KWQE_OPCODE(x) ((x & KWQE_OPCODE_MASK) >> KWQE_OPCODE_SHIFT)
+
+ u32 kwqe_info0;
+ u32 kwqe_info1;
+ u32 kwqe_info2;
+ u32 kwqe_info3;
+ u32 kwqe_info4;
+ u32 kwqe_info5;
+ u32 kwqe_info6;
+};
+
+struct kwqe_16 {
+ u32 kwqe_info0;
+ u32 kwqe_info1;
+ u32 kwqe_info2;
+ u32 kwqe_info3;
+};
+
+struct kcqe {
+ u32 kcqe_info0;
+ u32 kcqe_info1;
+ u32 kcqe_info2;
+ u32 kcqe_info3;
+ u32 kcqe_info4;
+ u32 kcqe_info5;
+ u32 kcqe_info6;
+ u32 kcqe_op_flag;
+ #define KCQE_RAMROD_COMPLETION (0x1<<27) /* Everest */
+ #define KCQE_FLAGS_LAYER_MASK (0x7<<28)
+ #define KCQE_FLAGS_LAYER_MASK_MISC (0<<28)
+ #define KCQE_FLAGS_LAYER_MASK_L2 (2<<28)
+ #define KCQE_FLAGS_LAYER_MASK_L3 (3<<28)
+ #define KCQE_FLAGS_LAYER_MASK_L4 (4<<28)
+ #define KCQE_FLAGS_LAYER_MASK_L5_RDMA (5<<28)
+ #define KCQE_FLAGS_LAYER_MASK_L5_ISCSI (6<<28)
+ #define KCQE_FLAGS_NEXT (1<<31)
+ #define KCQE_FLAGS_OPCODE_MASK (0xff<<16)
+ #define KCQE_FLAGS_OPCODE_SHIFT (16)
+ #define KCQE_OPCODE(op) \
+ (((op) & KCQE_FLAGS_OPCODE_MASK) >> KCQE_FLAGS_OPCODE_SHIFT)
+};
+
+#define MAX_CNIC_CTL_DATA 64
+#define MAX_DRV_CTL_DATA 64
+
+#define CNIC_CTL_STOP_CMD 1
+#define CNIC_CTL_START_CMD 2
+#define CNIC_CTL_COMPLETION_CMD 3
+
+#define DRV_CTL_IO_WR_CMD 0x101
+#define DRV_CTL_IO_RD_CMD 0x102
+#define DRV_CTL_CTX_WR_CMD 0x103
+#define DRV_CTL_CTXTBL_WR_CMD 0x104
+#define DRV_CTL_COMPLETION_CMD 0x105
+
+struct cnic_ctl_completion {
+ u32 cid;
+};
+
+struct drv_ctl_completion {
+ u32 comp_count;
+};
+
+struct cnic_ctl_info {
+ int cmd;
+ union {
+ struct cnic_ctl_completion comp;
+ char bytes[MAX_CNIC_CTL_DATA];
+ } data;
+};
+
+struct drv_ctl_io {
+ u32 cid_addr;
+ u32 offset;
+ u32 data;
+ dma_addr_t dma_addr;
+};
+
+struct drv_ctl_info {
+ int cmd;
+ union {
+ struct drv_ctl_completion comp;
+ struct drv_ctl_io io;
+ char bytes[MAX_DRV_CTL_DATA];
+ } data;
+};
+
+struct cnic_ops {
+ struct module *cnic_owner;
+ /* Calls to these functions are protected by RCU. When
+ * unregistering, we wait for any calls to complete before
+ * continuing.
+ */
+ int (*cnic_handler)(void *, void *);
+ int (*cnic_ctl)(void *, struct cnic_ctl_info *);
+};
+
+#define MAX_CNIC_VEC 8
+
+struct cnic_irq {
+ unsigned int vector;
+ void *status_blk;
+ u32 status_blk_num;
+ u32 irq_flags;
+#define CNIC_IRQ_FL_MSIX 0x00000001
+};
+
+struct cnic_eth_dev {
+ struct module *drv_owner;
+ u32 drv_state;
+#define CNIC_DRV_STATE_REGD 0x00000001
+#define CNIC_DRV_STATE_USING_MSIX 0x00000002
+ u32 chip_id;
+ u32 max_kwqe_pending;
+ struct pci_dev *pdev;
+ void __iomem *io_base;
+
+ u32 ctx_tbl_offset;
+ u32 ctx_tbl_len;
+ int ctx_blk_size;
+ u32 starting_cid;
+ u32 max_iscsi_conn;
+ u32 max_fcoe_conn;
+ u32 max_rdma_conn;
+ u32 reserved0[2];
+
+ int num_irq;
+ struct cnic_irq irq_arr[MAX_CNIC_VEC];
+ int (*drv_register_cnic)(struct net_device *,
+ struct cnic_ops *, void *);
+ int (*drv_unregister_cnic)(struct net_device *);
+ int (*drv_submit_kwqes_32)(struct net_device *,
+ struct kwqe *[], u32);
+ int (*drv_submit_kwqes_16)(struct net_device *,
+ struct kwqe_16 *[], u32);
+ int (*drv_ctl)(struct net_device *, struct drv_ctl_info *);
+ unsigned long reserved1[2];
+};
+
+struct cnic_sockaddr {
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } local;
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } remote;
+};
+
+struct cnic_sock {
+ struct cnic_dev *dev;
+ void *context;
+ u32 src_ip[4];
+ u32 dst_ip[4];
+ u16 src_port;
+ u16 dst_port;
+ u16 vlan_id;
+ unsigned char old_ha[6];
+ unsigned char ha[6];
+ u32 mtu;
+ u32 cid;
+ u32 l5_cid;
+ u32 pg_cid;
+ int ulp_type;
+
+ u32 ka_timeout;
+ u32 ka_interval;
+ u8 ka_max_probe_count;
+ u8 tos;
+ u8 ttl;
+ u8 snd_seq_scale;
+ u32 rcv_buf;
+ u32 snd_buf;
+ u32 seed;
+
+ unsigned long tcp_flags;
+#define SK_TCP_NO_DELAY_ACK 0x1
+#define SK_TCP_KEEP_ALIVE 0x2
+#define SK_TCP_NAGLE 0x4
+#define SK_TCP_TIMESTAMP 0x8
+#define SK_TCP_SACK 0x10
+#define SK_TCP_SEG_SCALING 0x20
+ unsigned long flags;
+#define SK_F_INUSE 0
+#define SK_F_OFFLD_COMPLETE 1
+#define SK_F_OFFLD_SCHED 2
+#define SK_F_PG_OFFLD_COMPLETE 3
+#define SK_F_CONNECT_START 4
+#define SK_F_IPV6 5
+#define SK_F_CLOSING 7
+
+ atomic_t ref_count;
+ u32 state;
+ struct kwqe kwqe1;
+ struct kwqe kwqe2;
+ struct kwqe kwqe3;
+};
+
+struct cnic_dev {
+ struct net_device *netdev;
+ struct pci_dev *pcidev;
+ void __iomem *regview;
+ struct list_head list;
+
+ int (*register_device)(struct cnic_dev *dev, int ulp_type,
+ void *ulp_ctx);
+ int (*unregister_device)(struct cnic_dev *dev, int ulp_type);
+ int (*submit_kwqes)(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num_wqes);
+ int (*submit_kwqes_16)(struct cnic_dev *dev, struct kwqe_16 *wqes[],
+ u32 num_wqes);
+
+ int (*cm_create)(struct cnic_dev *, int, u32, u32, struct cnic_sock **,
+ void *);
+ int (*cm_destroy)(struct cnic_sock *);
+ int (*cm_connect)(struct cnic_sock *, struct cnic_sockaddr *);
+ int (*cm_abort)(struct cnic_sock *);
+ int (*cm_close)(struct cnic_sock *);
+ struct cnic_dev *(*cm_select_dev)(struct sockaddr_in *, int ulp_type);
+ int (*iscsi_nl_msg_recv)(struct cnic_dev *dev, u32 msg_type,
+ char *data, u16 data_size);
+ unsigned long flags;
+#define CNIC_F_CNIC_UP 1
+#define CNIC_F_BNX2_CLASS 3
+#define CNIC_F_BNX2X_CLASS 4
+ atomic_t ref_count;
+ u8 mac_addr[6];
+
+ int max_iscsi_conn;
+ int max_fcoe_conn;
+ int max_rdma_conn;
+
+ void *cnic_priv;
+};
+
+#define CNIC_WR(dev, off, val) writel(val, dev->regview + off)
+#define CNIC_WR16(dev, off, val) writew(val, dev->regview + off)
+#define CNIC_WR8(dev, off, val) writeb(val, dev->regview + off)
+#define CNIC_RD(dev, off) readl(dev->regview + off)
+#define CNIC_RD16(dev, off) readw(dev->regview + off)
+
+struct cnic_ulp_ops {
+ /* Calls to these functions are protected by RCU. When
+ * unregistering, we wait for any calls to complete before
+ * continuing.
+ */
+
+ void (*cnic_init)(struct cnic_dev *dev);
+ void (*cnic_exit)(struct cnic_dev *dev);
+ void (*cnic_start)(void *ulp_ctx);
+ void (*cnic_stop)(void *ulp_ctx);
+ void (*indicate_kcqes)(void *ulp_ctx, struct kcqe *cqes[],
+ u32 num_cqes);
+ void (*indicate_netevent)(void *ulp_ctx, unsigned long event);
+ void (*cm_connect_complete)(struct cnic_sock *);
+ void (*cm_close_complete)(struct cnic_sock *);
+ void (*cm_abort_complete)(struct cnic_sock *);
+ void (*cm_remote_close)(struct cnic_sock *);
+ void (*cm_remote_abort)(struct cnic_sock *);
+ void (*iscsi_nl_send_msg)(struct cnic_dev *dev, u32 msg_type,
+ char *data, u16 data_size);
+ struct module *owner;
+};
+
+extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
+
+extern int cnic_unregister_driver(int ulp_type);
+
+#endif
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index 714df2b675e6..c888e97c9671 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -85,8 +85,8 @@ struct fl_pg_chunk {
struct page *page;
void *va;
unsigned int offset;
- u64 *p_cnt;
- DECLARE_PCI_UNMAP_ADDR(mapping);
+ unsigned long *p_cnt;
+ dma_addr_t mapping;
};
struct rx_desc;
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index 7ea48414c6cb..17858b9a5830 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -2496,14 +2496,16 @@ static void check_link_status(struct adapter *adapter)
for_each_port(adapter, i) {
struct net_device *dev = adapter->port[i];
struct port_info *p = netdev_priv(dev);
+ int link_fault;
spin_lock_irq(&adapter->work_lock);
- if (p->link_fault) {
+ link_fault = p->link_fault;
+ spin_unlock_irq(&adapter->work_lock);
+
+ if (link_fault) {
t3_link_fault(adapter, i);
- spin_unlock_irq(&adapter->work_lock);
continue;
}
- spin_unlock_irq(&adapter->work_lock);
if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) {
t3_xgm_intr_disable(adapter, i);
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index 26d3587f3399..b3ee2bc1a005 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -355,7 +355,7 @@ static void clear_rx_desc(struct pci_dev *pdev, const struct sge_fl *q,
(*d->pg_chunk.p_cnt)--;
if (!*d->pg_chunk.p_cnt)
pci_unmap_page(pdev,
- pci_unmap_addr(&d->pg_chunk, mapping),
+ d->pg_chunk.mapping,
q->alloc_size, PCI_DMA_FROMDEVICE);
put_page(d->pg_chunk.page);
@@ -454,7 +454,7 @@ static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q,
q->pg_chunk.offset = 0;
mapping = pci_map_page(adapter->pdev, q->pg_chunk.page,
0, q->alloc_size, PCI_DMA_FROMDEVICE);
- pci_unmap_addr_set(&q->pg_chunk, mapping, mapping);
+ q->pg_chunk.mapping = mapping;
}
sd->pg_chunk = q->pg_chunk;
@@ -511,8 +511,7 @@ static int refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp)
nomem: q->alloc_failed++;
break;
}
- mapping = pci_unmap_addr(&sd->pg_chunk, mapping) +
- sd->pg_chunk.offset;
+ mapping = sd->pg_chunk.mapping + sd->pg_chunk.offset;
pci_unmap_addr_set(sd, dma_addr, mapping);
add_one_rx_chunk(mapping, d, q->gen);
@@ -881,7 +880,7 @@ recycle:
(*sd->pg_chunk.p_cnt)--;
if (!*sd->pg_chunk.p_cnt)
pci_unmap_page(adap->pdev,
- pci_unmap_addr(&sd->pg_chunk, mapping),
+ sd->pg_chunk.mapping,
fl->alloc_size,
PCI_DMA_FROMDEVICE);
if (!skb) {
@@ -2096,7 +2095,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
(*sd->pg_chunk.p_cnt)--;
if (!*sd->pg_chunk.p_cnt)
pci_unmap_page(adap->pdev,
- pci_unmap_addr(&sd->pg_chunk, mapping),
+ sd->pg_chunk.mapping,
fl->alloc_size,
PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index e1bd690ff831..4950d5d789ae 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -1274,6 +1274,11 @@ void t3_link_fault(struct adapter *adapter, int port_id)
A_XGM_INT_STATUS + mac->offset);
link_fault &= F_LINKFAULTCHANGE;
+ link_ok = lc->link_ok;
+ speed = lc->speed;
+ duplex = lc->duplex;
+ fc = lc->fc;
+
phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
if (link_fault) {
@@ -3779,7 +3784,7 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
adapter->params.info = ai;
adapter->params.nports = ai->nports0 + ai->nports1;
- adapter->params.chan_map = !!ai->nports0 | (!!ai->nports1 << 1);
+ adapter->params.chan_map = (!!ai->nports0) | (!!ai->nports1 << 1);
adapter->params.rev = t3_read_reg(adapter, A_PL_REV);
/*
* We used to only run the "adapter check task" once a second if
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index 5c0b457c7868..0f9ee1348552 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -2728,7 +2728,7 @@ static void __devexit e100_remove(struct pci_dev *pdev)
#define E100_82552_SMARTSPEED 0x14 /* SmartSpeed Ctrl register */
#define E100_82552_REV_ANEG 0x0200 /* Reverse auto-negotiation */
#define E100_82552_ANEG_NOW 0x0400 /* Auto-negotiate now */
-static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
+static void __e100_shutdown(struct pci_dev *pdev, bool *enable_wake)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct nic *nic = netdev_priv(netdev);
@@ -2749,19 +2749,32 @@ static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
E100_82552_SMARTSPEED, smartspeed |
E100_82552_REV_ANEG | E100_82552_ANEG_NOW);
}
- if (pci_enable_wake(pdev, PCI_D3cold, true))
- pci_enable_wake(pdev, PCI_D3hot, true);
+ *enable_wake = true;
} else {
- pci_enable_wake(pdev, PCI_D3hot, false);
+ *enable_wake = false;
}
pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
+}
- return 0;
+static int __e100_power_off(struct pci_dev *pdev, bool wake)
+{
+ if (wake) {
+ return pci_prepare_to_sleep(pdev);
+ } else {
+ pci_wake_from_d3(pdev, false);
+ return pci_set_power_state(pdev, PCI_D3hot);
+ }
}
#ifdef CONFIG_PM
+static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ bool wake;
+ __e100_shutdown(pdev, &wake);
+ return __e100_power_off(pdev, wake);
+}
+
static int e100_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
@@ -2792,7 +2805,10 @@ static int e100_resume(struct pci_dev *pdev)
static void e100_shutdown(struct pci_dev *pdev)
{
- e100_suspend(pdev, PMSG_SUSPEND);
+ bool wake;
+ __e100_shutdown(pdev, &wake);
+ if (system_state == SYSTEM_POWER_OFF)
+ __e100_power_off(pdev, wake);
}
/* ------------------ PCI Error Recovery infrastructure -------------- */
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 6a46ceed9436..fffb006b7d95 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -3738,7 +3738,7 @@ static irqreturn_t e1000_intr(int irq, void *data)
struct e1000_hw *hw = &adapter->hw;
u32 rctl, icr = er32(ICR);
- if (unlikely((!icr) || test_bit(__E1000_RESETTING, &adapter->flags)))
+ if (unlikely((!icr) || test_bit(__E1000_DOWN, &adapter->flags)))
return IRQ_NONE; /* Not our interrupt */
/* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
@@ -4027,8 +4027,9 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
PCI_DMA_FROMDEVICE);
length = le16_to_cpu(rx_desc->length);
-
- if (unlikely(!(status & E1000_RXD_STAT_EOP))) {
+ /* !EOP means multiple descriptors were used to store a single
+ * packet, also make sure the frame isn't just CRC only */
+ if (unlikely(!(status & E1000_RXD_STAT_EOP) || (length <= 4))) {
/* All receives must fit into a single buffer */
E1000_DBG("%s: Receive packet consumed multiple"
" buffers\n", netdev->name);
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 6e317caf429c..16a41389575a 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -40,7 +40,7 @@
#include <asm/io.h>
#define DRV_NAME "ehea"
-#define DRV_VERSION "EHEA_0100"
+#define DRV_VERSION "EHEA_0101"
/* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 604c844d0769..b22dab9153f6 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -545,14 +545,17 @@ static inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
x &= (arr_len - 1);
pref = skb_array[x];
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
-
- pref = (skb_array[x]->data);
- prefetch(pref);
- prefetch(pref + EHEA_CACHE_LINE);
- prefetch(pref + EHEA_CACHE_LINE * 2);
- prefetch(pref + EHEA_CACHE_LINE * 3);
+ if (pref) {
+ prefetchw(pref);
+ prefetchw(pref + EHEA_CACHE_LINE);
+
+ pref = (skb_array[x]->data);
+ prefetch(pref);
+ prefetch(pref + EHEA_CACHE_LINE);
+ prefetch(pref + EHEA_CACHE_LINE * 2);
+ prefetch(pref + EHEA_CACHE_LINE * 3);
+ }
+
skb = skb_array[skb_index];
skb_array[skb_index] = NULL;
return skb;
@@ -569,12 +572,14 @@ static inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
x &= (arr_len - 1);
pref = skb_array[x];
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
+ if (pref) {
+ prefetchw(pref);
+ prefetchw(pref + EHEA_CACHE_LINE);
- pref = (skb_array[x]->data);
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
+ pref = (skb_array[x]->data);
+ prefetchw(pref);
+ prefetchw(pref + EHEA_CACHE_LINE);
+ }
skb = skb_array[wqe_index];
skb_array[wqe_index] = NULL;
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 11d5db16ed9c..9f6a68fb7b45 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -897,6 +897,12 @@ enum {
};
static int phy_cross = NV_CROSSOVER_DETECTION_DISABLED;
+/*
+ * Power down phy when interface is down (persists through reboot;
+ * older Linux and other OSes may not power it up again)
+ */
+static int phy_power_down = 0;
+
static inline struct fe_priv *get_nvpriv(struct net_device *dev)
{
return netdev_priv(dev);
@@ -1485,7 +1491,10 @@ static int phy_init(struct net_device *dev)
/* restart auto negotiation, power down phy */
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
- mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE | BMCR_PDOWN);
+ mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
+ if (phy_power_down) {
+ mii_control |= BMCR_PDOWN;
+ }
if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
return PHY_ERROR;
}
@@ -1880,6 +1889,7 @@ static void nv_init_tx(struct net_device *dev)
np->tx_pkts_in_progress = 0;
np->tx_change_owner = NULL;
np->tx_end_flip = NULL;
+ np->tx_stop = 0;
for (i = 0; i < np->tx_ring_size; i++) {
if (!nv_optimized(np)) {
@@ -2530,6 +2540,8 @@ static void nv_tx_timeout(struct net_device *dev)
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 status;
+ union ring_type put_tx;
+ int saved_tx_limit;
if (np->msi_flags & NV_MSI_X_ENABLED)
status = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
@@ -2589,24 +2601,32 @@ static void nv_tx_timeout(struct net_device *dev)
/* 1) stop tx engine */
nv_stop_tx(dev);
- /* 2) check that the packets were not sent already: */
+ /* 2) complete any outstanding tx and do not give HW any limited tx pkts */
+ saved_tx_limit = np->tx_limit;
+ np->tx_limit = 0; /* prevent giving HW any limited pkts */
+ np->tx_stop = 0; /* prevent waking tx queue */
if (!nv_optimized(np))
nv_tx_done(dev, np->tx_ring_size);
else
nv_tx_done_optimized(dev, np->tx_ring_size);
- /* 3) if there are dead entries: clear everything */
- if (np->get_tx_ctx != np->put_tx_ctx) {
- printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name);
- nv_drain_tx(dev);
- nv_init_tx(dev);
- setup_hw_rings(dev, NV_SETUP_TX_RING);
- }
+ /* save current HW postion */
+ if (np->tx_change_owner)
+ put_tx.ex = np->tx_change_owner->first_tx_desc;
+ else
+ put_tx = np->put_tx;
- netif_wake_queue(dev);
+ /* 3) clear all tx state */
+ nv_drain_tx(dev);
+ nv_init_tx(dev);
- /* 4) restart tx engine */
+ /* 4) restore state to current HW position */
+ np->get_tx = np->put_tx = put_tx;
+ np->tx_limit = saved_tx_limit;
+
+ /* 5) restart tx engine */
nv_start_tx(dev);
+ netif_wake_queue(dev);
spin_unlock_irq(&np->lock);
}
@@ -5502,7 +5522,7 @@ static int nv_close(struct net_device *dev)
nv_drain_rxtx(dev);
- if (np->wolenabled) {
+ if (np->wolenabled || !phy_power_down) {
writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
nv_start_rx(dev);
} else {
@@ -6356,6 +6376,8 @@ module_param(dma_64bit, int, 0);
MODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0.");
module_param(phy_cross, int, 0);
MODULE_PARM_DESC(phy_cross, "Phy crossover detection for Realtek 8201 phy is enabled by setting to 1 and disabled by setting to 0.");
+module_param(phy_power_down, int, 0);
+MODULE_PARM_DESC(phy_power_down, "Power down phy and disable link when interface is down (1), or leave phy powered up (0).");
MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index b2c49679bba7..a0519184e54e 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1885,8 +1885,17 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
if (unlikely(!newskb))
newskb = skb;
- else if (skb)
+ else if (skb) {
+ /*
+ * We need to reset ->data to what it
+ * was before gfar_new_skb() re-aligned
+ * it to an RXBUF_ALIGNMENT boundary
+ * before we put the skb back on the
+ * recycle list.
+ */
+ skb->data = skb->head + NET_SKB_PAD;
__skb_queue_head(&priv->rx_recycle, skb);
+ }
} else {
/* Increment the number of packets */
dev->stats.rx_packets++;
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 0642d52aef5c..cf352961ae9b 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -259,7 +259,7 @@ extern const char gfar_driver_version[];
(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
| IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \
- | IEVENT_MAG)
+ | IEVENT_MAG | IEVENT_BABR)
#define IMASK_INIT_CLEAR 0x00000000
#define IMASK_BABR 0x80000000
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c
index 08c801490c72..e25343588fc7 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/igb/igb_main.c
@@ -2006,7 +2006,7 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 rctl;
u32 srrctl = 0;
- int i, j;
+ int i;
rctl = rd32(E1000_RCTL);
@@ -2071,8 +2071,6 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
if (adapter->vfs_allocated_count) {
u32 vmolr;
- j = adapter->rx_ring[0].reg_idx;
-
/* set all queue drop enable bits */
wr32(E1000_QDE, ALL_QUEUES);
srrctl |= E1000_SRRCTL_DROP_EN;
@@ -2080,16 +2078,16 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
/* disable queue 0 to prevent tail write w/o re-config */
wr32(E1000_RXDCTL(0), 0);
- vmolr = rd32(E1000_VMOLR(j));
+ vmolr = rd32(E1000_VMOLR(adapter->vfs_allocated_count));
if (rctl & E1000_RCTL_LPE)
vmolr |= E1000_VMOLR_LPE;
- if (adapter->num_rx_queues > 0)
+ if (adapter->num_rx_queues > 1)
vmolr |= E1000_VMOLR_RSSE;
- wr32(E1000_VMOLR(j), vmolr);
+ wr32(E1000_VMOLR(adapter->vfs_allocated_count), vmolr);
}
for (i = 0; i < adapter->num_rx_queues; i++) {
- j = adapter->rx_ring[i].reg_idx;
+ int j = adapter->rx_ring[i].reg_idx;
wr32(E1000_SRRCTL(j), srrctl);
}
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c
index 5567519676d5..186a65069b33 100644
--- a/drivers/net/ixgbe/ixgbe_common.c
+++ b/drivers/net/ixgbe/ixgbe_common.c
@@ -50,7 +50,6 @@ static u16 ixgbe_calc_eeprom_checksum(struct ixgbe_hw *hw);
static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index);
static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index);
static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr);
-static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr);
static void ixgbe_add_uc_addr(struct ixgbe_hw *hw, u8 *addr, u32 vmdq);
/**
@@ -1377,8 +1376,7 @@ s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw, u8 *addr_list,
* Clear accounting of old secondary address list,
* don't count RAR[0]
*/
- uc_addr_in_use = hw->addr_ctrl.rar_used_count -
- hw->addr_ctrl.mc_addr_in_rar_count - 1;
+ uc_addr_in_use = hw->addr_ctrl.rar_used_count - 1;
hw->addr_ctrl.rar_used_count -= uc_addr_in_use;
hw->addr_ctrl.overflow_promisc = 0;
@@ -1493,40 +1491,6 @@ static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr)
}
/**
- * ixgbe_add_mc_addr - Adds a multicast address.
- * @hw: pointer to hardware structure
- * @mc_addr: new multicast address
- *
- * Adds it to unused receive address register or to the multicast table.
- **/
-static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr)
-{
- u32 rar_entries = hw->mac.num_rar_entries;
- u32 rar;
-
- hw_dbg(hw, " MC Addr =%.2X %.2X %.2X %.2X %.2X %.2X\n",
- mc_addr[0], mc_addr[1], mc_addr[2],
- mc_addr[3], mc_addr[4], mc_addr[5]);
-
- /*
- * Place this multicast address in the RAR if there is room,
- * else put it in the MTA
- */
- if (hw->addr_ctrl.rar_used_count < rar_entries) {
- /* use RAR from the end up for multicast */
- rar = rar_entries - hw->addr_ctrl.mc_addr_in_rar_count - 1;
- hw->mac.ops.set_rar(hw, rar, mc_addr, 0, IXGBE_RAH_AV);
- hw_dbg(hw, "Added a multicast address to RAR[%d]\n", rar);
- hw->addr_ctrl.rar_used_count++;
- hw->addr_ctrl.mc_addr_in_rar_count++;
- } else {
- ixgbe_set_mta(hw, mc_addr);
- }
-
- hw_dbg(hw, "ixgbe_add_mc_addr Complete\n");
-}
-
-/**
* ixgbe_update_mc_addr_list_generic - Updates MAC list of multicast addresses
* @hw: pointer to hardware structure
* @mc_addr_list: the list of new multicast addresses
@@ -1542,7 +1506,6 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, u8 *mc_addr_list,
u32 mc_addr_count, ixgbe_mc_addr_itr next)
{
u32 i;
- u32 rar_entries = hw->mac.num_rar_entries;
u32 vmdq;
/*
@@ -1550,18 +1513,8 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, u8 *mc_addr_list,
* use.
*/
hw->addr_ctrl.num_mc_addrs = mc_addr_count;
- hw->addr_ctrl.rar_used_count -= hw->addr_ctrl.mc_addr_in_rar_count;
- hw->addr_ctrl.mc_addr_in_rar_count = 0;
hw->addr_ctrl.mta_in_use = 0;
- /* Zero out the other receive addresses. */
- hw_dbg(hw, "Clearing RAR[%d-%d]\n", hw->addr_ctrl.rar_used_count,
- rar_entries - 1);
- for (i = hw->addr_ctrl.rar_used_count; i < rar_entries; i++) {
- IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
- IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
- }
-
/* Clear the MTA */
hw_dbg(hw, " Clearing MTA\n");
for (i = 0; i < hw->mac.mcft_size; i++)
@@ -1570,7 +1523,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, u8 *mc_addr_list,
/* Add the new addresses */
for (i = 0; i < mc_addr_count; i++) {
hw_dbg(hw, " Adding the multicast addresses:\n");
- ixgbe_add_mc_addr(hw, next(hw, &mc_addr_list, &vmdq));
+ ixgbe_set_mta(hw, next(hw, &mc_addr_list, &vmdq));
}
/* Enable mta */
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 01884256f4c9..07e778d3e5d2 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -3646,6 +3646,8 @@ static int ixgbe_resume(struct pci_dev *pdev)
ixgbe_reset(adapter);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_WUS, ~0);
+
if (netif_running(netdev)) {
err = ixgbe_open(adapter->netdev);
if (err)
@@ -4575,7 +4577,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
const struct ixgbe_info *ii = ixgbe_info_tbl[ent->driver_data];
static int cards_found;
int i, err, pci_using_dac;
- u16 pm_value = 0;
u32 part_num, eec;
err = pci_enable_device(pdev);
@@ -4763,11 +4764,8 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
switch (pdev->device) {
case IXGBE_DEV_ID_82599_KX4:
-#define IXGBE_PCIE_PMCSR 0x44
- adapter->wol = IXGBE_WUFC_MAG;
- pci_read_config_word(pdev, IXGBE_PCIE_PMCSR, &pm_value);
- pci_write_config_word(pdev, IXGBE_PCIE_PMCSR,
- (pm_value | (1 << 8)));
+ adapter->wol = (IXGBE_WUFC_MAG | IXGBE_WUFC_EX |
+ IXGBE_WUFC_MC | IXGBE_WUFC_BC);
break;
default:
adapter->wol = 0;
diff --git a/drivers/net/mac8390.c b/drivers/net/mac8390.c
index 8e884869a05b..22e74a0e0361 100644
--- a/drivers/net/mac8390.c
+++ b/drivers/net/mac8390.c
@@ -304,7 +304,7 @@ struct net_device * __init mac8390_probe(int unit)
if (!MACH_IS_MAC)
return ERR_PTR(-ENODEV);
- dev = alloc_ei_netdev();
+ dev = ____alloc_ei_netdev(0);
if (!dev)
return ERR_PTR(-ENOMEM);
@@ -481,15 +481,15 @@ void cleanup_module(void)
static const struct net_device_ops mac8390_netdev_ops = {
.ndo_open = mac8390_open,
.ndo_stop = mac8390_close,
- .ndo_start_xmit = ei_start_xmit,
- .ndo_tx_timeout = ei_tx_timeout,
- .ndo_get_stats = ei_get_stats,
- .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_multicast_list = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = ei_poll,
+ .ndo_poll_controller = __ei_poll,
#endif
};
diff --git a/drivers/net/meth.c b/drivers/net/meth.c
index aa08987f6e81..dbd3436912b8 100644
--- a/drivers/net/meth.c
+++ b/drivers/net/meth.c
@@ -127,11 +127,11 @@ static unsigned long mdio_read(struct meth_private *priv, unsigned long phyreg)
static int mdio_probe(struct meth_private *priv)
{
int i;
- unsigned long p2, p3;
+ unsigned long p2, p3, flags;
/* check if phy is detected already */
if(priv->phy_addr>=0&&priv->phy_addr<32)
return 0;
- spin_lock(&priv->meth_lock);
+ spin_lock_irqsave(&priv->meth_lock, flags);
for (i=0;i<32;++i){
priv->phy_addr=i;
p2=mdio_read(priv,2);
@@ -157,7 +157,7 @@ static int mdio_probe(struct meth_private *priv)
break;
}
}
- spin_unlock(&priv->meth_lock);
+ spin_unlock_irqrestore(&priv->meth_lock, flags);
if(priv->phy_addr<32) {
return 0;
}
@@ -373,14 +373,14 @@ static int meth_release(struct net_device *dev)
static void meth_rx(struct net_device* dev, unsigned long int_status)
{
struct sk_buff *skb;
- unsigned long status;
+ unsigned long status, flags;
struct meth_private *priv = netdev_priv(dev);
unsigned long fifo_rptr = (int_status & METH_INT_RX_RPTR_MASK) >> 8;
- spin_lock(&priv->meth_lock);
+ spin_lock_irqsave(&priv->meth_lock, flags);
priv->dma_ctrl &= ~METH_DMA_RX_INT_EN;
mace->eth.dma_ctrl = priv->dma_ctrl;
- spin_unlock(&priv->meth_lock);
+ spin_unlock_irqrestore(&priv->meth_lock, flags);
if (int_status & METH_INT_RX_UNDERFLOW) {
fifo_rptr = (fifo_rptr - 1) & 0x0f;
@@ -452,12 +452,12 @@ static void meth_rx(struct net_device* dev, unsigned long int_status)
mace->eth.rx_fifo = priv->rx_ring_dmas[priv->rx_write];
ADVANCE_RX_PTR(priv->rx_write);
}
- spin_lock(&priv->meth_lock);
+ spin_lock_irqsave(&priv->meth_lock, flags);
/* In case there was underflow, and Rx DMA was disabled */
priv->dma_ctrl |= METH_DMA_RX_INT_EN | METH_DMA_RX_EN;
mace->eth.dma_ctrl = priv->dma_ctrl;
mace->eth.int_stat = METH_INT_RX_THRESHOLD;
- spin_unlock(&priv->meth_lock);
+ spin_unlock_irqrestore(&priv->meth_lock, flags);
}
static int meth_tx_full(struct net_device *dev)
@@ -470,11 +470,11 @@ static int meth_tx_full(struct net_device *dev)
static void meth_tx_cleanup(struct net_device* dev, unsigned long int_status)
{
struct meth_private *priv = netdev_priv(dev);
- unsigned long status;
+ unsigned long status, flags;
struct sk_buff *skb;
unsigned long rptr = (int_status&TX_INFO_RPTR) >> 16;
- spin_lock(&priv->meth_lock);
+ spin_lock_irqsave(&priv->meth_lock, flags);
/* Stop DMA notification */
priv->dma_ctrl &= ~(METH_DMA_TX_INT_EN);
@@ -527,12 +527,13 @@ static void meth_tx_cleanup(struct net_device* dev, unsigned long int_status)
}
mace->eth.int_stat = METH_INT_TX_EMPTY | METH_INT_TX_PKT;
- spin_unlock(&priv->meth_lock);
+ spin_unlock_irqrestore(&priv->meth_lock, flags);
}
static void meth_error(struct net_device* dev, unsigned status)
{
struct meth_private *priv = netdev_priv(dev);
+ unsigned long flags;
printk(KERN_WARNING "meth: error status: 0x%08x\n",status);
/* check for errors too... */
@@ -547,7 +548,7 @@ static void meth_error(struct net_device* dev, unsigned status)
printk(KERN_WARNING "meth: Rx overflow\n");
if (status & (METH_INT_RX_UNDERFLOW)) {
printk(KERN_WARNING "meth: Rx underflow\n");
- spin_lock(&priv->meth_lock);
+ spin_lock_irqsave(&priv->meth_lock, flags);
mace->eth.int_stat = METH_INT_RX_UNDERFLOW;
/* more underflow interrupts will be delivered,
* effectively throwing us into an infinite loop.
@@ -555,7 +556,7 @@ static void meth_error(struct net_device* dev, unsigned status)
priv->dma_ctrl &= ~METH_DMA_RX_EN;
mace->eth.dma_ctrl = priv->dma_ctrl;
DPRINTK("Disabled meth Rx DMA temporarily\n");
- spin_unlock(&priv->meth_lock);
+ spin_unlock_irqrestore(&priv->meth_lock, flags);
}
mace->eth.int_stat = METH_INT_ERROR;
}
diff --git a/drivers/net/mlx4/en_cq.c b/drivers/net/mlx4/en_cq.c
index 91f50de84be9..a276125b709b 100644
--- a/drivers/net/mlx4/en_cq.c
+++ b/drivers/net/mlx4/en_cq.c
@@ -125,8 +125,10 @@ void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
if (cq->is_tx)
del_timer(&cq->timer);
- else
+ else {
napi_disable(&cq->napi);
+ netif_napi_del(&cq->napi);
+ }
mlx4_cq_free(mdev->dev, &cq->mcq);
}
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c
index 438678ab2a10..7bcc49de1637 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/mlx4/en_netdev.c
@@ -583,7 +583,7 @@ int mlx4_en_start_port(struct net_device *dev)
err = mlx4_en_activate_cq(priv, cq);
if (err) {
mlx4_err(mdev, "Failed activating Rx CQ\n");
- goto rx_err;
+ goto cq_err;
}
for (j = 0; j < cq->size; j++)
cq->buf[j].owner_sr_opcode = MLX4_CQE_OWNER_MASK;
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c
index 0cbb78ca7b29..9ee873e872b3 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/mlx4/en_rx.c
@@ -610,6 +610,10 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
used_frags = mlx4_en_complete_rx_desc(priv, rx_desc, skb_frags,
skb_shinfo(skb)->frags,
page_alloc, length);
+ if (unlikely(!used_frags)) {
+ kfree_skb(skb);
+ return NULL;
+ }
skb_shinfo(skb)->nr_frags = used_frags;
/* Copy headers into the skb linear buffer */
@@ -947,7 +951,6 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv,
if (err) {
mlx4_err(mdev, "Failed to allocate qp #%d\n", qpn);
goto out;
- return err;
}
qp->event = mlx4_en_sqp_event;
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c
index ac6fc499b280..e5c98a98ad37 100644
--- a/drivers/net/mlx4/en_tx.c
+++ b/drivers/net/mlx4/en_tx.c
@@ -426,7 +426,7 @@ void mlx4_en_poll_tx_cq(unsigned long data)
INC_PERF_COUNTER(priv->pstats.tx_poll);
- if (!spin_trylock(&ring->comp_lock)) {
+ if (!spin_trylock_irq(&ring->comp_lock)) {
mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT);
return;
}
@@ -439,7 +439,7 @@ void mlx4_en_poll_tx_cq(unsigned long data)
if (inflight && priv->port_up)
mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT);
- spin_unlock(&ring->comp_lock);
+ spin_unlock_irq(&ring->comp_lock);
}
static struct mlx4_en_tx_desc *mlx4_en_bounce_to_desc(struct mlx4_en_priv *priv,
@@ -482,9 +482,9 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind)
/* Poll the CQ every mlx4_en_TX_MODER_POLL packets */
if ((++ring->poll_cnt & (MLX4_EN_TX_POLL_MODER - 1)) == 0)
- if (spin_trylock(&ring->comp_lock)) {
+ if (spin_trylock_irq(&ring->comp_lock)) {
mlx4_en_process_tx_cq(priv->dev, cq);
- spin_unlock(&ring->comp_lock);
+ spin_unlock_irq(&ring->comp_lock);
}
}
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index 102bac90a302..30bea9689694 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -976,7 +976,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
nreq = err;
goto retry;
}
-
+ kfree(entries);
goto no_msi;
}
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index b3185bf2c158..6bb5af35eda6 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -393,12 +393,12 @@ struct mv643xx_eth_private {
struct work_struct tx_timeout_task;
struct napi_struct napi;
+ u8 oom;
u8 work_link;
u8 work_tx;
u8 work_tx_end;
u8 work_rx;
u8 work_rx_refill;
- u8 work_rx_oom;
int skb_size;
struct sk_buff_head rx_recycle;
@@ -569,7 +569,7 @@ static int rxq_process(struct rx_queue *rxq, int budget)
if (rxq->rx_curr_desc == rxq->rx_ring_size)
rxq->rx_curr_desc = 0;
- dma_unmap_single(NULL, rx_desc->buf_ptr,
+ dma_unmap_single(mp->dev->dev.parent, rx_desc->buf_ptr,
rx_desc->buf_size, DMA_FROM_DEVICE);
rxq->rx_desc_count--;
rx++;
@@ -661,7 +661,7 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
dma_get_cache_alignment() - 1);
if (skb == NULL) {
- mp->work_rx_oom |= 1 << rxq->index;
+ mp->oom = 1;
goto oom;
}
@@ -678,8 +678,9 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
rx_desc = rxq->rx_desc_area + rx;
- rx_desc->buf_ptr = dma_map_single(NULL, skb->data,
- mp->skb_size, DMA_FROM_DEVICE);
+ rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent,
+ skb->data, mp->skb_size,
+ DMA_FROM_DEVICE);
rx_desc->buf_size = mp->skb_size;
rxq->rx_skb[rx] = skb;
wmb();
@@ -718,6 +719,7 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
{
+ struct mv643xx_eth_private *mp = txq_to_mp(txq);
int nr_frags = skb_shinfo(skb)->nr_frags;
int frag;
@@ -746,10 +748,10 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
desc->l4i_chk = 0;
desc->byte_cnt = this_frag->size;
- desc->buf_ptr = dma_map_page(NULL, this_frag->page,
- this_frag->page_offset,
- this_frag->size,
- DMA_TO_DEVICE);
+ desc->buf_ptr = dma_map_page(mp->dev->dev.parent,
+ this_frag->page,
+ this_frag->page_offset,
+ this_frag->size, DMA_TO_DEVICE);
}
}
@@ -826,7 +828,8 @@ no_csum:
desc->l4i_chk = l4i_chk;
desc->byte_cnt = length;
- desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+ desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data,
+ length, DMA_TO_DEVICE);
__skb_queue_tail(&txq->tx_skb, skb);
@@ -956,10 +959,10 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
}
if (cmd_sts & TX_FIRST_DESC) {
- dma_unmap_single(NULL, desc->buf_ptr,
+ dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
desc->byte_cnt, DMA_TO_DEVICE);
} else {
- dma_unmap_page(NULL, desc->buf_ptr,
+ dma_unmap_page(mp->dev->dev.parent, desc->buf_ptr,
desc->byte_cnt, DMA_TO_DEVICE);
}
@@ -1255,7 +1258,6 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
spin_lock_bh(&mp->mib_counters_lock);
p->good_octets_received += mib_read(mp, 0x00);
- p->good_octets_received += (u64)mib_read(mp, 0x04) << 32;
p->bad_octets_received += mib_read(mp, 0x08);
p->internal_mac_transmit_err += mib_read(mp, 0x0c);
p->good_frames_received += mib_read(mp, 0x10);
@@ -1269,7 +1271,6 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
p->frames_512_to_1023_octets += mib_read(mp, 0x30);
p->frames_1024_to_max_octets += mib_read(mp, 0x34);
p->good_octets_sent += mib_read(mp, 0x38);
- p->good_octets_sent += (u64)mib_read(mp, 0x3c) << 32;
p->good_frames_sent += mib_read(mp, 0x40);
p->excessive_collision += mib_read(mp, 0x44);
p->multicast_frames_sent += mib_read(mp, 0x48);
@@ -1896,9 +1897,9 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index)
mp->rx_desc_sram_size);
rxq->rx_desc_dma = mp->rx_desc_sram_addr;
} else {
- rxq->rx_desc_area = dma_alloc_coherent(NULL, size,
- &rxq->rx_desc_dma,
- GFP_KERNEL);
+ rxq->rx_desc_area = dma_alloc_coherent(mp->dev->dev.parent,
+ size, &rxq->rx_desc_dma,
+ GFP_KERNEL);
}
if (rxq->rx_desc_area == NULL) {
@@ -1949,7 +1950,7 @@ out_free:
if (index == 0 && size <= mp->rx_desc_sram_size)
iounmap(rxq->rx_desc_area);
else
- dma_free_coherent(NULL, size,
+ dma_free_coherent(mp->dev->dev.parent, size,
rxq->rx_desc_area,
rxq->rx_desc_dma);
@@ -1981,7 +1982,7 @@ static void rxq_deinit(struct rx_queue *rxq)
rxq->rx_desc_area_size <= mp->rx_desc_sram_size)
iounmap(rxq->rx_desc_area);
else
- dma_free_coherent(NULL, rxq->rx_desc_area_size,
+ dma_free_coherent(mp->dev->dev.parent, rxq->rx_desc_area_size,
rxq->rx_desc_area, rxq->rx_desc_dma);
kfree(rxq->rx_skb);
@@ -2009,9 +2010,9 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
mp->tx_desc_sram_size);
txq->tx_desc_dma = mp->tx_desc_sram_addr;
} else {
- txq->tx_desc_area = dma_alloc_coherent(NULL, size,
- &txq->tx_desc_dma,
- GFP_KERNEL);
+ txq->tx_desc_area = dma_alloc_coherent(mp->dev->dev.parent,
+ size, &txq->tx_desc_dma,
+ GFP_KERNEL);
}
if (txq->tx_desc_area == NULL) {
@@ -2055,7 +2056,7 @@ static void txq_deinit(struct tx_queue *txq)
txq->tx_desc_area_size <= mp->tx_desc_sram_size)
iounmap(txq->tx_desc_area);
else
- dma_free_coherent(NULL, txq->tx_desc_area_size,
+ dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
txq->tx_desc_area, txq->tx_desc_dma);
}
@@ -2167,8 +2168,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
mp = container_of(napi, struct mv643xx_eth_private, napi);
- mp->work_rx_refill |= mp->work_rx_oom;
- mp->work_rx_oom = 0;
+ if (unlikely(mp->oom)) {
+ mp->oom = 0;
+ del_timer(&mp->rx_oom);
+ }
work_done = 0;
while (work_done < budget) {
@@ -2182,8 +2185,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
continue;
}
- queue_mask = mp->work_tx | mp->work_tx_end |
- mp->work_rx | mp->work_rx_refill;
+ queue_mask = mp->work_tx | mp->work_tx_end | mp->work_rx;
+ if (likely(!mp->oom))
+ queue_mask |= mp->work_rx_refill;
+
if (!queue_mask) {
if (mv643xx_eth_collect_events(mp))
continue;
@@ -2204,7 +2209,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
txq_maybe_wake(mp->txq + queue);
} else if (mp->work_rx & queue_mask) {
work_done += rxq_process(mp->rxq + queue, work_tbd);
- } else if (mp->work_rx_refill & queue_mask) {
+ } else if (!mp->oom && (mp->work_rx_refill & queue_mask)) {
work_done += rxq_refill(mp->rxq + queue, work_tbd);
} else {
BUG();
@@ -2212,7 +2217,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
}
if (work_done < budget) {
- if (mp->work_rx_oom)
+ if (mp->oom)
mod_timer(&mp->rx_oom, jiffies + (HZ / 10));
napi_complete(napi);
wrlp(mp, INT_MASK, INT_TX_END | INT_RX | INT_EXT);
@@ -2372,7 +2377,7 @@ static int mv643xx_eth_open(struct net_device *dev)
rxq_refill(mp->rxq + i, INT_MAX);
}
- if (mp->work_rx_oom) {
+ if (mp->oom) {
mp->rx_oom.expires = jiffies + (HZ / 10);
add_timer(&mp->rx_oom);
}
diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c
index eb66f658f9d1..7d83896b8c26 100644
--- a/drivers/net/ne2k-pci.c
+++ b/drivers/net/ne2k-pci.c
@@ -374,18 +374,17 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
dev->ethtool_ops = &ne2k_pci_ethtool_ops;
NS8390_init(dev, 0);
+ memcpy(dev->dev_addr, SA_prom, 6);
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
i = register_netdev(dev);
if (i)
goto err_out_free_netdev;
- for(i = 0; i < 6; i++)
- dev->dev_addr[i] = SA_prom[i];
printk("%s: %s found at %#lx, IRQ %d, %pM.\n",
dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq,
dev->dev_addr);
- memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
-
return 0;
err_out_free_netdev:
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index eceadf787a67..bf4af5248cb7 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -664,7 +664,7 @@ static int netconsole_netdev_event(struct notifier_block *this,
struct netconsole_target *nt;
struct net_device *dev = ptr;
- if (!(event == NETDEV_CHANGENAME))
+ if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER))
goto done;
spin_lock_irqsave(&target_list_lock, flags);
@@ -675,6 +675,15 @@ static int netconsole_netdev_event(struct notifier_block *this,
case NETDEV_CHANGENAME:
strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
break;
+ case NETDEV_UNREGISTER:
+ if (!nt->enabled)
+ break;
+ netpoll_cleanup(&nt->np);
+ nt->enabled = 0;
+ printk(KERN_INFO "netconsole: network logging stopped"
+ ", interface %s unregistered\n",
+ dev->name);
+ break;
}
}
netconsole_target_put(nt);
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 0b6e8c896835..3b19e0ce290f 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -66,7 +66,6 @@ static const int multicast_filter_limit = 32;
#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */
-#define RxPacketMaxSize 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */
#define SafeMtu 0x1c20 /* ... actually life sucks beyond ~7k */
#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
@@ -2357,10 +2356,10 @@ static u16 rtl_rw_cpluscmd(void __iomem *ioaddr)
return cmd;
}
-static void rtl_set_rx_max_size(void __iomem *ioaddr)
+static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
{
/* Low hurts. Let's disable the filtering. */
- RTL_W16(RxMaxSize, 16383);
+ RTL_W16(RxMaxSize, rx_buf_sz);
}
static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
@@ -2407,7 +2406,7 @@ static void rtl_hw_start_8169(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
if ((tp->mac_version == RTL_GIGA_MAC_VER_01) ||
(tp->mac_version == RTL_GIGA_MAC_VER_02) ||
@@ -2668,7 +2667,7 @@ static void rtl_hw_start_8168(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1;
@@ -2846,7 +2845,7 @@ static void rtl_hw_start_8101(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW;
@@ -3554,54 +3553,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
int handled = 0;
int status;
+ /* loop handling interrupts until we have no new ones or
+ * we hit a invalid/hotplug case.
+ */
status = RTL_R16(IntrStatus);
+ while (status && status != 0xffff) {
+ handled = 1;
- /* hotplug/major error/no more work/shared irq */
- if ((status == 0xffff) || !status)
- goto out;
-
- handled = 1;
+ /* Handle all of the error cases first. These will reset
+ * the chip, so just exit the loop.
+ */
+ if (unlikely(!netif_running(dev))) {
+ rtl8169_asic_down(ioaddr);
+ break;
+ }
- if (unlikely(!netif_running(dev))) {
- rtl8169_asic_down(ioaddr);
- goto out;
- }
+ /* Work around for rx fifo overflow */
+ if (unlikely(status & RxFIFOOver) &&
+ (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
+ netif_stop_queue(dev);
+ rtl8169_tx_timeout(dev);
+ break;
+ }
- status &= tp->intr_mask;
- RTL_W16(IntrStatus,
- (status & RxFIFOOver) ? (status | RxOverflow) : status);
+ if (unlikely(status & SYSErr)) {
+ rtl8169_pcierr_interrupt(dev);
+ break;
+ }
- if (!(status & tp->intr_event))
- goto out;
+ if (status & LinkChg)
+ rtl8169_check_link_status(dev, tp, ioaddr);
- /* Work around for rx fifo overflow */
- if (unlikely(status & RxFIFOOver) &&
- (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
- netif_stop_queue(dev);
- rtl8169_tx_timeout(dev);
- goto out;
- }
+ /* We need to see the lastest version of tp->intr_mask to
+ * avoid ignoring an MSI interrupt and having to wait for
+ * another event which may never come.
+ */
+ smp_rmb();
+ if (status & tp->intr_mask & tp->napi_event) {
+ RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
+ tp->intr_mask = ~tp->napi_event;
+
+ if (likely(napi_schedule_prep(&tp->napi)))
+ __napi_schedule(&tp->napi);
+ else if (netif_msg_intr(tp)) {
+ printk(KERN_INFO "%s: interrupt %04x in poll\n",
+ dev->name, status);
+ }
+ }
- if (unlikely(status & SYSErr)) {
- rtl8169_pcierr_interrupt(dev);
- goto out;
+ /* We only get a new MSI interrupt when all active irq
+ * sources on the chip have been acknowledged. So, ack
+ * everything we've seen and check if new sources have become
+ * active to avoid blocking all interrupts from the chip.
+ */
+ RTL_W16(IntrStatus,
+ (status & RxFIFOOver) ? (status | RxOverflow) : status);
+ status = RTL_R16(IntrStatus);
}
- if (status & LinkChg)
- rtl8169_check_link_status(dev, tp, ioaddr);
-
- if (status & tp->napi_event) {
- RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
- tp->intr_mask = ~tp->napi_event;
-
- if (likely(napi_schedule_prep(&tp->napi)))
- __napi_schedule(&tp->napi);
- else if (netif_msg_intr(tp)) {
- printk(KERN_INFO "%s: interrupt %04x in poll\n",
- dev->name, status);
- }
- }
-out:
return IRQ_RETVAL(handled);
}
@@ -3617,13 +3626,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
if (work_done < budget) {
napi_complete(napi);
- tp->intr_mask = 0xffff;
- /*
- * 20040426: the barrier is not strictly required but the
- * behavior of the irq handler could be less predictable
- * without it. Btw, the lack of flush for the posted pci
- * write is safe - FR
+
+ /* We need for force the visibility of tp->intr_mask
+ * for other CPUs, as we can loose an MSI interrupt
+ * and potentially wait for a retransmit timeout if we don't.
+ * The posted write to IntrMask is safe, as it will
+ * eventually make it to the chip and we won't loose anything
+ * until it does.
*/
+ tp->intr_mask = 0xffff;
smp_wmb();
RTL_W16(IntrMask, tp->intr_event);
}
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 8ee21030e9ac..dfc6cf765fbd 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -180,6 +180,20 @@ config USB_NET_CDCETHER
IEEE 802 "local assignment" bit is set in the address, a "usbX"
name is used instead.
+config USB_NET_CDC_EEM
+ tristate "CDC EEM support"
+ depends on USB_USBNET && EXPERIMENTAL
+ help
+ This option supports devices conforming to the Communication Device
+ Class (CDC) Ethernet Emulation Model, a specification that's easy to
+ implement in device firmware. The CDC EEM specifications are available
+ from <http://www.usb.org/>.
+
+ This driver creates an interface named "ethX", where X depends on
+ what other networking devices you have in use. However, if the
+ IEEE 802 "local assignment" bit is set in the address, a "usbX"
+ name is used instead.
+
config USB_NET_DM9601
tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices"
depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 88a87eeb376a..c8aef62cf2b7 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150) += rtl8150.o
obj-$(CONFIG_USB_HSO) += hso.o
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
+obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o
obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
new file mode 100644
index 000000000000..80e01778dd3b
--- /dev/null
+++ b/drivers/net/usb/cdc_eem.c
@@ -0,0 +1,381 @@
+/*
+ * USB CDC EEM network interface driver
+ * Copyright (C) 2009 Oberthur Technologies
+ * by Omar Laazimani, Olivier Condemine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+
+
+/*
+ * This driver is an implementation of the CDC "Ethernet Emulation
+ * Model" (EEM) specification, which encapsulates Ethernet frames
+ * for transport over USB using a simpler USB device model than the
+ * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet").
+ *
+ * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf
+ *
+ * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24,
+ * 2.6.27 and 2.6.30rc2 kernel.
+ * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel).
+ * build on 23-April-2009
+ */
+
+#define EEM_HEAD 2 /* 2 byte header */
+
+/*-------------------------------------------------------------------------*/
+
+static void eem_linkcmd_complete(struct urb *urb)
+{
+ dev_kfree_skb(urb->context);
+ usb_free_urb(urb);
+}
+
+static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb)
+{
+ struct urb *urb;
+ int status;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ goto fail;
+
+ usb_fill_bulk_urb(urb, dev->udev, dev->out,
+ skb->data, skb->len, eem_linkcmd_complete, skb);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ usb_free_urb(urb);
+fail:
+ dev_kfree_skb(skb);
+ devwarn(dev, "link cmd failure\n");
+ return;
+ }
+}
+
+static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int status = 0;
+
+ status = usbnet_get_endpoints(dev, intf);
+ if (status < 0) {
+ usb_set_intfdata(intf, NULL);
+ usb_driver_release_interface(driver_of(intf), intf);
+ return status;
+ }
+
+ /* no jumbogram (16K) support for now */
+
+ dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN;
+
+ return 0;
+}
+
+/*
+ * EEM permits packing multiple Ethernet frames into USB transfers
+ * (a "bundle"), but for TX we don't try to do that.
+ */
+static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ gfp_t flags)
+{
+ struct sk_buff *skb2 = NULL;
+ u16 len = skb->len;
+ u32 crc = 0;
+ int padlen = 0;
+
+ /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is
+ * zero, stick two bytes of zero length EEM packet on the end.
+ * Else the framework would add invalid single byte padding,
+ * since it can't know whether ZLPs will be handled right by
+ * all the relevant hardware and software.
+ */
+ if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
+ padlen += 2;
+
+ if (!skb_cloned(skb)) {
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
+
+ if ((tailroom >= ETH_FCS_LEN + padlen)
+ && (headroom >= EEM_HEAD))
+ goto done;
+
+ if ((headroom + tailroom)
+ > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
+ skb->data = memmove(skb->head +
+ EEM_HEAD,
+ skb->data,
+ skb->len);
+ skb_set_tail_pointer(skb, len);
+ goto done;
+ }
+ }
+
+ skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
+ if (!skb2)
+ return NULL;
+
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+
+done:
+ /* we don't use the "no Ethernet CRC" option */
+ crc = crc32_le(~0, skb->data, skb->len);
+ crc = ~crc;
+
+ put_unaligned_le32(crc, skb_put(skb, 4));
+
+ /* EEM packet header format:
+ * b0..13: length of ethernet frame
+ * b14: bmCRC (1 == valid Ethernet CRC)
+ * b15: bmType (0 == data)
+ */
+ len = skb->len;
+ put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
+
+ /* Bundle a zero length EEM packet if needed */
+ if (padlen)
+ put_unaligned_le16(0, skb_put(skb, 2));
+
+ return skb;
+}
+
+static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ /*
+ * Our task here is to strip off framing, leaving skb with one
+ * data frame for the usbnet framework code to process. But we
+ * may have received multiple EEM payloads, or command payloads.
+ * So we must process _everything_ as if it's a header, except
+ * maybe the last data payload
+ *
+ * REVISIT the framework needs updating so that when we consume
+ * all payloads (the last or only message was a command, or a
+ * zero length EEM packet) that is not accounted as an rx_error.
+ */
+ do {
+ struct sk_buff *skb2 = NULL;
+ u16 header;
+ u16 len = 0;
+
+ /* incomplete EEM header? */
+ if (skb->len < EEM_HEAD)
+ return 0;
+
+ /*
+ * EEM packet header format:
+ * b0..14: EEM type dependant (Data or Command)
+ * b15: bmType
+ */
+ header = get_unaligned_le16(skb->data);
+ skb_pull(skb, EEM_HEAD);
+
+ /*
+ * The bmType bit helps to denote when EEM
+ * packet is data or command :
+ * bmType = 0 : EEM data payload
+ * bmType = 1 : EEM (link) command
+ */
+ if (header & BIT(15)) {
+ u16 bmEEMCmd;
+
+ /*
+ * EEM (link) command packet:
+ * b0..10: bmEEMCmdParam
+ * b11..13: bmEEMCmd
+ * b14: bmReserved (must be 0)
+ * b15: 1 (EEM command)
+ */
+ if (header & BIT(14)) {
+ devdbg(dev, "reserved command %04x\n", header);
+ continue;
+ }
+
+ bmEEMCmd = (header >> 11) & 0x7;
+ switch (bmEEMCmd) {
+
+ /* Responding to echo requests is mandatory. */
+ case 0: /* Echo command */
+ len = header & 0x7FF;
+
+ /* bogus command? */
+ if (skb->len < len)
+ return 0;
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (unlikely(!skb2))
+ goto next;
+ skb_trim(skb2, len);
+ put_unaligned_le16(BIT(15) | (1 << 11) | len,
+ skb_push(skb2, 2));
+ eem_linkcmd(dev, skb2);
+ break;
+
+ /*
+ * Host may choose to ignore hints.
+ * - suspend: peripheral ready to suspend
+ * - response: suggest N millisec polling
+ * - response complete: suggest N sec polling
+ */
+ case 2: /* Suspend hint */
+ case 3: /* Response hint */
+ case 4: /* Response complete hint */
+ continue;
+
+ /*
+ * Hosts should never receive host-to-peripheral
+ * or reserved command codes; or responses to an
+ * echo command we didn't send.
+ */
+ case 1: /* Echo response */
+ case 5: /* Tickle */
+ default: /* reserved */
+ devwarn(dev, "unexpected link command %d\n",
+ bmEEMCmd);
+ continue;
+ }
+
+ } else {
+ u32 crc, crc2;
+ int is_last;
+
+ /* zero length EEM packet? */
+ if (header == 0)
+ continue;
+
+ /*
+ * EEM data packet header :
+ * b0..13: length of ethernet frame
+ * b14: bmCRC
+ * b15: 0 (EEM data)
+ */
+ len = header & 0x3FFF;
+
+ /* bogus EEM payload? */
+ if (skb->len < len)
+ return 0;
+
+ /* bogus ethernet frame? */
+ if (len < (ETH_HLEN + ETH_FCS_LEN))
+ goto next;
+
+ /*
+ * Treat the last payload differently: framework
+ * code expects our "fixup" to have stripped off
+ * headers, so "skb" is a data packet (or error).
+ * Else if it's not the last payload, keep "skb"
+ * for further processing.
+ */
+ is_last = (len == skb->len);
+ if (is_last)
+ skb2 = skb;
+ else {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (unlikely(!skb2))
+ return 0;
+ }
+
+ crc = get_unaligned_le32(skb2->data
+ + len - ETH_FCS_LEN);
+ skb_trim(skb2, len - ETH_FCS_LEN);
+
+ /*
+ * The bmCRC helps to denote when the CRC field in
+ * the Ethernet frame contains a calculated CRC:
+ * bmCRC = 1 : CRC is calculated
+ * bmCRC = 0 : CRC = 0xDEADBEEF
+ */
+ if (header & BIT(14))
+ crc2 = ~crc32_le(~0, skb2->data, len);
+ else
+ crc2 = 0xdeadbeef;
+
+ if (is_last)
+ return crc == crc2;
+
+ if (unlikely(crc != crc2)) {
+ dev->stats.rx_errors++;
+ dev_kfree_skb_any(skb2);
+ } else
+ usbnet_skb_return(dev, skb2);
+ }
+
+next:
+ skb_pull(skb, len);
+ } while (skb->len);
+
+ return 1;
+}
+
+static const struct driver_info eem_info = {
+ .description = "CDC EEM Device",
+ .flags = FLAG_ETHER,
+ .bind = eem_bind,
+ .rx_fixup = eem_rx_fixup,
+ .tx_fixup = eem_tx_fixup,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_device_id products[] = {
+{
+ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
+ USB_CDC_PROTO_EEM),
+ .driver_info = (unsigned long) &eem_info,
+},
+{
+ /* EMPTY == end of list */
+},
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver eem_driver = {
+ .name = "cdc_eem",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+
+static int __init eem_init(void)
+{
+ return usb_register(&eem_driver);
+}
+module_init(eem_init);
+
+static void __exit eem_exit(void)
+{
+ usb_deregister(&eem_driver);
+}
+module_exit(eem_exit);
+
+MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
+MODULE_DESCRIPTION("USB CDC EEM");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index dc1665326592..5a7283372b53 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -941,6 +941,16 @@ static int smsc95xx_reset(struct usbnet *dev)
if (netif_msg_ifup(dev))
devdbg(dev, "ID_REV = 0x%08x", read_buf);
+ /* Configure GPIO pins as LED outputs */
+ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED |
+ LED_GPIO_CFG_FDX_LED;
+ ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf);
+ if (ret < 0) {
+ devwarn(dev, "Failed to write LED_GPIO_CFG register, ret=%d",
+ ret);
+ return ret;
+ }
+
/* Init Tx */
write_buf = 0;
ret = smsc95xx_write_reg(dev, FLOW, write_buf);
@@ -1231,6 +1241,11 @@ static const struct usb_device_id products[] = {
USB_DEVICE(0x0424, 0x9500),
.driver_info = (unsigned long) &smsc95xx_info,
},
+ {
+ /* SMSC9512/9514 USB Hub & Ethernet Device */
+ USB_DEVICE(0x0424, 0xec00),
+ .driver_info = (unsigned long) &smsc95xx_info,
+ },
{ }, /* END */
};
MODULE_DEVICE_TABLE(usb, products);
diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h
index 66b5c84f302e..86bc44977fbd 100644
--- a/drivers/net/usb/smsc95xx.h
+++ b/drivers/net/usb/smsc95xx.h
@@ -99,6 +99,9 @@
#define PM_CTL_WUPS_MULTI_ (0x00000003)
#define LED_GPIO_CFG (0x24)
+#define LED_GPIO_CFG_SPD_LED (0x01000000)
+#define LED_GPIO_CFG_LNK_LED (0x00100000)
+#define LED_GPIO_CFG_FDX_LED (0x00010000)
#define GPIO_CFG (0x28)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 015db1cece72..8e56fcf0a0e3 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -210,14 +210,11 @@ rx_drop:
static struct net_device_stats *veth_get_stats(struct net_device *dev)
{
- struct veth_priv *priv;
- struct net_device_stats *dev_stats;
- int cpu;
+ struct veth_priv *priv = netdev_priv(dev);
+ struct net_device_stats *dev_stats = &dev->stats;
+ unsigned int cpu;
struct veth_net_stats *stats;
- priv = netdev_priv(dev);
- dev_stats = &dev->stats;
-
dev_stats->rx_packets = 0;
dev_stats->tx_packets = 0;
dev_stats->rx_bytes = 0;
@@ -225,16 +222,17 @@ static struct net_device_stats *veth_get_stats(struct net_device *dev)
dev_stats->tx_dropped = 0;
dev_stats->rx_dropped = 0;
- for_each_online_cpu(cpu) {
- stats = per_cpu_ptr(priv->stats, cpu);
+ if (priv->stats)
+ for_each_online_cpu(cpu) {
+ stats = per_cpu_ptr(priv->stats, cpu);
- dev_stats->rx_packets += stats->rx_packets;
- dev_stats->tx_packets += stats->tx_packets;
- dev_stats->rx_bytes += stats->rx_bytes;
- dev_stats->tx_bytes += stats->tx_bytes;
- dev_stats->tx_dropped += stats->tx_dropped;
- dev_stats->rx_dropped += stats->rx_dropped;
- }
+ dev_stats->rx_packets += stats->rx_packets;
+ dev_stats->tx_packets += stats->tx_packets;
+ dev_stats->rx_bytes += stats->rx_bytes;
+ dev_stats->tx_bytes += stats->tx_bytes;
+ dev_stats->tx_dropped += stats->tx_dropped;
+ dev_stats->rx_dropped += stats->rx_dropped;
+ }
return dev_stats;
}
@@ -261,6 +259,8 @@ static int veth_close(struct net_device *dev)
netif_carrier_off(dev);
netif_carrier_off(priv->peer);
+ free_percpu(priv->stats);
+ priv->stats = NULL;
return 0;
}
@@ -291,15 +291,6 @@ static int veth_dev_init(struct net_device *dev)
return 0;
}
-static void veth_dev_free(struct net_device *dev)
-{
- struct veth_priv *priv;
-
- priv = netdev_priv(dev);
- free_percpu(priv->stats);
- free_netdev(dev);
-}
-
static const struct net_device_ops veth_netdev_ops = {
.ndo_init = veth_dev_init,
.ndo_open = veth_open,
@@ -317,7 +308,7 @@ static void veth_setup(struct net_device *dev)
dev->netdev_ops = &veth_netdev_ops;
dev->ethtool_ops = &veth_ethtool_ops;
dev->features |= NETIF_F_LLTX;
- dev->destructor = veth_dev_free;
+ dev->destructor = free_netdev;
}
/*
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 9c82a39497e5..7fa620ddeb21 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -616,10 +616,11 @@ static int virtnet_open(struct net_device *dev)
static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
struct scatterlist *data, int out, int in)
{
- struct scatterlist sg[VIRTNET_SEND_COMMAND_SG_MAX + 2];
+ struct scatterlist *s, sg[VIRTNET_SEND_COMMAND_SG_MAX + 2];
struct virtio_net_ctrl_hdr ctrl;
virtio_net_ctrl_ack status = ~0;
unsigned int tmp;
+ int i;
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
BUG(); /* Caller should know better */
@@ -637,7 +638,8 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
sg_init_table(sg, out + in);
sg_set_buf(&sg[0], &ctrl, sizeof(ctrl));
- memcpy(&sg[1], data, sizeof(struct scatterlist) * (out + in - 2));
+ for_each_sg(data, s, out + in - 2, i)
+ sg_set_buf(&sg[i + 1], sg_virt(s), s->length);
sg_set_buf(&sg[out + in - 1], &status, sizeof(status));
if (vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi) != 0)
@@ -692,7 +694,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
promisc = ((dev->flags & IFF_PROMISC) != 0);
allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
- sg_set_buf(sg, &promisc, sizeof(promisc));
+ sg_init_one(sg, &promisc, sizeof(promisc));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_PROMISC,
@@ -700,7 +702,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
dev_warn(&dev->dev, "Failed to %sable promisc mode.\n",
promisc ? "en" : "dis");
- sg_set_buf(sg, &allmulti, sizeof(allmulti));
+ sg_init_one(sg, &allmulti, sizeof(allmulti));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_ALLMULTI,
@@ -716,6 +718,8 @@ static void virtnet_set_rx_mode(struct net_device *dev)
return;
}
+ sg_init_table(sg, 2);
+
/* Store the unicast list and count in the front of the buffer */
mac_data->entries = dev->uc_count;
addr = dev->uc_list;
@@ -744,24 +748,24 @@ static void virtnet_set_rx_mode(struct net_device *dev)
kfree(buf);
}
-static void virnet_vlan_rx_add_vid(struct net_device *dev, u16 vid)
+static void virtnet_vlan_rx_add_vid(struct net_device *dev, u16 vid)
{
struct virtnet_info *vi = netdev_priv(dev);
struct scatterlist sg;
- sg_set_buf(&sg, &vid, sizeof(vid));
+ sg_init_one(&sg, &vid, sizeof(vid));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_ADD, &sg, 1, 0))
dev_warn(&dev->dev, "Failed to add VLAN ID %d.\n", vid);
}
-static void virnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
+static void virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
{
struct virtnet_info *vi = netdev_priv(dev);
struct scatterlist sg;
- sg_set_buf(&sg, &vid, sizeof(vid));
+ sg_init_one(&sg, &vid, sizeof(vid));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_DEL, &sg, 1, 0))
@@ -794,8 +798,8 @@ static const struct net_device_ops virtnet_netdev = {
.ndo_set_mac_address = virtnet_set_mac_address,
.ndo_set_rx_mode = virtnet_set_rx_mode,
.ndo_change_mtu = virtnet_change_mtu,
- .ndo_vlan_rx_add_vid = virnet_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = virnet_vlan_rx_kill_vid,
+ .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = virtnet_netpoll,
#endif
@@ -841,6 +845,10 @@ static int virtnet_probe(struct virtio_device *vdev)
int err;
struct net_device *dev;
struct virtnet_info *vi;
+ struct virtqueue *vqs[3];
+ vq_callback_t *callbacks[] = { skb_recv_done, skb_xmit_done, NULL};
+ const char *names[] = { "input", "output", "control" };
+ int nvqs;
/* Allocate ourselves a network device with room for our info */
dev = alloc_etherdev(sizeof(struct virtnet_info));
@@ -901,25 +909,19 @@ static int virtnet_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
vi->mergeable_rx_bufs = true;
- /* We expect two virtqueues, receive then send. */
- vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
- if (IS_ERR(vi->rvq)) {
- err = PTR_ERR(vi->rvq);
+ /* We expect two virtqueues, receive then send,
+ * and optionally control. */
+ nvqs = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2;
+
+ err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names);
+ if (err)
goto free;
- }
- vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
- if (IS_ERR(vi->svq)) {
- err = PTR_ERR(vi->svq);
- goto free_recv;
- }
+ vi->rvq = vqs[0];
+ vi->svq = vqs[1];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
- vi->cvq = vdev->config->find_vq(vdev, 2, NULL);
- if (IS_ERR(vi->cvq)) {
- err = PTR_ERR(vi->svq);
- goto free_send;
- }
+ vi->cvq = vqs[2];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
dev->features |= NETIF_F_HW_VLAN_FILTER;
@@ -937,7 +939,7 @@ static int virtnet_probe(struct virtio_device *vdev)
err = register_netdev(dev);
if (err) {
pr_debug("virtio_net: registering device failed\n");
- goto free_ctrl;
+ goto free_vqs;
}
/* Last of all, set up some receive buffers. */
@@ -958,13 +960,8 @@ static int virtnet_probe(struct virtio_device *vdev)
unregister:
unregister_netdev(dev);
-free_ctrl:
- if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
- vdev->config->del_vq(vi->cvq);
-free_send:
- vdev->config->del_vq(vi->svq);
-free_recv:
- vdev->config->del_vq(vi->rvq);
+free_vqs:
+ vdev->config->del_vqs(vdev);
free:
free_netdev(dev);
return err;
@@ -990,12 +987,10 @@ static void virtnet_remove(struct virtio_device *vdev)
BUG_ON(vi->num != 0);
- vdev->config->del_vq(vi->svq);
- vdev->config->del_vq(vi->rvq);
- if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
- vdev->config->del_vq(vi->cvq);
unregister_netdev(vi->dev);
+ vdev->config->del_vqs(vi->vdev);
+
while (vi->pages)
__free_pages(get_a_page(vi, GFP_KERNEL), 0);
diff --git a/drivers/net/vxge/vxge-traffic.c b/drivers/net/vxge/vxge-traffic.c
index 7be0ae10d69b..c2eeac4125f3 100644
--- a/drivers/net/vxge/vxge-traffic.c
+++ b/drivers/net/vxge/vxge-traffic.c
@@ -115,7 +115,7 @@ enum vxge_hw_status vxge_hw_vpath_intr_enable(struct __vxge_hw_vpath_handle *vp)
VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_POISON|
VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO2_POISON|
VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_DMA_ERR|
- VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_DMA_ERR), 0, 32),
+ VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO2_DMA_ERR), 0, 32),
&vp_reg->kdfcctl_errors_mask);
__vxge_hw_pio_mem_write32_upper(0, &vp_reg->vpath_ppif_int_mask);
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 3bf7d3f447db..765a7f5d6aa4 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -1249,7 +1249,7 @@ static int __devinit hss_init_one(struct platform_device *pdev)
return -ENOMEM;
if ((port->npe = npe_request(0)) == NULL) {
- err = -ENOSYS;
+ err = -ENODEV;
goto err_free;
}
@@ -1311,7 +1311,7 @@ static int __init hss_init_module(void)
if ((ixp4xx_read_feature_bits() &
(IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS)) !=
(IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS))
- return -ENOSYS;
+ return -ENODEV;
spin_lock_init(&npe_lock);
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index 02419bfd64b5..f9fc38902322 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -819,10 +819,9 @@ void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq,
roq_data = (struct i2400m_roq_data *) &skb->cb;
i2400m_net_erx(i2400m, skb, roq_data->cs);
}
- else {
+ else
__i2400m_roq_queue(i2400m, roq, skb, sn, nsn);
- __i2400m_roq_update_ws(i2400m, roq, sn + 1);
- }
+ __i2400m_roq_update_ws(i2400m, roq, sn + 1);
i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET_WS,
old_ws, len, sn, nsn, roq->ws);
}
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index ca4151a9e222..17851321b7fd 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
#ifdef CONFIG_PM
struct usb_device *usb_dev = i2400mu->usb_dev;
#endif
+ unsigned is_autosuspend = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
+#ifdef CONFIG_PM
+ if (usb_dev->auto_pm > 0)
+ is_autosuspend = 1;
+#endif
+
d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event);
if (i2400m->updown == 0)
goto no_firmware;
- d_printf(1, dev, "fw up, requesting standby\n");
+ if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) {
+ /* ugh -- the device is connected and this suspend
+ * request is an autosuspend one (not a system standby
+ * / hibernate).
+ *
+ * The only way the device can go to standby is if the
+ * link with the base station is in IDLE mode; that
+ * were the case, we'd be in status
+ * I2400M_SS_CONNECTED_IDLE. But we are not.
+ *
+ * If we *tell* him to go power save now, it'll reset
+ * as a precautionary measure, so if this is an
+ * autosuspend thing, say no and it'll come back
+ * later, when the link is IDLE
+ */
+ result = -EBADF;
+ d_printf(1, dev, "fw up, link up, not-idle, autosuspend: "
+ "not entering powersave\n");
+ goto error_not_now;
+ }
+ d_printf(1, dev, "fw up: entering powersave\n");
atomic_dec(&i2400mu->do_autopm);
result = i2400m_cmd_enter_powersave(i2400m);
atomic_inc(&i2400mu->do_autopm);
-#ifdef CONFIG_PM
- if (result < 0 && usb_dev->auto_pm == 0) {
+ if (result < 0 && !is_autosuspend) {
/* System suspend, can't fail */
dev_err(dev, "failed to suspend, will reset on resume\n");
result = 0;
}
-#endif
if (result < 0)
goto error_enter_powersave;
i2400mu_notification_release(i2400mu);
- d_printf(1, dev, "fw up, got standby\n");
+ d_printf(1, dev, "powersave requested\n");
error_enter_powersave:
+error_not_now:
no_firmware:
d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n",
iface, pm_msg.event, result);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 8a0823588c51..3d94e7dfea69 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -430,6 +430,7 @@ config RTL8187
ASUS P5B Deluxe
Toshiba Satellite Pro series of laptops
Asus Wireless Link
+ Linksys WUSB54GC-EU
Thanks to Realtek for their support!
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index c36d3a3d655f..9eabf4d1f2e7 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -6467,6 +6467,7 @@ static int airo_get_encode(struct net_device *dev,
{
struct airo_info *local = dev->ml_priv;
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ int wep_key_len;
u8 buf[16];
if (!local->wep_capable)
@@ -6500,8 +6501,13 @@ static int airo_get_encode(struct net_device *dev,
dwrq->flags |= index + 1;
/* Copy the key to the user buffer */
- dwrq->length = get_wep_key(local, index, &buf[0], sizeof(buf));
- memcpy(extra, buf, dwrq->length);
+ wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
+ if (wep_key_len < 0) {
+ dwrq->length = 0;
+ } else {
+ dwrq->length = wep_key_len;
+ memcpy(extra, buf, dwrq->length);
+ }
return 0;
}
@@ -6614,7 +6620,7 @@ static int airo_get_encodeext(struct net_device *dev,
struct airo_info *local = dev->ml_priv;
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, max_key_len;
+ int idx, max_key_len, wep_key_len;
u8 buf[16];
if (!local->wep_capable)
@@ -6658,8 +6664,13 @@ static int airo_get_encodeext(struct net_device *dev,
memset(extra, 0, 16);
/* Copy the key to the user buffer */
- ext->key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
- memcpy(extra, buf, ext->key_len);
+ wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
+ if (wep_key_len < 0) {
+ ext->key_len = 0;
+ } else {
+ ext->key_len = wep_key_len;
+ memcpy(extra, buf, ext->key_len);
+ }
return 0;
}
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 744f4f4dd3d1..8d93ca4651b9 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1873,18 +1873,18 @@ static void at76_dwork_hw_scan(struct work_struct *work)
if (ret != CMD_STATUS_COMPLETE) {
queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan,
SCAN_POLL_INTERVAL);
- goto exit;
+ mutex_unlock(&priv->mtx);
+ return;
}
- ieee80211_scan_completed(priv->hw, false);
-
if (is_valid_ether_addr(priv->bssid))
at76_join(priv);
- ieee80211_wake_queues(priv->hw);
-
-exit:
mutex_unlock(&priv->mtx);
+
+ ieee80211_scan_completed(priv->hw, false);
+
+ ieee80211_wake_queues(priv->hw);
}
static int at76_hw_scan(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index a08bc8a4fb69..32df27a9c7a2 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -214,7 +214,7 @@ static struct pci_driver ath5k_pci_driver = {
* Prototypes - MAC 802.11 stack related functions
*/
static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel);
+static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
static int ath5k_reset_wake(struct ath5k_softc *sc);
static int ath5k_start(struct ieee80211_hw *hw);
static void ath5k_stop(struct ieee80211_hw *hw);
@@ -1038,16 +1038,13 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
if (chan->center_freq != sc->curchan->center_freq ||
chan->hw_value != sc->curchan->hw_value) {
- sc->curchan = chan;
- sc->curband = &sc->sbands[chan->band];
-
/*
* To switch channels clear any pending DMA operations;
* wait long enough for the RX fifo to drain, reset the
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- return ath5k_reset(sc, true, true);
+ return ath5k_reset(sc, chan);
}
return 0;
@@ -2314,7 +2311,7 @@ ath5k_init(struct ath5k_softc *sc)
sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
AR5K_INT_FATAL | AR5K_INT_GLOBAL;
- ret = ath5k_reset(sc, false, false);
+ ret = ath5k_reset(sc, NULL);
if (ret)
goto done;
@@ -2599,18 +2596,25 @@ drop_packet:
return NETDEV_TX_OK;
}
+/*
+ * Reset the hardware. If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
+ */
static int
-ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel)
+ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
{
struct ath5k_hw *ah = sc->ah;
int ret;
ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
- if (stop) {
+ if (chan) {
ath5k_hw_set_imr(ah, 0);
ath5k_txq_cleanup(sc);
ath5k_rx_stop(sc);
+
+ sc->curchan = chan;
+ sc->curband = &sc->sbands[chan->band];
}
ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true);
if (ret) {
@@ -2648,7 +2652,7 @@ ath5k_reset_wake(struct ath5k_softc *sc)
{
int ret;
- ret = ath5k_reset(sc, true, true);
+ ret = ath5k_reset(sc, sc->curchan);
if (!ret)
ieee80211_wake_queues(sc->hw);
diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c
index 9770bb3d40f9..4904a07e4b59 100644
--- a/drivers/net/wireless/ath5k/debug.c
+++ b/drivers/net/wireless/ath5k/debug.c
@@ -424,7 +424,7 @@ ath5k_debug_dump_bands(struct ath5k_softc *sc)
for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
struct ieee80211_supported_band *band = &sc->sbands[b];
- char bname[5];
+ char bname[6];
switch (band->band) {
case IEEE80211_BAND_2GHZ:
strcpy(bname, "2 GHz");
diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c
index 9e2faae5ae94..b48b29dca3d2 100644
--- a/drivers/net/wireless/ath5k/phy.c
+++ b/drivers/net/wireless/ath5k/phy.c
@@ -1487,28 +1487,35 @@ ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
{
s8 tmp;
s16 min_pwrL, min_pwrR;
- s16 pwr_i = pwrL[0];
-
- do {
- pwr_i--;
- tmp = (s8) ath5k_get_interpolated_value(pwr_i,
- pwrL[0], pwrL[1],
- stepL[0], stepL[1]);
-
- } while (tmp > 1);
-
- min_pwrL = pwr_i;
-
- pwr_i = pwrR[0];
- do {
- pwr_i--;
- tmp = (s8) ath5k_get_interpolated_value(pwr_i,
- pwrR[0], pwrR[1],
- stepR[0], stepR[1]);
-
- } while (tmp > 1);
+ s16 pwr_i;
+
+ if (pwrL[0] == pwrL[1])
+ min_pwrL = pwrL[0];
+ else {
+ pwr_i = pwrL[0];
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrL[0], pwrL[1],
+ stepL[0], stepL[1]);
+ } while (tmp > 1);
+
+ min_pwrL = pwr_i;
+ }
- min_pwrR = pwr_i;
+ if (pwrR[0] == pwrR[1])
+ min_pwrR = pwrR[0];
+ else {
+ pwr_i = pwrR[0];
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrR[0], pwrR[1],
+ stepR[0], stepR[1]);
+ } while (tmp > 1);
+
+ min_pwrR = pwr_i;
+ }
/* Keep the right boundary so that it works for both curves */
return max(min_pwrL, min_pwrR);
diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c
index 7a17d31b2fd9..5f72c111c2e8 100644
--- a/drivers/net/wireless/ath5k/reset.c
+++ b/drivers/net/wireless/ath5k/reset.c
@@ -26,7 +26,7 @@
\*****************************/
#include <linux/pci.h> /* To determine if a card is pci-e */
-#include <linux/bitops.h> /* For get_bitmask_order */
+#include <linux/log2.h>
#include "ath5k.h"
#include "reg.h"
#include "base.h"
@@ -69,10 +69,10 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
/* Get exponent
* ALGO: coef_exp = 14 - highest set bit position */
- coef_exp = get_bitmask_order(coef_scaled);
+ coef_exp = ilog2(coef_scaled);
/* Doesn't make sense if it's zero*/
- if (!coef_exp)
+ if (!coef_scaled || !coef_exp)
return -EINVAL;
/* Note: we've shifted coef_scaled by 24 */
@@ -359,7 +359,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
mode |= AR5K_PHY_MODE_FREQ_5GHZ;
if (ah->ah_radio == AR5K_RF5413)
- clock |= AR5K_PHY_PLL_40MHZ_5413;
+ clock = AR5K_PHY_PLL_40MHZ_5413;
else
clock |= AR5K_PHY_PLL_40MHZ;
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index e5ca2511a81a..9452461ce864 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -46,7 +46,7 @@
#include "iwl-6000-hw.h"
/* Highest firmware API version supported */
-#define IWL5000_UCODE_API_MAX 1
+#define IWL5000_UCODE_API_MAX 2
#define IWL5150_UCODE_API_MAX 2
/* Lowest firmware API version supported */
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index edfa5e149f71..bd438d8acf55 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -101,8 +101,8 @@ struct iwl_cfg iwl6000_2agn_cfg = {
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
- .valid_tx_ant = ANT_BC,
- .valid_rx_ant = ANT_BC,
+ .valid_tx_ant = ANT_AB,
+ .valid_rx_ant = ANT_AB,
.need_pll_cfg = false,
};
@@ -117,8 +117,8 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
- .valid_tx_ant = ANT_BC,
- .valid_rx_ant = ANT_BC,
+ .valid_tx_ant = ANT_AB,
+ .valid_rx_ant = ANT_AB,
.need_pll_cfg = false,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 1ef4192207a5..f46ba2475776 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -669,13 +669,6 @@ static int iwl_set_mode(struct iwl_priv *priv, int mode)
if (!iwl_is_ready_rf(priv))
return -EAGAIN;
- cancel_delayed_work(&priv->scan_check);
- if (iwl_scan_cancel_timeout(priv, 100)) {
- IWL_WARN(priv, "Aborted scan still in progress after 100ms\n");
- IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n");
- return -EAGAIN;
- }
-
iwl_commit_rxon(priv);
return 0;
@@ -3636,7 +3629,9 @@ static struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x0085, 0x1112, iwl6000_2ag_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1122, iwl6000_2ag_cfg)},
{IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000_2agn_cfg)},
{IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000_2agn_cfg)},
{IWL_PCI_DEVICE(0x0082, PCI_ANY_ID, iwl6000_2agn_cfg)},
{IWL_PCI_DEVICE(0x0085, PCI_ANY_ID, iwl6000_3agn_cfg)},
{IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)},
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 23644cf884f1..6330b91e37ce 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -227,9 +227,6 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
/* The HW is no longer scanning */
clear_bit(STATUS_SCAN_HW, &priv->status);
- /* The scan completion notification came in, so kill that timer... */
- cancel_delayed_work(&priv->scan_check);
-
IWL_DEBUG_INFO(priv, "Scan pass on %sGHz took %dms\n",
(priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ?
"2.4" : "5.2",
@@ -712,6 +709,8 @@ static void iwl_bg_request_scan(struct work_struct *data)
mutex_lock(&priv->mutex);
+ cancel_delayed_work(&priv->scan_check);
+
if (!iwl_is_ready(priv)) {
IWL_WARN(priv, "request scan called when driver not ready.\n");
goto done;
@@ -925,11 +924,13 @@ void iwl_bg_scan_completed(struct work_struct *work)
IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
+ cancel_delayed_work(&priv->scan_check);
ieee80211_scan_completed(priv->hw, false);
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
/* Since setting the TXPOWER may have been deferred while
* performing the scan, fire one off */
mutex_lock(&priv->mutex);
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 5798fe49c771..44ab03a12e40 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -719,6 +719,14 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
{
unsigned long flags;
int ret = 0;
+ __le16 key_flags = 0;
+
+ key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
+ key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags &= ~STA_KEY_FLG_INVALID;
+
+ if (sta_id == priv->hw_params.bcast_sta_id)
+ key_flags |= STA_KEY_MULTICAST_MSK;
keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
@@ -738,6 +746,9 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
"no space for a new key");
+ priv->stations[sta_id].sta.key.key_flags = key_flags;
+
+
/* This copy is acutally not needed: we get the key with each TX */
memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16);
@@ -754,9 +765,7 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
{
u8 sta_id = IWL_INVALID_STATION;
unsigned long flags;
- __le16 key_flags = 0;
int i;
- DECLARE_MAC_BUF(mac);
sta_id = iwl_find_station(priv, addr);
if (sta_id == IWL_INVALID_STATION) {
@@ -771,16 +780,8 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
return;
}
- key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (sta_id == priv->hw_params.bcast_sta_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].sta.key.key_flags = key_flags;
priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
for (i = 0; i < 5; i++)
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 617c4235d971..ff4d0e41d7c4 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -782,13 +782,6 @@ static int iwl3945_set_mode(struct iwl_priv *priv, int mode)
if (!iwl_is_ready_rf(priv))
return -EAGAIN;
- cancel_delayed_work(&priv->scan_check);
- if (iwl_scan_cancel_timeout(priv, 100)) {
- IWL_WARN(priv, "Aborted scan still in progress after 100ms\n");
- IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n");
- return -EAGAIN;
- }
-
iwl3945_commit_rxon(priv);
return 0;
@@ -1694,7 +1687,6 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
rxq->free_count = 0;
spin_unlock_irqrestore(&rxq->lock, flags);
}
-EXPORT_SYMBOL(iwl3945_rx_queue_reset);
/*
* this should be called while priv->lock is locked
@@ -1745,7 +1737,6 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx
rxq->bd = NULL;
rxq->rb_stts = NULL;
}
-EXPORT_SYMBOL(iwl3945_rx_queue_free);
/* Convert linear signal-to-noise ratio into dB */
@@ -3300,6 +3291,8 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
mutex_lock(&priv->mutex);
+ cancel_delayed_work(&priv->scan_check);
+
if (!iwl_is_ready(priv)) {
IWL_WARN(priv, "request scan called when driver not ready.\n");
goto done;
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index db91db776508..bebf735cd4bd 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2558,6 +2558,11 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
mutex_init(&priv->command_lock);
spin_lock_init(&priv->stats_lock);
+ /* because rndis_command() sleeps we need to use workqueue */
+ priv->workqueue = create_singlethread_workqueue("rndis_wlan");
+ INIT_WORK(&priv->work, rndis_wext_worker);
+ INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
+
/* try bind rndis_host */
retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS);
if (retval < 0)
@@ -2603,16 +2608,17 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
disassociate(usbdev, 1);
netif_carrier_off(usbdev->net);
- /* because rndis_command() sleeps we need to use workqueue */
- priv->workqueue = create_singlethread_workqueue("rndis_wlan");
- INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
queue_delayed_work(priv->workqueue, &priv->stats_work,
round_jiffies_relative(STATS_UPDATE_JIFFIES));
- INIT_WORK(&priv->work, rndis_wext_worker);
return 0;
fail:
+ cancel_delayed_work_sync(&priv->stats_work);
+ cancel_work_sync(&priv->work);
+ flush_workqueue(priv->workqueue);
+ destroy_workqueue(priv->workqueue);
+
kfree(priv);
return retval;
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 07d378ef0b46..7b3ee8c2eaef 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -138,7 +138,7 @@ void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
if (cipher == CIPHER_TKIP_NO_MIC)
cipher = CIPHER_TKIP;
- if (cipher == CIPHER_NONE || cipher > CIPHER_MAX)
+ if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX)
return;
/* Remove CIPHER_NONE index */
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h
index 9718f61809cf..edeff82a4d06 100644
--- a/drivers/net/wireless/rtl818x/rtl8187.h
+++ b/drivers/net/wireless/rtl818x/rtl8187.h
@@ -120,6 +120,12 @@ struct rtl8187_priv {
__le64 buf;
struct sk_buff_head queue;
} b_tx_status; /* This queue is used by both -b and non-b devices */
+ struct mutex io_mutex;
+ union {
+ u8 bits8;
+ __le16 bits16;
+ __le32 bits32;
+ } *io_dmabuf;
};
void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);
@@ -129,10 +135,14 @@ static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv,
{
u8 val;
+ mutex_lock(&priv->io_mutex);
usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits8, sizeof(val), HZ / 2);
+
+ val = priv->io_dmabuf->bits8;
+ mutex_unlock(&priv->io_mutex);
return val;
}
@@ -147,10 +157,14 @@ static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv,
{
__le16 val;
+ mutex_lock(&priv->io_mutex);
usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits16, sizeof(val), HZ / 2);
+
+ val = priv->io_dmabuf->bits16;
+ mutex_unlock(&priv->io_mutex);
return le16_to_cpu(val);
}
@@ -165,10 +179,14 @@ static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv,
{
__le32 val;
+ mutex_lock(&priv->io_mutex);
usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits32, sizeof(val), HZ / 2);
+
+ val = priv->io_dmabuf->bits32;
+ mutex_unlock(&priv->io_mutex);
return le32_to_cpu(val);
}
@@ -181,10 +199,15 @@ static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr)
static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv,
u8 *addr, u8 val, u8 idx)
{
+ mutex_lock(&priv->io_mutex);
+
+ priv->io_dmabuf->bits8 = val;
usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits8, sizeof(val), HZ / 2);
+
+ mutex_unlock(&priv->io_mutex);
}
static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val)
@@ -195,12 +218,15 @@ static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val)
static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv,
__le16 *addr, u16 val, u8 idx)
{
- __le16 buf = cpu_to_le16(val);
+ mutex_lock(&priv->io_mutex);
+ priv->io_dmabuf->bits16 = cpu_to_le16(val);
usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
- HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits16, sizeof(val), HZ / 2);
+
+ mutex_unlock(&priv->io_mutex);
}
static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr,
@@ -212,12 +238,15 @@ static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr,
static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv,
__le32 *addr, u32 val, u8 idx)
{
- __le32 buf = cpu_to_le32(val);
+ mutex_lock(&priv->io_mutex);
+ priv->io_dmabuf->bits32 = cpu_to_le32(val);
usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
- HZ / 2);
+ (unsigned long)addr, idx & 0x03,
+ &priv->io_dmabuf->bits32, sizeof(val), HZ / 2);
+
+ mutex_unlock(&priv->io_mutex);
}
static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr,
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index fd81884b9c7d..d51ba0a88c23 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -71,6 +71,8 @@ static struct usb_device_id rtl8187_table[] __devinitdata = {
{USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187},
/* AirLive */
{USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187},
+ /* Linksys */
+ {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B},
{}
};
@@ -1329,6 +1331,14 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
priv = dev->priv;
priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);
+ /* allocate "DMA aware" buffer for register accesses */
+ priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL);
+ if (!priv->io_dmabuf) {
+ err = -ENOMEM;
+ goto err_free_dev;
+ }
+ mutex_init(&priv->io_mutex);
+
SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
priv->udev = udev;
@@ -1495,7 +1505,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
err = ieee80211_register_hw(dev);
if (err) {
printk(KERN_ERR "rtl8187: Cannot register device\n");
- goto err_free_dev;
+ goto err_free_dmabuf;
}
mutex_init(&priv->conf_mutex);
skb_queue_head_init(&priv->b_tx_status.queue);
@@ -1506,6 +1516,8 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
return 0;
+ err_free_dmabuf:
+ kfree(priv->io_dmabuf);
err_free_dev:
ieee80211_free_hw(dev);
usb_set_intfdata(intf, NULL);
@@ -1526,6 +1538,7 @@ static void __devexit rtl8187_disconnect(struct usb_interface *intf)
priv = dev->priv;
usb_reset_device(priv->udev);
usb_put_dev(interface_to_usbdev(intf));
+ kfree(priv->io_dmabuf);
ieee80211_free_hw(dev);
}
diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c
index 78df281b297a..a09819386a1e 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c
@@ -88,9 +88,15 @@ static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data)
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
udelay(10);
+ mutex_lock(&priv->io_mutex);
+
+ priv->io_dmabuf->bits16 = data;
usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- addr, 0x8225, &data, sizeof(data), HZ / 2);
+ addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data),
+ HZ / 2);
+
+ mutex_unlock(&priv->io_mutex);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(10);
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index f821dbc952a4..27f3b81333de 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,21 +1,21 @@
config OF_DEVICE
def_bool y
- depends on OF && (SPARC || PPC_OF)
+ depends on OF && (SPARC || PPC_OF || MICROBLAZE)
config OF_GPIO
def_bool y
- depends on OF && PPC_OF && GPIOLIB
+ depends on OF && (PPC_OF || MICROBLAZE) && GPIOLIB
help
OpenFirmware GPIO accessors
config OF_I2C
def_tristate I2C
- depends on PPC_OF && I2C
+ depends on (PPC_OF || MICROBLAZE) && I2C
help
OpenFirmware I2C accessors
config OF_SPI
def_tristate SPI
- depends on OF && PPC_OF && SPI
+ depends on OF && (PPC_OF || MICROBLAZE) && SPI
help
OpenFirmware SPI accessors
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index f0e99d4c066b..242257b19441 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -78,16 +78,20 @@ void free_cpu_buffers(void)
op_ring_buffer_write = NULL;
}
+#define RB_EVENT_HDR_SIZE 4
+
int alloc_cpu_buffers(void)
{
int i;
unsigned long buffer_size = oprofile_cpu_buffer_size;
+ unsigned long byte_size = buffer_size * (sizeof(struct op_sample) +
+ RB_EVENT_HDR_SIZE);
- op_ring_buffer_read = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ op_ring_buffer_read = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS);
if (!op_ring_buffer_read)
goto fail;
- op_ring_buffer_write = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ op_ring_buffer_write = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS);
if (!op_ring_buffer_write)
goto fail;
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 73348c4047e9..4a9cc92d4d18 100644
--- a/drivers/parisc/iosapic.c
+++ b/drivers/parisc/iosapic.c
@@ -702,7 +702,7 @@ static unsigned int iosapic_startup_irq(unsigned int irq)
}
#ifdef CONFIG_SMP
-static void iosapic_set_affinity_irq(unsigned int irq,
+static int iosapic_set_affinity_irq(unsigned int irq,
const struct cpumask *dest)
{
struct vector_info *vi = iosapic_get_vector(irq);
@@ -712,7 +712,7 @@ static void iosapic_set_affinity_irq(unsigned int irq,
dest_cpu = cpu_check_affinity(irq, dest);
if (dest_cpu < 0)
- return;
+ return -1;
cpumask_copy(irq_desc[irq].affinity, cpumask_of(dest_cpu));
vi->txn_addr = txn_affinity_addr(irq, dest_cpu);
@@ -724,6 +724,8 @@ static void iosapic_set_affinity_irq(unsigned int irq,
iosapic_set_irt_data(vi, &dummy_d0, &d1);
iosapic_wr_irt_entry(vi, d0, d1);
spin_unlock_irqrestore(&iosapic_lock, flags);
+
+ return 0;
}
#endif
diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c
index e6a7e847ee80..ea31a452b153 100644
--- a/drivers/parport/parport_gsc.c
+++ b/drivers/parport/parport_gsc.c
@@ -352,8 +352,8 @@ static int __devinit parport_init_chip(struct parisc_device *dev)
unsigned long port;
if (!dev->irq) {
- printk(KERN_WARNING "IRQ not found for parallel device at 0x%lx\n",
- dev->hpa.start);
+ printk(KERN_WARNING "IRQ not found for parallel device at 0x%llx\n",
+ (unsigned long long)dev->hpa.start);
return -ENODEV;
}
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 4e63cc9e2778..151bf5bc8afe 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -1,5 +1,5 @@
/* Low-level parallel-port routines for 8255-based PC-style hardware.
- *
+ *
* Authors: Phil Blundell <philb@gnu.org>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
@@ -11,7 +11,7 @@
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
* DMA support - Bert De Jonghe <bert@sophis.be>
* Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999
- * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G.
+ * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G.
* Various hacks, Fred Barnes, 04/2001
* Updated probing logic - Adam Belay <ambx1@neo.rr.com>
*/
@@ -56,10 +56,10 @@
#include <linux/pnp.h>
#include <linux/platform_device.h>
#include <linux/sysctl.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
-#include <asm/io.h>
#include <asm/dma.h>
-#include <asm/uaccess.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
@@ -82,7 +82,7 @@
#define ECR_TST 06
#define ECR_CNF 07
#define ECR_MODE_MASK 0xe0
-#define ECR_WRITE(p,v) frob_econtrol((p),0xff,(v))
+#define ECR_WRITE(p, v) frob_econtrol((p), 0xff, (v))
#undef DEBUG
@@ -109,27 +109,27 @@ static int pci_registered_parport;
static int pnp_registered_parport;
/* frob_control, but for ECR */
-static void frob_econtrol (struct parport *pb, unsigned char m,
+static void frob_econtrol(struct parport *pb, unsigned char m,
unsigned char v)
{
unsigned char ectr = 0;
if (m != 0xff)
- ectr = inb (ECONTROL (pb));
+ ectr = inb(ECONTROL(pb));
- DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
+ DPRINTK(KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
m, v, ectr, (ectr & ~m) ^ v);
- outb ((ectr & ~m) ^ v, ECONTROL (pb));
+ outb((ectr & ~m) ^ v, ECONTROL(pb));
}
-static __inline__ void frob_set_mode (struct parport *p, int mode)
+static inline void frob_set_mode(struct parport *p, int mode)
{
- frob_econtrol (p, ECR_MODE_MASK, mode << 5);
+ frob_econtrol(p, ECR_MODE_MASK, mode << 5);
}
#ifdef CONFIG_PARPORT_PC_FIFO
-/* Safely change the mode bits in the ECR
+/* Safely change the mode bits in the ECR
Returns:
0 : Success
-EBUSY: Could not drain FIFO in some finite amount of time,
@@ -141,17 +141,18 @@ static int change_mode(struct parport *p, int m)
unsigned char oecr;
int mode;
- DPRINTK(KERN_INFO "parport change_mode ECP-ISA to mode 0x%02x\n",m);
+ DPRINTK(KERN_INFO "parport change_mode ECP-ISA to mode 0x%02x\n", m);
if (!priv->ecr) {
- printk (KERN_DEBUG "change_mode: but there's no ECR!\n");
+ printk(KERN_DEBUG "change_mode: but there's no ECR!\n");
return 0;
}
/* Bits <7:5> contain the mode. */
- oecr = inb (ECONTROL (p));
+ oecr = inb(ECONTROL(p));
mode = (oecr >> 5) & 0x7;
- if (mode == m) return 0;
+ if (mode == m)
+ return 0;
if (mode >= 2 && !(priv->ctr & 0x20)) {
/* This mode resets the FIFO, so we may
@@ -163,19 +164,21 @@ static int change_mode(struct parport *p, int m)
case ECR_ECP: /* ECP Parallel Port mode */
/* Busy wait for 200us */
for (counter = 0; counter < 40; counter++) {
- if (inb (ECONTROL (p)) & 0x01)
+ if (inb(ECONTROL(p)) & 0x01)
+ break;
+ if (signal_pending(current))
break;
- if (signal_pending (current)) break;
- udelay (5);
+ udelay(5);
}
/* Poll slowly. */
- while (!(inb (ECONTROL (p)) & 0x01)) {
- if (time_after_eq (jiffies, expire))
+ while (!(inb(ECONTROL(p)) & 0x01)) {
+ if (time_after_eq(jiffies, expire))
/* The FIFO is stuck. */
return -EBUSY;
- schedule_timeout_interruptible(msecs_to_jiffies(10));
- if (signal_pending (current))
+ schedule_timeout_interruptible(
+ msecs_to_jiffies(10));
+ if (signal_pending(current))
break;
}
}
@@ -185,20 +188,20 @@ static int change_mode(struct parport *p, int m)
/* We have to go through mode 001 */
oecr &= ~(7 << 5);
oecr |= ECR_PS2 << 5;
- ECR_WRITE (p, oecr);
+ ECR_WRITE(p, oecr);
}
/* Set the mode. */
oecr &= ~(7 << 5);
oecr |= m << 5;
- ECR_WRITE (p, oecr);
+ ECR_WRITE(p, oecr);
return 0;
}
#ifdef CONFIG_PARPORT_1284
/* Find FIFO lossage; FIFO is reset */
#if 0
-static int get_fifo_residue (struct parport *p)
+static int get_fifo_residue(struct parport *p)
{
int residue;
int cnfga;
@@ -206,26 +209,26 @@ static int get_fifo_residue (struct parport *p)
/* Adjust for the contents of the FIFO. */
for (residue = priv->fifo_depth; ; residue--) {
- if (inb (ECONTROL (p)) & 0x2)
+ if (inb(ECONTROL(p)) & 0x2)
/* Full up. */
break;
- outb (0, FIFO (p));
+ outb(0, FIFO(p));
}
- printk (KERN_DEBUG "%s: %d PWords were left in FIFO\n", p->name,
+ printk(KERN_DEBUG "%s: %d PWords were left in FIFO\n", p->name,
residue);
/* Reset the FIFO. */
- frob_set_mode (p, ECR_PS2);
+ frob_set_mode(p, ECR_PS2);
/* Now change to config mode and clean up. FIXME */
- frob_set_mode (p, ECR_CNF);
- cnfga = inb (CONFIGA (p));
- printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
+ frob_set_mode(p, ECR_CNF);
+ cnfga = inb(CONFIGA(p));
+ printk(KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
if (!(cnfga & (1<<2))) {
- printk (KERN_DEBUG "%s: Accounting for extra byte\n", p->name);
+ printk(KERN_DEBUG "%s: Accounting for extra byte\n", p->name);
residue++;
}
@@ -233,9 +236,11 @@ static int get_fifo_residue (struct parport *p)
* PWord != 1 byte. */
/* Back to PS2 mode. */
- frob_set_mode (p, ECR_PS2);
+ frob_set_mode(p, ECR_PS2);
- DPRINTK (KERN_DEBUG "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n", inb (ECONTROL (p)));
+ DPRINTK(KERN_DEBUG
+ "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n",
+ inb(ECONTROL(p)));
return residue;
}
#endif /* 0 */
@@ -257,8 +262,8 @@ static int clear_epp_timeout(struct parport *pb)
/* To clear timeout some chips require double read */
parport_pc_read_status(pb);
r = parport_pc_read_status(pb);
- outb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
- outb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
+ outb(r | 0x01, STATUS(pb)); /* Some reset by writing 1 */
+ outb(r & 0xfe, STATUS(pb)); /* Others by writing 0 */
r = parport_pc_read_status(pb);
return !(r & 0x01);
@@ -272,7 +277,8 @@ static int clear_epp_timeout(struct parport *pb)
* of these are in parport_pc.h.
*/
-static void parport_pc_init_state(struct pardevice *dev, struct parport_state *s)
+static void parport_pc_init_state(struct pardevice *dev,
+ struct parport_state *s)
{
s->u.pc.ctr = 0xc;
if (dev->irq_func &&
@@ -289,22 +295,23 @@ static void parport_pc_save_state(struct parport *p, struct parport_state *s)
const struct parport_pc_private *priv = p->physport->private_data;
s->u.pc.ctr = priv->ctr;
if (priv->ecr)
- s->u.pc.ecr = inb (ECONTROL (p));
+ s->u.pc.ecr = inb(ECONTROL(p));
}
-static void parport_pc_restore_state(struct parport *p, struct parport_state *s)
+static void parport_pc_restore_state(struct parport *p,
+ struct parport_state *s)
{
struct parport_pc_private *priv = p->physport->private_data;
register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
- outb (c, CONTROL (p));
+ outb(c, CONTROL(p));
priv->ctr = c;
if (priv->ecr)
- ECR_WRITE (p, s->u.pc.ecr);
+ ECR_WRITE(p, s->u.pc.ecr);
}
#ifdef CONFIG_PARPORT_1284
-static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
- size_t length, int flags)
+static size_t parport_pc_epp_read_data(struct parport *port, void *buf,
+ size_t length, int flags)
{
size_t got = 0;
@@ -316,54 +323,52 @@ static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
* nFault is 0 if there is at least 1 byte in the Warp's FIFO
* pError is 1 if there are 16 bytes in the Warp's FIFO
*/
- status = inb (STATUS (port));
+ status = inb(STATUS(port));
- while (!(status & 0x08) && (got < length)) {
- if ((left >= 16) && (status & 0x20) && !(status & 0x08)) {
+ while (!(status & 0x08) && got < length) {
+ if (left >= 16 && (status & 0x20) && !(status & 0x08)) {
/* can grab 16 bytes from warp fifo */
- if (!((long)buf & 0x03)) {
- insl (EPPDATA (port), buf, 4);
- } else {
- insb (EPPDATA (port), buf, 16);
- }
+ if (!((long)buf & 0x03))
+ insl(EPPDATA(port), buf, 4);
+ else
+ insb(EPPDATA(port), buf, 16);
buf += 16;
got += 16;
left -= 16;
} else {
/* grab single byte from the warp fifo */
- *((char *)buf) = inb (EPPDATA (port));
+ *((char *)buf) = inb(EPPDATA(port));
buf++;
got++;
left--;
}
- status = inb (STATUS (port));
+ status = inb(STATUS(port));
if (status & 0x01) {
/* EPP timeout should never occur... */
- printk (KERN_DEBUG "%s: EPP timeout occurred while talking to "
- "w91284pic (should not have done)\n", port->name);
- clear_epp_timeout (port);
+ printk(KERN_DEBUG
+"%s: EPP timeout occurred while talking to w91284pic (should not have done)\n", port->name);
+ clear_epp_timeout(port);
}
}
return got;
}
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
- if (!(((long)buf | length) & 0x03)) {
- insl (EPPDATA (port), buf, (length >> 2));
- } else {
- insb (EPPDATA (port), buf, length);
- }
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ if (!(((long)buf | length) & 0x03))
+ insl(EPPDATA(port), buf, (length >> 2));
+ else
+ insb(EPPDATA(port), buf, length);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
return -EIO;
}
return length;
}
for (; got < length; got++) {
- *((char*)buf) = inb (EPPDATA(port));
+ *((char *)buf) = inb(EPPDATA(port));
buf++;
- if (inb (STATUS (port)) & 0x01) {
+ if (inb(STATUS(port)) & 0x01) {
/* EPP timeout */
- clear_epp_timeout (port);
+ clear_epp_timeout(port);
break;
}
}
@@ -371,28 +376,27 @@ static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
return got;
}
-static size_t parport_pc_epp_write_data (struct parport *port, const void *buf,
- size_t length, int flags)
+static size_t parport_pc_epp_write_data(struct parport *port, const void *buf,
+ size_t length, int flags)
{
size_t written = 0;
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
- if (!(((long)buf | length) & 0x03)) {
- outsl (EPPDATA (port), buf, (length >> 2));
- } else {
- outsb (EPPDATA (port), buf, length);
- }
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ if (!(((long)buf | length) & 0x03))
+ outsl(EPPDATA(port), buf, (length >> 2));
+ else
+ outsb(EPPDATA(port), buf, length);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
return -EIO;
}
return length;
}
for (; written < length; written++) {
- outb (*((char*)buf), EPPDATA(port));
+ outb(*((char *)buf), EPPDATA(port));
buf++;
- if (inb (STATUS(port)) & 0x01) {
- clear_epp_timeout (port);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
break;
}
}
@@ -400,24 +404,24 @@ static size_t parport_pc_epp_write_data (struct parport *port, const void *buf,
return written;
}
-static size_t parport_pc_epp_read_addr (struct parport *port, void *buf,
+static size_t parport_pc_epp_read_addr(struct parport *port, void *buf,
size_t length, int flags)
{
size_t got = 0;
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
- insb (EPPADDR (port), buf, length);
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ insb(EPPADDR(port), buf, length);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
return -EIO;
}
return length;
}
for (; got < length; got++) {
- *((char*)buf) = inb (EPPADDR (port));
+ *((char *)buf) = inb(EPPADDR(port));
buf++;
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
break;
}
}
@@ -425,25 +429,25 @@ static size_t parport_pc_epp_read_addr (struct parport *port, void *buf,
return got;
}
-static size_t parport_pc_epp_write_addr (struct parport *port,
+static size_t parport_pc_epp_write_addr(struct parport *port,
const void *buf, size_t length,
int flags)
{
size_t written = 0;
if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
- outsb (EPPADDR (port), buf, length);
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ outsb(EPPADDR(port), buf, length);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
return -EIO;
}
return length;
}
for (; written < length; written++) {
- outb (*((char*)buf), EPPADDR (port));
+ outb(*((char *)buf), EPPADDR(port));
buf++;
- if (inb (STATUS (port)) & 0x01) {
- clear_epp_timeout (port);
+ if (inb(STATUS(port)) & 0x01) {
+ clear_epp_timeout(port);
break;
}
}
@@ -451,74 +455,74 @@ static size_t parport_pc_epp_write_addr (struct parport *port,
return written;
}
-static size_t parport_pc_ecpepp_read_data (struct parport *port, void *buf,
- size_t length, int flags)
+static size_t parport_pc_ecpepp_read_data(struct parport *port, void *buf,
+ size_t length, int flags)
{
size_t got;
- frob_set_mode (port, ECR_EPP);
- parport_pc_data_reverse (port);
- parport_pc_write_control (port, 0x4);
- got = parport_pc_epp_read_data (port, buf, length, flags);
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_EPP);
+ parport_pc_data_reverse(port);
+ parport_pc_write_control(port, 0x4);
+ got = parport_pc_epp_read_data(port, buf, length, flags);
+ frob_set_mode(port, ECR_PS2);
return got;
}
-static size_t parport_pc_ecpepp_write_data (struct parport *port,
- const void *buf, size_t length,
- int flags)
+static size_t parport_pc_ecpepp_write_data(struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
size_t written;
- frob_set_mode (port, ECR_EPP);
- parport_pc_write_control (port, 0x4);
- parport_pc_data_forward (port);
- written = parport_pc_epp_write_data (port, buf, length, flags);
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_EPP);
+ parport_pc_write_control(port, 0x4);
+ parport_pc_data_forward(port);
+ written = parport_pc_epp_write_data(port, buf, length, flags);
+ frob_set_mode(port, ECR_PS2);
return written;
}
-static size_t parport_pc_ecpepp_read_addr (struct parport *port, void *buf,
- size_t length, int flags)
+static size_t parport_pc_ecpepp_read_addr(struct parport *port, void *buf,
+ size_t length, int flags)
{
size_t got;
- frob_set_mode (port, ECR_EPP);
- parport_pc_data_reverse (port);
- parport_pc_write_control (port, 0x4);
- got = parport_pc_epp_read_addr (port, buf, length, flags);
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_EPP);
+ parport_pc_data_reverse(port);
+ parport_pc_write_control(port, 0x4);
+ got = parport_pc_epp_read_addr(port, buf, length, flags);
+ frob_set_mode(port, ECR_PS2);
return got;
}
-static size_t parport_pc_ecpepp_write_addr (struct parport *port,
+static size_t parport_pc_ecpepp_write_addr(struct parport *port,
const void *buf, size_t length,
int flags)
{
size_t written;
- frob_set_mode (port, ECR_EPP);
- parport_pc_write_control (port, 0x4);
- parport_pc_data_forward (port);
- written = parport_pc_epp_write_addr (port, buf, length, flags);
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_EPP);
+ parport_pc_write_control(port, 0x4);
+ parport_pc_data_forward(port);
+ written = parport_pc_epp_write_addr(port, buf, length, flags);
+ frob_set_mode(port, ECR_PS2);
return written;
}
#endif /* IEEE 1284 support */
#ifdef CONFIG_PARPORT_PC_FIFO
-static size_t parport_pc_fifo_write_block_pio (struct parport *port,
+static size_t parport_pc_fifo_write_block_pio(struct parport *port,
const void *buf, size_t length)
{
int ret = 0;
const unsigned char *bufp = buf;
size_t left = length;
unsigned long expire = jiffies + port->physport->cad->timeout;
- const int fifo = FIFO (port);
+ const int fifo = FIFO(port);
int poll_for = 8; /* 80 usecs */
const struct parport_pc_private *priv = port->physport->private_data;
const int fifo_depth = priv->fifo_depth;
@@ -526,25 +530,25 @@ static size_t parport_pc_fifo_write_block_pio (struct parport *port,
port = port->physport;
/* We don't want to be interrupted every character. */
- parport_pc_disable_irq (port);
+ parport_pc_disable_irq(port);
/* set nErrIntrEn and serviceIntr */
- frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));
+ frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
/* Forward mode. */
- parport_pc_data_forward (port); /* Must be in PS2 mode */
+ parport_pc_data_forward(port); /* Must be in PS2 mode */
while (left) {
unsigned char byte;
- unsigned char ecrval = inb (ECONTROL (port));
+ unsigned char ecrval = inb(ECONTROL(port));
int i = 0;
- if (need_resched() && time_before (jiffies, expire))
+ if (need_resched() && time_before(jiffies, expire))
/* Can't yield the port. */
- schedule ();
+ schedule();
/* Anyone else waiting for the port? */
if (port->waithead) {
- printk (KERN_DEBUG "Somebody wants the port\n");
+ printk(KERN_DEBUG "Somebody wants the port\n");
break;
}
@@ -552,21 +556,22 @@ static size_t parport_pc_fifo_write_block_pio (struct parport *port,
/* FIFO is full. Wait for interrupt. */
/* Clear serviceIntr */
- ECR_WRITE (port, ecrval & ~(1<<2));
- false_alarm:
- ret = parport_wait_event (port, HZ);
- if (ret < 0) break;
+ ECR_WRITE(port, ecrval & ~(1<<2));
+false_alarm:
+ ret = parport_wait_event(port, HZ);
+ if (ret < 0)
+ break;
ret = 0;
- if (!time_before (jiffies, expire)) {
+ if (!time_before(jiffies, expire)) {
/* Timed out. */
- printk (KERN_DEBUG "FIFO write timed out\n");
+ printk(KERN_DEBUG "FIFO write timed out\n");
break;
}
- ecrval = inb (ECONTROL (port));
+ ecrval = inb(ECONTROL(port));
if (!(ecrval & (1<<2))) {
if (need_resched() &&
- time_before (jiffies, expire))
- schedule ();
+ time_before(jiffies, expire))
+ schedule();
goto false_alarm;
}
@@ -577,38 +582,38 @@ static size_t parport_pc_fifo_write_block_pio (struct parport *port,
/* Can't fail now. */
expire = jiffies + port->cad->timeout;
- poll:
- if (signal_pending (current))
+poll:
+ if (signal_pending(current))
break;
if (ecrval & 0x01) {
/* FIFO is empty. Blast it full. */
const int n = left < fifo_depth ? left : fifo_depth;
- outsb (fifo, bufp, n);
+ outsb(fifo, bufp, n);
bufp += n;
left -= n;
/* Adjust the poll time. */
- if (i < (poll_for - 2)) poll_for--;
+ if (i < (poll_for - 2))
+ poll_for--;
continue;
} else if (i++ < poll_for) {
- udelay (10);
- ecrval = inb (ECONTROL (port));
+ udelay(10);
+ ecrval = inb(ECONTROL(port));
goto poll;
}
- /* Half-full (call me an optimist) */
+ /* Half-full(call me an optimist) */
byte = *bufp++;
- outb (byte, fifo);
+ outb(byte, fifo);
left--;
- }
-
-dump_parport_state ("leave fifo_write_block_pio", port);
+ }
+ dump_parport_state("leave fifo_write_block_pio", port);
return length - left;
}
#ifdef HAS_DMA
-static size_t parport_pc_fifo_write_block_dma (struct parport *port,
+static size_t parport_pc_fifo_write_block_dma(struct parport *port,
const void *buf, size_t length)
{
int ret = 0;
@@ -621,7 +626,7 @@ static size_t parport_pc_fifo_write_block_dma (struct parport *port,
unsigned long start = (unsigned long) buf;
unsigned long end = (unsigned long) buf + length - 1;
-dump_parport_state ("enter fifo_write_block_dma", port);
+ dump_parport_state("enter fifo_write_block_dma", port);
if (end < MAX_DMA_ADDRESS) {
/* If it would cross a 64k boundary, cap it at the end. */
if ((start ^ end) & ~0xffffUL)
@@ -629,8 +634,9 @@ dump_parport_state ("enter fifo_write_block_dma", port);
dma_addr = dma_handle = dma_map_single(dev, (void *)buf, length,
DMA_TO_DEVICE);
- } else {
- /* above 16 MB we use a bounce buffer as ISA-DMA is not possible */
+ } else {
+ /* above 16 MB we use a bounce buffer as ISA-DMA
+ is not possible */
maxlen = PAGE_SIZE; /* sizeof(priv->dma_buf) */
dma_addr = priv->dma_handle;
dma_handle = 0;
@@ -639,12 +645,12 @@ dump_parport_state ("enter fifo_write_block_dma", port);
port = port->physport;
/* We don't want to be interrupted every character. */
- parport_pc_disable_irq (port);
+ parport_pc_disable_irq(port);
/* set nErrIntrEn and serviceIntr */
- frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));
+ frob_econtrol(port, (1<<4) | (1<<2), (1<<4) | (1<<2));
/* Forward mode. */
- parport_pc_data_forward (port); /* Must be in PS2 mode */
+ parport_pc_data_forward(port); /* Must be in PS2 mode */
while (left) {
unsigned long expire = jiffies + port->physport->cad->timeout;
@@ -665,10 +671,10 @@ dump_parport_state ("enter fifo_write_block_dma", port);
set_dma_count(port->dma, count);
/* Set DMA mode */
- frob_econtrol (port, 1<<3, 1<<3);
+ frob_econtrol(port, 1<<3, 1<<3);
/* Clear serviceIntr */
- frob_econtrol (port, 1<<2, 0);
+ frob_econtrol(port, 1<<2, 0);
enable_dma(port->dma);
release_dma_lock(dmaflag);
@@ -676,20 +682,22 @@ dump_parport_state ("enter fifo_write_block_dma", port);
/* assume DMA will be successful */
left -= count;
buf += count;
- if (dma_handle) dma_addr += count;
+ if (dma_handle)
+ dma_addr += count;
/* Wait for interrupt. */
- false_alarm:
- ret = parport_wait_event (port, HZ);
- if (ret < 0) break;
+false_alarm:
+ ret = parport_wait_event(port, HZ);
+ if (ret < 0)
+ break;
ret = 0;
- if (!time_before (jiffies, expire)) {
+ if (!time_before(jiffies, expire)) {
/* Timed out. */
- printk (KERN_DEBUG "DMA write timed out\n");
+ printk(KERN_DEBUG "DMA write timed out\n");
break;
}
/* Is serviceIntr set? */
- if (!(inb (ECONTROL (port)) & (1<<2))) {
+ if (!(inb(ECONTROL(port)) & (1<<2))) {
cond_resched();
goto false_alarm;
@@ -705,14 +713,15 @@ dump_parport_state ("enter fifo_write_block_dma", port);
/* Anyone else waiting for the port? */
if (port->waithead) {
- printk (KERN_DEBUG "Somebody wants the port\n");
+ printk(KERN_DEBUG "Somebody wants the port\n");
break;
}
/* update for possible DMA residue ! */
buf -= count;
left += count;
- if (dma_handle) dma_addr -= count;
+ if (dma_handle)
+ dma_addr -= count;
}
/* Maybe got here through break, so adjust for DMA residue! */
@@ -723,12 +732,12 @@ dump_parport_state ("enter fifo_write_block_dma", port);
release_dma_lock(dmaflag);
/* Turn off DMA mode */
- frob_econtrol (port, 1<<3, 0);
+ frob_econtrol(port, 1<<3, 0);
if (dma_handle)
dma_unmap_single(dev, dma_handle, length, DMA_TO_DEVICE);
-dump_parport_state ("leave fifo_write_block_dma", port);
+ dump_parport_state("leave fifo_write_block_dma", port);
return length - left;
}
#endif
@@ -738,13 +747,13 @@ static inline size_t parport_pc_fifo_write_block(struct parport *port,
{
#ifdef HAS_DMA
if (port->dma != PARPORT_DMA_NONE)
- return parport_pc_fifo_write_block_dma (port, buf, length);
+ return parport_pc_fifo_write_block_dma(port, buf, length);
#endif
- return parport_pc_fifo_write_block_pio (port, buf, length);
+ return parport_pc_fifo_write_block_pio(port, buf, length);
}
/* Parallel Port FIFO mode (ECP chipsets) */
-static size_t parport_pc_compat_write_block_pio (struct parport *port,
+static size_t parport_pc_compat_write_block_pio(struct parport *port,
const void *buf, size_t length,
int flags)
{
@@ -756,14 +765,16 @@ static size_t parport_pc_compat_write_block_pio (struct parport *port,
/* Special case: a timeout of zero means we cannot call schedule().
* Also if O_NONBLOCK is set then use the default implementation. */
if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
- return parport_ieee1284_write_compat (port, buf,
+ return parport_ieee1284_write_compat(port, buf,
length, flags);
/* Set up parallel port FIFO mode.*/
- parport_pc_data_forward (port); /* Must be in PS2 mode */
- parport_pc_frob_control (port, PARPORT_CONTROL_STROBE, 0);
- r = change_mode (port, ECR_PPF); /* Parallel port FIFO */
- if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n", port->name);
+ parport_pc_data_forward(port); /* Must be in PS2 mode */
+ parport_pc_frob_control(port, PARPORT_CONTROL_STROBE, 0);
+ r = change_mode(port, ECR_PPF); /* Parallel port FIFO */
+ if (r)
+ printk(KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n",
+ port->name);
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
@@ -775,40 +786,39 @@ static size_t parport_pc_compat_write_block_pio (struct parport *port,
* the FIFO is empty, so allow 4 seconds for each position
* in the fifo.
*/
- expire = jiffies + (priv->fifo_depth * HZ * 4);
+ expire = jiffies + (priv->fifo_depth * HZ * 4);
do {
/* Wait for the FIFO to empty */
- r = change_mode (port, ECR_PS2);
- if (r != -EBUSY) {
+ r = change_mode(port, ECR_PS2);
+ if (r != -EBUSY)
break;
- }
- } while (time_before (jiffies, expire));
+ } while (time_before(jiffies, expire));
if (r == -EBUSY) {
- printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+ printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
/* Prevent further data transfer. */
- frob_set_mode (port, ECR_TST);
+ frob_set_mode(port, ECR_TST);
/* Adjust for the contents of the FIFO. */
for (written -= priv->fifo_depth; ; written++) {
- if (inb (ECONTROL (port)) & 0x2) {
+ if (inb(ECONTROL(port)) & 0x2) {
/* Full up. */
break;
}
- outb (0, FIFO (port));
+ outb(0, FIFO(port));
}
/* Reset the FIFO and return to PS2 mode. */
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_PS2);
}
- r = parport_wait_peripheral (port,
+ r = parport_wait_peripheral(port,
PARPORT_STATUS_BUSY,
PARPORT_STATUS_BUSY);
if (r)
- printk (KERN_DEBUG
- "%s: BUSY timeout (%d) in compat_write_block_pio\n",
+ printk(KERN_DEBUG
+ "%s: BUSY timeout (%d) in compat_write_block_pio\n",
port->name, r);
port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
@@ -818,7 +828,7 @@ static size_t parport_pc_compat_write_block_pio (struct parport *port,
/* ECP */
#ifdef CONFIG_PARPORT_1284
-static size_t parport_pc_ecp_write_block_pio (struct parport *port,
+static size_t parport_pc_ecp_write_block_pio(struct parport *port,
const void *buf, size_t length,
int flags)
{
@@ -830,36 +840,38 @@ static size_t parport_pc_ecp_write_block_pio (struct parport *port,
/* Special case: a timeout of zero means we cannot call schedule().
* Also if O_NONBLOCK is set then use the default implementation. */
if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
- return parport_ieee1284_ecp_write_data (port, buf,
+ return parport_ieee1284_ecp_write_data(port, buf,
length, flags);
/* Switch to forward mode if necessary. */
if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
/* Event 47: Set nInit high. */
- parport_frob_control (port,
+ parport_frob_control(port,
PARPORT_CONTROL_INIT
| PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_INIT
| PARPORT_CONTROL_AUTOFD);
/* Event 49: PError goes high. */
- r = parport_wait_peripheral (port,
+ r = parport_wait_peripheral(port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
if (r) {
- printk (KERN_DEBUG "%s: PError timeout (%d) "
+ printk(KERN_DEBUG "%s: PError timeout (%d) "
"in ecp_write_block_pio\n", port->name, r);
}
}
/* Set up ECP parallel port mode.*/
- parport_pc_data_forward (port); /* Must be in PS2 mode */
- parport_pc_frob_control (port,
+ parport_pc_data_forward(port); /* Must be in PS2 mode */
+ parport_pc_frob_control(port,
PARPORT_CONTROL_STROBE |
PARPORT_CONTROL_AUTOFD,
0);
- r = change_mode (port, ECR_ECP); /* ECP FIFO */
- if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name);
+ r = change_mode(port, ECR_ECP); /* ECP FIFO */
+ if (r)
+ printk(KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n",
+ port->name);
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
/* Write the data to the FIFO. */
@@ -873,55 +885,54 @@ static size_t parport_pc_ecp_write_block_pio (struct parport *port,
expire = jiffies + (priv->fifo_depth * (HZ * 4));
do {
/* Wait for the FIFO to empty */
- r = change_mode (port, ECR_PS2);
- if (r != -EBUSY) {
+ r = change_mode(port, ECR_PS2);
+ if (r != -EBUSY)
break;
- }
- } while (time_before (jiffies, expire));
+ } while (time_before(jiffies, expire));
if (r == -EBUSY) {
- printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+ printk(KERN_DEBUG "%s: FIFO is stuck\n", port->name);
/* Prevent further data transfer. */
- frob_set_mode (port, ECR_TST);
+ frob_set_mode(port, ECR_TST);
/* Adjust for the contents of the FIFO. */
for (written -= priv->fifo_depth; ; written++) {
- if (inb (ECONTROL (port)) & 0x2) {
+ if (inb(ECONTROL(port)) & 0x2) {
/* Full up. */
break;
}
- outb (0, FIFO (port));
+ outb(0, FIFO(port));
}
/* Reset the FIFO and return to PS2 mode. */
- frob_set_mode (port, ECR_PS2);
+ frob_set_mode(port, ECR_PS2);
/* Host transfer recovery. */
- parport_pc_data_reverse (port); /* Must be in PS2 mode */
- udelay (5);
- parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
- r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
+ parport_pc_data_reverse(port); /* Must be in PS2 mode */
+ udelay(5);
+ parport_frob_control(port, PARPORT_CONTROL_INIT, 0);
+ r = parport_wait_peripheral(port, PARPORT_STATUS_PAPEROUT, 0);
if (r)
- printk (KERN_DEBUG "%s: PE,1 timeout (%d) "
+ printk(KERN_DEBUG "%s: PE,1 timeout (%d) "
"in ecp_write_block_pio\n", port->name, r);
- parport_frob_control (port,
+ parport_frob_control(port,
PARPORT_CONTROL_INIT,
PARPORT_CONTROL_INIT);
- r = parport_wait_peripheral (port,
+ r = parport_wait_peripheral(port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
- if (r)
- printk (KERN_DEBUG "%s: PE,2 timeout (%d) "
+ if (r)
+ printk(KERN_DEBUG "%s: PE,2 timeout (%d) "
"in ecp_write_block_pio\n", port->name, r);
}
- r = parport_wait_peripheral (port,
- PARPORT_STATUS_BUSY,
+ r = parport_wait_peripheral(port,
+ PARPORT_STATUS_BUSY,
PARPORT_STATUS_BUSY);
- if(r)
- printk (KERN_DEBUG
+ if (r)
+ printk(KERN_DEBUG
"%s: BUSY timeout (%d) in ecp_write_block_pio\n",
port->name, r);
@@ -931,7 +942,7 @@ static size_t parport_pc_ecp_write_block_pio (struct parport *port,
}
#if 0
-static size_t parport_pc_ecp_read_block_pio (struct parport *port,
+static size_t parport_pc_ecp_read_block_pio(struct parport *port,
void *buf, size_t length,
int flags)
{
@@ -944,13 +955,13 @@ static size_t parport_pc_ecp_read_block_pio (struct parport *port,
char *bufp = buf;
port = port->physport;
-DPRINTK (KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n");
-dump_parport_state ("enter fcn", port);
+ DPRINTK(KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n");
+ dump_parport_state("enter fcn", port);
/* Special case: a timeout of zero means we cannot call schedule().
* Also if O_NONBLOCK is set then use the default implementation. */
if (port->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
- return parport_ieee1284_ecp_read_data (port, buf,
+ return parport_ieee1284_ecp_read_data(port, buf,
length, flags);
if (port->ieee1284.mode == IEEE1284_MODE_ECPRLE) {
@@ -966,173 +977,178 @@ dump_parport_state ("enter fcn", port);
* go through software emulation. Otherwise we may have to throw
* away data. */
if (length < fifofull)
- return parport_ieee1284_ecp_read_data (port, buf,
+ return parport_ieee1284_ecp_read_data(port, buf,
length, flags);
if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) {
/* change to reverse-idle phase (must be in forward-idle) */
/* Event 38: Set nAutoFd low (also make sure nStrobe is high) */
- parport_frob_control (port,
+ parport_frob_control(port,
PARPORT_CONTROL_AUTOFD
| PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_AUTOFD);
- parport_pc_data_reverse (port); /* Must be in PS2 mode */
- udelay (5);
+ parport_pc_data_reverse(port); /* Must be in PS2 mode */
+ udelay(5);
/* Event 39: Set nInit low to initiate bus reversal */
- parport_frob_control (port,
+ parport_frob_control(port,
PARPORT_CONTROL_INIT,
0);
/* Event 40: Wait for nAckReverse (PError) to go low */
- r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
- if (r) {
- printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
+ r = parport_wait_peripheral(port, PARPORT_STATUS_PAPEROUT, 0);
+ if (r) {
+ printk(KERN_DEBUG "%s: PE timeout Event 40 (%d) "
"in ecp_read_block_pio\n", port->name, r);
return 0;
}
}
/* Set up ECP FIFO mode.*/
-/* parport_pc_frob_control (port,
+/* parport_pc_frob_control(port,
PARPORT_CONTROL_STROBE |
PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_AUTOFD); */
- r = change_mode (port, ECR_ECP); /* ECP FIFO */
- if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name);
+ r = change_mode(port, ECR_ECP); /* ECP FIFO */
+ if (r)
+ printk(KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n",
+ port->name);
port->ieee1284.phase = IEEE1284_PH_REV_DATA;
/* the first byte must be collected manually */
-dump_parport_state ("pre 43", port);
+ dump_parport_state("pre 43", port);
/* Event 43: Wait for nAck to go low */
- r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
+ r = parport_wait_peripheral(port, PARPORT_STATUS_ACK, 0);
if (r) {
/* timed out while reading -- no data */
- printk (KERN_DEBUG "PIO read timed out (initial byte)\n");
+ printk(KERN_DEBUG "PIO read timed out (initial byte)\n");
goto out_no_data;
}
/* read byte */
- *bufp++ = inb (DATA (port));
+ *bufp++ = inb(DATA(port));
left--;
-dump_parport_state ("43-44", port);
+ dump_parport_state("43-44", port);
/* Event 44: nAutoFd (HostAck) goes high to acknowledge */
- parport_pc_frob_control (port,
+ parport_pc_frob_control(port,
PARPORT_CONTROL_AUTOFD,
0);
-dump_parport_state ("pre 45", port);
+ dump_parport_state("pre 45", port);
/* Event 45: Wait for nAck to go high */
-/* r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); */
-dump_parport_state ("post 45", port);
-r = 0;
+ /* r = parport_wait_peripheral(port, PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK); */
+ dump_parport_state("post 45", port);
+ r = 0;
if (r) {
/* timed out while waiting for peripheral to respond to ack */
- printk (KERN_DEBUG "ECP PIO read timed out (waiting for nAck)\n");
+ printk(KERN_DEBUG "ECP PIO read timed out (waiting for nAck)\n");
/* keep hold of the byte we've got already */
goto out_no_data;
}
/* Event 46: nAutoFd (HostAck) goes low to accept more data */
- parport_pc_frob_control (port,
+ parport_pc_frob_control(port,
PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_AUTOFD);
-dump_parport_state ("rev idle", port);
+ dump_parport_state("rev idle", port);
/* Do the transfer. */
while (left > fifofull) {
int ret;
unsigned long expire = jiffies + port->cad->timeout;
- unsigned char ecrval = inb (ECONTROL (port));
+ unsigned char ecrval = inb(ECONTROL(port));
- if (need_resched() && time_before (jiffies, expire))
+ if (need_resched() && time_before(jiffies, expire))
/* Can't yield the port. */
- schedule ();
+ schedule();
/* At this point, the FIFO may already be full. In
- * that case ECP is already holding back the
- * peripheral (assuming proper design) with a delayed
- * handshake. Work fast to avoid a peripheral
- * timeout. */
+ * that case ECP is already holding back the
+ * peripheral (assuming proper design) with a delayed
+ * handshake. Work fast to avoid a peripheral
+ * timeout. */
if (ecrval & 0x01) {
/* FIFO is empty. Wait for interrupt. */
-dump_parport_state ("FIFO empty", port);
+ dump_parport_state("FIFO empty", port);
/* Anyone else waiting for the port? */
if (port->waithead) {
- printk (KERN_DEBUG "Somebody wants the port\n");
+ printk(KERN_DEBUG "Somebody wants the port\n");
break;
}
/* Clear serviceIntr */
- ECR_WRITE (port, ecrval & ~(1<<2));
- false_alarm:
-dump_parport_state ("waiting", port);
- ret = parport_wait_event (port, HZ);
-DPRINTK (KERN_DEBUG "parport_wait_event returned %d\n", ret);
+ ECR_WRITE(port, ecrval & ~(1<<2));
+false_alarm:
+ dump_parport_state("waiting", port);
+ ret = parport_wait_event(port, HZ);
+ DPRINTK(KERN_DEBUG "parport_wait_event returned %d\n",
+ ret);
if (ret < 0)
break;
ret = 0;
- if (!time_before (jiffies, expire)) {
+ if (!time_before(jiffies, expire)) {
/* Timed out. */
-dump_parport_state ("timeout", port);
- printk (KERN_DEBUG "PIO read timed out\n");
+ dump_parport_state("timeout", port);
+ printk(KERN_DEBUG "PIO read timed out\n");
break;
}
- ecrval = inb (ECONTROL (port));
+ ecrval = inb(ECONTROL(port));
if (!(ecrval & (1<<2))) {
if (need_resched() &&
- time_before (jiffies, expire)) {
- schedule ();
+ time_before(jiffies, expire)) {
+ schedule();
}
goto false_alarm;
}
/* Depending on how the FIFO threshold was
- * set, how long interrupt service took, and
- * how fast the peripheral is, we might be
- * lucky and have a just filled FIFO. */
+ * set, how long interrupt service took, and
+ * how fast the peripheral is, we might be
+ * lucky and have a just filled FIFO. */
continue;
}
if (ecrval & 0x02) {
/* FIFO is full. */
-dump_parport_state ("FIFO full", port);
- insb (fifo, bufp, fifo_depth);
+ dump_parport_state("FIFO full", port);
+ insb(fifo, bufp, fifo_depth);
bufp += fifo_depth;
left -= fifo_depth;
continue;
}
-DPRINTK (KERN_DEBUG "*** ecp_read_block_pio: reading one byte from the FIFO\n");
+ DPRINTK(KERN_DEBUG
+ "*** ecp_read_block_pio: reading one byte from the FIFO\n");
/* FIFO not filled. We will cycle this loop for a while
- * and either the peripheral will fill it faster,
- * tripping a fast empty with insb, or we empty it. */
- *bufp++ = inb (fifo);
+ * and either the peripheral will fill it faster,
+ * tripping a fast empty with insb, or we empty it. */
+ *bufp++ = inb(fifo);
left--;
}
/* scoop up anything left in the FIFO */
- while (left && !(inb (ECONTROL (port) & 0x01))) {
- *bufp++ = inb (fifo);
+ while (left && !(inb(ECONTROL(port) & 0x01))) {
+ *bufp++ = inb(fifo);
left--;
}
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
-dump_parport_state ("rev idle2", port);
+ dump_parport_state("rev idle2", port);
out_no_data:
/* Go to forward idle mode to shut the peripheral up (event 47). */
- parport_frob_control (port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT);
+ parport_frob_control(port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT);
/* event 49: PError goes high */
- r = parport_wait_peripheral (port,
+ r = parport_wait_peripheral(port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
if (r) {
- printk (KERN_DEBUG
+ printk(KERN_DEBUG
"%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n",
port->name, r);
}
@@ -1141,14 +1157,14 @@ out_no_data:
/* Finish up. */
{
- int lost = get_fifo_residue (port);
+ int lost = get_fifo_residue(port);
if (lost)
/* Shouldn't happen with compliant peripherals. */
- printk (KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n",
+ printk(KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n",
port->name, lost);
}
-dump_parport_state ("fwd idle", port);
+ dump_parport_state("fwd idle", port);
return length - left;
}
#endif /* 0 */
@@ -1164,8 +1180,7 @@ dump_parport_state ("fwd idle", port);
/* GCC is not inlining extern inline function later overwriten to non-inline,
so we use outlined_ variants here. */
-static const struct parport_operations parport_pc_ops =
-{
+static const struct parport_operations parport_pc_ops = {
.write_data = parport_pc_write_data,
.read_data = parport_pc_read_data,
@@ -1202,88 +1217,107 @@ static const struct parport_operations parport_pc_ops =
};
#ifdef CONFIG_PARPORT_PC_SUPERIO
+
+static struct superio_struct *find_free_superio(void)
+{
+ int i;
+ for (i = 0; i < NR_SUPERIOS; i++)
+ if (superios[i].io == 0)
+ return &superios[i];
+ return NULL;
+}
+
+
/* Super-IO chipset detection, Winbond, SMSC */
static void __devinit show_parconfig_smsc37c669(int io, int key)
{
- int cr1,cr4,cra,cr23,cr26,cr27,i=0;
- static const char *const modes[]={
+ int cr1, cr4, cra, cr23, cr26, cr27;
+ struct superio_struct *s;
+
+ static const char *const modes[] = {
"SPP and Bidirectional (PS/2)",
"EPP and SPP",
"ECP",
"ECP and EPP" };
- outb(key,io);
- outb(key,io);
- outb(1,io);
- cr1=inb(io+1);
- outb(4,io);
- cr4=inb(io+1);
- outb(0x0a,io);
- cra=inb(io+1);
- outb(0x23,io);
- cr23=inb(io+1);
- outb(0x26,io);
- cr26=inb(io+1);
- outb(0x27,io);
- cr27=inb(io+1);
- outb(0xaa,io);
+ outb(key, io);
+ outb(key, io);
+ outb(1, io);
+ cr1 = inb(io + 1);
+ outb(4, io);
+ cr4 = inb(io + 1);
+ outb(0x0a, io);
+ cra = inb(io + 1);
+ outb(0x23, io);
+ cr23 = inb(io + 1);
+ outb(0x26, io);
+ cr26 = inb(io + 1);
+ outb(0x27, io);
+ cr27 = inb(io + 1);
+ outb(0xaa, io);
if (verbose_probing) {
- printk (KERN_INFO "SMSC 37c669 LPT Config: cr_1=0x%02x, 4=0x%02x, "
+ printk(KERN_INFO
+ "SMSC 37c669 LPT Config: cr_1=0x%02x, 4=0x%02x, "
"A=0x%2x, 23=0x%02x, 26=0x%02x, 27=0x%02x\n",
- cr1,cr4,cra,cr23,cr26,cr27);
-
+ cr1, cr4, cra, cr23, cr26, cr27);
+
/* The documentation calls DMA and IRQ-Lines by letters, so
the board maker can/will wire them
appropriately/randomly... G=reserved H=IDE-irq, */
- printk (KERN_INFO "SMSC LPT Config: io=0x%04x, irq=%c, dma=%c, "
- "fifo threshold=%d\n", cr23*4,
- (cr27 &0x0f) ? 'A'-1+(cr27 &0x0f): '-',
- (cr26 &0x0f) ? 'A'-1+(cr26 &0x0f): '-', cra & 0x0f);
+ printk(KERN_INFO
+ "SMSC LPT Config: io=0x%04x, irq=%c, dma=%c, fifo threshold=%d\n",
+ cr23 * 4,
+ (cr27 & 0x0f) ? 'A' - 1 + (cr27 & 0x0f) : '-',
+ (cr26 & 0x0f) ? 'A' - 1 + (cr26 & 0x0f) : '-',
+ cra & 0x0f);
printk(KERN_INFO "SMSC LPT Config: enabled=%s power=%s\n",
- (cr23*4 >=0x100) ?"yes":"no", (cr1 & 4) ? "yes" : "no");
- printk(KERN_INFO "SMSC LPT Config: Port mode=%s, EPP version =%s\n",
- (cr1 & 0x08 ) ? "Standard mode only (SPP)" : modes[cr4 & 0x03],
- (cr4 & 0x40) ? "1.7" : "1.9");
+ (cr23 * 4 >= 0x100) ? "yes" : "no",
+ (cr1 & 4) ? "yes" : "no");
+ printk(KERN_INFO
+ "SMSC LPT Config: Port mode=%s, EPP version =%s\n",
+ (cr1 & 0x08) ? "Standard mode only (SPP)"
+ : modes[cr4 & 0x03],
+ (cr4 & 0x40) ? "1.7" : "1.9");
}
-
+
/* Heuristics ! BIOS setup for this mainboard device limits
the choices to standard settings, i.e. io-address and IRQ
are related, however DMA can be 1 or 3, assume DMA_A=DMA1,
DMA_C=DMA3 (this is true e.g. for TYAN 1564D Tomcat IV) */
- if(cr23*4 >=0x100) { /* if active */
- while((superios[i].io!= 0) && (i<NR_SUPERIOS))
- i++;
- if(i==NR_SUPERIOS)
+ if (cr23 * 4 >= 0x100) { /* if active */
+ s = find_free_superio();
+ if (s == NULL)
printk(KERN_INFO "Super-IO: too many chips!\n");
else {
int d;
- switch (cr23*4) {
- case 0x3bc:
- superios[i].io = 0x3bc;
- superios[i].irq = 7;
- break;
- case 0x378:
- superios[i].io = 0x378;
- superios[i].irq = 7;
- break;
- case 0x278:
- superios[i].io = 0x278;
- superios[i].irq = 5;
+ switch (cr23 * 4) {
+ case 0x3bc:
+ s->io = 0x3bc;
+ s->irq = 7;
+ break;
+ case 0x378:
+ s->io = 0x378;
+ s->irq = 7;
+ break;
+ case 0x278:
+ s->io = 0x278;
+ s->irq = 5;
}
- d=(cr26 &0x0f);
- if((d==1) || (d==3))
- superios[i].dma= d;
+ d = (cr26 & 0x0f);
+ if (d == 1 || d == 3)
+ s->dma = d;
else
- superios[i].dma= PARPORT_DMA_NONE;
+ s->dma = PARPORT_DMA_NONE;
}
- }
+ }
}
static void __devinit show_parconfig_winbond(int io, int key)
{
- int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
+ int cr30, cr60, cr61, cr70, cr74, crf0;
+ struct superio_struct *s;
static const char *const modes[] = {
"Standard (SPP) and Bidirectional(PS/2)", /* 0 */
"EPP-1.9 and SPP",
@@ -1296,110 +1330,134 @@ static void __devinit show_parconfig_winbond(int io, int key)
static char *const irqtypes[] = {
"pulsed low, high-Z",
"follows nACK" };
-
+
/* The registers are called compatible-PnP because the
- register layout is modelled after ISA-PnP, the access
- method is just another ... */
- outb(key,io);
- outb(key,io);
- outb(0x07,io); /* Register 7: Select Logical Device */
- outb(0x01,io+1); /* LD1 is Parallel Port */
- outb(0x30,io);
- cr30=inb(io+1);
- outb(0x60,io);
- cr60=inb(io+1);
- outb(0x61,io);
- cr61=inb(io+1);
- outb(0x70,io);
- cr70=inb(io+1);
- outb(0x74,io);
- cr74=inb(io+1);
- outb(0xf0,io);
- crf0=inb(io+1);
- outb(0xaa,io);
+ register layout is modelled after ISA-PnP, the access
+ method is just another ... */
+ outb(key, io);
+ outb(key, io);
+ outb(0x07, io); /* Register 7: Select Logical Device */
+ outb(0x01, io + 1); /* LD1 is Parallel Port */
+ outb(0x30, io);
+ cr30 = inb(io + 1);
+ outb(0x60, io);
+ cr60 = inb(io + 1);
+ outb(0x61, io);
+ cr61 = inb(io + 1);
+ outb(0x70, io);
+ cr70 = inb(io + 1);
+ outb(0x74, io);
+ cr74 = inb(io + 1);
+ outb(0xf0, io);
+ crf0 = inb(io + 1);
+ outb(0xaa, io);
if (verbose_probing) {
- printk(KERN_INFO "Winbond LPT Config: cr_30=%02x 60,61=%02x%02x "
- "70=%02x 74=%02x, f0=%02x\n", cr30,cr60,cr61,cr70,cr74,crf0);
- printk(KERN_INFO "Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
- (cr30 & 0x01) ? "yes":"no", cr60,cr61,cr70&0x0f );
+ printk(KERN_INFO
+ "Winbond LPT Config: cr_30=%02x 60,61=%02x%02x 70=%02x 74=%02x, f0=%02x\n",
+ cr30, cr60, cr61, cr70, cr74, crf0);
+ printk(KERN_INFO "Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
+ (cr30 & 0x01) ? "yes" : "no", cr60, cr61, cr70 & 0x0f);
if ((cr74 & 0x07) > 3)
printk("dma=none\n");
else
- printk("dma=%d\n",cr74 & 0x07);
- printk(KERN_INFO "Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
- irqtypes[crf0>>7], (crf0>>3)&0x0f);
- printk(KERN_INFO "Winbond LPT Config: Port mode=%s\n", modes[crf0 & 0x07]);
+ printk("dma=%d\n", cr74 & 0x07);
+ printk(KERN_INFO
+ "Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
+ irqtypes[crf0>>7], (crf0>>3)&0x0f);
+ printk(KERN_INFO "Winbond LPT Config: Port mode=%s\n",
+ modes[crf0 & 0x07]);
}
- if(cr30 & 0x01) { /* the settings can be interrogated later ... */
- while((superios[i].io!= 0) && (i<NR_SUPERIOS))
- i++;
- if(i==NR_SUPERIOS)
+ if (cr30 & 0x01) { /* the settings can be interrogated later ... */
+ s = find_free_superio();
+ if (s == NULL)
printk(KERN_INFO "Super-IO: too many chips!\n");
else {
- superios[i].io = (cr60<<8)|cr61;
- superios[i].irq = cr70&0x0f;
- superios[i].dma = (((cr74 & 0x07) > 3) ?
+ s->io = (cr60 << 8) | cr61;
+ s->irq = cr70 & 0x0f;
+ s->dma = (((cr74 & 0x07) > 3) ?
PARPORT_DMA_NONE : (cr74 & 0x07));
}
}
}
-static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid,
+ int devrev, int oldid)
{
const char *type = "unknown";
- int id,progif=2;
+ int id, progif = 2;
if (devid == devrev)
/* simple heuristics, we happened to read some
- non-winbond register */
+ non-winbond register */
return;
- id=(devid<<8) | devrev;
+ id = (devid << 8) | devrev;
/* Values are from public data sheets pdf files, I can just
- confirm 83977TF is correct :-) */
- if (id == 0x9771) type="83977F/AF";
- else if (id == 0x9773) type="83977TF / SMSC 97w33x/97w34x";
- else if (id == 0x9774) type="83977ATF";
- else if ((id & ~0x0f) == 0x5270) type="83977CTF / SMSC 97w36x";
- else if ((id & ~0x0f) == 0x52f0) type="83977EF / SMSC 97w35x";
- else if ((id & ~0x0f) == 0x5210) type="83627";
- else if ((id & ~0x0f) == 0x6010) type="83697HF";
- else if ((oldid &0x0f ) == 0x0a) { type="83877F"; progif=1;}
- else if ((oldid &0x0f ) == 0x0b) { type="83877AF"; progif=1;}
- else if ((oldid &0x0f ) == 0x0c) { type="83877TF"; progif=1;}
- else if ((oldid &0x0f ) == 0x0d) { type="83877ATF"; progif=1;}
- else progif=0;
+ confirm 83977TF is correct :-) */
+ if (id == 0x9771)
+ type = "83977F/AF";
+ else if (id == 0x9773)
+ type = "83977TF / SMSC 97w33x/97w34x";
+ else if (id == 0x9774)
+ type = "83977ATF";
+ else if ((id & ~0x0f) == 0x5270)
+ type = "83977CTF / SMSC 97w36x";
+ else if ((id & ~0x0f) == 0x52f0)
+ type = "83977EF / SMSC 97w35x";
+ else if ((id & ~0x0f) == 0x5210)
+ type = "83627";
+ else if ((id & ~0x0f) == 0x6010)
+ type = "83697HF";
+ else if ((oldid & 0x0f) == 0x0a) {
+ type = "83877F";
+ progif = 1;
+ } else if ((oldid & 0x0f) == 0x0b) {
+ type = "83877AF";
+ progif = 1;
+ } else if ((oldid & 0x0f) == 0x0c) {
+ type = "83877TF";
+ progif = 1;
+ } else if ((oldid & 0x0f) == 0x0d) {
+ type = "83877ATF";
+ progif = 1;
+ } else
+ progif = 0;
if (verbose_probing)
printk(KERN_INFO "Winbond chip at EFER=0x%x key=0x%02x "
- "devid=%02x devrev=%02x oldid=%02x type=%s\n",
+ "devid=%02x devrev=%02x oldid=%02x type=%s\n",
efer, key, devid, devrev, oldid, type);
if (progif == 2)
- show_parconfig_winbond(efer,key);
+ show_parconfig_winbond(efer, key);
}
static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
{
- const char *type = "unknown";
+ const char *type = "unknown";
void (*func)(int io, int key);
- int id;
+ int id;
- if (devid == devrev)
+ if (devid == devrev)
/* simple heuristics, we happened to read some
- non-smsc register */
+ non-smsc register */
return;
- func=NULL;
- id=(devid<<8) | devrev;
+ func = NULL;
+ id = (devid << 8) | devrev;
- if (id==0x0302) {type="37c669"; func=show_parconfig_smsc37c669;}
- else if (id==0x6582) type="37c665IR";
- else if (devid==0x65) type="37c665GT";
- else if (devid==0x66) type="37c666GT";
+ if (id == 0x0302) {
+ type = "37c669";
+ func = show_parconfig_smsc37c669;
+ } else if (id == 0x6582)
+ type = "37c665IR";
+ else if (devid == 0x65)
+ type = "37c665GT";
+ else if (devid == 0x66)
+ type = "37c666GT";
if (verbose_probing)
printk(KERN_INFO "SMSC chip at EFER=0x%x "
@@ -1407,138 +1465,138 @@ static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
efer, key, devid, devrev, type);
if (func)
- func(efer,key);
+ func(efer, key);
}
static void __devinit winbond_check(int io, int key)
{
- int devid,devrev,oldid,x_devid,x_devrev,x_oldid;
+ int devid, devrev, oldid, x_devid, x_devrev, x_oldid;
if (!request_region(io, 3, __func__))
return;
/* First probe without key */
- outb(0x20,io);
- x_devid=inb(io+1);
- outb(0x21,io);
- x_devrev=inb(io+1);
- outb(0x09,io);
- x_oldid=inb(io+1);
-
- outb(key,io);
- outb(key,io); /* Write Magic Sequence to EFER, extended
- funtion enable register */
- outb(0x20,io); /* Write EFIR, extended function index register */
- devid=inb(io+1); /* Read EFDR, extended function data register */
- outb(0x21,io);
- devrev=inb(io+1);
- outb(0x09,io);
- oldid=inb(io+1);
- outb(0xaa,io); /* Magic Seal */
+ outb(0x20, io);
+ x_devid = inb(io + 1);
+ outb(0x21, io);
+ x_devrev = inb(io + 1);
+ outb(0x09, io);
+ x_oldid = inb(io + 1);
+
+ outb(key, io);
+ outb(key, io); /* Write Magic Sequence to EFER, extended
+ funtion enable register */
+ outb(0x20, io); /* Write EFIR, extended function index register */
+ devid = inb(io + 1); /* Read EFDR, extended function data register */
+ outb(0x21, io);
+ devrev = inb(io + 1);
+ outb(0x09, io);
+ oldid = inb(io + 1);
+ outb(0xaa, io); /* Magic Seal */
if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
goto out; /* protection against false positives */
- decode_winbond(io,key,devid,devrev,oldid);
+ decode_winbond(io, key, devid, devrev, oldid);
out:
release_region(io, 3);
}
-static void __devinit winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io, int key)
{
- int devid,devrev,oldid,x_devid,x_devrev,x_oldid;
+ int devid, devrev, oldid, x_devid, x_devrev, x_oldid;
if (!request_region(io, 3, __func__))
return;
/* First probe without the key */
- outb(0x20,io+2);
- x_devid=inb(io+2);
- outb(0x21,io+1);
- x_devrev=inb(io+2);
- outb(0x09,io+1);
- x_oldid=inb(io+2);
-
- outb(key,io); /* Write Magic Byte to EFER, extended
- funtion enable register */
- outb(0x20,io+2); /* Write EFIR, extended function index register */
- devid=inb(io+2); /* Read EFDR, extended function data register */
- outb(0x21,io+1);
- devrev=inb(io+2);
- outb(0x09,io+1);
- oldid=inb(io+2);
- outb(0xaa,io); /* Magic Seal */
-
- if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
+ outb(0x20, io + 2);
+ x_devid = inb(io + 2);
+ outb(0x21, io + 1);
+ x_devrev = inb(io + 2);
+ outb(0x09, io + 1);
+ x_oldid = inb(io + 2);
+
+ outb(key, io); /* Write Magic Byte to EFER, extended
+ funtion enable register */
+ outb(0x20, io + 2); /* Write EFIR, extended function index register */
+ devid = inb(io + 2); /* Read EFDR, extended function data register */
+ outb(0x21, io + 1);
+ devrev = inb(io + 2);
+ outb(0x09, io + 1);
+ oldid = inb(io + 2);
+ outb(0xaa, io); /* Magic Seal */
+
+ if (x_devid == devid && x_devrev == devrev && x_oldid == oldid)
goto out; /* protection against false positives */
- decode_winbond(io,key,devid,devrev,oldid);
+ decode_winbond(io, key, devid, devrev, oldid);
out:
release_region(io, 3);
}
static void __devinit smsc_check(int io, int key)
{
- int id,rev,oldid,oldrev,x_id,x_rev,x_oldid,x_oldrev;
+ int id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev;
if (!request_region(io, 3, __func__))
return;
/* First probe without the key */
- outb(0x0d,io);
- x_oldid=inb(io+1);
- outb(0x0e,io);
- x_oldrev=inb(io+1);
- outb(0x20,io);
- x_id=inb(io+1);
- outb(0x21,io);
- x_rev=inb(io+1);
-
- outb(key,io);
- outb(key,io); /* Write Magic Sequence to EFER, extended
- funtion enable register */
- outb(0x0d,io); /* Write EFIR, extended function index register */
- oldid=inb(io+1); /* Read EFDR, extended function data register */
- outb(0x0e,io);
- oldrev=inb(io+1);
- outb(0x20,io);
- id=inb(io+1);
- outb(0x21,io);
- rev=inb(io+1);
- outb(0xaa,io); /* Magic Seal */
-
- if ((x_id == id) && (x_oldrev == oldrev) &&
- (x_oldid == oldid) && (x_rev == rev))
+ outb(0x0d, io);
+ x_oldid = inb(io + 1);
+ outb(0x0e, io);
+ x_oldrev = inb(io + 1);
+ outb(0x20, io);
+ x_id = inb(io + 1);
+ outb(0x21, io);
+ x_rev = inb(io + 1);
+
+ outb(key, io);
+ outb(key, io); /* Write Magic Sequence to EFER, extended
+ funtion enable register */
+ outb(0x0d, io); /* Write EFIR, extended function index register */
+ oldid = inb(io + 1); /* Read EFDR, extended function data register */
+ outb(0x0e, io);
+ oldrev = inb(io + 1);
+ outb(0x20, io);
+ id = inb(io + 1);
+ outb(0x21, io);
+ rev = inb(io + 1);
+ outb(0xaa, io); /* Magic Seal */
+
+ if (x_id == id && x_oldrev == oldrev &&
+ x_oldid == oldid && x_rev == rev)
goto out; /* protection against false positives */
- decode_smsc(io,key,oldid,oldrev);
+ decode_smsc(io, key, oldid, oldrev);
out:
release_region(io, 3);
}
-static void __devinit detect_and_report_winbond (void)
-{
+static void __devinit detect_and_report_winbond(void)
+{
if (verbose_probing)
printk(KERN_DEBUG "Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
- winbond_check(0x3f0,0x87);
- winbond_check(0x370,0x87);
- winbond_check(0x2e ,0x87);
- winbond_check(0x4e ,0x87);
- winbond_check(0x3f0,0x86);
- winbond_check2(0x250,0x88);
- winbond_check2(0x250,0x89);
+ winbond_check(0x3f0, 0x87);
+ winbond_check(0x370, 0x87);
+ winbond_check(0x2e , 0x87);
+ winbond_check(0x4e , 0x87);
+ winbond_check(0x3f0, 0x86);
+ winbond_check2(0x250, 0x88);
+ winbond_check2(0x250, 0x89);
}
-static void __devinit detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc(void)
{
if (verbose_probing)
printk(KERN_DEBUG "SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
- smsc_check(0x3f0,0x55);
- smsc_check(0x370,0x55);
- smsc_check(0x3f0,0x44);
- smsc_check(0x370,0x44);
+ smsc_check(0x3f0, 0x55);
+ smsc_check(0x370, 0x55);
+ smsc_check(0x3f0, 0x44);
+ smsc_check(0x370, 0x44);
}
static void __devinit detect_and_report_it87(void)
@@ -1573,34 +1631,39 @@ static void __devinit detect_and_report_it87(void)
}
#endif /* CONFIG_PARPORT_PC_SUPERIO */
-static int get_superio_dma (struct parport *p)
+static struct superio_struct *find_superio(struct parport *p)
{
- int i=0;
- while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
- i++;
- if (i!=NR_SUPERIOS)
- return superios[i].dma;
+ int i;
+ for (i = 0; i < NR_SUPERIOS; i++)
+ if (superios[i].io != p->base)
+ return &superios[i];
+ return NULL;
+}
+
+static int get_superio_dma(struct parport *p)
+{
+ struct superio_struct *s = find_superio(p);
+ if (s)
+ return s->dma;
return PARPORT_DMA_NONE;
}
-static int get_superio_irq (struct parport *p)
+static int get_superio_irq(struct parport *p)
{
- int i=0;
- while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
- i++;
- if (i!=NR_SUPERIOS)
- return superios[i].irq;
- return PARPORT_IRQ_NONE;
+ struct superio_struct *s = find_superio(p);
+ if (s)
+ return s->irq;
+ return PARPORT_IRQ_NONE;
}
-
+
/* --- Mode detection ------------------------------------- */
/*
* Checks for port existence, all ports support SPP MODE
- * Returns:
+ * Returns:
* 0 : No parallel port at this address
- * PARPORT_MODE_PCSPP : SPP port detected
+ * PARPORT_MODE_PCSPP : SPP port detected
* (if the user specified an ioport himself,
* this shall always be the case!)
*
@@ -1610,7 +1673,7 @@ static int parport_SPP_supported(struct parport *pb)
unsigned char r, w;
/*
- * first clear an eventually pending EPP timeout
+ * first clear an eventually pending EPP timeout
* I (sailer@ife.ee.ethz.ch) have an SMSC chipset
* that does not even respond to SPP cycles if an EPP
* timeout is pending
@@ -1619,19 +1682,19 @@ static int parport_SPP_supported(struct parport *pb)
/* Do a simple read-write test to make sure the port exists. */
w = 0xc;
- outb (w, CONTROL (pb));
+ outb(w, CONTROL(pb));
/* Is there a control register that we can read from? Some
* ports don't allow reads, so read_control just returns a
* software copy. Some ports _do_ allow reads, so bypass the
* software copy here. In addition, some bits aren't
* writable. */
- r = inb (CONTROL (pb));
+ r = inb(CONTROL(pb));
if ((r & 0xf) == w) {
w = 0xe;
- outb (w, CONTROL (pb));
- r = inb (CONTROL (pb));
- outb (0xc, CONTROL (pb));
+ outb(w, CONTROL(pb));
+ r = inb(CONTROL(pb));
+ outb(0xc, CONTROL(pb));
if ((r & 0xf) == w)
return PARPORT_MODE_PCSPP;
}
@@ -1639,18 +1702,18 @@ static int parport_SPP_supported(struct parport *pb)
if (user_specified)
/* That didn't work, but the user thinks there's a
* port here. */
- printk (KERN_INFO "parport 0x%lx (WARNING): CTR: "
+ printk(KERN_INFO "parport 0x%lx (WARNING): CTR: "
"wrote 0x%02x, read 0x%02x\n", pb->base, w, r);
/* Try the data register. The data lines aren't tri-stated at
* this stage, so we expect back what we wrote. */
w = 0xaa;
- parport_pc_write_data (pb, w);
- r = parport_pc_read_data (pb);
+ parport_pc_write_data(pb, w);
+ r = parport_pc_read_data(pb);
if (r == w) {
w = 0x55;
- parport_pc_write_data (pb, w);
- r = parport_pc_read_data (pb);
+ parport_pc_write_data(pb, w);
+ r = parport_pc_read_data(pb);
if (r == w)
return PARPORT_MODE_PCSPP;
}
@@ -1658,9 +1721,9 @@ static int parport_SPP_supported(struct parport *pb)
if (user_specified) {
/* Didn't work, but the user is convinced this is the
* place. */
- printk (KERN_INFO "parport 0x%lx (WARNING): DATA: "
+ printk(KERN_INFO "parport 0x%lx (WARNING): DATA: "
"wrote 0x%02x, read 0x%02x\n", pb->base, w, r);
- printk (KERN_INFO "parport 0x%lx: You gave this address, "
+ printk(KERN_INFO "parport 0x%lx: You gave this address, "
"but there is probably no parallel port there!\n",
pb->base);
}
@@ -1691,33 +1754,33 @@ static int parport_ECR_present(struct parport *pb)
struct parport_pc_private *priv = pb->private_data;
unsigned char r = 0xc;
- outb (r, CONTROL (pb));
- if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) {
- outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */
+ outb(r, CONTROL(pb));
+ if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) {
+ outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */
- r = inb (CONTROL (pb));
- if ((inb (ECONTROL (pb)) & 0x2) == (r & 0x2))
+ r = inb(CONTROL(pb));
+ if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2))
goto no_reg; /* Sure that no ECR register exists */
}
-
- if ((inb (ECONTROL (pb)) & 0x3 ) != 0x1)
+
+ if ((inb(ECONTROL(pb)) & 0x3) != 0x1)
goto no_reg;
- ECR_WRITE (pb, 0x34);
- if (inb (ECONTROL (pb)) != 0x35)
+ ECR_WRITE(pb, 0x34);
+ if (inb(ECONTROL(pb)) != 0x35)
goto no_reg;
priv->ecr = 1;
- outb (0xc, CONTROL (pb));
-
+ outb(0xc, CONTROL(pb));
+
/* Go to mode 000 */
- frob_set_mode (pb, ECR_SPP);
+ frob_set_mode(pb, ECR_SPP);
return 1;
no_reg:
- outb (0xc, CONTROL (pb));
- return 0;
+ outb(0xc, CONTROL(pb));
+ return 0;
}
#ifdef CONFIG_PARPORT_1284
@@ -1727,7 +1790,7 @@ static int parport_ECR_present(struct parport *pb)
* allows us to read data from the data lines. In theory we would get back
* 0xff but any peripheral attached to the port may drag some or all of the
* lines down to zero. So if we get back anything that isn't the contents
- * of the data register we deem PS/2 support to be present.
+ * of the data register we deem PS/2 support to be present.
*
* Some SPP ports have "half PS/2" ability - you can't turn off the line
* drivers, but an external peripheral with sufficiently beefy drivers of
@@ -1735,26 +1798,28 @@ static int parport_ECR_present(struct parport *pb)
* where they can then be read back as normal. Ports with this property
* and the right type of device attached are likely to fail the SPP test,
* (as they will appear to have stuck bits) and so the fact that they might
- * be misdetected here is rather academic.
+ * be misdetected here is rather academic.
*/
static int parport_PS2_supported(struct parport *pb)
{
int ok = 0;
-
+
clear_epp_timeout(pb);
/* try to tri-state the buffer */
- parport_pc_data_reverse (pb);
-
+ parport_pc_data_reverse(pb);
+
parport_pc_write_data(pb, 0x55);
- if (parport_pc_read_data(pb) != 0x55) ok++;
+ if (parport_pc_read_data(pb) != 0x55)
+ ok++;
parport_pc_write_data(pb, 0xaa);
- if (parport_pc_read_data(pb) != 0xaa) ok++;
+ if (parport_pc_read_data(pb) != 0xaa)
+ ok++;
/* cancel input mode */
- parport_pc_data_forward (pb);
+ parport_pc_data_forward(pb);
if (ok) {
pb->modes |= PARPORT_MODE_TRISTATE;
@@ -1773,68 +1838,68 @@ static int parport_ECP_supported(struct parport *pb)
int config, configb;
int pword;
struct parport_pc_private *priv = pb->private_data;
- /* Translate ECP intrLine to ISA irq value */
- static const int intrline[]= { 0, 7, 9, 10, 11, 14, 15, 5 };
+ /* Translate ECP intrLine to ISA irq value */
+ static const int intrline[] = { 0, 7, 9, 10, 11, 14, 15, 5 };
/* If there is no ECR, we have no hope of supporting ECP. */
if (!priv->ecr)
return 0;
/* Find out FIFO depth */
- ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
- ECR_WRITE (pb, ECR_TST << 5); /* TEST FIFO */
- for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
- outb (0xaa, FIFO (pb));
+ ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
+ ECR_WRITE(pb, ECR_TST << 5); /* TEST FIFO */
+ for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02); i++)
+ outb(0xaa, FIFO(pb));
/*
* Using LGS chipset it uses ECR register, but
* it doesn't support ECP or FIFO MODE
*/
if (i == 1024) {
- ECR_WRITE (pb, ECR_SPP << 5);
+ ECR_WRITE(pb, ECR_SPP << 5);
return 0;
}
priv->fifo_depth = i;
if (verbose_probing)
- printk (KERN_DEBUG "0x%lx: FIFO is %d bytes\n", pb->base, i);
+ printk(KERN_DEBUG "0x%lx: FIFO is %d bytes\n", pb->base, i);
/* Find out writeIntrThreshold */
- frob_econtrol (pb, 1<<2, 1<<2);
- frob_econtrol (pb, 1<<2, 0);
+ frob_econtrol(pb, 1<<2, 1<<2);
+ frob_econtrol(pb, 1<<2, 0);
for (i = 1; i <= priv->fifo_depth; i++) {
- inb (FIFO (pb));
- udelay (50);
- if (inb (ECONTROL (pb)) & (1<<2))
+ inb(FIFO(pb));
+ udelay(50);
+ if (inb(ECONTROL(pb)) & (1<<2))
break;
}
if (i <= priv->fifo_depth) {
if (verbose_probing)
- printk (KERN_DEBUG "0x%lx: writeIntrThreshold is %d\n",
+ printk(KERN_DEBUG "0x%lx: writeIntrThreshold is %d\n",
pb->base, i);
} else
/* Number of bytes we know we can write if we get an
- interrupt. */
+ interrupt. */
i = 0;
priv->writeIntrThreshold = i;
/* Find out readIntrThreshold */
- frob_set_mode (pb, ECR_PS2); /* Reset FIFO and enable PS2 */
- parport_pc_data_reverse (pb); /* Must be in PS2 mode */
- frob_set_mode (pb, ECR_TST); /* Test FIFO */
- frob_econtrol (pb, 1<<2, 1<<2);
- frob_econtrol (pb, 1<<2, 0);
+ frob_set_mode(pb, ECR_PS2); /* Reset FIFO and enable PS2 */
+ parport_pc_data_reverse(pb); /* Must be in PS2 mode */
+ frob_set_mode(pb, ECR_TST); /* Test FIFO */
+ frob_econtrol(pb, 1<<2, 1<<2);
+ frob_econtrol(pb, 1<<2, 0);
for (i = 1; i <= priv->fifo_depth; i++) {
- outb (0xaa, FIFO (pb));
- if (inb (ECONTROL (pb)) & (1<<2))
+ outb(0xaa, FIFO(pb));
+ if (inb(ECONTROL(pb)) & (1<<2))
break;
}
if (i <= priv->fifo_depth) {
if (verbose_probing)
- printk (KERN_INFO "0x%lx: readIntrThreshold is %d\n",
+ printk(KERN_INFO "0x%lx: readIntrThreshold is %d\n",
pb->base, i);
} else
/* Number of bytes we can read if we get an interrupt. */
@@ -1842,23 +1907,23 @@ static int parport_ECP_supported(struct parport *pb)
priv->readIntrThreshold = i;
- ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
- ECR_WRITE (pb, 0xf4); /* Configuration mode */
- config = inb (CONFIGA (pb));
+ ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
+ ECR_WRITE(pb, 0xf4); /* Configuration mode */
+ config = inb(CONFIGA(pb));
pword = (config >> 4) & 0x7;
switch (pword) {
case 0:
pword = 2;
- printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+ printk(KERN_WARNING "0x%lx: Unsupported pword size!\n",
pb->base);
break;
case 2:
pword = 4;
- printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+ printk(KERN_WARNING "0x%lx: Unsupported pword size!\n",
pb->base);
break;
default:
- printk (KERN_WARNING "0x%lx: Unknown implementation ID\n",
+ printk(KERN_WARNING "0x%lx: Unknown implementation ID\n",
pb->base);
/* Assume 1 */
case 1:
@@ -1867,28 +1932,29 @@ static int parport_ECP_supported(struct parport *pb)
priv->pword = pword;
if (verbose_probing) {
- printk (KERN_DEBUG "0x%lx: PWord is %d bits\n", pb->base, 8 * pword);
-
- printk (KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
+ printk(KERN_DEBUG "0x%lx: PWord is %d bits\n",
+ pb->base, 8 * pword);
+
+ printk(KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
config & 0x80 ? "Level" : "Pulses");
- configb = inb (CONFIGB (pb));
- printk (KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
+ configb = inb(CONFIGB(pb));
+ printk(KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
pb->base, config, configb);
- printk (KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
- if ((configb >>3) & 0x07)
- printk("%d",intrline[(configb >>3) & 0x07]);
+ printk(KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
+ if ((configb >> 3) & 0x07)
+ printk("%d", intrline[(configb >> 3) & 0x07]);
else
printk("<none or set by other means>");
- printk (" dma=");
- if( (configb & 0x03 ) == 0x00)
+ printk(" dma=");
+ if ((configb & 0x03) == 0x00)
printk("<none or set by other means>\n");
else
- printk("%d\n",configb & 0x07);
+ printk("%d\n", configb & 0x07);
}
/* Go back to mode 000 */
- frob_set_mode (pb, ECR_SPP);
+ frob_set_mode(pb, ECR_SPP);
return 1;
}
@@ -1903,10 +1969,10 @@ static int parport_ECPPS2_supported(struct parport *pb)
if (!priv->ecr)
return 0;
- oecr = inb (ECONTROL (pb));
- ECR_WRITE (pb, ECR_PS2 << 5);
+ oecr = inb(ECONTROL(pb));
+ ECR_WRITE(pb, ECR_PS2 << 5);
result = parport_PS2_supported(pb);
- ECR_WRITE (pb, oecr);
+ ECR_WRITE(pb, oecr);
return result;
}
@@ -1930,16 +1996,15 @@ static int parport_EPP_supported(struct parport *pb)
*/
/* If EPP timeout bit clear then EPP available */
- if (!clear_epp_timeout(pb)) {
+ if (!clear_epp_timeout(pb))
return 0; /* No way to clear timeout */
- }
/* Check for Intel bug. */
if (priv->ecr) {
unsigned char i;
for (i = 0x00; i < 0x80; i += 0x20) {
- ECR_WRITE (pb, i);
- if (clear_epp_timeout (pb)) {
+ ECR_WRITE(pb, i);
+ if (clear_epp_timeout(pb)) {
/* Phony EPP in ECP. */
return 0;
}
@@ -1963,17 +2028,16 @@ static int parport_ECPEPP_supported(struct parport *pb)
int result;
unsigned char oecr;
- if (!priv->ecr) {
+ if (!priv->ecr)
return 0;
- }
- oecr = inb (ECONTROL (pb));
+ oecr = inb(ECONTROL(pb));
/* Search for SMC style EPP+ECP mode */
- ECR_WRITE (pb, 0x80);
- outb (0x04, CONTROL (pb));
+ ECR_WRITE(pb, 0x80);
+ outb(0x04, CONTROL(pb));
result = parport_EPP_supported(pb);
- ECR_WRITE (pb, oecr);
+ ECR_WRITE(pb, oecr);
if (result) {
/* Set up access functions to use ECP+EPP hardware. */
@@ -1991,11 +2055,25 @@ static int parport_ECPEPP_supported(struct parport *pb)
/* Don't bother probing for modes we know we won't use. */
static int __devinit parport_PS2_supported(struct parport *pb) { return 0; }
#ifdef CONFIG_PARPORT_PC_FIFO
-static int parport_ECP_supported(struct parport *pb) { return 0; }
+static int parport_ECP_supported(struct parport *pb)
+{
+ return 0;
+}
#endif
-static int __devinit parport_EPP_supported(struct parport *pb) { return 0; }
-static int __devinit parport_ECPEPP_supported(struct parport *pb){return 0;}
-static int __devinit parport_ECPPS2_supported(struct parport *pb){return 0;}
+static int __devinit parport_EPP_supported(struct parport *pb)
+{
+ return 0;
+}
+
+static int __devinit parport_ECPEPP_supported(struct parport *pb)
+{
+ return 0;
+}
+
+static int __devinit parport_ECPPS2_supported(struct parport *pb)
+{
+ return 0;
+}
#endif /* No IEEE 1284 support */
@@ -2005,17 +2083,17 @@ static int __devinit parport_ECPPS2_supported(struct parport *pb){return 0;}
static int programmable_irq_support(struct parport *pb)
{
int irq, intrLine;
- unsigned char oecr = inb (ECONTROL (pb));
+ unsigned char oecr = inb(ECONTROL(pb));
static const int lookup[8] = {
PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
};
- ECR_WRITE (pb, ECR_CNF << 5); /* Configuration MODE */
+ ECR_WRITE(pb, ECR_CNF << 5); /* Configuration MODE */
- intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
+ intrLine = (inb(CONFIGB(pb)) >> 3) & 0x07;
irq = lookup[intrLine];
- ECR_WRITE (pb, oecr);
+ ECR_WRITE(pb, oecr);
return irq;
}
@@ -2025,17 +2103,17 @@ static int irq_probe_ECP(struct parport *pb)
unsigned long irqs;
irqs = probe_irq_on();
-
- ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
- ECR_WRITE (pb, (ECR_TST << 5) | 0x04);
- ECR_WRITE (pb, ECR_TST << 5);
+
+ ECR_WRITE(pb, ECR_SPP << 5); /* Reset FIFO */
+ ECR_WRITE(pb, (ECR_TST << 5) | 0x04);
+ ECR_WRITE(pb, ECR_TST << 5);
/* If Full FIFO sure that writeIntrThreshold is generated */
- for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02) ; i++)
- outb (0xaa, FIFO (pb));
-
+ for (i = 0; i < 1024 && !(inb(ECONTROL(pb)) & 0x02) ; i++)
+ outb(0xaa, FIFO(pb));
+
pb->irq = probe_irq_off(irqs);
- ECR_WRITE (pb, ECR_SPP << 5);
+ ECR_WRITE(pb, ECR_SPP << 5);
if (pb->irq <= 0)
pb->irq = PARPORT_IRQ_NONE;
@@ -2045,7 +2123,7 @@ static int irq_probe_ECP(struct parport *pb)
/*
* This detection seems that only works in National Semiconductors
- * This doesn't work in SMC, LGS, and Winbond
+ * This doesn't work in SMC, LGS, and Winbond
*/
static int irq_probe_EPP(struct parport *pb)
{
@@ -2056,16 +2134,16 @@ static int irq_probe_EPP(struct parport *pb)
unsigned char oecr;
if (pb->modes & PARPORT_MODE_PCECR)
- oecr = inb (ECONTROL (pb));
+ oecr = inb(ECONTROL(pb));
irqs = probe_irq_on();
if (pb->modes & PARPORT_MODE_PCECR)
- frob_econtrol (pb, 0x10, 0x10);
-
+ frob_econtrol(pb, 0x10, 0x10);
+
clear_epp_timeout(pb);
- parport_pc_frob_control (pb, 0x20, 0x20);
- parport_pc_frob_control (pb, 0x10, 0x10);
+ parport_pc_frob_control(pb, 0x20, 0x20);
+ parport_pc_frob_control(pb, 0x10, 0x10);
clear_epp_timeout(pb);
/* Device isn't expecting an EPP read
@@ -2074,9 +2152,9 @@ static int irq_probe_EPP(struct parport *pb)
parport_pc_read_epp(pb);
udelay(20);
- pb->irq = probe_irq_off (irqs);
+ pb->irq = probe_irq_off(irqs);
if (pb->modes & PARPORT_MODE_PCECR)
- ECR_WRITE (pb, oecr);
+ ECR_WRITE(pb, oecr);
parport_pc_write_control(pb, 0xc);
if (pb->irq <= 0)
@@ -2133,28 +2211,28 @@ static int parport_irq_probe(struct parport *pb)
/* --- DMA detection -------------------------------------- */
/* Only if chipset conforms to ECP ISA Interface Standard */
-static int programmable_dma_support (struct parport *p)
+static int programmable_dma_support(struct parport *p)
{
- unsigned char oecr = inb (ECONTROL (p));
+ unsigned char oecr = inb(ECONTROL(p));
int dma;
- frob_set_mode (p, ECR_CNF);
-
- dma = inb (CONFIGB(p)) & 0x07;
+ frob_set_mode(p, ECR_CNF);
+
+ dma = inb(CONFIGB(p)) & 0x07;
/* 000: Indicates jumpered 8-bit DMA if read-only.
100: Indicates jumpered 16-bit DMA if read-only. */
if ((dma & 0x03) == 0)
dma = PARPORT_DMA_NONE;
- ECR_WRITE (p, oecr);
+ ECR_WRITE(p, oecr);
return dma;
}
-static int parport_dma_probe (struct parport *p)
+static int parport_dma_probe(struct parport *p)
{
const struct parport_pc_private *priv = p->private_data;
- if (priv->ecr)
- p->dma = programmable_dma_support(p); /* ask ECP chipset first */
+ if (priv->ecr) /* ask ECP chipset first */
+ p->dma = programmable_dma_support(p);
if (p->dma == PARPORT_DMA_NONE) {
/* ask known Super-IO chips proper, although these
claim ECP compatible, some don't report their DMA
@@ -2212,7 +2290,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
if (!base_res)
goto out4;
- memcpy(ops, &parport_pc_ops, sizeof (struct parport_operations));
+ memcpy(ops, &parport_pc_ops, sizeof(struct parport_operations));
priv->ctr = 0xc;
priv->ctr_writable = ~0x10;
priv->ecr = 0;
@@ -2239,7 +2317,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
if (!parport_EPP_supported(p))
parport_ECPEPP_supported(p);
}
- if (!parport_SPP_supported (p))
+ if (!parport_SPP_supported(p))
/* No port. */
goto out5;
if (priv->ecr)
@@ -2247,7 +2325,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
else
parport_PS2_supported(p);
- p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
+ p->size = (p->modes & PARPORT_MODE_EPP) ? 8 : 3;
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
if (p->base_hi && priv->ecr)
@@ -2271,7 +2349,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
}
}
if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
- is mandatory (see above) */
+ is mandatory (see above) */
p->dma = PARPORT_DMA_NONE;
#ifdef CONFIG_PARPORT_PC_FIFO
@@ -2288,16 +2366,23 @@ struct parport *parport_pc_probe_port(unsigned long int base,
if (p->dma != PARPORT_DMA_NONE) {
printk(", dma %d", p->dma);
p->modes |= PARPORT_MODE_DMA;
- }
- else printk(", using FIFO");
- }
- else
+ } else
+ printk(", using FIFO");
+ } else
/* We can't use the DMA channel after all. */
p->dma = PARPORT_DMA_NONE;
#endif /* Allowed to use FIFO/DMA */
printk(" [");
-#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+
+#define printmode(x) \
+ {\
+ if (p->modes & PARPORT_MODE_##x) {\
+ printk("%s%s", f ? "," : "", #x);\
+ f++;\
+ } \
+ }
+
{
int f = 0;
printmode(PCSPP);
@@ -2309,10 +2394,10 @@ struct parport *parport_pc_probe_port(unsigned long int base,
}
#undef printmode
#ifndef CONFIG_PARPORT_1284
- printk ("(,...)");
+ printk("(,...)");
#endif /* CONFIG_PARPORT_1284 */
printk("]\n");
- if (probedirq != PARPORT_IRQ_NONE)
+ if (probedirq != PARPORT_IRQ_NONE)
printk(KERN_INFO "%s: irq %d detected\n", p->name, probedirq);
/* If No ECP release the ports grabbed above. */
@@ -2328,7 +2413,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
if (p->irq != PARPORT_IRQ_NONE) {
if (request_irq(p->irq, parport_irq_handler,
irqflags, p->name, p)) {
- printk (KERN_WARNING "%s: irq %d in use, "
+ printk(KERN_WARNING "%s: irq %d in use, "
"resorting to polled operation\n",
p->name, p->irq);
p->irq = PARPORT_IRQ_NONE;
@@ -2338,8 +2423,8 @@ struct parport *parport_pc_probe_port(unsigned long int base,
#ifdef CONFIG_PARPORT_PC_FIFO
#ifdef HAS_DMA
if (p->dma != PARPORT_DMA_NONE) {
- if (request_dma (p->dma, p->name)) {
- printk (KERN_WARNING "%s: dma %d in use, "
+ if (request_dma(p->dma, p->name)) {
+ printk(KERN_WARNING "%s: dma %d in use, "
"resorting to PIO operation\n",
p->name, p->dma);
p->dma = PARPORT_DMA_NONE;
@@ -2349,8 +2434,8 @@ struct parport *parport_pc_probe_port(unsigned long int base,
PAGE_SIZE,
&priv->dma_handle,
GFP_KERNEL);
- if (! priv->dma_buf) {
- printk (KERN_WARNING "%s: "
+ if (!priv->dma_buf) {
+ printk(KERN_WARNING "%s: "
"cannot get buffer for DMA, "
"resorting to PIO operation\n",
p->name);
@@ -2369,10 +2454,10 @@ struct parport *parport_pc_probe_port(unsigned long int base,
* Put the ECP detected port in PS2 mode.
* Do this also for ports that have ECR but don't do ECP.
*/
- ECR_WRITE (p, 0x34);
+ ECR_WRITE(p, 0x34);
parport_pc_write_data(p, 0);
- parport_pc_data_forward (p);
+ parport_pc_data_forward(p);
/* Now that we've told the sharing engine about the port, and
found out its characteristics, let the high-level drivers
@@ -2380,7 +2465,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
spin_lock(&ports_lock);
list_add(&priv->list, &ports_list);
spin_unlock(&ports_lock);
- parport_announce_port (p);
+ parport_announce_port(p);
return p;
@@ -2393,18 +2478,17 @@ out5:
out4:
parport_put_port(p);
out3:
- kfree (priv);
+ kfree(priv);
out2:
- kfree (ops);
+ kfree(ops);
out1:
if (pdev)
platform_device_unregister(pdev);
return NULL;
}
+EXPORT_SYMBOL(parport_pc_probe_port);
-EXPORT_SYMBOL (parport_pc_probe_port);
-
-void parport_pc_unregister_port (struct parport *p)
+void parport_pc_unregister_port(struct parport *p)
{
struct parport_pc_private *priv = p->private_data;
struct parport_operations *ops = p->ops;
@@ -2430,17 +2514,16 @@ void parport_pc_unregister_port (struct parport *p)
priv->dma_buf,
priv->dma_handle);
#endif
- kfree (p->private_data);
+ kfree(p->private_data);
parport_put_port(p);
- kfree (ops); /* hope no-one cached it */
+ kfree(ops); /* hope no-one cached it */
}
-
-EXPORT_SYMBOL (parport_pc_unregister_port);
+EXPORT_SYMBOL(parport_pc_unregister_port);
#ifdef CONFIG_PCI
/* ITE support maintained by Rich Liu <richliu@poorman.org> */
-static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq,
+static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq,
int autodma,
const struct parport_pc_via_data *via)
{
@@ -2452,73 +2535,74 @@ static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq,
int irq;
int i;
- DPRINTK (KERN_DEBUG "sio_ite_8872_probe()\n");
-
- // make sure which one chip
- for(i = 0; i < 5; i++) {
+ DPRINTK(KERN_DEBUG "sio_ite_8872_probe()\n");
+
+ /* make sure which one chip */
+ for (i = 0; i < 5; i++) {
base_res = request_region(inta_addr[i], 32, "it887x");
if (base_res) {
int test;
- pci_write_config_dword (pdev, 0x60,
+ pci_write_config_dword(pdev, 0x60,
0xe5000000 | inta_addr[i]);
- pci_write_config_dword (pdev, 0x78,
+ pci_write_config_dword(pdev, 0x78,
0x00000000 | inta_addr[i]);
- test = inb (inta_addr[i]);
- if (test != 0xff) break;
+ test = inb(inta_addr[i]);
+ if (test != 0xff)
+ break;
release_region(inta_addr[i], 0x8);
}
}
- if(i >= 5) {
- printk (KERN_INFO "parport_pc: cannot find ITE8872 INTA\n");
+ if (i >= 5) {
+ printk(KERN_INFO "parport_pc: cannot find ITE8872 INTA\n");
return 0;
}
- type = inb (inta_addr[i] + 0x18);
+ type = inb(inta_addr[i] + 0x18);
type &= 0x0f;
switch (type) {
case 0x2:
- printk (KERN_INFO "parport_pc: ITE8871 found (1P)\n");
+ printk(KERN_INFO "parport_pc: ITE8871 found (1P)\n");
ite8872set = 0x64200000;
break;
case 0xa:
- printk (KERN_INFO "parport_pc: ITE8875 found (1P)\n");
+ printk(KERN_INFO "parport_pc: ITE8875 found (1P)\n");
ite8872set = 0x64200000;
break;
case 0xe:
- printk (KERN_INFO "parport_pc: ITE8872 found (2S1P)\n");
+ printk(KERN_INFO "parport_pc: ITE8872 found (2S1P)\n");
ite8872set = 0x64e00000;
break;
case 0x6:
- printk (KERN_INFO "parport_pc: ITE8873 found (1S)\n");
+ printk(KERN_INFO "parport_pc: ITE8873 found (1S)\n");
return 0;
case 0x8:
- DPRINTK (KERN_DEBUG "parport_pc: ITE8874 found (2S)\n");
+ DPRINTK(KERN_DEBUG "parport_pc: ITE8874 found (2S)\n");
return 0;
default:
- printk (KERN_INFO "parport_pc: unknown ITE887x\n");
- printk (KERN_INFO "parport_pc: please mail 'lspci -nvv' "
+ printk(KERN_INFO "parport_pc: unknown ITE887x\n");
+ printk(KERN_INFO "parport_pc: please mail 'lspci -nvv' "
"output to Rich.Liu@ite.com.tw\n");
return 0;
}
- pci_read_config_byte (pdev, 0x3c, &ite8872_irq);
- pci_read_config_dword (pdev, 0x1c, &ite8872_lpt);
+ pci_read_config_byte(pdev, 0x3c, &ite8872_irq);
+ pci_read_config_dword(pdev, 0x1c, &ite8872_lpt);
ite8872_lpt &= 0x0000ff00;
- pci_read_config_dword (pdev, 0x20, &ite8872_lpthi);
+ pci_read_config_dword(pdev, 0x20, &ite8872_lpthi);
ite8872_lpthi &= 0x0000ff00;
- pci_write_config_dword (pdev, 0x6c, 0xe3000000 | ite8872_lpt);
- pci_write_config_dword (pdev, 0x70, 0xe3000000 | ite8872_lpthi);
- pci_write_config_dword (pdev, 0x80, (ite8872_lpthi<<16) | ite8872_lpt);
- // SET SPP&EPP , Parallel Port NO DMA , Enable All Function
- // SET Parallel IRQ
- pci_write_config_dword (pdev, 0x9c,
+ pci_write_config_dword(pdev, 0x6c, 0xe3000000 | ite8872_lpt);
+ pci_write_config_dword(pdev, 0x70, 0xe3000000 | ite8872_lpthi);
+ pci_write_config_dword(pdev, 0x80, (ite8872_lpthi<<16) | ite8872_lpt);
+ /* SET SPP&EPP , Parallel Port NO DMA , Enable All Function */
+ /* SET Parallel IRQ */
+ pci_write_config_dword(pdev, 0x9c,
ite8872set | (ite8872_irq * 0x11111));
- DPRINTK (KERN_DEBUG "ITE887x: The IRQ is %d.\n", ite8872_irq);
- DPRINTK (KERN_DEBUG "ITE887x: The PARALLEL I/O port is 0x%x.\n",
+ DPRINTK(KERN_DEBUG "ITE887x: The IRQ is %d.\n", ite8872_irq);
+ DPRINTK(KERN_DEBUG "ITE887x: The PARALLEL I/O port is 0x%x.\n",
ite8872_lpt);
- DPRINTK (KERN_DEBUG "ITE887x: The PARALLEL I/O porthi is 0x%x.\n",
+ DPRINTK(KERN_DEBUG "ITE887x: The PARALLEL I/O porthi is 0x%x.\n",
ite8872_lpthi);
/* Let the user (or defaults) steer us away from interrupts */
@@ -2530,14 +2614,14 @@ static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq,
* Release the resource so that parport_pc_probe_port can get it.
*/
release_resource(base_res);
- if (parport_pc_probe_port (ite8872_lpt, ite8872_lpthi,
+ if (parport_pc_probe_port(ite8872_lpt, ite8872_lpthi,
irq, PARPORT_DMA_NONE, &pdev->dev, 0)) {
- printk (KERN_INFO
+ printk(KERN_INFO
"parport_pc: ITE 8872 parallel port: io=0x%X",
- ite8872_lpt);
+ ite8872_lpt);
if (irq != PARPORT_IRQ_NONE)
- printk (", irq=%d", irq);
- printk ("\n");
+ printk(", irq=%d", irq);
+ printk("\n");
return 1;
}
@@ -2546,7 +2630,7 @@ static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq,
/* VIA 8231 support by Pavel Fedin <sonic_amiga@rambler.ru>
based on VIA 686a support code by Jeff Garzik <jgarzik@pobox.com> */
-static int __devinitdata parport_init_mode = 0;
+static int __devinitdata parport_init_mode;
/* Data for two known VIA chips */
static struct parport_pc_via_data via_686a_data __devinitdata = {
@@ -2568,7 +2652,7 @@ static struct parport_pc_via_data via_8231_data __devinitdata = {
0xF6
};
-static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
+static int __devinit sio_via_probe(struct pci_dev *pdev, int autoirq,
int autodma,
const struct parport_pc_via_data *via)
{
@@ -2580,38 +2664,38 @@ static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
printk(KERN_DEBUG "parport_pc: VIA 686A/8231 detected\n");
- switch(parport_init_mode)
- {
+ switch (parport_init_mode) {
case 1:
- printk(KERN_DEBUG "parport_pc: setting SPP mode\n");
- siofunc = VIA_FUNCTION_PARPORT_SPP;
- break;
+ printk(KERN_DEBUG "parport_pc: setting SPP mode\n");
+ siofunc = VIA_FUNCTION_PARPORT_SPP;
+ break;
case 2:
- printk(KERN_DEBUG "parport_pc: setting PS/2 mode\n");
- siofunc = VIA_FUNCTION_PARPORT_SPP;
- ppcontrol = VIA_PARPORT_BIDIR;
- break;
+ printk(KERN_DEBUG "parport_pc: setting PS/2 mode\n");
+ siofunc = VIA_FUNCTION_PARPORT_SPP;
+ ppcontrol = VIA_PARPORT_BIDIR;
+ break;
case 3:
- printk(KERN_DEBUG "parport_pc: setting EPP mode\n");
- siofunc = VIA_FUNCTION_PARPORT_EPP;
- ppcontrol = VIA_PARPORT_BIDIR;
- have_epp = 1;
- break;
+ printk(KERN_DEBUG "parport_pc: setting EPP mode\n");
+ siofunc = VIA_FUNCTION_PARPORT_EPP;
+ ppcontrol = VIA_PARPORT_BIDIR;
+ have_epp = 1;
+ break;
case 4:
- printk(KERN_DEBUG "parport_pc: setting ECP mode\n");
- siofunc = VIA_FUNCTION_PARPORT_ECP;
- ppcontrol = VIA_PARPORT_BIDIR;
- break;
+ printk(KERN_DEBUG "parport_pc: setting ECP mode\n");
+ siofunc = VIA_FUNCTION_PARPORT_ECP;
+ ppcontrol = VIA_PARPORT_BIDIR;
+ break;
case 5:
- printk(KERN_DEBUG "parport_pc: setting EPP+ECP mode\n");
- siofunc = VIA_FUNCTION_PARPORT_ECP;
- ppcontrol = VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP;
- have_epp = 1;
- break;
- default:
- printk(KERN_DEBUG "parport_pc: probing current configuration\n");
- siofunc = VIA_FUNCTION_PROBE;
- break;
+ printk(KERN_DEBUG "parport_pc: setting EPP+ECP mode\n");
+ siofunc = VIA_FUNCTION_PARPORT_ECP;
+ ppcontrol = VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP;
+ have_epp = 1;
+ break;
+ default:
+ printk(KERN_DEBUG
+ "parport_pc: probing current configuration\n");
+ siofunc = VIA_FUNCTION_PROBE;
+ break;
}
/*
* unlock super i/o configuration
@@ -2622,38 +2706,36 @@ static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
/* Bits 1-0: Parallel Port Mode / Enable */
outb(via->viacfg_function, VIA_CONFIG_INDEX);
- tmp = inb (VIA_CONFIG_DATA);
+ tmp = inb(VIA_CONFIG_DATA);
/* Bit 5: EPP+ECP enable; bit 7: PS/2 bidirectional port enable */
outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
- tmp2 = inb (VIA_CONFIG_DATA);
- if (siofunc == VIA_FUNCTION_PROBE)
- {
- siofunc = tmp & VIA_FUNCTION_PARPORT_DISABLE;
- ppcontrol = tmp2;
+ tmp2 = inb(VIA_CONFIG_DATA);
+ if (siofunc == VIA_FUNCTION_PROBE) {
+ siofunc = tmp & VIA_FUNCTION_PARPORT_DISABLE;
+ ppcontrol = tmp2;
+ } else {
+ tmp &= ~VIA_FUNCTION_PARPORT_DISABLE;
+ tmp |= siofunc;
+ outb(via->viacfg_function, VIA_CONFIG_INDEX);
+ outb(tmp, VIA_CONFIG_DATA);
+ tmp2 &= ~(VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP);
+ tmp2 |= ppcontrol;
+ outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
+ outb(tmp2, VIA_CONFIG_DATA);
}
- else
- {
- tmp &= ~VIA_FUNCTION_PARPORT_DISABLE;
- tmp |= siofunc;
- outb(via->viacfg_function, VIA_CONFIG_INDEX);
- outb(tmp, VIA_CONFIG_DATA);
- tmp2 &= ~(VIA_PARPORT_BIDIR|VIA_PARPORT_ECPEPP);
- tmp2 |= ppcontrol;
- outb(via->viacfg_parport_control, VIA_CONFIG_INDEX);
- outb(tmp2, VIA_CONFIG_DATA);
- }
-
+
/* Parallel Port I/O Base Address, bits 9-2 */
outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
port1 = inb(VIA_CONFIG_DATA) << 2;
-
- printk (KERN_DEBUG "parport_pc: Current parallel port base: 0x%X\n",port1);
- if ((port1 == 0x3BC) && have_epp)
- {
- outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
- outb((0x378 >> 2), VIA_CONFIG_DATA);
- printk(KERN_DEBUG "parport_pc: Parallel port base changed to 0x378\n");
- port1 = 0x378;
+
+ printk(KERN_DEBUG "parport_pc: Current parallel port base: 0x%X\n",
+ port1);
+ if (port1 == 0x3BC && have_epp) {
+ outb(via->viacfg_parport_base, VIA_CONFIG_INDEX);
+ outb((0x378 >> 2), VIA_CONFIG_DATA);
+ printk(KERN_DEBUG
+ "parport_pc: Parallel port base changed to 0x378\n");
+ port1 = 0x378;
}
/*
@@ -2667,36 +2749,39 @@ static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
printk(KERN_INFO "parport_pc: VIA parallel port disabled in BIOS\n");
return 0;
}
-
+
/* Bits 7-4: PnP Routing for Parallel Port IRQ */
pci_read_config_byte(pdev, via->via_pci_parport_irq_reg, &tmp);
irq = ((tmp & VIA_IRQCONTROL_PARALLEL) >> 4);
- if (siofunc == VIA_FUNCTION_PARPORT_ECP)
- {
- /* Bits 3-2: PnP Routing for Parallel Port DMA */
- pci_read_config_byte(pdev, via->via_pci_parport_dma_reg, &tmp);
- dma = ((tmp & VIA_DMACONTROL_PARALLEL) >> 2);
- }
- else
- /* if ECP not enabled, DMA is not enabled, assumed bogus 'dma' value */
- dma = PARPORT_DMA_NONE;
+ if (siofunc == VIA_FUNCTION_PARPORT_ECP) {
+ /* Bits 3-2: PnP Routing for Parallel Port DMA */
+ pci_read_config_byte(pdev, via->via_pci_parport_dma_reg, &tmp);
+ dma = ((tmp & VIA_DMACONTROL_PARALLEL) >> 2);
+ } else
+ /* if ECP not enabled, DMA is not enabled, assumed
+ bogus 'dma' value */
+ dma = PARPORT_DMA_NONE;
/* Let the user (or defaults) steer us away from interrupts and DMA */
if (autoirq == PARPORT_IRQ_NONE) {
- irq = PARPORT_IRQ_NONE;
- dma = PARPORT_DMA_NONE;
+ irq = PARPORT_IRQ_NONE;
+ dma = PARPORT_DMA_NONE;
}
if (autodma == PARPORT_DMA_NONE)
- dma = PARPORT_DMA_NONE;
+ dma = PARPORT_DMA_NONE;
switch (port1) {
- case 0x3bc: port2 = 0x7bc; break;
- case 0x378: port2 = 0x778; break;
- case 0x278: port2 = 0x678; break;
+ case 0x3bc:
+ port2 = 0x7bc; break;
+ case 0x378:
+ port2 = 0x778; break;
+ case 0x278:
+ port2 = 0x678; break;
default:
- printk(KERN_INFO "parport_pc: Weird VIA parport base 0x%X, ignoring\n",
- port1);
+ printk(KERN_INFO
+ "parport_pc: Weird VIA parport base 0x%X, ignoring\n",
+ port1);
return 0;
}
@@ -2714,17 +2799,17 @@ static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
}
/* finally, do the probe with values obtained */
- if (parport_pc_probe_port (port1, port2, irq, dma, &pdev->dev, 0)) {
- printk (KERN_INFO
+ if (parport_pc_probe_port(port1, port2, irq, dma, &pdev->dev, 0)) {
+ printk(KERN_INFO
"parport_pc: VIA parallel port: io=0x%X", port1);
if (irq != PARPORT_IRQ_NONE)
- printk (", irq=%d", irq);
+ printk(", irq=%d", irq);
if (dma != PARPORT_DMA_NONE)
- printk (", dma=%d", dma);
- printk ("\n");
+ printk(", dma=%d", dma);
+ printk("\n");
return 1;
}
-
+
printk(KERN_WARNING "parport_pc: Strange, can't probe VIA parallel port: io=0x%X, irq=%d, dma=%d\n",
port1, irq, dma);
return 0;
@@ -2732,8 +2817,8 @@ static int __devinit sio_via_probe (struct pci_dev *pdev, int autoirq,
enum parport_pc_sio_types {
- sio_via_686a = 0, /* Via VT82C686A motherboard Super I/O */
- sio_via_8231, /* Via VT8231 south bridge integrated Super IO */
+ sio_via_686a = 0, /* Via VT82C686A motherboard Super I/O */
+ sio_via_8231, /* Via VT8231 south bridge integrated Super IO */
sio_ite_8872,
last_sio
};
@@ -2804,15 +2889,15 @@ enum parport_pc_pci_cards {
};
-/* each element directly indexed from enum list, above
+/* each element directly indexed from enum list, above
* (but offset by last_sio) */
static struct parport_pc_pci {
int numports;
struct { /* BAR (base address registers) numbers in the config
- space header */
+ space header */
int lo;
- int hi; /* -1 if not there, >6 for offset-method (max
- BAR is 6) */
+ int hi;
+ /* -1 if not there, >6 for offset-method (max BAR is 6) */
} addr[4];
/* If set, this is called immediately after pci_enable_device.
@@ -2857,7 +2942,7 @@ static struct parport_pc_pci {
/* timedia_4018 */ { 2, { { 0, 1 }, { 2, 3 }, } },
/* timedia_9018a */ { 2, { { 0, 1 }, { 2, 3 }, } },
/* SYBA uses fixed offsets in
- a 1K io window */
+ a 1K io window */
/* syba_2p_epp AP138B */ { 2, { { 0, 0x078 }, { 0, 0x178 }, } },
/* syba_1p_ecp W83787 */ { 1, { { 0, 0x078 }, } },
/* titan_010l */ { 1, { { 3, -1 }, } },
@@ -2873,11 +2958,14 @@ static struct parport_pc_pci {
/* oxsemi_pcie_pport */ { 1, { { 0, 1 }, } },
/* aks_0100 */ { 1, { { 0, -1 }, } },
/* mobility_pp */ { 1, { { 0, 1 }, } },
- /* netmos_9705 */ { 1, { { 0, -1 }, } }, /* untested */
- /* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */
- /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */
- /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */
- /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */
+
+ /* The netmos entries below are untested */
+ /* netmos_9705 */ { 1, { { 0, -1 }, } },
+ /* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} },
+ /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} },
+ /* netmos_9805 */ { 1, { { 0, -1 }, } },
+ /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } },
+
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
};
@@ -2906,7 +2994,7 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
- PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0,0, plx_9050 },
+ PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 },
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
{ 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
{ 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
@@ -2940,7 +3028,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
{ 0x9710, 0x9805, 0x1000, 0x0010, 0, 0, titan_1284p1 },
{ 0x9710, 0x9815, 0x1000, 0x0020, 0, 0, titan_1284p2 },
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
- { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p}, /* AFAVLAB_TK9902 */
+ /* AFAVLAB_TK9902 */
+ { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p},
{ 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p},
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 },
@@ -2983,14 +3072,14 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
{ 0, } /* terminate list */
};
-MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl);
+MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
struct pci_parport_data {
int num;
struct parport *ports[2];
};
-static int parport_pc_pci_probe (struct pci_dev *dev,
+static int parport_pc_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int err, count, n, i = id->driver_data;
@@ -3003,7 +3092,8 @@ static int parport_pc_pci_probe (struct pci_dev *dev,
/* This is a PCI card */
i -= last_sio;
count = 0;
- if ((err = pci_enable_device (dev)) != 0)
+ err = pci_enable_device(dev);
+ if (err)
return err;
data = kmalloc(sizeof(struct pci_parport_data), GFP_KERNEL);
@@ -3011,7 +3101,7 @@ static int parport_pc_pci_probe (struct pci_dev *dev,
return -ENOMEM;
if (cards[i].preinit_hook &&
- cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) {
+ cards[i].preinit_hook(dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) {
kfree(data);
return -ENODEV;
}
@@ -3021,25 +3111,25 @@ static int parport_pc_pci_probe (struct pci_dev *dev,
int hi = cards[i].addr[n].hi;
int irq;
unsigned long io_lo, io_hi;
- io_lo = pci_resource_start (dev, lo);
+ io_lo = pci_resource_start(dev, lo);
io_hi = 0;
if ((hi >= 0) && (hi <= 6))
- io_hi = pci_resource_start (dev, hi);
+ io_hi = pci_resource_start(dev, hi);
else if (hi > 6)
io_lo += hi; /* Reinterpret the meaning of
- "hi" as an offset (see SYBA
- def.) */
+ "hi" as an offset (see SYBA
+ def.) */
/* TODO: test if sharing interrupts works */
irq = dev->irq;
if (irq == IRQ_NONE) {
- printk (KERN_DEBUG
+ printk(KERN_DEBUG
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx)\n",
parport_pc_pci_tbl[i + last_sio].vendor,
parport_pc_pci_tbl[i + last_sio].device,
io_lo, io_hi);
irq = PARPORT_IRQ_NONE;
} else {
- printk (KERN_DEBUG
+ printk(KERN_DEBUG
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx), IRQ %d\n",
parport_pc_pci_tbl[i + last_sio].vendor,
parport_pc_pci_tbl[i + last_sio].device,
@@ -3056,7 +3146,7 @@ static int parport_pc_pci_probe (struct pci_dev *dev,
data->num = count;
if (cards[i].postinit_hook)
- cards[i].postinit_hook (dev, count == 0);
+ cards[i].postinit_hook(dev, count == 0);
if (count) {
pci_set_drvdata(dev, data);
@@ -3090,7 +3180,7 @@ static struct pci_driver parport_pc_pci_driver = {
.remove = __devexit_p(parport_pc_pci_remove),
};
-static int __init parport_pc_init_superio (int autoirq, int autodma)
+static int __init parport_pc_init_superio(int autoirq, int autodma)
{
const struct pci_device_id *id;
struct pci_dev *pdev = NULL;
@@ -3101,8 +3191,9 @@ static int __init parport_pc_init_superio (int autoirq, int autodma)
if (id == NULL || id->driver_data >= last_sio)
continue;
- if (parport_pc_superio_info[id->driver_data].probe
- (pdev, autoirq, autodma,parport_pc_superio_info[id->driver_data].via)) {
+ if (parport_pc_superio_info[id->driver_data].probe(
+ pdev, autoirq, autodma,
+ parport_pc_superio_info[id->driver_data].via)) {
ret++;
}
}
@@ -3111,7 +3202,10 @@ static int __init parport_pc_init_superio (int autoirq, int autodma)
}
#else
static struct pci_driver parport_pc_pci_driver;
-static int __init parport_pc_init_superio(int autoirq, int autodma) {return 0;}
+static int __init parport_pc_init_superio(int autoirq, int autodma)
+{
+ return 0;
+}
#endif /* CONFIG_PCI */
#ifdef CONFIG_PNP
@@ -3124,44 +3218,45 @@ static const struct pnp_device_id parport_pc_pnp_tbl[] = {
{ }
};
-MODULE_DEVICE_TABLE(pnp,parport_pc_pnp_tbl);
+MODULE_DEVICE_TABLE(pnp, parport_pc_pnp_tbl);
-static int parport_pc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id)
+static int parport_pc_pnp_probe(struct pnp_dev *dev,
+ const struct pnp_device_id *id)
{
struct parport *pdata;
unsigned long io_lo, io_hi;
int dma, irq;
- if (pnp_port_valid(dev,0) &&
- !(pnp_port_flags(dev,0) & IORESOURCE_DISABLED)) {
- io_lo = pnp_port_start(dev,0);
+ if (pnp_port_valid(dev, 0) &&
+ !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
+ io_lo = pnp_port_start(dev, 0);
} else
return -EINVAL;
- if (pnp_port_valid(dev,1) &&
- !(pnp_port_flags(dev,1) & IORESOURCE_DISABLED)) {
- io_hi = pnp_port_start(dev,1);
+ if (pnp_port_valid(dev, 1) &&
+ !(pnp_port_flags(dev, 1) & IORESOURCE_DISABLED)) {
+ io_hi = pnp_port_start(dev, 1);
} else
io_hi = 0;
- if (pnp_irq_valid(dev,0) &&
- !(pnp_irq_flags(dev,0) & IORESOURCE_DISABLED)) {
- irq = pnp_irq(dev,0);
+ if (pnp_irq_valid(dev, 0) &&
+ !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) {
+ irq = pnp_irq(dev, 0);
} else
irq = PARPORT_IRQ_NONE;
- if (pnp_dma_valid(dev,0) &&
- !(pnp_dma_flags(dev,0) & IORESOURCE_DISABLED)) {
- dma = pnp_dma(dev,0);
+ if (pnp_dma_valid(dev, 0) &&
+ !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) {
+ dma = pnp_dma(dev, 0);
} else
dma = PARPORT_DMA_NONE;
dev_info(&dev->dev, "reported by %s\n", dev->protocol->name);
- if (!(pdata = parport_pc_probe_port(io_lo, io_hi,
- irq, dma, &dev->dev, 0)))
+ pdata = parport_pc_probe_port(io_lo, io_hi, irq, dma, &dev->dev, 0);
+ if (pdata == NULL)
return -ENODEV;
- pnp_set_drvdata(dev,pdata);
+ pnp_set_drvdata(dev, pdata);
return 0;
}
@@ -3203,7 +3298,7 @@ static struct platform_driver parport_pc_platform_driver = {
/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
static int __devinit __attribute__((unused))
-parport_pc_find_isa_ports (int autoirq, int autodma)
+parport_pc_find_isa_ports(int autoirq, int autodma)
{
int count = 0;
@@ -3227,7 +3322,7 @@ parport_pc_find_isa_ports (int autoirq, int autodma)
* autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
* autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
*/
-static void __init parport_pc_find_ports (int autoirq, int autodma)
+static void __init parport_pc_find_ports(int autoirq, int autodma)
{
int count = 0, err;
@@ -3261,11 +3356,18 @@ static void __init parport_pc_find_ports (int autoirq, int autodma)
* syntax and keep in mind that code below is a cleaned up version.
*/
-static int __initdata io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
-static int __initdata io_hi[PARPORT_PC_MAX_PORTS+1] =
- { [0 ... PARPORT_PC_MAX_PORTS] = PARPORT_IOHI_AUTO };
-static int __initdata dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE };
-static int __initdata irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
+static int __initdata io[PARPORT_PC_MAX_PORTS+1] = {
+ [0 ... PARPORT_PC_MAX_PORTS] = 0
+};
+static int __initdata io_hi[PARPORT_PC_MAX_PORTS+1] = {
+ [0 ... PARPORT_PC_MAX_PORTS] = PARPORT_IOHI_AUTO
+};
+static int __initdata dmaval[PARPORT_PC_MAX_PORTS] = {
+ [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE
+};
+static int __initdata irqval[PARPORT_PC_MAX_PORTS] = {
+ [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY
+};
static int __init parport_parse_param(const char *s, int *val,
int automatic, int none, int nofifo)
@@ -3306,18 +3408,19 @@ static int __init parport_parse_dma(const char *dmastr, int *val)
#ifdef CONFIG_PCI
static int __init parport_init_mode_setup(char *str)
{
- printk(KERN_DEBUG "parport_pc.c: Specified parameter parport_init_mode=%s\n", str);
-
- if (!strcmp (str, "spp"))
- parport_init_mode=1;
- if (!strcmp (str, "ps2"))
- parport_init_mode=2;
- if (!strcmp (str, "epp"))
- parport_init_mode=3;
- if (!strcmp (str, "ecp"))
- parport_init_mode=4;
- if (!strcmp (str, "ecpepp"))
- parport_init_mode=5;
+ printk(KERN_DEBUG
+ "parport_pc.c: Specified parameter parport_init_mode=%s\n", str);
+
+ if (!strcmp(str, "spp"))
+ parport_init_mode = 1;
+ if (!strcmp(str, "ps2"))
+ parport_init_mode = 2;
+ if (!strcmp(str, "epp"))
+ parport_init_mode = 3;
+ if (!strcmp(str, "ecp"))
+ parport_init_mode = 4;
+ if (!strcmp(str, "ecpepp"))
+ parport_init_mode = 5;
return 1;
}
#endif
@@ -3341,7 +3444,8 @@ module_param(verbose_probing, int, 0644);
#endif
#ifdef CONFIG_PCI
static char *init_mode;
-MODULE_PARM_DESC(init_mode, "Initialise mode for VIA VT8231 port (spp, ps2, epp, ecp or ecpepp)");
+MODULE_PARM_DESC(init_mode,
+ "Initialise mode for VIA VT8231 port (spp, ps2, epp, ecp or ecpepp)");
module_param(init_mode, charp, 0);
#endif
@@ -3372,7 +3476,7 @@ static int __init parse_parport_params(void)
irqval[0] = val;
break;
default:
- printk (KERN_WARNING
+ printk(KERN_WARNING
"parport_pc: irq specified "
"without base address. Use 'io=' "
"to specify one\n");
@@ -3385,7 +3489,7 @@ static int __init parse_parport_params(void)
dmaval[0] = val;
break;
default:
- printk (KERN_WARNING
+ printk(KERN_WARNING
"parport_pc: dma specified "
"without base address. Use 'io=' "
"to specify one\n");
@@ -3396,7 +3500,7 @@ static int __init parse_parport_params(void)
#else
-static int parport_setup_ptr __initdata = 0;
+static int parport_setup_ptr __initdata;
/*
* Acceptable parameters:
@@ -3407,7 +3511,7 @@ static int parport_setup_ptr __initdata = 0;
*
* IRQ/DMA may be numeric or 'auto' or 'none'
*/
-static int __init parport_setup (char *str)
+static int __init parport_setup(char *str)
{
char *endptr;
char *sep;
@@ -3419,15 +3523,15 @@ static int __init parport_setup (char *str)
return 1;
}
- if (!strncmp (str, "auto", 4)) {
+ if (!strncmp(str, "auto", 4)) {
irqval[0] = PARPORT_IRQ_AUTO;
dmaval[0] = PARPORT_DMA_AUTO;
return 1;
}
- val = simple_strtoul (str, &endptr, 0);
+ val = simple_strtoul(str, &endptr, 0);
if (endptr == str) {
- printk (KERN_WARNING "parport=%s not understood\n", str);
+ printk(KERN_WARNING "parport=%s not understood\n", str);
return 1;
}
@@ -3461,7 +3565,7 @@ static int __init parse_parport_params(void)
return io[0] == PARPORT_DISABLE;
}
-__setup ("parport=", parport_setup);
+__setup("parport=", parport_setup);
/*
* Acceptable parameters:
@@ -3469,7 +3573,7 @@ __setup ("parport=", parport_setup);
* parport_init_mode=[spp|ps2|epp|ecp|ecpepp]
*/
#ifdef CONFIG_PCI
-__setup("parport_init_mode=",parport_init_mode_setup);
+__setup("parport_init_mode=", parport_init_mode_setup);
#endif
#endif
@@ -3493,13 +3597,13 @@ static int __init parport_pc_init(void)
for (i = 0; i < PARPORT_PC_MAX_PORTS; i++) {
if (!io[i])
break;
- if ((io_hi[i]) == PARPORT_IOHI_AUTO)
- io_hi[i] = 0x400 + io[i];
+ if (io_hi[i] == PARPORT_IOHI_AUTO)
+ io_hi[i] = 0x400 + io[i];
parport_pc_probe_port(io[i], io_hi[i],
- irqval[i], dmaval[i], NULL, 0);
+ irqval[i], dmaval[i], NULL, 0);
}
} else
- parport_pc_find_ports (irqval[0], dmaval[0]);
+ parport_pc_find_ports(irqval[0], dmaval[0]);
return 0;
}
@@ -3507,9 +3611,9 @@ static int __init parport_pc_init(void)
static void __exit parport_pc_exit(void)
{
if (pci_registered_parport)
- pci_unregister_driver (&parport_pc_pci_driver);
+ pci_unregister_driver(&parport_pc_pci_driver);
if (pnp_registered_parport)
- pnp_unregister_driver (&parport_pc_pnp_driver);
+ pnp_unregister_driver(&parport_pc_pnp_driver);
platform_driver_unregister(&parport_pc_platform_driver);
while (!list_empty(&ports_list)) {
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 0ebca450ed29..dffa5d4fb298 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -614,7 +614,10 @@ parport_register_device(struct parport *port, const char *name,
* pardevice fields. -arca
*/
port->ops->init_state(tmp, tmp->state);
- parport_device_proc_register(tmp);
+ if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) {
+ port->proc_device = tmp;
+ parport_device_proc_register(tmp);
+ }
return tmp;
out_free_all:
@@ -646,10 +649,14 @@ void parport_unregister_device(struct pardevice *dev)
}
#endif
- parport_device_proc_unregister(dev);
-
port = dev->port->physport;
+ if (port->proc_device == dev) {
+ port->proc_device = NULL;
+ clear_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags);
+ parport_device_proc_unregister(dev);
+ }
+
if (port->cad == dev) {
printk(KERN_DEBUG "%s: %s forgot to release port\n",
port->name, dev->name);
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 64dd7df90e62..0f3706512686 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -87,8 +87,8 @@ EXPORT_SYMBOL(pci_read_vpd);
* pci_write_vpd - Write entry to Vital Product Data
* @dev: pci device struct
* @pos: offset in vpd space
- * @count: number of bytes to read
- * @val: value to write
+ * @count: number of bytes to write
+ * @buf: buffer containing write data
*
*/
ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 4fc168b70095..e68d5f20ffb3 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -129,7 +129,6 @@ struct acpiphp_func {
struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */
struct list_head sibling;
- struct pci_dev *pci_dev;
struct notifier_block nb;
acpi_handle handle;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index a33794d9e0dc..3a6064bce561 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -32,9 +32,6 @@
/*
* Lifetime rules for pci_dev:
- * - The one in acpiphp_func has its refcount elevated by pci_get_slot()
- * when the driver is loaded or when an insertion event occurs. It loses
- * a refcount when its ejected or the driver unloads.
* - The one in acpiphp_bridge has its refcount elevated by pci_get_slot()
* when the bridge is scanned and it loses a refcount when the bridge
* is removed.
@@ -130,6 +127,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
unsigned long long adr, sun;
int device, function, retval;
struct pci_bus *pbus = bridge->pci_bus;
+ struct pci_dev *pdev;
if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle))
return AE_OK;
@@ -213,10 +211,10 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
newfunc->slot = slot;
list_add_tail(&newfunc->sibling, &slot->funcs);
- /* associate corresponding pci_dev */
- newfunc->pci_dev = pci_get_slot(pbus, PCI_DEVFN(device, function));
- if (newfunc->pci_dev) {
+ pdev = pci_get_slot(pbus, PCI_DEVFN(device, function));
+ if (pdev) {
slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
+ pci_dev_put(pdev);
}
if (is_dock_device(handle)) {
@@ -617,7 +615,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
- pci_dev_put(func->pci_dev);
list_del(list);
kfree(func);
}
@@ -1101,22 +1098,24 @@ static int __ref enable_device(struct acpiphp_slot *slot)
pci_enable_bridges(bus);
pci_bus_add_devices(bus);
- /* associate pci_dev to our representation */
list_for_each (l, &slot->funcs) {
func = list_entry(l, struct acpiphp_func, sibling);
- func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
- func->function));
- if (!func->pci_dev)
+ dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
+ func->function));
+ if (!dev)
continue;
- if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
- func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS)
+ if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
+ dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) {
+ pci_dev_put(dev);
continue;
+ }
status = find_p2p_bridge(func->handle, (u32)1, bus, NULL);
if (ACPI_FAILURE(status))
warn("find_p2p_bridge failed (error code = 0x%x)\n",
status);
+ pci_dev_put(dev);
}
slot->flags |= SLOT_ENABLED;
@@ -1142,17 +1141,14 @@ static void disable_bridges(struct pci_bus *bus)
*/
static int disable_device(struct acpiphp_slot *slot)
{
- int retval = 0;
struct acpiphp_func *func;
- struct list_head *l;
+ struct pci_dev *pdev;
/* is this slot already disabled? */
if (!(slot->flags & SLOT_ENABLED))
goto err_exit;
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
+ list_for_each_entry(func, &slot->funcs, sibling) {
if (func->bridge) {
/* cleanup p2p bridges under this P2P bridge */
cleanup_p2p_bridge(func->bridge->handle,
@@ -1160,35 +1156,28 @@ static int disable_device(struct acpiphp_slot *slot)
func->bridge = NULL;
}
- if (func->pci_dev) {
- pci_stop_bus_device(func->pci_dev);
- if (func->pci_dev->subordinate) {
- disable_bridges(func->pci_dev->subordinate);
- pci_disable_device(func->pci_dev);
+ pdev = pci_get_slot(slot->bridge->pci_bus,
+ PCI_DEVFN(slot->device, func->function));
+ if (pdev) {
+ pci_stop_bus_device(pdev);
+ if (pdev->subordinate) {
+ disable_bridges(pdev->subordinate);
+ pci_disable_device(pdev);
}
+ pci_remove_bus_device(pdev);
+ pci_dev_put(pdev);
}
}
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
+ list_for_each_entry(func, &slot->funcs, sibling) {
acpiphp_unconfigure_ioapics(func->handle);
acpiphp_bus_trim(func->handle);
- /* try to remove anyway.
- * acpiphp_bus_add might have been failed */
-
- if (!func->pci_dev)
- continue;
-
- pci_remove_bus_device(func->pci_dev);
- pci_dev_put(func->pci_dev);
- func->pci_dev = NULL;
}
slot->flags &= (~SLOT_ENABLED);
- err_exit:
- return retval;
+err_exit:
+ return 0;
}
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index dd18f857dfb0..42e4260c3b12 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -153,45 +153,47 @@ int ibmphp_init_devno(struct slot **cur_slot)
return -1;
}
for (loop = 0; loop < len; loop++) {
- if ((*cur_slot)->number == rtable->slots[loop].slot) {
- if ((*cur_slot)->bus == rtable->slots[loop].bus) {
+ if ((*cur_slot)->number == rtable->slots[loop].slot &&
+ (*cur_slot)->bus == rtable->slots[loop].bus) {
+ struct io_apic_irq_attr irq_attr;
+
(*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn);
for (i = 0; i < 4; i++)
(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector((int) (*cur_slot)->bus,
- (int) (*cur_slot)->device, i);
-
- debug("(*cur_slot)->irq[0] = %x\n",
- (*cur_slot)->irq[0]);
- debug("(*cur_slot)->irq[1] = %x\n",
- (*cur_slot)->irq[1]);
- debug("(*cur_slot)->irq[2] = %x\n",
- (*cur_slot)->irq[2]);
- debug("(*cur_slot)->irq[3] = %x\n",
- (*cur_slot)->irq[3]);
-
- debug("rtable->exlusive_irqs = %x\n",
+ (int) (*cur_slot)->device, i,
+ &irq_attr);
+
+ debug("(*cur_slot)->irq[0] = %x\n",
+ (*cur_slot)->irq[0]);
+ debug("(*cur_slot)->irq[1] = %x\n",
+ (*cur_slot)->irq[1]);
+ debug("(*cur_slot)->irq[2] = %x\n",
+ (*cur_slot)->irq[2]);
+ debug("(*cur_slot)->irq[3] = %x\n",
+ (*cur_slot)->irq[3]);
+
+ debug("rtable->exlusive_irqs = %x\n",
rtable->exclusive_irqs);
- debug("rtable->slots[loop].irq[0].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[0].bitmap = %x\n",
rtable->slots[loop].irq[0].bitmap);
- debug("rtable->slots[loop].irq[1].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[1].bitmap = %x\n",
rtable->slots[loop].irq[1].bitmap);
- debug("rtable->slots[loop].irq[2].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[2].bitmap = %x\n",
rtable->slots[loop].irq[2].bitmap);
- debug("rtable->slots[loop].irq[3].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[3].bitmap = %x\n",
rtable->slots[loop].irq[3].bitmap);
- debug("rtable->slots[loop].irq[0].link = %x\n",
+ debug("rtable->slots[loop].irq[0].link = %x\n",
rtable->slots[loop].irq[0].link);
- debug("rtable->slots[loop].irq[1].link = %x\n",
+ debug("rtable->slots[loop].irq[1].link = %x\n",
rtable->slots[loop].irq[1].link);
- debug("rtable->slots[loop].irq[2].link = %x\n",
+ debug("rtable->slots[loop].irq[2].link = %x\n",
rtable->slots[loop].irq[2].link);
- debug("rtable->slots[loop].irq[3].link = %x\n",
+ debug("rtable->slots[loop].irq[3].link = %x\n",
rtable->slots[loop].irq[3].link);
- debug("end of init_devno\n");
- kfree(rtable);
- return 0;
- }
+ debug("end of init_devno\n");
+ kfree(rtable);
+ return 0;
}
}
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
index bf7d6ce9bbb3..737a1c44b07a 100644
--- a/drivers/pci/htirq.c
+++ b/drivers/pci/htirq.c
@@ -98,6 +98,7 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
int max_irq;
int pos;
int irq;
+ int node;
pos = pci_find_ht_capability(dev, HT_CAPTYPE_IRQ);
if (!pos)
@@ -125,7 +126,8 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
cfg->msg.address_lo = 0xffffffff;
cfg->msg.address_hi = 0xffffffff;
- irq = create_irq();
+ node = dev_to_node(&dev->dev);
+ irq = create_irq_nr(0, node);
if (irq <= 0) {
kfree(cfg);
@@ -158,6 +160,7 @@ int ht_create_irq(struct pci_dev *dev, int idx)
/**
* ht_destroy_irq - destroy an irq created with ht_create_irq
+ * @irq: irq to be destroyed
*
* This reverses ht_create_irq removing the specified irq from
* existence. The irq should be free before this happens.
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 001b328adf80..cd389162735f 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -59,6 +59,10 @@
#define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32))
#define DMA_64BIT_PFN IOVA_PFN(DMA_BIT_MASK(64))
+#ifndef PHYSICAL_PAGE_MASK
+#define PHYSICAL_PAGE_MASK PAGE_MASK
+#endif
+
/* global iommu list, set NULL for ignored DMAR units */
static struct intel_iommu **g_iommus;
@@ -1216,7 +1220,7 @@ static void dmar_init_reserved_ranges(void)
if (!r->flags || !(r->flags & IORESOURCE_MEM))
continue;
addr = r->start;
- addr &= PAGE_MASK;
+ addr &= PHYSICAL_PAGE_MASK;
size = r->end - addr;
size = PAGE_ALIGN(size);
iova = reserve_iova(&reserved_iova_list, IOVA_PFN(addr),
@@ -1968,15 +1972,6 @@ static int __init init_dmars(void)
}
}
-#ifdef CONFIG_INTR_REMAP
- if (!intr_remapping_enabled) {
- ret = enable_intr_remapping(0);
- if (ret)
- printk(KERN_ERR
- "IOMMU: enable interrupt remapping failed\n");
- }
-#endif
-
/*
* For each rmrr
* for each dev attached to rmrr
@@ -2173,7 +2168,8 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
* is not a big problem
*/
ret = domain_page_mapping(domain, start_paddr,
- ((u64)paddr) & PAGE_MASK, size, prot);
+ ((u64)paddr) & PHYSICAL_PAGE_MASK,
+ size, prot);
if (ret)
goto error;
@@ -2463,8 +2459,8 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
addr = page_to_phys(sg_page(sg)) + sg->offset;
size = aligned_size((u64)addr, sg->length);
ret = domain_page_mapping(domain, start_addr + offset,
- ((u64)addr) & PAGE_MASK,
- size, prot);
+ ((u64)addr) & PHYSICAL_PAGE_MASK,
+ size, prot);
if (ret) {
/* clear the page */
dma_pte_clear_range(domain, start_addr,
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
index f5e0ea724a6f..3a0cb0bb0593 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/pci/intr_remapping.c
@@ -15,6 +15,14 @@ static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
static int ir_ioapic_num;
int intr_remapping_enabled;
+static int disable_intremap;
+static __init int setup_nointremap(char *str)
+{
+ disable_intremap = 1;
+ return 0;
+}
+early_param("nointremap", setup_nointremap);
+
struct irq_2_iommu {
struct intel_iommu *iommu;
u16 irte_index;
@@ -23,15 +31,12 @@ struct irq_2_iommu {
};
#ifdef CONFIG_GENERIC_HARDIRQS
-static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
+static struct irq_2_iommu *get_one_free_irq_2_iommu(int node)
{
struct irq_2_iommu *iommu;
- int node;
-
- node = cpu_to_node(cpu);
iommu = kzalloc_node(sizeof(*iommu), GFP_ATOMIC, node);
- printk(KERN_DEBUG "alloc irq_2_iommu on cpu %d node %d\n", cpu, node);
+ printk(KERN_DEBUG "alloc irq_2_iommu on node %d\n", node);
return iommu;
}
@@ -48,7 +53,7 @@ static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
return desc->irq_2_iommu;
}
-static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
+static struct irq_2_iommu *irq_2_iommu_alloc_node(unsigned int irq, int node)
{
struct irq_desc *desc;
struct irq_2_iommu *irq_iommu;
@@ -56,7 +61,7 @@ static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
/*
* alloc irq desc if not allocated already.
*/
- desc = irq_to_desc_alloc_cpu(irq, cpu);
+ desc = irq_to_desc_alloc_node(irq, node);
if (!desc) {
printk(KERN_INFO "can not get irq_desc for %d\n", irq);
return NULL;
@@ -65,14 +70,14 @@ static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
irq_iommu = desc->irq_2_iommu;
if (!irq_iommu)
- desc->irq_2_iommu = get_one_free_irq_2_iommu(cpu);
+ desc->irq_2_iommu = get_one_free_irq_2_iommu(node);
return desc->irq_2_iommu;
}
static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
{
- return irq_2_iommu_alloc_cpu(irq, boot_cpu_id);
+ return irq_2_iommu_alloc_node(irq, cpu_to_node(boot_cpu_id));
}
#else /* !CONFIG_SPARSE_IRQ */
@@ -423,20 +428,6 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
readl, (sts & DMA_GSTS_IRTPS), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags);
- if (mode == 0) {
- spin_lock_irqsave(&iommu->register_lock, flags);
-
- /* enable comaptiblity format interrupt pass through */
- cmd = iommu->gcmd | DMA_GCMD_CFI;
- iommu->gcmd |= DMA_GCMD_CFI;
- writel(cmd, iommu->reg + DMAR_GCMD_REG);
-
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (sts & DMA_GSTS_CFIS), sts);
-
- spin_unlock_irqrestore(&iommu->register_lock, flags);
- }
-
/*
* global invalidation of interrupt entry cache before enabling
* interrupt-remapping.
@@ -516,6 +507,23 @@ end:
spin_unlock_irqrestore(&iommu->register_lock, flags);
}
+int __init intr_remapping_supported(void)
+{
+ struct dmar_drhd_unit *drhd;
+
+ if (disable_intremap)
+ return 0;
+
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ return 0;
+ }
+
+ return 1;
+}
+
int __init enable_intr_remapping(int eim)
{
struct dmar_drhd_unit *drhd;
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 6f2e6295e773..362773247fbf 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -455,8 +455,6 @@ static int msix_capability_init(struct pci_dev *dev,
entry->msi_attrib.default_irq = dev->irq;
entry->msi_attrib.pos = pos;
entry->mask_base = base;
- entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
msix_mask_irq(entry, 1);
list_add_tail(&entry->list, &dev->msi_list);
@@ -493,6 +491,12 @@ static int msix_capability_init(struct pci_dev *dev,
msix_set_enable(dev, 1);
dev->msix_enabled = 1;
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int vector = entry->msi_attrib.entry_nr;
+ entry->masked = readl(base + vector * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+ }
+
return 0;
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index a7eb1b46a5a8..85ebd02a64a7 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -492,6 +492,7 @@ write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr,
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
* @kobj: kobject corresponding to file to read from
+ * @bin_attr: struct bin_attribute for this file
* @buf: buffer to store results
* @off: offset into legacy I/O port space
* @count: number of bytes to read
@@ -517,6 +518,7 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
/**
* pci_write_legacy_io - write byte(s) to legacy I/O port space
* @kobj: kobject corresponding to file to read from
+ * @bin_attr: struct bin_attribute for this file
* @buf: buffer containing value to be written
* @off: offset into legacy I/O port space
* @count: number of bytes to write
@@ -733,9 +735,9 @@ pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr,
/**
* pci_remove_resource_files - cleanup resource files
- * @dev: dev to cleanup
+ * @pdev: dev to cleanup
*
- * If we created resource files for @dev, remove them from sysfs and
+ * If we created resource files for @pdev, remove them from sysfs and
* free their resources.
*/
static void
@@ -793,9 +795,9 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
/**
* pci_create_resource_files - create resource files in sysfs for @dev
- * @dev: dev in question
+ * @pdev: dev in question
*
- * Walk the resources in @dev creating files for each resource available.
+ * Walk the resources in @pdev creating files for each resource available.
*/
static int pci_create_resource_files(struct pci_dev *pdev)
{
@@ -829,6 +831,7 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
/**
* pci_write_rom - used to enable access to the PCI ROM display
* @kobj: kernel object handle
+ * @bin_attr: struct bin_attribute for this file
* @buf: user input
* @off: file offset
* @count: number of byte in input
@@ -852,6 +855,7 @@ pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
/**
* pci_read_rom - read a PCI ROM
* @kobj: kernel object handle
+ * @bin_attr: struct bin_attribute for this file
* @buf: where to put the data we read from the ROM
* @off: file offset
* @count: number of bytes to read
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 16fd0d4c3166..1a91bf9687af 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -557,7 +557,8 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
} else {
error = -ENODEV;
/* Fall back to PCI_D0 if native PM is not supported */
- pci_update_current_state(dev, PCI_D0);
+ if (!dev->pm_cap)
+ dev->current_state = PCI_D0;
}
return error;
@@ -681,11 +682,34 @@ EXPORT_SYMBOL(pci_choose_state);
#define PCI_EXP_SAVE_REGS 7
+#define pcie_cap_has_devctl(type, flags) 1
+#define pcie_cap_has_lnkctl(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
+ (type == PCI_EXP_TYPE_ROOT_PORT || \
+ type == PCI_EXP_TYPE_ENDPOINT || \
+ type == PCI_EXP_TYPE_LEG_END))
+#define pcie_cap_has_sltctl(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
+ ((type == PCI_EXP_TYPE_ROOT_PORT) || \
+ (type == PCI_EXP_TYPE_DOWNSTREAM && \
+ (flags & PCI_EXP_FLAGS_SLOT))))
+#define pcie_cap_has_rtctl(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
+ (type == PCI_EXP_TYPE_ROOT_PORT || \
+ type == PCI_EXP_TYPE_RC_EC))
+#define pcie_cap_has_devctl2(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1)
+#define pcie_cap_has_lnkctl2(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1)
+#define pcie_cap_has_sltctl2(type, flags) \
+ ((flags & PCI_EXP_FLAGS_VERS) > 1)
+
static int pci_save_pcie_state(struct pci_dev *dev)
{
int pos, i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
+ u16 flags;
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (pos <= 0)
@@ -698,13 +722,22 @@ static int pci_save_pcie_state(struct pci_dev *dev)
}
cap = (u16 *)&save_state->data[0];
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
+
+ if (pcie_cap_has_devctl(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
+ if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
+ if (pcie_cap_has_sltctl(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
+ if (pcie_cap_has_rtctl(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
+ if (pcie_cap_has_devctl2(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]);
+ if (pcie_cap_has_lnkctl2(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]);
+ if (pcie_cap_has_sltctl2(dev->pcie_type, flags))
+ pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]);
return 0;
}
@@ -714,6 +747,7 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
int i = 0, pos;
struct pci_cap_saved_state *save_state;
u16 *cap;
+ u16 flags;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
@@ -721,13 +755,22 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
return;
cap = (u16 *)&save_state->data[0];
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
+
+ if (pcie_cap_has_devctl(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
+ if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
+ if (pcie_cap_has_sltctl(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
+ if (pcie_cap_has_rtctl(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
+ if (pcie_cap_has_devctl2(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]);
+ if (pcie_cap_has_lnkctl2(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]);
+ if (pcie_cap_has_sltctl2(dev->pcie_type, flags))
+ pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]);
}
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index c7ad68b6c6d6..aa14482a4779 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -95,6 +95,9 @@ struct aer_broadcast_data {
static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
enum pci_ers_result new)
{
+ if (new == PCI_ERS_RESULT_NONE)
+ return orig;
+
switch (orig) {
case PCI_ERS_RESULT_CAN_RECOVER:
case PCI_ERS_RESULT_RECOVERED:
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index b924e2463f85..091ce70051e0 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -200,7 +200,7 @@ static int slot_reset_iter(struct device *device, void *data)
static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
{
- pci_ers_result_t status = PCI_ERS_RESULT_NONE;
+ pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
int retval;
/* If fatal, restore cfg space for possible link reset at upstream */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 8eb50dffb78a..f1ae2475ffff 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -745,6 +745,8 @@ int pci_setup_device(struct pci_dev *dev)
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
+ /* device class may be changed after fixup */
+ class = dev->class >> 8;
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
@@ -1118,10 +1120,6 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
return max;
}
-void __attribute__((weak)) set_pci_bus_resources_arch_default(struct pci_bus *b)
-{
-}
-
struct pci_bus * pci_create_bus(struct device *parent,
int bus, struct pci_ops *ops, void *sysdata)
{
@@ -1180,8 +1178,6 @@ struct pci_bus * pci_create_bus(struct device *parent,
b->resource[0] = &ioport_resource;
b->resource[1] = &iomem_resource;
- set_pci_bus_resources_arch_default(b);
-
return b;
dev_create_file_err:
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 0254741bece0..3067673d54f6 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2033,6 +2033,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_di
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3364, quirk_disable_all_msi);
/* Disable MSI on chipsets that are known to not support it */
static void __devinit quirk_disable_msi(struct pci_dev *dev)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 8d9da9d30a61..a00f85471b6e 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -536,11 +536,13 @@ static void pci_bus_dump_res(struct pci_bus *bus)
for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
struct resource *res = bus->resource[i];
- if (!res)
+ if (!res || !res->end)
continue;
dev_printk(KERN_DEBUG, &bus->dev, "resource %d %s %pR\n", i,
- (res->flags & IORESOURCE_IO) ? "io: " : "mem:", res);
+ (res->flags & IORESOURCE_IO) ? "io: " :
+ ((res->flags & IORESOURCE_PREFETCH)? "pref mem":"mem:"),
+ res);
}
}
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 21189447e545..fe95ce20bcbd 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -264,8 +264,8 @@ EXPORT_SYMBOL_GPL(pci_create_slot);
/**
* pci_renumber_slot - update %struct pci_slot -> number
- * @slot - %struct pci_slot to update
- * @slot_nr - new number for slot
+ * @slot: &struct pci_slot to update
+ * @slot_nr: new number for slot
*
* The primary purpose of this interface is to allow callers who earlier
* created a placeholder slot in pci_create_slot() by passing a -1 as
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index eeafc6c0160d..bfc1a8892a32 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -269,16 +269,16 @@ static struct key_entry asus_keymap[] = {
{KE_KEY, 0x34, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x40, KEY_PREVIOUSSONG},
{KE_KEY, 0x41, KEY_NEXTSONG},
- {KE_KEY, 0x43, KEY_STOP},
+ {KE_KEY, 0x43, KEY_STOPCD},
{KE_KEY, 0x45, KEY_PLAYPAUSE},
{KE_KEY, 0x50, KEY_EMAIL},
{KE_KEY, 0x51, KEY_WWW},
- {KE_KEY, 0x5C, BTN_EXTRA}, /* Performance */
+ {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */
{KE_KEY, 0x5D, KEY_WLAN},
{KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
{KE_KEY, 0x82, KEY_CAMERA},
- {KE_KEY, 0x8A, KEY_TV},
+ {KE_KEY, 0x8A, KEY_PROG1},
{KE_KEY, 0x95, KEY_MEDIA},
{KE_KEY, 0x99, KEY_PHONE},
{KE_END, 0},
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 6f54fd1757cd..353a898c3693 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -158,6 +158,7 @@ enum { KE_KEY, KE_END };
static struct key_entry eeepc_keymap[] = {
/* Sleep already handled via generic ACPI code */
{KE_KEY, 0x10, KEY_WLAN },
+ {KE_KEY, 0x11, KEY_WLAN },
{KE_KEY, 0x12, KEY_PROG1 },
{KE_KEY, 0x13, KEY_MUTE },
{KE_KEY, 0x14, KEY_VOLUMEDOWN },
@@ -166,6 +167,8 @@ static struct key_entry eeepc_keymap[] = {
{KE_KEY, 0x1b, KEY_ZOOM },
{KE_KEY, 0x1c, KEY_PROG2 },
{KE_KEY, 0x1d, KEY_PROG3 },
+ {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
+ {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
{KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
@@ -381,11 +384,13 @@ static ssize_t show_sys_acpi(int cm, char *buf)
EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
+EEEPC_CREATE_DEVICE_ATTR(cpufv, CM_ASL_CPUFV);
static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_disp.attr,
+ &dev_attr_cpufv.attr,
NULL
};
@@ -512,15 +517,21 @@ static int eeepc_hotk_check(void)
return 0;
}
-static void notify_brn(void)
+static int notify_brn(void)
{
+ /* returns the *previous* brightness, or -1 */
struct backlight_device *bd = eeepc_backlight_device;
- if (bd)
+ if (bd) {
+ int old = bd->props.brightness;
bd->props.brightness = read_brightness(bd);
+ return old;
+ }
+ return -1;
}
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
+ enum rfkill_state state;
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);
@@ -532,7 +543,9 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
return;
}
- if (get_acpi(CM_ASL_WLAN) == 1) {
+ eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state);
+
+ if (state == RFKILL_STATE_UNBLOCKED) {
dev = pci_get_slot(bus, 0);
if (dev) {
/* Device already present */
@@ -552,23 +565,41 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
pci_dev_put(dev);
}
}
+
+ rfkill_force_state(ehotk->eeepc_wlan_rfkill, state);
}
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
static struct key_entry *key;
u16 count;
+ int brn = -ENODEV;
if (!ehotk)
return;
if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
- notify_brn();
+ brn = notify_brn();
count = ehotk->event_count[event % 128]++;
acpi_bus_generate_proc_event(ehotk->device, event, count);
acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
dev_name(&ehotk->device->dev), event,
count);
if (ehotk->inputdev) {
+ if (brn != -ENODEV) {
+ /* brightness-change events need special
+ * handling for conversion to key events
+ */
+ if (brn < 0)
+ brn = event;
+ else
+ brn += NOTIFY_BRN_MIN;
+ if (event < brn)
+ event = NOTIFY_BRN_MIN; /* brightness down */
+ else if (event > brn)
+ event = NOTIFY_BRN_MIN + 2; /* ... up */
+ else
+ event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
+ }
key = eepc_get_entry_by_scancode(event);
if (key) {
switch (key->type) {
@@ -649,6 +680,9 @@ static int eeepc_hotk_add(struct acpi_device *device)
if (ACPI_FAILURE(status))
printk(EEEPC_ERR "Error installing notify handler\n");
+ eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
+ eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
+
if (get_acpi(CM_ASL_WLAN) != -1) {
ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_WLAN);
@@ -704,9 +738,6 @@ static int eeepc_hotk_add(struct acpi_device *device)
goto bluetooth_fail;
}
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
-
return 0;
bluetooth_fail:
@@ -717,6 +748,8 @@ static int eeepc_hotk_add(struct acpi_device *device)
wlan_fail:
if (ehotk->eeepc_wlan_rfkill)
rfkill_free(ehotk->eeepc_wlan_rfkill);
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 9a3a682c6981..9496494f340e 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -110,11 +110,9 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
ret = 0;
- if (acpi_bus_power_manageable(handle)) {
- ret = acpi_bus_set_power(handle, ACPI_STATE_D3);
- if (ret)
- return ret;
- }
+ if (acpi_bus_power_manageable(handle))
+ acpi_bus_set_power(handle, ACPI_STATE_D3);
+ /* continue even if acpi_bus_set_power() fails */
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DIS", NULL, NULL)))
ret = -ENODEV;
return ret;
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index adf17856bacc..7f207f335bec 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/drivers/pnp/pnpacpi/rsparser.c
@@ -123,7 +123,7 @@ static void pnpacpi_parse_allocated_irqresource(struct pnp_dev *dev,
}
flags = irq_flags(triggering, polarity, shareable);
- irq = acpi_register_gsi(gsi, triggering, polarity);
+ irq = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
if (irq >= 0)
pcibios_penalize_isa_irq(irq, 1);
else
diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c
index f604061d2bb0..ba9765427886 100644
--- a/drivers/pnp/resource.c
+++ b/drivers/pnp/resource.c
@@ -638,6 +638,24 @@ int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start,
}
EXPORT_SYMBOL(pnp_possible_config);
+int pnp_range_reserved(resource_size_t start, resource_size_t end)
+{
+ struct pnp_dev *dev;
+ struct pnp_resource *pnp_res;
+ resource_size_t *dev_start, *dev_end;
+
+ pnp_for_each_dev(dev) {
+ list_for_each_entry(pnp_res, &dev->resources, list) {
+ dev_start = &pnp_res->res.start;
+ dev_end = &pnp_res->res.end;
+ if (ranged_conflict(&start, &end, dev_start, dev_end))
+ return 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(pnp_range_reserved);
+
/* format is: pnp_reserve_irq=irq1[,irq2] .... */
static int __init pnp_setup_reserve_irq(char *str)
{
diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c
index 7ecb820ceebc..d08cd9b66c6d 100644
--- a/drivers/regulator/bq24022.c
+++ b/drivers/regulator/bq24022.c
@@ -61,8 +61,7 @@ static int bq24022_disable(struct regulator_dev *rdev)
static int bq24022_is_enabled(struct regulator_dev *rdev)
{
- struct platform_device *pdev = rdev_get_drvdata(rdev);
- struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+ struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
return !gpio_get_value(pdata->gpio_nce);
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 01f7702a805d..98c3a74e9949 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -540,8 +540,8 @@ static void drms_uA_update(struct regulator_dev *rdev)
err = regulator_check_drms(rdev);
if (err < 0 || !rdev->desc->ops->get_optimum_mode ||
- !rdev->desc->ops->get_voltage || !rdev->desc->ops->set_mode);
- return;
+ !rdev->desc->ops->get_voltage || !rdev->desc->ops->set_mode)
+ return;
/* get output voltage */
output_uV = rdev->desc->ops->get_voltage(rdev);
@@ -703,10 +703,13 @@ static int set_machine_constraints(struct regulator_dev *rdev,
int cmin = constraints->min_uV;
int cmax = constraints->max_uV;
- /* it's safe to autoconfigure fixed-voltage supplies */
+ /* it's safe to autoconfigure fixed-voltage supplies
+ and the constraints are used by list_voltage. */
if (count == 1 && !cmin) {
- cmin = INT_MIN;
+ cmin = 1;
cmax = INT_MAX;
+ constraints->min_uV = cmin;
+ constraints->max_uV = cmax;
}
/* voltage constraints are optional */
@@ -2001,8 +2004,8 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
return ERR_PTR(-EINVAL);
- if (!regulator_desc->type == REGULATOR_VOLTAGE &&
- !regulator_desc->type == REGULATOR_CURRENT)
+ if (regulator_desc->type != REGULATOR_VOLTAGE &&
+ regulator_desc->type != REGULATOR_CURRENT)
return ERR_PTR(-EINVAL);
if (!init_data)
@@ -2080,6 +2083,10 @@ out:
scrub:
device_unregister(&rdev->dev);
+ /* device core frees rdev */
+ rdev = ERR_PTR(ret);
+ goto out;
+
clean:
kfree(rdev);
rdev = ERR_PTR(ret);
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
index 72b15495183c..c6628f5a0af7 100644
--- a/drivers/regulator/da903x.c
+++ b/drivers/regulator/da903x.c
@@ -497,7 +497,7 @@ static struct platform_driver da903x_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = da903x_regulator_probe,
- .remove = da903x_regulator_remove,
+ .remove = __devexit_p(da903x_regulator_remove),
};
static int __init da903x_regulator_init(void)
diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c
index 3d08348584e1..71403fa3ffa1 100644
--- a/drivers/regulator/virtual.c
+++ b/drivers/regulator/virtual.c
@@ -230,13 +230,13 @@ static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
* sysfs_streq() doesn't need the \n's, but we add them so the strings
* will be shared with show_mode(), above.
*/
- if (sysfs_streq(buf, "fast\n") == 0)
+ if (sysfs_streq(buf, "fast\n"))
mode = REGULATOR_MODE_FAST;
- else if (sysfs_streq(buf, "normal\n") == 0)
+ else if (sysfs_streq(buf, "normal\n"))
mode = REGULATOR_MODE_NORMAL;
- else if (sysfs_streq(buf, "idle\n") == 0)
+ else if (sysfs_streq(buf, "idle\n"))
mode = REGULATOR_MODE_IDLE;
- else if (sysfs_streq(buf, "standby\n") == 0)
+ else if (sysfs_streq(buf, "standby\n"))
mode = REGULATOR_MODE_STANDBY;
else {
dev_err(dev, "Configuring invalid mode\n");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4e9851fc1746..277d35d232fa 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -692,7 +692,7 @@ config RTC_DRV_GENERIC
tristate "Generic RTC support"
# Please consider writing a new RTC driver instead of using the generic
# RTC abstraction
- depends on PARISC || M68K || PPC
+ depends on PARISC || M68K || PPC || SUPERH32
help
Say Y or M here to enable RTC support on systems using the generic
RTC abstraction. If you do not know what you are doing, you should
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index 826153552157..aaf1f75fa293 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -102,7 +102,7 @@ static const struct rtc_class_ops pl030_ops = {
.set_alarm = pl030_set_alarm,
};
-static int pl030_probe(struct amba_device *dev, void *id)
+static int pl030_probe(struct amba_device *dev, struct amba_id *id)
{
struct pl030_rtc *rtc;
int ret;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 333eec689d2f..451fc13784d1 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -127,7 +127,7 @@ static int pl031_remove(struct amba_device *adev)
return 0;
}
-static int pl031_probe(struct amba_device *adev, void *id)
+static int pl031_probe(struct amba_device *adev, struct amba_id *id)
{
int ret;
struct pl031_local *ldata;
diff --git a/drivers/rtc/rtc-twl4030.c b/drivers/rtc/rtc-twl4030.c
index a6341e4f9a0f..9c8c70c497dc 100644
--- a/drivers/rtc/rtc-twl4030.c
+++ b/drivers/rtc/rtc-twl4030.c
@@ -495,9 +495,7 @@ static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
irqstat = rtc_irq_bits;
- /* REVISIT alarm may need to wake us from sleep */
- mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M |
- BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
return 0;
}
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index e64f62d5e0fc..442bb98a2821 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -851,8 +851,10 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
/* Check the cqr */
rc = dasd_check_cqr(cqr);
- if (rc)
+ if (rc) {
+ cqr->intrc = rc;
return rc;
+ }
device = (struct dasd_device *) cqr->startdev;
if (cqr->retries < 0) {
/* internal error 14 - start_IO run out of retries */
@@ -915,6 +917,7 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
BUG();
break;
}
+ cqr->intrc = rc;
return rc;
}
@@ -1454,8 +1457,12 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr)
dasd_add_request_tail(cqr);
wait_event(generic_waitq, _wait_for_wakeup(cqr));
- /* Request status is either done or failed. */
- rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
+ if (cqr->status == DASD_CQR_DONE)
+ rc = 0;
+ else if (cqr->intrc)
+ rc = cqr->intrc;
+ else
+ rc = -EIO;
return rc;
}
@@ -1477,8 +1484,15 @@ int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
dasd_cancel_req(cqr);
/* wait (non-interruptible) for final status */
wait_event(generic_waitq, _wait_for_wakeup(cqr));
+ cqr->intrc = rc;
}
- rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
+
+ if (cqr->status == DASD_CQR_DONE)
+ rc = 0;
+ else if (cqr->intrc)
+ rc = cqr->intrc;
+ else
+ rc = -EIO;
return rc;
}
@@ -1523,8 +1537,12 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
wait_event(generic_waitq, _wait_for_wakeup(cqr));
- /* Request status is either done or failed. */
- rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
+ if (cqr->status == DASD_CQR_DONE)
+ rc = 0;
+ else if (cqr->intrc)
+ rc = cqr->intrc;
+ else
+ rc = -EIO;
return rc;
}
@@ -1990,7 +2008,7 @@ static void dasd_setup_queue(struct dasd_block *block)
{
int max;
- blk_queue_hardsect_size(block->request_queue, block->bp_block);
+ blk_queue_logical_block_size(block->request_queue, block->bp_block);
max = block->base->discipline->max_blocks << block->s2b_shift;
blk_queue_max_sectors(block->request_queue, max);
blk_queue_max_phys_segments(block->request_queue, -1L);
@@ -2427,12 +2445,12 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
- void **rdc_buffer, int rdc_buffer_size)
+ void *rdc_buffer, int rdc_buffer_size)
{
int ret;
struct dasd_ccw_req *cqr;
- cqr = dasd_generic_build_rdc(device, *rdc_buffer, rdc_buffer_size,
+ cqr = dasd_generic_build_rdc(device, rdc_buffer, rdc_buffer_size,
magic);
if (IS_ERR(cqr))
return PTR_ERR(cqr);
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 2efaddfae560..644086ba2ede 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -202,6 +202,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
rc = -EIO;
break;
}
+ cqr->intrc = rc;
return rc;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index a41c94053e64..cf0cfdba1244 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1097,20 +1097,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
{
struct dasd_eckd_private *private;
struct dasd_block *block;
- void *rdc_data;
int is_known, rc;
private = (struct dasd_eckd_private *) device->private;
- if (private == NULL) {
- private = kzalloc(sizeof(struct dasd_eckd_private),
- GFP_KERNEL | GFP_DMA);
- if (private == NULL) {
+ if (!private) {
+ private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
+ if (!private) {
dev_warn(&device->cdev->dev,
"Allocating memory for private DASD data "
"failed\n");
return -ENOMEM;
}
device->private = (void *) private;
+ } else {
+ memset(private, 0, sizeof(*private));
}
/* Invalidate status of initial analysis. */
private->init_cqr_status = -1;
@@ -1161,9 +1161,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
goto out_err3;
/* Read Device Characteristics */
- rdc_data = (void *) &(private->rdc_data);
- memset(rdc_data, 0, sizeof(rdc_data));
- rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
+ rc = dasd_generic_read_dev_chars(device, "ECKD", &private->rdc_data,
+ 64);
if (rc) {
DBF_EVENT(DBF_WARNING,
"Read device characteristics failed, rc=%d for "
@@ -1183,7 +1182,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->rdc_data.dev_model,
private->rdc_data.cu_type,
private->rdc_data.cu_model.model,
- private->real_cyl,
+ private->real_cyl,
private->rdc_data.trk_per_cyl,
private->rdc_data.sec_per_trk);
return 0;
@@ -2336,9 +2335,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
{
int tpm, cmdrtd, cmdwtd;
int use_prefix;
-
- struct dasd_eckd_private *private;
+#if defined(CONFIG_64BIT)
int fcx_in_css, fcx_in_gneq, fcx_in_features;
+#endif
+ struct dasd_eckd_private *private;
struct dasd_device *basedev;
sector_t first_rec, last_rec;
sector_t first_trk, last_trk;
@@ -2361,11 +2361,15 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
last_offs = sector_div(last_trk, blk_per_trk);
cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk);
- /* is transport mode supported ? */
+ /* is transport mode supported? */
+#if defined(CONFIG_64BIT)
fcx_in_css = css_general_characteristics.fcx;
fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
fcx_in_features = private->features.feature[40] & 0x80;
tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
+#else
+ tpm = 0;
+#endif
/* is read track data and write track data in command mode supported? */
cmdrtd = private->features.feature[9] & 0x20;
@@ -3013,8 +3017,9 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
- scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw));
+ " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n",
+ req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
+ scsw_cc(&irb->scsw), req->intrc);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
dev_name(&device->cdev->dev),
@@ -3115,9 +3120,10 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X "
+ " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d "
"fcxs: 0x%02X schxs: 0x%02X\n", req,
scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
+ scsw_cc(&irb->scsw), req->intrc,
irb->scsw.tm.fcxs, irb->scsw.tm.schxs);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing TCW: %p\n",
@@ -3273,8 +3279,14 @@ static struct dasd_discipline dasd_eckd_discipline = {
static int __init
dasd_eckd_init(void)
{
+ int ret;
+
ASCEBC(dasd_eckd_discipline.ebcname, 4);
- return ccw_driver_register(&dasd_eckd_driver);
+ ret = ccw_driver_register(&dasd_eckd_driver);
+ if (!ret)
+ wait_for_device_probe();
+
+ return ret;
}
static void __exit
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 8912358daa2f..597c6ffdb9f2 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -122,20 +122,20 @@ dasd_fba_check_characteristics(struct dasd_device *device)
struct dasd_block *block;
struct dasd_fba_private *private;
struct ccw_device *cdev = device->cdev;
- void *rdc_data;
int rc;
private = (struct dasd_fba_private *) device->private;
- if (private == NULL) {
- private = kzalloc(sizeof(struct dasd_fba_private),
- GFP_KERNEL | GFP_DMA);
- if (private == NULL) {
+ if (!private) {
+ private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
+ if (!private) {
dev_warn(&device->cdev->dev,
"Allocating memory for private DASD "
"data failed\n");
return -ENOMEM;
}
device->private = (void *) private;
+ } else {
+ memset(private, 0, sizeof(*private));
}
block = dasd_alloc_block();
if (IS_ERR(block)) {
@@ -150,8 +150,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
block->base = device;
/* Read Device Characteristics */
- rdc_data = (void *) &(private->rdc_data);
- rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
+ rc = dasd_generic_read_dev_chars(device, "FBA ", &private->rdc_data,
+ 32);
if (rc) {
DBF_EVENT(DBF_WARNING, "Read device characteristics returned "
"error %d for device: %s",
@@ -604,8 +604,14 @@ static struct dasd_discipline dasd_fba_discipline = {
static int __init
dasd_fba_init(void)
{
+ int ret;
+
ASCEBC(dasd_fba_discipline.ebcname, 4);
- return ccw_driver_register(&dasd_fba_driver);
+ ret = ccw_driver_register(&dasd_fba_driver);
+ if (!ret)
+ wait_for_device_probe();
+
+ return ret;
}
static void __exit
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index c1e487f774c6..f97ceb795078 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -173,6 +173,7 @@ struct dasd_ccw_req {
void *data; /* pointer to data area */
/* these are important for recovering erroneous requests */
+ int intrc; /* internal error, e.g. from start_IO */
struct irb irb; /* device status in case of an error */
struct dasd_ccw_req *refers; /* ERP-chain queueing. */
void *function; /* originating ERP action */
@@ -578,7 +579,7 @@ int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
void dasd_generic_handle_state_change(struct dasd_device *);
-int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
+int dasd_generic_read_dev_chars(struct dasd_device *, char *, void *, int);
char *dasd_get_sense(struct irb *);
/* externals in dasd_devmap.c */
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index cfdcf1aed33c..b21caf177e37 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -127,7 +127,7 @@ dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
found = 0;
// test if minor available
list_for_each_entry(entry, &dcssblk_devices, lh)
- if (minor == MINOR(disk_devt(entry->gd)))
+ if (minor == entry->gd->first_minor)
found++;
if (!found) break; // got unused minor
}
@@ -602,7 +602,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
dev_info->gd->private_data = dev_info;
dev_info->gd->driverfs_dev = &dev_info->dev;
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
- blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
+ blk_queue_logical_block_size(dev_info->dcssblk_queue, 4096);
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
@@ -625,7 +625,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
if (rc)
goto release_gd;
sprintf(dev_info->gd->disk_name, "dcssblk%d",
- MINOR(disk_devt(dev_info->gd)));
+ dev_info->gd->first_minor);
list_add_tail(&dev_info->lh, &dcssblk_devices);
if (!try_module_get(THIS_MODULE)) {
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 76814f3e898a..0ae0c83ef879 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -343,7 +343,7 @@ static int __init xpram_setup_blkdev(void)
goto out;
}
blk_queue_make_request(xpram_queues[i], xpram_make_request);
- blk_queue_hardsect_size(xpram_queues[i], 4096);
+ blk_queue_logical_block_size(xpram_queues[i], 4096);
}
/*
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index d028d2ee83dd..ed5396dae58e 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -64,7 +64,7 @@ static struct con3270 *condev;
#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */
#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */
#define CON_UPDATE_STATUS 4 /* Update status line. */
-#define CON_UPDATE_ALL 7
+#define CON_UPDATE_ALL 8 /* Recreate screen. */
static void con3270_update(struct con3270 *);
@@ -73,18 +73,10 @@ static void con3270_update(struct con3270 *);
*/
static void con3270_set_timer(struct con3270 *cp, int expires)
{
- if (expires == 0) {
- if (timer_pending(&cp->timer))
- del_timer(&cp->timer);
- return;
- }
- if (timer_pending(&cp->timer) &&
- mod_timer(&cp->timer, jiffies + expires))
- return;
- cp->timer.function = (void (*)(unsigned long)) con3270_update;
- cp->timer.data = (unsigned long) cp;
- cp->timer.expires = jiffies + expires;
- add_timer(&cp->timer);
+ if (expires == 0)
+ del_timer(&cp->timer);
+ else
+ mod_timer(&cp->timer, jiffies + expires);
}
/*
@@ -225,6 +217,12 @@ con3270_update(struct con3270 *cp)
spin_lock_irqsave(&cp->view.lock, flags);
updated = 0;
+ if (cp->update_flags & CON_UPDATE_ALL) {
+ con3270_rebuild_update(cp);
+ con3270_update_status(cp);
+ cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST |
+ CON_UPDATE_STATUS;
+ }
if (cp->update_flags & CON_UPDATE_ERASE) {
/* Use erase write alternate to initialize display. */
raw3270_request_set_cmd(wrq, TC_EWRITEA);
@@ -302,7 +300,6 @@ con3270_read_tasklet(struct raw3270_request *rrq)
deactivate = 1;
break;
case 0x6d: /* clear: start from scratch. */
- con3270_rebuild_update(cp);
cp->update_flags = CON_UPDATE_ALL;
con3270_set_timer(cp, 1);
break;
@@ -382,30 +379,21 @@ con3270_issue_read(struct con3270 *cp)
static int
con3270_activate(struct raw3270_view *view)
{
- unsigned long flags;
struct con3270 *cp;
cp = (struct con3270 *) view;
- spin_lock_irqsave(&cp->view.lock, flags);
- cp->nr_up = 0;
- con3270_rebuild_update(cp);
- con3270_update_status(cp);
cp->update_flags = CON_UPDATE_ALL;
con3270_set_timer(cp, 1);
- spin_unlock_irqrestore(&cp->view.lock, flags);
return 0;
}
static void
con3270_deactivate(struct raw3270_view *view)
{
- unsigned long flags;
struct con3270 *cp;
cp = (struct con3270 *) view;
- spin_lock_irqsave(&cp->view.lock, flags);
del_timer(&cp->timer);
- spin_unlock_irqrestore(&cp->view.lock, flags);
}
static int
@@ -504,6 +492,7 @@ con3270_write(struct console *co, const char *str, unsigned int count)
con3270_cline_end(cp);
}
/* Setup timer to output current console buffer after 1/10 second */
+ cp->nr_up = 0;
if (cp->view.dev && !timer_pending(&cp->timer))
con3270_set_timer(cp, HZ/10);
spin_unlock_irqrestore(&cp->view.lock,flags);
@@ -624,7 +613,8 @@ con3270_init(void)
INIT_LIST_HEAD(&condev->lines);
INIT_LIST_HEAD(&condev->update);
- init_timer(&condev->timer);
+ setup_timer(&condev->timer, (void (*)(unsigned long)) con3270_update,
+ (unsigned long) condev);
tasklet_init(&condev->readlet,
(void (*)(unsigned long)) con3270_read_tasklet,
(unsigned long) condev->read);
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 1e7967675980..47ff695255ea 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -222,7 +222,7 @@ tapeblock_setup_device(struct tape_device * device)
if (rc)
goto cleanup_queue;
- blk_queue_hardsect_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
+ blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
blk_queue_max_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
blk_queue_max_phys_segments(blkdat->request_queue, -1L);
blk_queue_max_hw_segments(blkdat->request_queue, -1L);
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index a7fe6302c982..38385677c653 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -112,7 +112,7 @@ struct tty3270 {
#define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */
#define TTY_UPDATE_INPUT 4 /* Update input line. */
#define TTY_UPDATE_STATUS 8 /* Update status line. */
-#define TTY_UPDATE_ALL 15
+#define TTY_UPDATE_ALL 16 /* Recreate screen. */
static void tty3270_update(struct tty3270 *);
@@ -121,19 +121,10 @@ static void tty3270_update(struct tty3270 *);
*/
static void tty3270_set_timer(struct tty3270 *tp, int expires)
{
- if (expires == 0) {
- if (timer_pending(&tp->timer) && del_timer(&tp->timer))
- raw3270_put_view(&tp->view);
- return;
- }
- if (timer_pending(&tp->timer) &&
- mod_timer(&tp->timer, jiffies + expires))
- return;
- raw3270_get_view(&tp->view);
- tp->timer.function = (void (*)(unsigned long)) tty3270_update;
- tp->timer.data = (unsigned long) tp;
- tp->timer.expires = jiffies + expires;
- add_timer(&tp->timer);
+ if (expires == 0)
+ del_timer(&tp->timer);
+ else
+ mod_timer(&tp->timer, jiffies + expires);
}
/*
@@ -337,7 +328,6 @@ tty3270_write_callback(struct raw3270_request *rq, void *data)
tp = (struct tty3270 *) rq->view;
if (rq->rc != 0) {
/* Write wasn't successfull. Refresh all. */
- tty3270_rebuild_update(tp);
tp->update_flags = TTY_UPDATE_ALL;
tty3270_set_timer(tp, 1);
}
@@ -366,6 +356,12 @@ tty3270_update(struct tty3270 *tp)
spin_lock(&tp->view.lock);
updated = 0;
+ if (tp->update_flags & TTY_UPDATE_ALL) {
+ tty3270_rebuild_update(tp);
+ tty3270_update_status(tp);
+ tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
+ TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
+ }
if (tp->update_flags & TTY_UPDATE_ERASE) {
/* Use erase write alternate to erase display. */
raw3270_request_set_cmd(wrq, TC_EWRITEA);
@@ -425,7 +421,6 @@ tty3270_update(struct tty3270 *tp)
xchg(&tp->write, wrq);
}
spin_unlock(&tp->view.lock);
- raw3270_put_view(&tp->view);
}
/*
@@ -570,7 +565,6 @@ tty3270_read_tasklet(struct raw3270_request *rrq)
tty3270_set_timer(tp, 1);
} else if (tp->input->string[0] == 0x6d) {
/* Display has been cleared. Redraw. */
- tty3270_rebuild_update(tp);
tp->update_flags = TTY_UPDATE_ALL;
tty3270_set_timer(tp, 1);
}
@@ -641,22 +635,20 @@ static int
tty3270_activate(struct raw3270_view *view)
{
struct tty3270 *tp;
- unsigned long flags;
tp = (struct tty3270 *) view;
- spin_lock_irqsave(&tp->view.lock, flags);
- tp->nr_up = 0;
- tty3270_rebuild_update(tp);
- tty3270_update_status(tp);
tp->update_flags = TTY_UPDATE_ALL;
tty3270_set_timer(tp, 1);
- spin_unlock_irqrestore(&tp->view.lock, flags);
return 0;
}
static void
tty3270_deactivate(struct raw3270_view *view)
{
+ struct tty3270 *tp;
+
+ tp = (struct tty3270 *) view;
+ del_timer(&tp->timer);
}
static int
@@ -743,6 +735,7 @@ tty3270_free_view(struct tty3270 *tp)
{
int pages;
+ del_timer_sync(&tp->timer);
kbd_free(tp->kbd);
raw3270_request_free(tp->kreset);
raw3270_request_free(tp->read);
@@ -889,7 +882,8 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
INIT_LIST_HEAD(&tp->update);
INIT_LIST_HEAD(&tp->rcl_lines);
tp->rcl_max = 20;
- init_timer(&tp->timer);
+ setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update,
+ (unsigned long) tp);
tasklet_init(&tp->readlet,
(void (*)(unsigned long)) tty3270_read_tasklet,
(unsigned long) tp->read);
@@ -1754,14 +1748,6 @@ static const struct tty_operations tty3270_ops = {
.set_termios = tty3270_set_termios
};
-static void tty3270_notifier(int index, int active)
-{
- if (active)
- tty_register_device(tty3270_driver, index, NULL);
- else
- tty_unregister_device(tty3270_driver, index);
-}
-
/*
* 3270 tty registration code called from tty_init().
* Most kernel services (incl. kmalloc) are available at this poimt.
@@ -1796,12 +1782,6 @@ static int __init tty3270_init(void)
return ret;
}
tty3270_driver = driver;
- ret = raw3270_register_notifier(tty3270_notifier);
- if (ret) {
- put_tty_driver(driver);
- return ret;
-
- }
return 0;
}
@@ -1810,7 +1790,6 @@ tty3270_exit(void)
{
struct tty_driver *driver;
- raw3270_unregister_notifier(tty3270_notifier);
driver = tty3270_driver;
tty3270_driver = NULL;
tty_unregister_driver(driver);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 2aebb9823044..5ec7789bd9d8 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -12,6 +12,7 @@
#define KMSG_COMPONENT "cio"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/ftrace.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -28,7 +29,7 @@
#include <asm/chpid.h>
#include <asm/airq.h>
#include <asm/isc.h>
-#include <asm/cpu.h>
+#include <asm/cputime.h>
#include <asm/fcx.h>
#include <asm/nmi.h>
#include <asm/crw.h>
@@ -626,8 +627,7 @@ out:
* handlers).
*
*/
-void
-do_IRQ (struct pt_regs *regs)
+void __irq_entry do_IRQ(struct pt_regs *regs)
{
struct tpi_info *tpi_info;
struct subchannel *sch;
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 151754d54745..bf0a24af39a0 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -114,7 +114,7 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
struct subchannel *sch;
int ret;
- if (!cdev)
+ if (!cdev || !cdev->dev.parent)
return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
@@ -122,8 +122,6 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
cdev->private->state != DEV_STATE_W4SENSE)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- if (!sch)
- return -ENODEV;
ret = cio_clear(sch);
if (ret == 0)
cdev->private->intparm = intparm;
@@ -161,11 +159,9 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
struct subchannel *sch;
int ret;
- if (!cdev)
+ if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
- if (!sch)
- return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state == DEV_STATE_VERIFY ||
@@ -339,7 +335,7 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
struct subchannel *sch;
int ret;
- if (!cdev)
+ if (!cdev || !cdev->dev.parent)
return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
@@ -347,8 +343,6 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
cdev->private->state != DEV_STATE_W4SENSE)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- if (!sch)
- return -ENODEV;
ret = cio_halt(sch);
if (ret == 0)
cdev->private->intparm = intparm;
@@ -372,11 +366,9 @@ int ccw_device_resume(struct ccw_device *cdev)
{
struct subchannel *sch;
- if (!cdev)
+ if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
- if (!sch)
- return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
@@ -471,11 +463,11 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
{
struct subchannel *sch;
- sch = to_subchannel(cdev->dev.parent);
- if (!sch)
+ if (!cdev->dev.parent)
return 0;
- else
- return sch->lpm;
+
+ sch = to_subchannel(cdev->dev.parent);
+ return sch->lpm;
}
/*
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index accd957454e7..d79cf5bf0e62 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -881,42 +881,26 @@ no_handler:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
}
-static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
- int dstat)
+static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
+ int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
- DBF_ERROR("EQ:ck con");
- goto error;
- }
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
- if (!(dstat & DEV_STAT_DEV_END)) {
- DBF_ERROR("EQ:no dev");
+ if (cstat)
goto error;
- }
-
- if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- DBF_ERROR("EQ: bad io");
+ if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END))
goto error;
- }
- return 0;
+ if (!(dstat & DEV_STAT_DEV_END))
+ goto error;
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
+ return;
+
error:
DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
-
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- return 1;
-}
-
-static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
- int dstat)
-{
- struct qdio_irq *irq_ptr = cdev->private->qdio_data;
-
- DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
- if (!qdio_establish_check_errors(cdev, cstat, dstat))
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
}
/* qdio interrupt handler */
@@ -946,7 +930,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
}
}
qdio_irq_check_sense(irq_ptr, irb);
-
cstat = irb->scsw.cmd.cstat;
dstat = irb->scsw.cmd.dstat;
@@ -954,22 +937,19 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
case QDIO_IRQ_STATE_INACTIVE:
qdio_establish_handle_irq(cdev, cstat, dstat);
break;
-
case QDIO_IRQ_STATE_CLEANUP:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
break;
-
case QDIO_IRQ_STATE_ESTABLISHED:
case QDIO_IRQ_STATE_ACTIVE:
if (cstat & SCHN_STAT_PCI) {
qdio_int_handler_pci(irq_ptr);
return;
}
- if ((cstat & ~SCHN_STAT_PCI) || dstat) {
+ if (cstat || dstat)
qdio_handle_activate_check(cdev, intparm, cstat,
dstat);
- break;
- }
+ break;
default:
WARN_ON(1);
}
@@ -1514,7 +1494,7 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
(count > QDIO_MAX_BUFFERS_PER_Q) ||
- (q_nr > QDIO_MAX_QUEUES_PER_IRQ))
+ (q_nr >= QDIO_MAX_QUEUES_PER_IRQ))
return -EINVAL;
if (!count)
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
index 136d0f0b1e93..eff943923c6f 100644
--- a/drivers/s390/cio/qdio_perf.c
+++ b/drivers/s390/cio/qdio_perf.c
@@ -25,18 +25,6 @@ struct qdio_perf_stats perf_stats;
static struct proc_dir_entry *qdio_perf_pde;
#endif
-inline void qdio_perf_stat_inc(atomic_long_t *count)
-{
- if (qdio_performance_stats)
- atomic_long_inc(count);
-}
-
-inline void qdio_perf_stat_dec(atomic_long_t *count)
-{
- if (qdio_performance_stats)
- atomic_long_dec(count);
-}
-
/*
* procfs functions
*/
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h
index 7821ac4fa517..ff4504ce1e3c 100644
--- a/drivers/s390/cio/qdio_perf.h
+++ b/drivers/s390/cio/qdio_perf.h
@@ -9,7 +9,6 @@
#define QDIO_PERF_H
#include <linux/types.h>
-#include <linux/device.h>
#include <asm/atomic.h>
struct qdio_perf_stats {
@@ -50,10 +49,13 @@ struct qdio_perf_stats {
extern struct qdio_perf_stats perf_stats;
extern int qdio_performance_stats;
+static inline void qdio_perf_stat_inc(atomic_long_t *count)
+{
+ if (qdio_performance_stats)
+ atomic_long_inc(count);
+}
+
int qdio_setup_perf_stats(void);
void qdio_remove_perf_stats(void);
-extern void qdio_perf_stat_inc(atomic_long_t *count);
-extern void qdio_perf_stat_dec(atomic_long_t *count);
-
#endif
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index cbc8566fab70..e38e5d306faf 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -173,8 +173,9 @@ static void kvm_notify(struct virtqueue *vq)
* this device and sets it up.
*/
static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
- unsigned index,
- void (*callback)(struct virtqueue *vq))
+ unsigned index,
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
{
struct kvm_device *kdev = to_kvmdev(vdev);
struct kvm_vqconfig *config;
@@ -194,7 +195,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN,
vdev, (void *) config->address,
- kvm_notify, callback);
+ kvm_notify, callback, name);
if (!vq) {
err = -ENOMEM;
goto unmap;
@@ -226,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq)
KVM_S390_VIRTIO_RING_ALIGN));
}
+static void kvm_del_vqs(struct virtio_device *vdev)
+{
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ kvm_del_vq(vq);
+}
+
+static int kvm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct kvm_device *kdev = to_kvmdev(vdev);
+ int i;
+
+ /* We must have this many virtqueues. */
+ if (nvqs > kdev->desc->num_vq)
+ return -ENOENT;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = kvm_find_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i]))
+ goto error;
+ }
+ return 0;
+
+error:
+ kvm_del_vqs(vdev);
+ return PTR_ERR(vqs[i]);
+}
+
/*
* The config ops structure as defined by virtio config
*/
@@ -237,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = {
.get_status = kvm_get_status,
.set_status = kvm_set_status,
.reset = kvm_reset,
- .find_vq = kvm_find_vq,
- .del_vq = kvm_del_vq,
+ .find_vqs = kvm_find_vqs,
+ .del_vqs = kvm_del_vqs,
};
/*
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 616c60ffcf2c..3ac27ee47396 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -97,9 +97,7 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
ccw_device_set_online(adapter->ccw_device);
zfcp_erp_wait(adapter);
- wait_event(adapter->erp_done_wqh,
- !(atomic_read(&unit->status) &
- ZFCP_STATUS_UNIT_SCSI_WORK_PENDING));
+ flush_work(&unit->scsi_work);
down(&zfcp_data.config_sema);
zfcp_unit_put(unit);
@@ -279,6 +277,7 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
atomic_set(&unit->refcount, 0);
init_waitqueue_head(&unit->remove_wq);
+ INIT_WORK(&unit->scsi_work, zfcp_scsi_scan);
unit->port = port;
unit->fcp_lun = fcp_lun;
@@ -525,6 +524,8 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+ zfcp_fc_nameserver_init(adapter);
+
if (!zfcp_adapter_scsi_register(adapter))
return 0;
@@ -553,7 +554,6 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
cancel_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
- cancel_delayed_work_sync(&adapter->nsp.work);
zfcp_adapter_scsi_unregister(adapter);
sysfs_remove_group(&adapter->ccw_device->dev.kobj,
&zfcp_sysfs_adapter_attrs);
@@ -671,8 +671,7 @@ void zfcp_port_dequeue(struct zfcp_port *port)
list_del(&port->list);
write_unlock_irq(&zfcp_data.config_lock);
if (port->rport)
- fc_remote_port_delete(port->rport);
- port->rport = NULL;
+ port->rport->dd_data = NULL;
zfcp_adapter_put(port->adapter);
sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
device_unregister(&port->sysfs_device);
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index cfb0dcb6e3ff..b2fe5cdbcaee 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -11,6 +11,24 @@
#include "zfcp_ext.h"
+#define ZFCP_MODEL_PRIV 0x4
+
+static struct ccw_device_id zfcp_ccw_device_id[] = {
+ { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
+ { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_MODEL_PRIV) },
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
+
+/**
+ * zfcp_ccw_priv_sch - check if subchannel is privileged
+ * @adapter: Adapter/Subchannel to check
+ */
+int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter)
+{
+ return adapter->ccw_device->id.dev_model == ZFCP_MODEL_PRIV;
+}
+
/**
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
@@ -108,7 +126,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
adapter->req_no = 0;
- zfcp_fc_nameserver_init(adapter);
zfcp_erp_modify_adapter_status(adapter, "ccsonl1", NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
@@ -177,8 +194,8 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
"ccnoti4", NULL);
break;
case CIO_BOXED:
- dev_warn(&adapter->ccw_device->dev,
- "The ccw device did not respond in time.\n");
+ dev_warn(&adapter->ccw_device->dev, "The FCP device "
+ "did not respond within the specified time\n");
zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL);
break;
}
@@ -200,14 +217,6 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev)
up(&zfcp_data.config_sema);
}
-static struct ccw_device_id zfcp_ccw_device_id[] = {
- { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
- { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
- {},
-};
-
-MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
-
static struct ccw_driver zfcp_ccw_driver = {
.owner = THIS_MODULE,
.name = "zfcp",
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index 10cbfd172a28..8305c874e86f 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -4,7 +4,7 @@
* Userspace interface for accessing the
* Access Control Lists / Control File Data Channel
*
- * Copyright IBM Corporation 2008
+ * Copyright IBM Corporation 2008, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -197,6 +197,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
retval = -ENXIO;
goto free_buffer;
}
+ zfcp_adapter_get(adapter);
retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
data_user->control_file);
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 0a1a5dd8d018..b99b87ce5a39 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -163,7 +163,7 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
}
response->fsf_command = fsf_req->fsf_command;
- response->fsf_reqid = (unsigned long)fsf_req;
+ response->fsf_reqid = fsf_req->req_id;
response->fsf_seqno = fsf_req->seq_no;
response->fsf_issued = fsf_req->issued;
response->fsf_prot_status = qtcb->prefix.prot_status;
@@ -737,7 +737,7 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
spin_lock_irqsave(&adapter->san_dbf_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE);
- r->fsf_reqid = (unsigned long)fsf_req;
+ r->fsf_reqid = fsf_req->req_id;
r->fsf_seqno = fsf_req->seq_no;
r->s_id = fc_host_port_id(adapter->scsi_host);
r->d_id = wka_port->d_id;
@@ -773,7 +773,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
spin_lock_irqsave(&adapter->san_dbf_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
- r->fsf_reqid = (unsigned long)fsf_req;
+ r->fsf_reqid = fsf_req->req_id;
r->fsf_seqno = fsf_req->seq_no;
r->s_id = wka_port->d_id;
r->d_id = fc_host_port_id(adapter->scsi_host);
@@ -803,7 +803,7 @@ static void zfcp_san_dbf_event_els(const char *tag, int level,
spin_lock_irqsave(&adapter->san_dbf_lock, flags);
memset(rec, 0, sizeof(*rec));
strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE);
- rec->fsf_reqid = (unsigned long)fsf_req;
+ rec->fsf_reqid = fsf_req->req_id;
rec->fsf_seqno = fsf_req->seq_no;
rec->s_id = s_id;
rec->d_id = d_id;
@@ -965,7 +965,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
ZFCP_DBF_SCSI_FCP_SNS_INFO);
}
- rec->fsf_reqid = (unsigned long)fsf_req;
+ rec->fsf_reqid = fsf_req->req_id;
rec->fsf_seqno = fsf_req->seq_no;
rec->fsf_issued = fsf_req->issued;
}
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index a0318630f047..2074d45dbf6c 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -47,13 +47,6 @@
/********************* CIO/QDIO SPECIFIC DEFINES *****************************/
-/* Adapter Identification Parameters */
-#define ZFCP_CONTROL_UNIT_TYPE 0x1731
-#define ZFCP_CONTROL_UNIT_MODEL 0x03
-#define ZFCP_DEVICE_TYPE 0x1732
-#define ZFCP_DEVICE_MODEL 0x03
-#define ZFCP_DEVICE_MODEL_PRIV 0x04
-
/* DMQ bug workaround: don't use last SBALE */
#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
@@ -255,7 +248,6 @@ enum zfcp_wka_status {
/* logical unit status */
#define ZFCP_STATUS_UNIT_SHARED 0x00000004
#define ZFCP_STATUS_UNIT_READONLY 0x00000008
-#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020
/* FSF request status (this does not have a common part) */
#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002
@@ -530,6 +522,7 @@ struct zfcp_unit {
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
struct zfcp_latencies latencies;
+ struct work_struct scsi_work;
};
/* FSF request */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 631bdb1dfd6c..e50ea465bc2b 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -719,6 +719,7 @@ static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act)
zfcp_qdio_close(adapter);
zfcp_fsf_req_dismiss_all(adapter);
adapter->fsf_req_seq_no = 0;
+ zfcp_fc_wka_port_force_offline(&adapter->nsp);
/* all ports and units are closed */
zfcp_erp_modify_adapter_status(adapter, "erascl1", NULL,
ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
@@ -879,6 +880,7 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
zfcp_port_put(port);
return ZFCP_ERP_CONTINUES;
}
+ /* fall through */
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
if (!port->d_id)
return ZFCP_ERP_FAILED;
@@ -893,8 +895,13 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
act->step = ZFCP_ERP_STEP_PORT_CLOSING;
return ZFCP_ERP_CONTINUES;
}
- /* fall through otherwise */
}
+ if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) {
+ port->d_id = 0;
+ _zfcp_erp_port_reopen(port, 0, "erpsoc1", NULL);
+ return ZFCP_ERP_EXIT;
+ }
+ /* fall through otherwise */
}
return ZFCP_ERP_FAILED;
}
@@ -1176,48 +1183,6 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
}
-struct zfcp_erp_add_work {
- struct zfcp_unit *unit;
- struct work_struct work;
-};
-
-static void zfcp_erp_scsi_scan(struct work_struct *work)
-{
- struct zfcp_erp_add_work *p =
- container_of(work, struct zfcp_erp_add_work, work);
- struct zfcp_unit *unit = p->unit;
- struct fc_rport *rport = unit->port->rport;
-
- if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
- scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
- scsilun_to_int((struct scsi_lun *)&unit->fcp_lun), 0);
- atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
- zfcp_unit_put(unit);
- wake_up(&unit->port->adapter->erp_done_wqh);
- kfree(p);
-}
-
-static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
-{
- struct zfcp_erp_add_work *p;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p) {
- dev_err(&unit->port->adapter->ccw_device->dev,
- "Registering unit 0x%016Lx on port 0x%016Lx failed\n",
- (unsigned long long)unit->fcp_lun,
- (unsigned long long)unit->port->wwpn);
- return;
- }
-
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
- INIT_WORK(&p->work, zfcp_erp_scsi_scan);
- p->unit = unit;
- if (!queue_work(zfcp_data.work_queue, &p->work))
- zfcp_unit_put(unit);
-}
-
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -1226,11 +1191,11 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
switch (act->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- flush_work(&port->rport_work);
if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
- if (!(atomic_read(&unit->status) &
- ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
- zfcp_erp_schedule_work(unit);
+ zfcp_unit_get(unit);
+ if (scsi_queue_work(unit->port->adapter->scsi_host,
+ &unit->scsi_work) <= 0)
+ zfcp_unit_put(unit);
}
zfcp_unit_put(unit);
break;
@@ -1352,6 +1317,11 @@ static int zfcp_erp_thread(void *data)
while (!(atomic_read(&adapter->status) &
ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL)) {
+
+ zfcp_rec_dbf_event_thread_lock("erthrd1", adapter);
+ ignore = down_interruptible(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock("erthrd2", adapter);
+
write_lock_irqsave(&adapter->erp_lock, flags);
next = adapter->erp_ready_head.next;
write_unlock_irqrestore(&adapter->erp_lock, flags);
@@ -1363,10 +1333,6 @@ static int zfcp_erp_thread(void *data)
if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED)
zfcp_erp_wakeup(adapter);
}
-
- zfcp_rec_dbf_event_thread_lock("erthrd1", adapter);
- ignore = down_interruptible(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock("erthrd2", adapter);
}
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index f6399ca97bcb..120a9a1c81f7 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -27,6 +27,7 @@ extern int zfcp_sg_setup_table(struct scatterlist *, int);
/* zfcp_ccw.c */
extern int zfcp_ccw_register(void);
+extern int zfcp_ccw_priv_sch(struct zfcp_adapter *);
extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
/* zfcp_cfdc.c */
@@ -106,6 +107,7 @@ extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
extern void zfcp_test_link(struct zfcp_port *);
extern void zfcp_fc_link_test_work(struct work_struct *);
extern void zfcp_fc_nameserver_init(struct zfcp_adapter *);
+extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *);
/* zfcp_fsf.c */
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
@@ -158,6 +160,7 @@ extern void zfcp_scsi_rport_work(struct work_struct *);
extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);
+extern void zfcp_scsi_scan(struct work_struct *);
/* zfcp_sysfs.c */
extern struct attribute_group zfcp_sysfs_unit_attrs;
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index e8d032b9dfbd..bb2752b4130f 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -98,13 +98,6 @@ static void zfcp_wka_port_offline(struct work_struct *work)
struct zfcp_wka_port *wka_port =
container_of(dw, struct zfcp_wka_port, work);
- /* Don't wait forvever. If the wka_port is too busy take it offline
- through a new call later */
- if (!wait_event_timeout(wka_port->completion_wq,
- atomic_read(&wka_port->refcount) == 0,
- HZ >> 1))
- return;
-
mutex_lock(&wka_port->mutex);
if ((atomic_read(&wka_port->refcount) != 0) ||
(wka_port->status != ZFCP_WKA_PORT_ONLINE))
@@ -142,6 +135,14 @@ void zfcp_fc_nameserver_init(struct zfcp_adapter *adapter)
INIT_DELAYED_WORK(&wka_port->work, zfcp_wka_port_offline);
}
+void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
+{
+ cancel_delayed_work_sync(&wka->work);
+ mutex_lock(&wka->mutex);
+ wka->status = ZFCP_WKA_PORT_OFFLINE;
+ mutex_unlock(&wka->mutex);
+}
+
static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
struct fcp_rscn_element *elem)
{
@@ -149,9 +150,14 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
struct zfcp_port *port;
read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &fsf_req->adapter->port_list_head, list)
+ list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
if ((port->d_id & range) == (elem->nport_did & range))
zfcp_test_link(port);
+ if (!port->d_id)
+ zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fcrscn1", NULL);
+ }
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
@@ -372,7 +378,8 @@ static void zfcp_fc_adisc_handler(unsigned long data)
if (adisc->els.status) {
/* request rejected or timed out */
- zfcp_erp_port_forced_reopen(port, 0, "fcadh_1", NULL);
+ zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fcadh_1", NULL);
goto out;
}
@@ -431,11 +438,6 @@ void zfcp_fc_link_test_work(struct work_struct *work)
container_of(work, struct zfcp_port, test_link_work);
int retval;
- if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) {
- zfcp_port_put(port);
- return; /* port erp is running and will update rport status */
- }
-
zfcp_port_get(port);
port->rport_task = RPORT_DEL;
zfcp_scsi_rport_work(&port->rport_work);
@@ -542,6 +544,9 @@ static void zfcp_validate_port(struct zfcp_port *port)
{
struct zfcp_adapter *adapter = port->adapter;
+ if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC))
+ return;
+
atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
if ((port->supported_classes != 0) ||
@@ -602,10 +607,8 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
if (acc->wwpn == fc_host_port_name(adapter->scsi_host))
continue;
port = zfcp_get_port_by_wwpn(adapter, acc->wwpn);
- if (port) {
- zfcp_port_get(port);
+ if (port)
continue;
- }
port = zfcp_port_enqueue(adapter, acc->wwpn,
ZFCP_STATUS_COMMON_NOESC, d_id);
@@ -637,7 +640,8 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
- if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
+ if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT &&
+ fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
return 0;
ret = zfcp_wka_port_get(&adapter->nsp);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index b29f3121b666..e6dae3744e79 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -172,12 +172,16 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id,
struct fsf_link_down_info *link_down)
{
struct zfcp_adapter *adapter = req->adapter;
+ unsigned long flags;
if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
return;
atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
zfcp_scsi_schedule_rports_block(adapter);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (!link_down)
goto out;
@@ -522,6 +526,7 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
break;
case FSF_TOPO_AL:
fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+ /* fall through */
default:
dev_err(&adapter->ccw_device->dev,
"Unknown or unsupported arbitrated loop "
@@ -645,30 +650,30 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
}
}
-static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
- __releases(&adapter->req_q_lock)
- __acquires(&adapter->req_q_lock)
+static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter)
{
struct zfcp_qdio_queue *req_q = &adapter->req_q;
- long ret;
- if (atomic_read(&req_q->count) <= -REQUEST_LIST_SIZE)
- return -EIO;
- if (atomic_read(&req_q->count) > 0)
- return 0;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (atomic_read(&req_q->count))
+ return 1;
+ spin_unlock_bh(&adapter->req_q_lock);
+ return 0;
+}
+
+static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+{
+ long ret;
- atomic_dec(&req_q->count);
spin_unlock_bh(&adapter->req_q_lock);
ret = wait_event_interruptible_timeout(adapter->request_wq,
- atomic_read(&req_q->count) >= 0,
- 5 * HZ);
- spin_lock_bh(&adapter->req_q_lock);
- atomic_inc(&req_q->count);
-
+ zfcp_fsf_sbal_check(adapter), 5 * HZ);
if (ret > 0)
return 0;
if (!ret)
atomic_inc(&adapter->qdio_outb_full);
+
+ spin_lock_bh(&adapter->req_q_lock);
return -EIO;
}
@@ -766,8 +771,9 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
{
struct zfcp_adapter *adapter = req->adapter;
- unsigned long flags;
- int idx;
+ unsigned long flags;
+ int idx;
+ int with_qtcb = (req->qtcb != NULL);
/* put allocated FSF request into hash table */
spin_lock_irqsave(&adapter->req_list_lock, flags);
@@ -789,7 +795,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
}
/* Don't increase for unsolicited status */
- if (req->qtcb)
+ if (with_qtcb)
adapter->fsf_req_seq_no++;
adapter->req_no++;
@@ -892,6 +898,7 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
switch (fsq->word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
zfcp_test_link(unit->port);
+ /* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
@@ -988,6 +995,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
break;
case FSF_PORT_HANDLE_NOT_VALID:
zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req);
+ /* fall through */
case FSF_GENERIC_COMMAND_REJECTED:
case FSF_PAYLOAD_SIZE_MISMATCH:
case FSF_REQUEST_SIZE_TOO_LARGE:
@@ -1253,13 +1261,13 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
spin_lock_bh(&adapter->req_q_lock);
if (zfcp_fsf_req_sbal_get(adapter))
- goto out;
+ goto out_unlock;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
0, NULL);
if (IS_ERR(req)) {
retval = PTR_ERR(req);
- goto out;
+ goto out_unlock;
}
sbale = zfcp_qdio_sbale_req(req);
@@ -1278,14 +1286,16 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
retval = zfcp_fsf_req_send(req);
-out:
spin_unlock_bh(&adapter->req_q_lock);
if (!retval)
wait_event(req->completion_wq,
req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
zfcp_fsf_req_free(req);
+ return retval;
+out_unlock:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
@@ -1352,13 +1362,13 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
spin_lock_bh(&adapter->req_q_lock);
if (zfcp_fsf_req_sbal_get(adapter))
- goto out;
+ goto out_unlock;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
NULL);
if (IS_ERR(req)) {
retval = PTR_ERR(req);
- goto out;
+ goto out_unlock;
}
if (data)
@@ -1371,14 +1381,18 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
req->handler = zfcp_fsf_exchange_port_data_handler;
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
retval = zfcp_fsf_req_send(req);
-out:
spin_unlock_bh(&adapter->req_q_lock);
+
if (!retval)
wait_event(req->completion_wq,
req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
zfcp_fsf_req_free(req);
return retval;
+
+out_unlock:
+ spin_unlock_bh(&adapter->req_q_lock);
+ return retval;
}
static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
@@ -1388,7 +1402,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
struct fsf_plogi *plogi;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
- return;
+ goto out;
switch (header->fsf_status) {
case FSF_PORT_ALREADY_OPEN:
@@ -1450,6 +1464,9 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
+
+out:
+ zfcp_port_put(port);
}
/**
@@ -1462,6 +1479,7 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
struct qdio_buffer_element *sbale;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_fsf_req *req;
+ struct zfcp_port *port = erp_action->port;
int retval = -EIO;
spin_lock_bh(&adapter->req_q_lock);
@@ -1482,16 +1500,18 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_open_port_handler;
- req->qtcb->bottom.support.d_id = erp_action->port->d_id;
- req->data = erp_action->port;
+ req->qtcb->bottom.support.d_id = port->d_id;
+ req->data = port;
req->erp_action = erp_action;
erp_action->fsf_req = req;
+ zfcp_port_get(port);
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
+ zfcp_port_put(port);
}
out:
spin_unlock_bh(&adapter->req_q_lock);
@@ -1579,8 +1599,10 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
dev_warn(&req->adapter->ccw_device->dev,
"Opening WKA port 0x%x failed\n", wka_port->d_id);
+ /* fall through */
case FSF_ADAPTER_STATUS_AVAILABLE:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ /* fall through */
case FSF_ACCESS_DENIED:
wka_port->status = ZFCP_WKA_PORT_OFFLINE;
break;
@@ -1865,7 +1887,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) &&
(adapter->adapter_features & FSF_FEATURE_LUN_SHARING) &&
- (adapter->ccw_device->id.dev_model != ZFCP_DEVICE_MODEL_PRIV)) {
+ !zfcp_ccw_priv_sch(adapter)) {
exclusive = (bottom->lun_access_info &
FSF_UNIT_ACCESS_EXCLUSIVE);
readwrite = (bottom->lun_access_info &
@@ -2303,7 +2325,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
{
struct zfcp_fsf_req *req;
struct fcp_cmnd_iu *fcp_cmnd_iu;
- unsigned int sbtype;
+ unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
int real_bytes, retval = -EIO;
struct zfcp_adapter *adapter = unit->port->adapter;
@@ -2345,11 +2367,9 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
switch (scsi_cmnd->sc_data_direction) {
case DMA_NONE:
req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
- sbtype = SBAL_FLAGS0_TYPE_READ;
break;
case DMA_FROM_DEVICE:
req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
- sbtype = SBAL_FLAGS0_TYPE_READ;
fcp_cmnd_iu->rddata = 1;
break;
case DMA_TO_DEVICE:
@@ -2358,8 +2378,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
fcp_cmnd_iu->wddata = 1;
break;
case DMA_BIDIRECTIONAL:
- default:
- retval = -EIO;
goto failed_scsi_cmnd;
}
@@ -2383,9 +2401,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
scsi_sglist(scsi_cmnd),
FSF_MAX_SBALS_PER_REQ);
if (unlikely(real_bytes < 0)) {
- if (req->sbal_number < FSF_MAX_SBALS_PER_REQ)
- retval = -EIO;
- else {
+ if (req->sbal_number >= FSF_MAX_SBALS_PER_REQ) {
dev_err(&adapter->ccw_device->dev,
"Oversize data package, unit 0x%016Lx "
"on port 0x%016Lx closed\n",
@@ -2472,8 +2488,6 @@ out:
static void zfcp_fsf_control_file_handler(struct zfcp_fsf_req *req)
{
- if (req->qtcb->header.fsf_status != FSF_GOOD)
- req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
/**
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 58201e1ae478..7d0da230eb63 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -12,6 +12,10 @@
#include "zfcp_ext.h"
#include <asm/atomic.h>
+static unsigned int default_depth = 32;
+module_param_named(queue_depth, default_depth, uint, 0600);
+MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices");
+
/* Find start of Sense Information in FCP response unit*/
char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
{
@@ -24,6 +28,12 @@ char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
return fcp_sns_info_ptr;
}
+static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth)
+{
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ return sdev->queue_depth;
+}
+
static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
@@ -34,7 +44,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
{
if (sdp->tagged_supported)
- scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, 32);
+ scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, default_depth);
else
scsi_adjust_queue_depth(sdp, 0, 1);
return 0;
@@ -171,7 +181,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
write_unlock_irqrestore(&adapter->abort_lock, flags);
zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL,
old_req_id);
- return SUCCESS;
+ return FAILED; /* completion could be in progress */
}
old_req->data = NULL;
@@ -486,10 +496,12 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
*/
static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
{
- struct zfcp_port *port = rport->dd_data;
+ struct zfcp_port *port;
write_lock_irq(&zfcp_data.config_lock);
- port->rport = NULL;
+ port = rport->dd_data;
+ if (port)
+ port->rport = NULL;
write_unlock_irq(&zfcp_data.config_lock);
}
@@ -503,9 +515,18 @@ static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
*/
static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
{
- struct zfcp_port *port = rport->dd_data;
+ struct zfcp_port *port;
- zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
+ write_lock_irq(&zfcp_data.config_lock);
+ port = rport->dd_data;
+ if (port)
+ zfcp_port_get(port);
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ if (port) {
+ zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
+ zfcp_port_put(port);
+ }
}
static void zfcp_scsi_rport_register(struct zfcp_port *port)
@@ -534,8 +555,10 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
static void zfcp_scsi_rport_block(struct zfcp_port *port)
{
- if (port->rport)
- fc_remote_port_delete(port->rport);
+ struct fc_rport *rport = port->rport;
+
+ if (rport)
+ fc_remote_port_delete(rport);
}
void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
@@ -583,6 +606,23 @@ void zfcp_scsi_rport_work(struct work_struct *work)
}
+void zfcp_scsi_scan(struct work_struct *work)
+{
+ struct zfcp_unit *unit = container_of(work, struct zfcp_unit,
+ scsi_work);
+ struct fc_rport *rport;
+
+ flush_work(&unit->port->rport_work);
+ rport = unit->port->rport;
+
+ if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
+ scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
+ scsilun_to_int((struct scsi_lun *)
+ &unit->fcp_lun), 0);
+
+ zfcp_unit_put(unit);
+}
+
struct fc_function_template zfcp_transport_functions = {
.show_starget_port_id = 1,
.show_starget_port_name = 1,
@@ -617,6 +657,7 @@ struct zfcp_data zfcp_data = {
.name = "zfcp",
.module = THIS_MODULE,
.proc_name = "zfcp",
+ .change_queue_depth = zfcp_scsi_change_queue_depth,
.slave_alloc = zfcp_scsi_slave_alloc,
.slave_configure = zfcp_scsi_slave_configure,
.slave_destroy = zfcp_scsi_slave_destroy,
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 9a3b8e261c0a..3e51e64d1108 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -254,12 +254,21 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
write_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
- if (unit && (atomic_read(&unit->refcount) == 0)) {
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
- list_move(&unit->list, &unit_remove_lh);
- } else
- unit = NULL;
+ if (unit) {
+ write_unlock_irq(&zfcp_data.config_lock);
+ /* wait for possible timeout during SCSI probe */
+ flush_work(&unit->scsi_work);
+ write_lock_irq(&zfcp_data.config_lock);
+
+ if (atomic_read(&unit->refcount) == 0) {
+ zfcp_unit_get(unit);
+ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
+ &unit->status);
+ list_move(&unit->list, &unit_remove_lh);
+ } else {
+ unit = NULL;
+ }
+ }
write_unlock_irq(&zfcp_data.config_lock);
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
index 8b7983aba8f7..36c21b19e5d7 100644
--- a/drivers/scsi/3w-9xxx.c
+++ b/drivers/scsi/3w-9xxx.c
@@ -1978,7 +1978,8 @@ static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
{
struct scsi_cmnd *cmd = tw_dev->srb[request_id];
- scsi_dma_unmap(cmd);
+ if (cmd->SCp.phase == TW_PHASE_SGLIST)
+ scsi_dma_unmap(cmd);
} /* End twa_unmap_scsi_data() */
/* scsi_host_template initializer */
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index c03f1d2c9e2e..faa0fcfed71e 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -6,7 +6,7 @@
Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com>
- Copyright (C) 1999-2007 3ware Inc.
+ Copyright (C) 1999-2009 3ware Inc.
Kernel compatiblity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
@@ -1294,7 +1294,8 @@ static void tw_unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
{
dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n");
- scsi_dma_unmap(cmd);
+ if (cmd->SCp.phase == TW_PHASE_SGLIST)
+ scsi_dma_unmap(cmd);
} /* End tw_unmap_scsi_data() */
/* This function will reset a device extension */
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
index 8e71e5e122b3..a5a2ba2561d9 100644
--- a/drivers/scsi/3w-xxxx.h
+++ b/drivers/scsi/3w-xxxx.h
@@ -6,7 +6,7 @@
Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com>
- Copyright (C) 1999-2007 3ware Inc.
+ Copyright (C) 1999-2009 3ware Inc.
Kernel compatiblity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 8ed2990c826e..6a19ed9a1194 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -191,20 +191,19 @@ config SCSI_ENCLOSURE
it has an enclosure device. Selecting this option will just allow
certain enclosure conditions to be reported and is not required.
-comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
- depends on SCSI
-
config SCSI_MULTI_LUN
bool "Probe all LUNs on each SCSI device"
depends on SCSI
help
- If you have a SCSI device that supports more than one LUN (Logical
- Unit Number), e.g. a CD jukebox, and only one LUN is detected, you
- can say Y here to force the SCSI driver to probe for multiple LUNs.
- A SCSI device with multiple LUNs acts logically like multiple SCSI
- devices. The vast majority of SCSI devices have only one LUN, and
- so most people can say N here. The max_luns boot/module parameter
- allows to override this setting.
+ Some devices support more than one LUN (Logical Unit Number) in order
+ to allow access to several media, e.g. CD jukebox, USB card reader,
+ mobile phone in mass storage mode. This option forces the kernel to
+ probe for all LUNs by default. This setting can be overriden by
+ max_luns boot/module parameter. Note that this option does not affect
+ devices conforming to SCSI-3 or higher as they can explicitely report
+ their number of LUNs. It is safe to say Y here unless you have one of
+ those rare devices which reacts in an unexpected way when probed for
+ multiple LUNs.
config SCSI_CONSTANTS
bool "Verbose SCSI error reporting (kernel size +=12K)"
@@ -355,6 +354,7 @@ config ISCSI_TCP
http://open-iscsi.org
source "drivers/scsi/cxgb3i/Kconfig"
+source "drivers/scsi/bnx2i/Kconfig"
config SGIWD93_SCSI
tristate "SGI WD93C93 SCSI Driver"
@@ -508,6 +508,7 @@ config SCSI_AIC7XXX_OLD
source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
source "drivers/scsi/aic94xx/Kconfig"
+source "drivers/scsi/mvsas/Kconfig"
config SCSI_DPT_I2O
tristate "Adaptec I2O RAID support "
@@ -628,6 +629,17 @@ config FCOE
---help---
Fibre Channel over Ethernet module
+config FCOE_FNIC
+ tristate "Cisco FNIC Driver"
+ depends on PCI && X86
+ select LIBFC
+ help
+ This is support for the Cisco PCI-Express FCoE HBA.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>.
+ The module will be called fnic.
+
config SCSI_DMX3191D
tristate "DMX3191D SCSI support"
depends on PCI && SCSI
@@ -1039,16 +1051,6 @@ config SCSI_IZIP_SLOW_CTR
Generally, saying N is fine.
-config SCSI_MVSAS
- tristate "Marvell 88SE6440 SAS/SATA support"
- depends on PCI && SCSI
- select SCSI_SAS_LIBSAS
- help
- This driver supports Marvell SAS/SATA PCI devices.
-
- To compiler this driver as a module, choose M here: the module
- will be called mvsas.
-
config SCSI_NCR53C406A
tristate "NCR53c406a SCSI support"
depends on ISA && SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index e7c861ac417d..25429ea63d0a 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SCSI_DH) += device_handler/
obj-$(CONFIG_LIBFC) += libfc/
obj-$(CONFIG_LIBFCOE) += fcoe/
obj-$(CONFIG_FCOE) += fcoe/
+obj-$(CONFIG_FCOE_FNIC) += fnic/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o
@@ -125,9 +126,10 @@ obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/
obj-$(CONFIG_SCSI_IBMVFC) += ibmvscsi/
obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
-obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
+obj-$(CONFIG_SCSI_MVSAS) += mvsas/
obj-$(CONFIG_PS3_ROM) += ps3rom.o
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
+obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c
index c889d8458684..1cdf09a4779a 100644
--- a/drivers/scsi/NCR_D700.c
+++ b/drivers/scsi/NCR_D700.c
@@ -224,7 +224,7 @@ NCR_D700_probe_one(struct NCR_D700_private *p, int siop, int irq,
return ret;
}
-static int
+static irqreturn_t
NCR_D700_intr(int irq, void *data)
{
struct NCR_D700_private *p = (struct NCR_D700_private *)data;
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
new file mode 100644
index 000000000000..2fceb19eb27b
--- /dev/null
+++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
@@ -0,0 +1,155 @@
+/* 57xx_iscsi_constants.h: Broadcom NetXtreme II iSCSI HSI
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+#ifndef __57XX_ISCSI_CONSTANTS_H_
+#define __57XX_ISCSI_CONSTANTS_H_
+
+/**
+* This file defines HSI constants for the iSCSI flows
+*/
+
+/* iSCSI request op codes */
+#define ISCSI_OPCODE_CLEANUP_REQUEST (7)
+
+/* iSCSI response/messages op codes */
+#define ISCSI_OPCODE_CLEANUP_RESPONSE (0x27)
+#define ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION (0)
+
+/* iSCSI task types */
+#define ISCSI_TASK_TYPE_READ (0)
+#define ISCSI_TASK_TYPE_WRITE (1)
+#define ISCSI_TASK_TYPE_MPATH (2)
+
+/* initial CQ sequence numbers */
+#define ISCSI_INITIAL_SN (1)
+
+/* KWQ (kernel work queue) layer codes */
+#define ISCSI_KWQE_LAYER_CODE (6)
+
+/* KWQ (kernel work queue) request op codes */
+#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN1 (0)
+#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN2 (1)
+#define ISCSI_KWQE_OPCODE_UPDATE_CONN (2)
+#define ISCSI_KWQE_OPCODE_DESTROY_CONN (3)
+#define ISCSI_KWQE_OPCODE_INIT1 (4)
+#define ISCSI_KWQE_OPCODE_INIT2 (5)
+
+/* KCQ (kernel completion queue) response op codes */
+#define ISCSI_KCQE_OPCODE_OFFLOAD_CONN (0x10)
+#define ISCSI_KCQE_OPCODE_UPDATE_CONN (0x12)
+#define ISCSI_KCQE_OPCODE_DESTROY_CONN (0x13)
+#define ISCSI_KCQE_OPCODE_INIT (0x14)
+#define ISCSI_KCQE_OPCODE_FW_CLEAN_TASK (0x15)
+#define ISCSI_KCQE_OPCODE_TCP_RESET (0x16)
+#define ISCSI_KCQE_OPCODE_TCP_SYN (0x17)
+#define ISCSI_KCQE_OPCODE_TCP_FIN (0X18)
+#define ISCSI_KCQE_OPCODE_TCP_ERROR (0x19)
+#define ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
+#define ISCSI_KCQE_OPCODE_ISCSI_ERROR (0x21)
+
+/* KCQ (kernel completion queue) completion status */
+#define ISCSI_KCQE_COMPLETION_STATUS_SUCCESS (0x0)
+#define ISCSI_KCQE_COMPLETION_STATUS_INVALID_OPCODE (0x1)
+#define ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x2)
+#define ISCSI_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x3)
+#define ISCSI_KCQE_COMPLETION_STATUS_NIC_ERROR (0x4)
+
+#define ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR (0x5)
+#define ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR (0x6)
+
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_UNEXPECTED_OPCODE (0xa)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE (0xb)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN (0xc)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT (0xd)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN (0xe)
+
+/* Response */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN (0xf)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T (0x10)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_IS_ZERO (0x2c)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_TOO_BIG (0x2d)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0 (0x11)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1 (0x12)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2 (0x13)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3 (0x14)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4 (0x15)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5 (0x16)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6 (0x17)
+
+/* Data-In */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN (0x18)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN (0x19)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO (0x1a)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV (0x1b)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN (0x1c)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN (0x1d)
+
+/* R2T */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF (0x1f)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN (0x20)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN (0x21)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0 (0x22)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1 (0x23)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED (0x24)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV (0x25)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN (0x26)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO (0x27)
+
+/* TMF */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN (0x28)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN (0x29)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN (0x2a)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP (0x2b)
+
+/* IP/TCP processing errors: */
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT (0x40)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS (0x41)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG (0x42)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_MAX_RTRANS (0x43)
+
+/* iSCSI licensing errors */
+/* general iSCSI license not installed */
+#define ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED (0x50)
+/* additional LOM specific iSCSI license not installed */
+#define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51)
+
+/* SQ/RQ/CQ DB structure sizes */
+#define ISCSI_SQ_DB_SIZE (16)
+#define ISCSI_RQ_DB_SIZE (16)
+#define ISCSI_CQ_DB_SIZE (80)
+
+#define ISCSI_SQN_TO_NOTIFY_NOT_VALID 0xFFFF
+
+/* Page size codes (for flags field in connection offload request) */
+#define ISCSI_PAGE_SIZE_256 (0)
+#define ISCSI_PAGE_SIZE_512 (1)
+#define ISCSI_PAGE_SIZE_1K (2)
+#define ISCSI_PAGE_SIZE_2K (3)
+#define ISCSI_PAGE_SIZE_4K (4)
+#define ISCSI_PAGE_SIZE_8K (5)
+#define ISCSI_PAGE_SIZE_16K (6)
+#define ISCSI_PAGE_SIZE_32K (7)
+#define ISCSI_PAGE_SIZE_64K (8)
+#define ISCSI_PAGE_SIZE_128K (9)
+#define ISCSI_PAGE_SIZE_256K (10)
+#define ISCSI_PAGE_SIZE_512K (11)
+#define ISCSI_PAGE_SIZE_1M (12)
+#define ISCSI_PAGE_SIZE_2M (13)
+#define ISCSI_PAGE_SIZE_4M (14)
+#define ISCSI_PAGE_SIZE_8M (15)
+
+/* Iscsi PDU related defines */
+#define ISCSI_HEADER_SIZE (48)
+#define ISCSI_DIGEST_SHIFT (2)
+#define ISCSI_DIGEST_SIZE (4)
+
+#define B577XX_ISCSI_CONNECTION_TYPE 3
+
+#endif /*__57XX_ISCSI_CONSTANTS_H_ */
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
new file mode 100644
index 000000000000..36af1afef9b6
--- /dev/null
+++ b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
@@ -0,0 +1,1509 @@
+/* 57xx_iscsi_hsi.h: Broadcom NetXtreme II iSCSI HSI.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+#ifndef __57XX_ISCSI_HSI_LINUX_LE__
+#define __57XX_ISCSI_HSI_LINUX_LE__
+
+/*
+ * iSCSI Async CQE
+ */
+struct bnx2i_async_msg {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 reserved2;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved5;
+ u8 err_code;
+ u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved4;
+ u8 err_code;
+ u16 reserved5;
+#endif
+ u32 reserved6;
+ u32 lun[2];
+#if defined(__BIG_ENDIAN)
+ u8 async_event;
+ u8 async_vcode;
+ u16 param1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 param1;
+ u8 async_vcode;
+ u8 async_event;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 param2;
+ u16 param3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 param3;
+ u16 param2;
+#endif
+ u32 reserved7[3];
+ u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Buffer Descriptor (BD)
+ */
+struct iscsi_bd {
+ u32 buffer_addr_hi;
+ u32 buffer_addr_lo;
+#if defined(__BIG_ENDIAN)
+ u16 reserved0;
+ u16 buffer_length;
+#elif defined(__LITTLE_ENDIAN)
+ u16 buffer_length;
+ u16 reserved0;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u16 flags;
+#define ISCSI_BD_RESERVED1 (0x3F<<0)
+#define ISCSI_BD_RESERVED1_SHIFT 0
+#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6)
+#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6
+#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7)
+#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7
+#define ISCSI_BD_RESERVED2 (0xFF<<8)
+#define ISCSI_BD_RESERVED2_SHIFT 8
+#elif defined(__LITTLE_ENDIAN)
+ u16 flags;
+#define ISCSI_BD_RESERVED1 (0x3F<<0)
+#define ISCSI_BD_RESERVED1_SHIFT 0
+#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6)
+#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6
+#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7)
+#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7
+#define ISCSI_BD_RESERVED2 (0xFF<<8)
+#define ISCSI_BD_RESERVED2_SHIFT 8
+ u16 reserved3;
+#endif
+};
+
+
+/*
+ * iSCSI Cleanup SQ WQE
+ */
+struct bnx2i_cleanup_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 reserved2[3];
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u16 itt;
+#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14
+ u16 reserved3;
+#endif
+ u32 reserved4[10];
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved6;
+ u16 reserved5;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved5;
+ u8 reserved6;
+ u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Cleanup CQE
+ */
+struct bnx2i_cleanup_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 status;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 status;
+ u8 op_code;
+#endif
+ u32 reserved1[3];
+ u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 err_code;
+ u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved3;
+ u8 err_code;
+ u16 reserved4;
+#endif
+ u32 reserved5[7];
+#if defined(__BIG_ENDIAN)
+ u16 reserved6;
+ u16 itt;
+#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14
+ u16 reserved6;
+#endif
+ u32 cq_req_sn;
+};
+
+
+/*
+ * SCSI read/write SQ WQE
+ */
+struct bnx2i_cmd_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0)
+#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0
+#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3)
+#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3
+#define ISCSI_CMD_REQUEST_WRITE (0x1<<5)
+#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5
+#define ISCSI_CMD_REQUEST_READ (0x1<<6)
+#define ISCSI_CMD_REQUEST_READ_SHIFT 6
+#define ISCSI_CMD_REQUEST_FINAL (0x1<<7)
+#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_attr;
+#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0)
+#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0
+#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3)
+#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3
+#define ISCSI_CMD_REQUEST_WRITE (0x1<<5)
+#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5
+#define ISCSI_CMD_REQUEST_READ (0x1<<6)
+#define ISCSI_CMD_REQUEST_READ_SHIFT 6
+#define ISCSI_CMD_REQUEST_FINAL (0x1<<7)
+#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7
+ u8 op_code;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 ud_buffer_offset;
+ u16 sd_buffer_offset;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sd_buffer_offset;
+ u16 ud_buffer_offset;
+#endif
+ u32 lun[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved2;
+ u16 itt;
+#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CMD_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CMD_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14
+ u16 reserved2;
+#endif
+ u32 total_data_transfer_length;
+ u32 cmd_sn;
+ u32 reserved3;
+ u32 cdb[4];
+ u32 zero_fill;
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 sd_start_bd_index;
+ u8 ud_start_bd_index;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 ud_start_bd_index;
+ u8 sd_start_bd_index;
+ u8 cq_index;
+#endif
+};
+
+
+/*
+ * task statistics for write response
+ */
+struct bnx2i_write_resp_task_stat {
+ u32 num_data_ins;
+};
+
+/*
+ * task statistics for read response
+ */
+struct bnx2i_read_resp_task_stat {
+#if defined(__BIG_ENDIAN)
+ u16 num_data_outs;
+ u16 num_r2ts;
+#elif defined(__LITTLE_ENDIAN)
+ u16 num_r2ts;
+ u16 num_data_outs;
+#endif
+};
+
+/*
+ * task statistics for iSCSI cmd response
+ */
+union bnx2i_cmd_resp_task_stat {
+ struct bnx2i_write_resp_task_stat write_stat;
+ struct bnx2i_read_resp_task_stat read_stat;
+};
+
+/*
+ * SCSI Command CQE
+ */
+struct bnx2i_cmd_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 response_flags;
+#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0)
+#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4
+#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5)
+#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5
+ u8 response;
+ u8 status;
+#elif defined(__LITTLE_ENDIAN)
+ u8 status;
+ u8 response;
+ u8 response_flags;
+#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0)
+#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4
+#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5)
+#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved2;
+ u32 residual_count;
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 err_code;
+ u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved3;
+ u8 err_code;
+ u16 reserved4;
+#endif
+ u32 reserved5[5];
+ union bnx2i_cmd_resp_task_stat task_stat;
+ u32 reserved6;
+#if defined(__BIG_ENDIAN)
+ u16 reserved7;
+ u16 itt;
+#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14
+ u16 reserved7;
+#endif
+ u32 cq_req_sn;
+};
+
+
+
+/*
+ * firmware middle-path request SQ WQE
+ */
+struct bnx2i_fw_mp_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+ u16 hdr_opaque1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hdr_opaque1;
+ u8 op_attr;
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 hdr_opaque2[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved0;
+ u16 itt;
+#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14
+ u16 reserved0;
+#endif
+ u32 hdr_opaque3[4];
+ u32 resp_bd_list_addr_lo;
+ u32 resp_bd_list_addr_hi;
+ u32 resp_buffer;
+#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 reserved3;
+ u8 flags;
+#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0)
+#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3)
+#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+ u8 flags;
+#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0)
+#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3)
+#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3
+ u8 reserved3;
+ u16 reserved4;
+#endif
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved6;
+ u8 reserved5;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved5;
+ u8 reserved6;
+ u8 cq_index;
+#endif
+};
+
+
+/*
+ * firmware response - CQE: used only by firmware
+ */
+struct bnx2i_fw_response {
+ u32 hdr_dword1[2];
+ u32 hdr_exp_cmd_sn;
+ u32 hdr_max_cmd_sn;
+ u32 hdr_ttt;
+ u32 hdr_res_cnt;
+ u32 cqe_flags;
+#define ISCSI_FW_RESPONSE_RESERVED2 (0xFF<<0)
+#define ISCSI_FW_RESPONSE_RESERVED2_SHIFT 0
+#define ISCSI_FW_RESPONSE_ERR_CODE (0xFF<<8)
+#define ISCSI_FW_RESPONSE_ERR_CODE_SHIFT 8
+#define ISCSI_FW_RESPONSE_RESERVED3 (0xFFFF<<16)
+#define ISCSI_FW_RESPONSE_RESERVED3_SHIFT 16
+ u32 stat_sn;
+ u32 hdr_dword2[2];
+ u32 hdr_dword3[2];
+ u32 task_stat;
+ u32 reserved0;
+ u32 hdr_itt;
+ u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI KCQ CQE parameters
+ */
+union iscsi_kcqe_params {
+ u32 reserved0[4];
+};
+
+/*
+ * iSCSI KCQ CQE
+ */
+struct iscsi_kcqe {
+ u32 iscsi_conn_id;
+ u32 completion_status;
+ u32 iscsi_conn_context_id;
+ union iscsi_kcqe_params params;
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define ISCSI_KCQE_RESERVED0 (0xF<<0)
+#define ISCSI_KCQE_RESERVED0_SHIFT 0
+#define ISCSI_KCQE_LAYER_CODE (0x7<<4)
+#define ISCSI_KCQE_LAYER_CODE_SHIFT 4
+#define ISCSI_KCQE_RESERVED1 (0x1<<7)
+#define ISCSI_KCQE_RESERVED1_SHIFT 7
+ u8 op_code;
+ u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+ u16 qe_self_seq;
+ u8 op_code;
+ u8 flags;
+#define ISCSI_KCQE_RESERVED0 (0xF<<0)
+#define ISCSI_KCQE_RESERVED0_SHIFT 0
+#define ISCSI_KCQE_LAYER_CODE (0x7<<4)
+#define ISCSI_KCQE_LAYER_CODE_SHIFT 4
+#define ISCSI_KCQE_RESERVED1 (0x1<<7)
+#define ISCSI_KCQE_RESERVED1_SHIFT 7
+#endif
+};
+
+
+
+/*
+ * iSCSI KWQE header
+ */
+struct iscsi_kwqe_header {
+#if defined(__BIG_ENDIAN)
+ u8 flags;
+#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0)
+#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0
+#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4)
+#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4
+#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7
+ u8 op_code;
+#elif defined(__LITTLE_ENDIAN)
+ u8 op_code;
+ u8 flags;
+#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0)
+#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0
+#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4)
+#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4
+#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7
+#endif
+};
+
+/*
+ * iSCSI firmware init request 1
+ */
+struct iscsi_kwqe_init1 {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u8 reserved0;
+ u8 num_cqs;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_cqs;
+ u8 reserved0;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 dummy_buffer_addr_lo;
+ u32 dummy_buffer_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u16 num_ccells_per_conn;
+ u16 num_tasks_per_conn;
+#elif defined(__LITTLE_ENDIAN)
+ u16 num_tasks_per_conn;
+ u16 num_ccells_per_conn;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 sq_wqes_per_page;
+ u16 sq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_num_wqes;
+ u16 sq_wqes_per_page;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 cq_log_wqes_per_page;
+ u8 flags;
+#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0)
+#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4)
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+ u16 cq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+ u16 cq_num_wqes;
+ u8 flags;
+#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0)
+#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4)
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+ u8 cq_log_wqes_per_page;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 cq_num_pages;
+ u16 sq_num_pages;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_num_pages;
+ u16 cq_num_pages;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 rq_buffer_size;
+ u16 rq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+ u16 rq_num_wqes;
+ u16 rq_buffer_size;
+#endif
+};
+
+/*
+ * iSCSI firmware init request 2
+ */
+struct iscsi_kwqe_init2 {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 max_cq_sqn;
+#elif defined(__LITTLE_ENDIAN)
+ u16 max_cq_sqn;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 error_bit_map[2];
+ u32 reserved1[5];
+};
+
+/*
+ * Initial iSCSI connection offload request 1
+ */
+struct iscsi_kwqe_conn_offload1 {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 iscsi_conn_id;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 sq_page_table_addr_lo;
+ u32 sq_page_table_addr_hi;
+ u32 cq_page_table_addr_lo;
+ u32 cq_page_table_addr_hi;
+ u32 reserved0[3];
+};
+
+/*
+ * iSCSI Page Table Entry (PTE)
+ */
+struct iscsi_pte {
+ u32 hi;
+ u32 lo;
+};
+
+/*
+ * Initial iSCSI connection offload request 2
+ */
+struct iscsi_kwqe_conn_offload2 {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 rq_page_table_addr_lo;
+ u32 rq_page_table_addr_hi;
+ struct iscsi_pte sq_first_pte;
+ struct iscsi_pte cq_first_pte;
+ u32 num_additional_wqes;
+};
+
+
+/*
+ * Initial iSCSI connection offload request 3
+ */
+struct iscsi_kwqe_conn_offload3 {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 reserved1;
+ struct iscsi_pte qp_first_pte[3];
+};
+
+
+/*
+ * iSCSI connection update request
+ */
+struct iscsi_kwqe_conn_update {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ struct iscsi_kwqe_header hdr;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 session_error_recovery_level;
+ u8 max_outstanding_r2ts;
+ u8 reserved2;
+ u8 conn_flags;
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0)
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1)
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2)
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+#elif defined(__LITTLE_ENDIAN)
+ u8 conn_flags;
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0)
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1)
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2)
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+ u8 reserved2;
+ u8 max_outstanding_r2ts;
+ u8 session_error_recovery_level;
+#endif
+ u32 context_id;
+ u32 max_send_pdu_length;
+ u32 max_recv_pdu_length;
+ u32 first_burst_length;
+ u32 max_burst_length;
+ u32 exp_stat_sn;
+};
+
+/*
+ * iSCSI destroy connection request
+ */
+struct iscsi_kwqe_conn_destroy {
+#if defined(__BIG_ENDIAN)
+ struct iscsi_kwqe_header hdr;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ struct iscsi_kwqe_header hdr;
+#endif
+ u32 context_id;
+ u32 reserved1[6];
+};
+
+/*
+ * iSCSI KWQ WQE
+ */
+union iscsi_kwqe {
+ struct iscsi_kwqe_init1 init1;
+ struct iscsi_kwqe_init2 init2;
+ struct iscsi_kwqe_conn_offload1 conn_offload1;
+ struct iscsi_kwqe_conn_offload2 conn_offload2;
+ struct iscsi_kwqe_conn_update conn_update;
+ struct iscsi_kwqe_conn_destroy conn_destroy;
+};
+
+/*
+ * iSCSI Login SQ WQE
+ */
+struct bnx2i_login_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6)
+#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6
+#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7
+ u8 version_max;
+ u8 version_min;
+#elif defined(__LITTLE_ENDIAN)
+ u8 version_min;
+ u8 version_max;
+ u8 op_attr;
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6)
+#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6
+#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 isid_lo;
+#if defined(__BIG_ENDIAN)
+ u16 isid_hi;
+ u16 tsih;
+#elif defined(__LITTLE_ENDIAN)
+ u16 tsih;
+ u16 isid_hi;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved2;
+ u16 itt;
+#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14
+ u16 reserved2;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 cid;
+ u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved3;
+ u16 cid;
+#endif
+ u32 cmd_sn;
+ u32 exp_stat_sn;
+ u32 reserved4;
+ u32 resp_bd_list_addr_lo;
+ u32 resp_bd_list_addr_hi;
+ u32 resp_buffer;
+#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 reserved8;
+ u8 reserved7;
+ u8 flags;
+#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3)
+#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+ u8 flags;
+#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3)
+#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3
+ u8 reserved7;
+ u16 reserved8;
+#endif
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved10;
+ u8 reserved9;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved9;
+ u8 reserved10;
+ u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Login CQE
+ */
+struct bnx2i_login_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 response_flags;
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6)
+#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6
+#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7
+ u8 version_max;
+ u8 version_active;
+#elif defined(__LITTLE_ENDIAN)
+ u8 version_active;
+ u8 version_max;
+ u8 response_flags;
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6)
+#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6
+#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved1[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u8 err_code;
+ u8 reserved2;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved2;
+ u8 err_code;
+ u16 reserved3;
+#endif
+ u32 stat_sn;
+ u32 isid_lo;
+#if defined(__BIG_ENDIAN)
+ u16 isid_hi;
+ u16 tsih;
+#elif defined(__LITTLE_ENDIAN)
+ u16 tsih;
+ u16 isid_hi;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 status_class;
+ u8 status_detail;
+ u16 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved4;
+ u8 status_detail;
+ u8 status_class;
+#endif
+ u32 reserved5[3];
+#if defined(__BIG_ENDIAN)
+ u16 reserved6;
+ u16 itt;
+#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14
+ u16 reserved6;
+#endif
+ u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Logout SQ WQE
+ */
+struct bnx2i_logout_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0)
+#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_attr;
+#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0)
+#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 reserved1[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved2;
+ u16 itt;
+#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14
+ u16 reserved2;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 cid;
+ u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved3;
+ u16 cid;
+#endif
+ u32 cmd_sn;
+ u32 reserved4[5];
+ u32 zero_fill;
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved6;
+ u8 reserved5;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved5;
+ u8 reserved6;
+ u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Logout CQE
+ */
+struct bnx2i_logout_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u8 response;
+ u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved0;
+ u8 response;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 reserved2;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved5;
+ u8 err_code;
+ u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved4;
+ u8 err_code;
+ u16 reserved5;
+#endif
+ u32 reserved6[3];
+#if defined(__BIG_ENDIAN)
+ u16 time_to_wait;
+ u16 time_to_retain;
+#elif defined(__LITTLE_ENDIAN)
+ u16 time_to_retain;
+ u16 time_to_wait;
+#endif
+ u32 reserved7[3];
+#if defined(__BIG_ENDIAN)
+ u16 reserved8;
+ u16 itt;
+#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14
+ u16 reserved8;
+#endif
+ u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Nop-In CQE
+ */
+struct bnx2i_nop_in_msg {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 ttt;
+ u32 reserved2;
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 err_code;
+ u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved3;
+ u8 err_code;
+ u16 reserved4;
+#endif
+ u32 reserved5;
+ u32 lun[2];
+ u32 reserved6[4];
+#if defined(__BIG_ENDIAN)
+ u16 reserved7;
+ u16 itt;
+#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0
+#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14)
+#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0
+#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14)
+#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14
+ u16 reserved7;
+#endif
+ u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI NOP-OUT SQ WQE
+ */
+struct bnx2i_nop_out_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_attr;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 lun[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved2;
+ u16 itt;
+#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14
+ u16 reserved2;
+#endif
+ u32 ttt;
+ u32 cmd_sn;
+ u32 reserved3[2];
+ u32 resp_bd_list_addr_lo;
+ u32 resp_bd_list_addr_hi;
+ u32 resp_buffer;
+#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 reserved7;
+ u8 reserved6;
+ u8 flags;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2)
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2
+#elif defined(__LITTLE_ENDIAN)
+ u8 flags;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2)
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2
+ u8 reserved6;
+ u16 reserved7;
+#endif
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved9;
+ u8 reserved8;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved8;
+ u8 reserved9;
+ u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI Reject CQE
+ */
+struct bnx2i_reject_msg {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u8 reason;
+ u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved0;
+ u8 reason;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 err_code;
+ u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved3;
+ u8 err_code;
+ u16 reserved4;
+#endif
+ u32 reserved5[8];
+ u32 cq_req_sn;
+};
+
+/*
+ * bnx2i iSCSI TMF SQ WQE
+ */
+struct bnx2i_tmf_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_attr;
+#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 lun[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved1;
+ u16 itt;
+#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TMF_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TMF_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14
+ u16 reserved1;
+#endif
+ u32 ref_itt;
+ u32 cmd_sn;
+ u32 reserved2;
+ u32 ref_cmd_sn;
+ u32 reserved3[3];
+ u32 zero_fill;
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved5;
+ u8 reserved4;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved4;
+ u8 reserved5;
+ u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI Text SQ WQE
+ */
+struct bnx2i_text_request {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 op_attr;
+#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_REQUEST_CONT (0x1<<6)
+#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6
+#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 op_attr;
+#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_REQUEST_CONT (0x1<<6)
+#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6
+#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 lun[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u16 itt;
+#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14
+ u16 reserved3;
+#endif
+ u32 ttt;
+ u32 cmd_sn;
+ u32 reserved4[2];
+ u32 resp_bd_list_addr_lo;
+ u32 resp_bd_list_addr_hi;
+ u32 resp_buffer;
+#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS_SHIFT 24
+ u32 zero_fill;
+ u32 bd_list_addr_lo;
+ u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+ u8 cq_index;
+ u8 reserved7;
+ u8 reserved6;
+ u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+ u8 num_bds;
+ u8 reserved6;
+ u8 reserved7;
+ u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI SQ WQE
+ */
+union iscsi_request {
+ struct bnx2i_cmd_request cmd;
+ struct bnx2i_tmf_request tmf;
+ struct bnx2i_nop_out_request nop_out;
+ struct bnx2i_login_request login_req;
+ struct bnx2i_text_request text;
+ struct bnx2i_logout_request logout_req;
+ struct bnx2i_cleanup_request cleanup;
+};
+
+
+/*
+ * iSCSI TMF CQE
+ */
+struct bnx2i_tmf_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 reserved1;
+ u8 response;
+ u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved0;
+ u8 response;
+ u8 reserved1;
+ u8 op_code;
+#endif
+ u32 reserved2;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+ u16 reserved5;
+ u8 err_code;
+ u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved4;
+ u8 err_code;
+ u16 reserved5;
+#endif
+ u32 reserved6[7];
+#if defined(__BIG_ENDIAN)
+ u16 reserved7;
+ u16 itt;
+#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14
+ u16 reserved7;
+#endif
+ u32 cq_req_sn;
+};
+
+/*
+ * iSCSI Text CQE
+ */
+struct bnx2i_text_response {
+#if defined(__BIG_ENDIAN)
+ u8 op_code;
+ u8 response_flags;
+#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6)
+#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6
+#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7)
+#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7
+ u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved0;
+ u8 response_flags;
+#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6)
+#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6
+#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7)
+#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7
+ u8 op_code;
+#endif
+ u32 data_length;
+ u32 exp_cmd_sn;
+ u32 max_cmd_sn;
+ u32 ttt;
+ u32 reserved2;
+#if defined(__BIG_ENDIAN)
+ u16 reserved4;
+ u8 err_code;
+ u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 reserved3;
+ u8 err_code;
+ u16 reserved4;
+#endif
+ u32 reserved5;
+ u32 lun[2];
+ u32 reserved6[4];
+#if defined(__BIG_ENDIAN)
+ u16 reserved7;
+ u16 itt;
+#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 itt;
+#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14
+ u16 reserved7;
+#endif
+ u32 cq_req_sn;
+};
+
+/*
+ * iSCSI CQE
+ */
+union iscsi_response {
+ struct bnx2i_cmd_response cmd;
+ struct bnx2i_tmf_response tmf;
+ struct bnx2i_login_response login_resp;
+ struct bnx2i_text_response text;
+ struct bnx2i_logout_response logout_resp;
+ struct bnx2i_cleanup_response cleanup;
+ struct bnx2i_reject_msg reject;
+ struct bnx2i_async_msg async;
+ struct bnx2i_nop_in_msg nop_in;
+};
+
+#endif /* __57XX_ISCSI_HSI_LINUX_LE__ */
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
new file mode 100644
index 000000000000..820d428ae839
--- /dev/null
+++ b/drivers/scsi/bnx2i/Kconfig
@@ -0,0 +1,7 @@
+config SCSI_BNX2_ISCSI
+ tristate "Broadcom NetXtreme II iSCSI support"
+ select SCSI_ISCSI_ATTRS
+ select CNIC
+ ---help---
+ This driver supports iSCSI offload for the Broadcom NetXtreme II
+ devices.
diff --git a/drivers/scsi/bnx2i/Makefile b/drivers/scsi/bnx2i/Makefile
new file mode 100644
index 000000000000..b5802bd2e76a
--- /dev/null
+++ b/drivers/scsi/bnx2i/Makefile
@@ -0,0 +1,3 @@
+bnx2i-y := bnx2i_init.o bnx2i_hwi.o bnx2i_iscsi.o bnx2i_sysfs.o
+
+obj-$(CONFIG_SCSI_BNX2_ISCSI) += bnx2i.o
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
new file mode 100644
index 000000000000..d7576f28c6e9
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i.h
@@ -0,0 +1,771 @@
+/* bnx2i.h: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#ifndef _BNX2I_H_
+#define _BNX2I_H_
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/kfifo.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/iscsi_proto.h>
+#include <scsi/libiscsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "../../net/cnic_if.h"
+#include "57xx_iscsi_hsi.h"
+#include "57xx_iscsi_constants.h"
+
+#define BNX2_ISCSI_DRIVER_NAME "bnx2i"
+
+#define BNX2I_MAX_ADAPTERS 8
+
+#define ISCSI_MAX_CONNS_PER_HBA 128
+#define ISCSI_MAX_SESS_PER_HBA ISCSI_MAX_CONNS_PER_HBA
+#define ISCSI_MAX_CMDS_PER_SESS 128
+
+/* Total active commands across all connections supported by devices */
+#define ISCSI_MAX_CMDS_PER_HBA_5708 (28 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+#define ISCSI_MAX_CMDS_PER_HBA_5709 (128 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+#define ISCSI_MAX_CMDS_PER_HBA_57710 (256 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+
+#define ISCSI_MAX_BDS_PER_CMD 32
+
+#define MAX_PAGES_PER_CTRL_STRUCT_POOL 8
+#define BNX2I_RESERVED_SLOW_PATH_CMD_SLOTS 4
+
+/* 5706/08 hardware has limit on maximum buffer size per BD it can handle */
+#define MAX_BD_LENGTH 65535
+#define BD_SPLIT_SIZE 32768
+
+/* min, max & default values for SQ/RQ/CQ size, configurable via' modparam */
+#define BNX2I_SQ_WQES_MIN 16
+#define BNX2I_570X_SQ_WQES_MAX 128
+#define BNX2I_5770X_SQ_WQES_MAX 512
+#define BNX2I_570X_SQ_WQES_DEFAULT 128
+#define BNX2I_5770X_SQ_WQES_DEFAULT 256
+
+#define BNX2I_570X_CQ_WQES_MAX 128
+#define BNX2I_5770X_CQ_WQES_MAX 512
+
+#define BNX2I_RQ_WQES_MIN 16
+#define BNX2I_RQ_WQES_MAX 32
+#define BNX2I_RQ_WQES_DEFAULT 16
+
+/* CCELLs per conn */
+#define BNX2I_CCELLS_MIN 16
+#define BNX2I_CCELLS_MAX 96
+#define BNX2I_CCELLS_DEFAULT 64
+
+#define ITT_INVALID_SIGNATURE 0xFFFF
+
+#define ISCSI_CMD_CLEANUP_TIMEOUT 100
+
+#define BNX2I_CONN_CTX_BUF_SIZE 16384
+
+#define BNX2I_SQ_WQE_SIZE 64
+#define BNX2I_RQ_WQE_SIZE 256
+#define BNX2I_CQE_SIZE 64
+
+#define MB_KERNEL_CTX_SHIFT 8
+#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT)
+
+#define CTX_SHIFT 7
+#define GET_CID_NUM(cid_addr) ((cid_addr) >> CTX_SHIFT)
+
+#define CTX_OFFSET 0x10000
+#define MAX_CID_CNT 0x4000
+
+/* 5709 context registers */
+#define BNX2_MQ_CONFIG2 0x00003d00
+#define BNX2_MQ_CONFIG2_CONT_SZ (0x7L<<4)
+#define BNX2_MQ_CONFIG2_FIRST_L4L5 (0x1fL<<8)
+
+/* 57710's BAR2 is mapped to doorbell registers */
+#define BNX2X_DOORBELL_PCI_BAR 2
+#define BNX2X_MAX_CQS 8
+
+#define CNIC_ARM_CQE 1
+#define CNIC_DISARM_CQE 0
+
+#define REG_RD(__hba, offset) \
+ readl(__hba->regview + offset)
+#define REG_WR(__hba, offset, val) \
+ writel(val, __hba->regview + offset)
+
+
+/**
+ * struct generic_pdu_resc - login pdu resource structure
+ *
+ * @req_buf: driver buffer used to stage payload associated with
+ * the login request
+ * @req_dma_addr: dma address for iscsi login request payload buffer
+ * @req_buf_size: actual login request payload length
+ * @req_wr_ptr: pointer into login request buffer when next data is
+ * to be written
+ * @resp_hdr: iscsi header where iscsi login response header is to
+ * be recreated
+ * @resp_buf: buffer to stage login response payload
+ * @resp_dma_addr: login response payload buffer dma address
+ * @resp_buf_size: login response paylod length
+ * @resp_wr_ptr: pointer into login response buffer when next data is
+ * to be written
+ * @req_bd_tbl: iscsi login request payload BD table
+ * @req_bd_dma: login request BD table dma address
+ * @resp_bd_tbl: iscsi login response payload BD table
+ * @resp_bd_dma: login request BD table dma address
+ *
+ * following structure defines buffer info for generic pdus such as iSCSI Login,
+ * Logout and NOP
+ */
+struct generic_pdu_resc {
+ char *req_buf;
+ dma_addr_t req_dma_addr;
+ u32 req_buf_size;
+ char *req_wr_ptr;
+ struct iscsi_hdr resp_hdr;
+ char *resp_buf;
+ dma_addr_t resp_dma_addr;
+ u32 resp_buf_size;
+ char *resp_wr_ptr;
+ char *req_bd_tbl;
+ dma_addr_t req_bd_dma;
+ char *resp_bd_tbl;
+ dma_addr_t resp_bd_dma;
+};
+
+
+/**
+ * struct bd_resc_page - tracks DMA'able memory allocated for BD tables
+ *
+ * @link: list head to link elements
+ * @max_ptrs: maximun pointers that can be stored in this page
+ * @num_valid: number of pointer valid in this page
+ * @page: base addess for page pointer array
+ *
+ * structure to track DMA'able memory allocated for command BD tables
+ */
+struct bd_resc_page {
+ struct list_head link;
+ u32 max_ptrs;
+ u32 num_valid;
+ void *page[1];
+};
+
+
+/**
+ * struct io_bdt - I/O buffer destricptor table
+ *
+ * @bd_tbl: BD table's virtual address
+ * @bd_tbl_dma: BD table's dma address
+ * @bd_valid: num valid BD entries
+ *
+ * IO BD table
+ */
+struct io_bdt {
+ struct iscsi_bd *bd_tbl;
+ dma_addr_t bd_tbl_dma;
+ u16 bd_valid;
+};
+
+
+/**
+ * bnx2i_cmd - iscsi command structure
+ *
+ * @scsi_cmd: SCSI-ML task pointer corresponding to this iscsi cmd
+ * @sg: SG list
+ * @io_tbl: buffer descriptor (BD) table
+ * @bd_tbl_dma: buffer descriptor (BD) table's dma address
+ */
+struct bnx2i_cmd {
+ struct iscsi_hdr hdr;
+ struct bnx2i_conn *conn;
+ struct scsi_cmnd *scsi_cmd;
+ struct scatterlist *sg;
+ struct io_bdt io_tbl;
+ dma_addr_t bd_tbl_dma;
+ struct bnx2i_cmd_request req;
+};
+
+
+/**
+ * struct bnx2i_conn - iscsi connection structure
+ *
+ * @cls_conn: pointer to iscsi cls conn
+ * @hba: adapter structure pointer
+ * @iscsi_conn_cid: iscsi conn id
+ * @fw_cid: firmware iscsi context id
+ * @ep: endpoint structure pointer
+ * @gen_pdu: login/nopout/logout pdu resources
+ * @violation_notified: bit mask used to track iscsi error/warning messages
+ * already printed out
+ *
+ * iSCSI connection structure
+ */
+struct bnx2i_conn {
+ struct iscsi_cls_conn *cls_conn;
+ struct bnx2i_hba *hba;
+ struct completion cmd_cleanup_cmpl;
+ int is_bound;
+
+ u32 iscsi_conn_cid;
+#define BNX2I_CID_RESERVED 0x5AFF
+ u32 fw_cid;
+
+ struct timer_list poll_timer;
+ /*
+ * Queue Pair (QP) related structure elements.
+ */
+ struct bnx2i_endpoint *ep;
+
+ /*
+ * Buffer for login negotiation process
+ */
+ struct generic_pdu_resc gen_pdu;
+ u64 violation_notified;
+};
+
+
+
+/**
+ * struct iscsi_cid_queue - Per adapter iscsi cid queue
+ *
+ * @cid_que_base: queue base memory
+ * @cid_que: queue memory pointer
+ * @cid_q_prod_idx: produce index
+ * @cid_q_cons_idx: consumer index
+ * @cid_q_max_idx: max index. used to detect wrap around condition
+ * @cid_free_cnt: queue size
+ * @conn_cid_tbl: iscsi cid to conn structure mapping table
+ *
+ * Per adapter iSCSI CID Queue
+ */
+struct iscsi_cid_queue {
+ void *cid_que_base;
+ u32 *cid_que;
+ u32 cid_q_prod_idx;
+ u32 cid_q_cons_idx;
+ u32 cid_q_max_idx;
+ u32 cid_free_cnt;
+ struct bnx2i_conn **conn_cid_tbl;
+};
+
+/**
+ * struct bnx2i_hba - bnx2i adapter structure
+ *
+ * @link: list head to link elements
+ * @cnic: pointer to cnic device
+ * @pcidev: pointer to pci dev
+ * @netdev: pointer to netdev structure
+ * @regview: mapped PCI register space
+ * @age: age, incremented by every recovery
+ * @cnic_dev_type: cnic device type, 5706/5708/5709/57710
+ * @mail_queue_access: mailbox queue access mode, applicable to 5709 only
+ * @reg_with_cnic: indicates whether the device is register with CNIC
+ * @adapter_state: adapter state, UP, GOING_DOWN, LINK_DOWN
+ * @mtu_supported: Ethernet MTU supported
+ * @shost: scsi host pointer
+ * @max_sqes: SQ size
+ * @max_rqes: RQ size
+ * @max_cqes: CQ size
+ * @num_ccell: number of command cells per connection
+ * @ofld_conns_active: active connection list
+ * @max_active_conns: max offload connections supported by this device
+ * @cid_que: iscsi cid queue
+ * @ep_rdwr_lock: read / write lock to synchronize various ep lists
+ * @ep_ofld_list: connection list for pending offload completion
+ * @ep_destroy_list: connection list for pending offload completion
+ * @mp_bd_tbl: BD table to be used with middle path requests
+ * @mp_bd_dma: DMA address of 'mp_bd_tbl' memory buffer
+ * @dummy_buffer: Dummy buffer to be used with zero length scsicmd reqs
+ * @dummy_buf_dma: DMA address of 'dummy_buffer' memory buffer
+ * @lock: lock to synchonize access to hba structure
+ * @pci_did: PCI device ID
+ * @pci_vid: PCI vendor ID
+ * @pci_sdid: PCI subsystem device ID
+ * @pci_svid: PCI subsystem vendor ID
+ * @pci_func: PCI function number in system pci tree
+ * @pci_devno: PCI device number in system pci tree
+ * @num_wqe_sent: statistic counter, total wqe's sent
+ * @num_cqe_rcvd: statistic counter, total cqe's received
+ * @num_intr_claimed: statistic counter, total interrupts claimed
+ * @link_changed_count: statistic counter, num of link change notifications
+ * received
+ * @ipaddr_changed_count: statistic counter, num times IP address changed while
+ * at least one connection is offloaded
+ * @num_sess_opened: statistic counter, total num sessions opened
+ * @num_conn_opened: statistic counter, total num conns opened on this hba
+ * @ctx_ccell_tasks: captures number of ccells and tasks supported by
+ * currently offloaded connection, used to decode
+ * context memory
+ *
+ * Adapter Data Structure
+ */
+struct bnx2i_hba {
+ struct list_head link;
+ struct cnic_dev *cnic;
+ struct pci_dev *pcidev;
+ struct net_device *netdev;
+ void __iomem *regview;
+
+ u32 age;
+ unsigned long cnic_dev_type;
+ #define BNX2I_NX2_DEV_5706 0x0
+ #define BNX2I_NX2_DEV_5708 0x1
+ #define BNX2I_NX2_DEV_5709 0x2
+ #define BNX2I_NX2_DEV_57710 0x3
+ u32 mail_queue_access;
+ #define BNX2I_MQ_KERNEL_MODE 0x0
+ #define BNX2I_MQ_KERNEL_BYPASS_MODE 0x1
+ #define BNX2I_MQ_BIN_MODE 0x2
+ unsigned long reg_with_cnic;
+ #define BNX2I_CNIC_REGISTERED 1
+
+ unsigned long adapter_state;
+ #define ADAPTER_STATE_UP 0
+ #define ADAPTER_STATE_GOING_DOWN 1
+ #define ADAPTER_STATE_LINK_DOWN 2
+ #define ADAPTER_STATE_INIT_FAILED 31
+ unsigned int mtu_supported;
+ #define BNX2I_MAX_MTU_SUPPORTED 1500
+
+ struct Scsi_Host *shost;
+
+ u32 max_sqes;
+ u32 max_rqes;
+ u32 max_cqes;
+ u32 num_ccell;
+
+ int ofld_conns_active;
+
+ int max_active_conns;
+ struct iscsi_cid_queue cid_que;
+
+ rwlock_t ep_rdwr_lock;
+ struct list_head ep_ofld_list;
+ struct list_head ep_destroy_list;
+
+ /*
+ * BD table to be used with MP (Middle Path requests.
+ */
+ char *mp_bd_tbl;
+ dma_addr_t mp_bd_dma;
+ char *dummy_buffer;
+ dma_addr_t dummy_buf_dma;
+
+ spinlock_t lock; /* protects hba structure access */
+ struct mutex net_dev_lock;/* sync net device access */
+
+ /*
+ * PCI related info.
+ */
+ u16 pci_did;
+ u16 pci_vid;
+ u16 pci_sdid;
+ u16 pci_svid;
+ u16 pci_func;
+ u16 pci_devno;
+
+ /*
+ * Following are a bunch of statistics useful during development
+ * and later stage for score boarding.
+ */
+ u32 num_wqe_sent;
+ u32 num_cqe_rcvd;
+ u32 num_intr_claimed;
+ u32 link_changed_count;
+ u32 ipaddr_changed_count;
+ u32 num_sess_opened;
+ u32 num_conn_opened;
+ unsigned int ctx_ccell_tasks;
+};
+
+
+/*******************************************************************************
+ * QP [ SQ / RQ / CQ ] info.
+ ******************************************************************************/
+
+/*
+ * SQ/RQ/CQ generic structure definition
+ */
+struct sqe {
+ u8 sqe_byte[BNX2I_SQ_WQE_SIZE];
+};
+
+struct rqe {
+ u8 rqe_byte[BNX2I_RQ_WQE_SIZE];
+};
+
+struct cqe {
+ u8 cqe_byte[BNX2I_CQE_SIZE];
+};
+
+
+enum {
+#if defined(__LITTLE_ENDIAN)
+ CNIC_EVENT_COAL_INDEX = 0x0,
+ CNIC_SEND_DOORBELL = 0x4,
+ CNIC_EVENT_CQ_ARM = 0x7,
+ CNIC_RECV_DOORBELL = 0x8
+#elif defined(__BIG_ENDIAN)
+ CNIC_EVENT_COAL_INDEX = 0x2,
+ CNIC_SEND_DOORBELL = 0x6,
+ CNIC_EVENT_CQ_ARM = 0x4,
+ CNIC_RECV_DOORBELL = 0xa
+#endif
+};
+
+
+/*
+ * CQ DB
+ */
+struct bnx2x_iscsi_cq_pend_cmpl {
+ /* CQ producer, updated by Ustorm */
+ u16 ustrom_prod;
+ /* CQ pending completion counter */
+ u16 pend_cntr;
+};
+
+
+struct bnx2i_5771x_cq_db {
+ struct bnx2x_iscsi_cq_pend_cmpl qp_pend_cmpl[BNX2X_MAX_CQS];
+ /* CQ pending completion ITT array */
+ u16 itt[BNX2X_MAX_CQS];
+ /* Cstorm CQ sequence to notify array, updated by driver */;
+ u16 sqn[BNX2X_MAX_CQS];
+ u32 reserved[4] /* 16 byte allignment */;
+};
+
+
+struct bnx2i_5771x_sq_rq_db {
+ u16 prod_idx;
+ u8 reserved0[14]; /* Pad structure size to 16 bytes */
+};
+
+
+struct bnx2i_5771x_dbell_hdr {
+ u8 header;
+ /* 1 for rx doorbell, 0 for tx doorbell */
+#define B577XX_DOORBELL_HDR_RX (0x1<<0)
+#define B577XX_DOORBELL_HDR_RX_SHIFT 0
+ /* 0 for normal doorbell, 1 for advertise wnd doorbell */
+#define B577XX_DOORBELL_HDR_DB_TYPE (0x1<<1)
+#define B577XX_DOORBELL_HDR_DB_TYPE_SHIFT 1
+ /* rdma tx only: DPM transaction size specifier (64/128/256/512B) */
+#define B577XX_DOORBELL_HDR_DPM_SIZE (0x3<<2)
+#define B577XX_DOORBELL_HDR_DPM_SIZE_SHIFT 2
+ /* connection type */
+#define B577XX_DOORBELL_HDR_CONN_TYPE (0xF<<4)
+#define B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT 4
+};
+
+struct bnx2i_5771x_dbell {
+ struct bnx2i_5771x_dbell_hdr dbell;
+ u8 pad[3];
+
+};
+
+/**
+ * struct qp_info - QP (share queue region) atrributes structure
+ *
+ * @ctx_base: ioremapped pci register base to access doorbell register
+ * pertaining to this offloaded connection
+ * @sq_virt: virtual address of send queue (SQ) region
+ * @sq_phys: DMA address of SQ memory region
+ * @sq_mem_size: SQ size
+ * @sq_prod_qe: SQ producer entry pointer
+ * @sq_cons_qe: SQ consumer entry pointer
+ * @sq_first_qe: virtaul address of first entry in SQ
+ * @sq_last_qe: virtaul address of last entry in SQ
+ * @sq_prod_idx: SQ producer index
+ * @sq_cons_idx: SQ consumer index
+ * @sqe_left: number sq entry left
+ * @sq_pgtbl_virt: page table describing buffer consituting SQ region
+ * @sq_pgtbl_phys: dma address of 'sq_pgtbl_virt'
+ * @sq_pgtbl_size: SQ page table size
+ * @cq_virt: virtual address of completion queue (CQ) region
+ * @cq_phys: DMA address of RQ memory region
+ * @cq_mem_size: CQ size
+ * @cq_prod_qe: CQ producer entry pointer
+ * @cq_cons_qe: CQ consumer entry pointer
+ * @cq_first_qe: virtaul address of first entry in CQ
+ * @cq_last_qe: virtaul address of last entry in CQ
+ * @cq_prod_idx: CQ producer index
+ * @cq_cons_idx: CQ consumer index
+ * @cqe_left: number cq entry left
+ * @cqe_size: size of each CQ entry
+ * @cqe_exp_seq_sn: next expected CQE sequence number
+ * @cq_pgtbl_virt: page table describing buffer consituting CQ region
+ * @cq_pgtbl_phys: dma address of 'cq_pgtbl_virt'
+ * @cq_pgtbl_size: CQ page table size
+ * @rq_virt: virtual address of receive queue (RQ) region
+ * @rq_phys: DMA address of RQ memory region
+ * @rq_mem_size: RQ size
+ * @rq_prod_qe: RQ producer entry pointer
+ * @rq_cons_qe: RQ consumer entry pointer
+ * @rq_first_qe: virtaul address of first entry in RQ
+ * @rq_last_qe: virtaul address of last entry in RQ
+ * @rq_prod_idx: RQ producer index
+ * @rq_cons_idx: RQ consumer index
+ * @rqe_left: number rq entry left
+ * @rq_pgtbl_virt: page table describing buffer consituting RQ region
+ * @rq_pgtbl_phys: dma address of 'rq_pgtbl_virt'
+ * @rq_pgtbl_size: RQ page table size
+ *
+ * queue pair (QP) is a per connection shared data structure which is used
+ * to send work requests (SQ), receive completion notifications (CQ)
+ * and receive asynchoronous / scsi sense info (RQ). 'qp_info' structure
+ * below holds queue memory, consumer/producer indexes and page table
+ * information
+ */
+struct qp_info {
+ void __iomem *ctx_base;
+#define DPM_TRIGER_TYPE 0x40
+
+#define BNX2I_570x_QUE_DB_SIZE 0
+#define BNX2I_5771x_QUE_DB_SIZE 16
+ struct sqe *sq_virt;
+ dma_addr_t sq_phys;
+ u32 sq_mem_size;
+
+ struct sqe *sq_prod_qe;
+ struct sqe *sq_cons_qe;
+ struct sqe *sq_first_qe;
+ struct sqe *sq_last_qe;
+ u16 sq_prod_idx;
+ u16 sq_cons_idx;
+ u32 sqe_left;
+
+ void *sq_pgtbl_virt;
+ dma_addr_t sq_pgtbl_phys;
+ u32 sq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */
+
+ struct cqe *cq_virt;
+ dma_addr_t cq_phys;
+ u32 cq_mem_size;
+
+ struct cqe *cq_prod_qe;
+ struct cqe *cq_cons_qe;
+ struct cqe *cq_first_qe;
+ struct cqe *cq_last_qe;
+ u16 cq_prod_idx;
+ u16 cq_cons_idx;
+ u32 cqe_left;
+ u32 cqe_size;
+ u32 cqe_exp_seq_sn;
+
+ void *cq_pgtbl_virt;
+ dma_addr_t cq_pgtbl_phys;
+ u32 cq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */
+
+ struct rqe *rq_virt;
+ dma_addr_t rq_phys;
+ u32 rq_mem_size;
+
+ struct rqe *rq_prod_qe;
+ struct rqe *rq_cons_qe;
+ struct rqe *rq_first_qe;
+ struct rqe *rq_last_qe;
+ u16 rq_prod_idx;
+ u16 rq_cons_idx;
+ u32 rqe_left;
+
+ void *rq_pgtbl_virt;
+ dma_addr_t rq_pgtbl_phys;
+ u32 rq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */
+};
+
+
+
+/*
+ * CID handles
+ */
+struct ep_handles {
+ u32 fw_cid;
+ u32 drv_iscsi_cid;
+ u16 pg_cid;
+ u16 rsvd;
+};
+
+
+enum {
+ EP_STATE_IDLE = 0x0,
+ EP_STATE_PG_OFLD_START = 0x1,
+ EP_STATE_PG_OFLD_COMPL = 0x2,
+ EP_STATE_OFLD_START = 0x4,
+ EP_STATE_OFLD_COMPL = 0x8,
+ EP_STATE_CONNECT_START = 0x10,
+ EP_STATE_CONNECT_COMPL = 0x20,
+ EP_STATE_ULP_UPDATE_START = 0x40,
+ EP_STATE_ULP_UPDATE_COMPL = 0x80,
+ EP_STATE_DISCONN_START = 0x100,
+ EP_STATE_DISCONN_COMPL = 0x200,
+ EP_STATE_CLEANUP_START = 0x400,
+ EP_STATE_CLEANUP_CMPL = 0x800,
+ EP_STATE_TCP_FIN_RCVD = 0x1000,
+ EP_STATE_TCP_RST_RCVD = 0x2000,
+ EP_STATE_PG_OFLD_FAILED = 0x1000000,
+ EP_STATE_ULP_UPDATE_FAILED = 0x2000000,
+ EP_STATE_CLEANUP_FAILED = 0x4000000,
+ EP_STATE_OFLD_FAILED = 0x8000000,
+ EP_STATE_CONNECT_FAILED = 0x10000000,
+ EP_STATE_DISCONN_TIMEDOUT = 0x20000000,
+};
+
+/**
+ * struct bnx2i_endpoint - representation of tcp connection in NX2 world
+ *
+ * @link: list head to link elements
+ * @hba: adapter to which this connection belongs
+ * @conn: iscsi connection this EP is linked to
+ * @sess: iscsi session this EP is linked to
+ * @cm_sk: cnic sock struct
+ * @hba_age: age to detect if 'iscsid' issues ep_disconnect()
+ * after HBA reset is completed by bnx2i/cnic/bnx2
+ * modules
+ * @state: tracks offload connection state machine
+ * @teardown_mode: indicates if conn teardown is abortive or orderly
+ * @qp: QP information
+ * @ids: contains chip allocated *context id* & driver assigned
+ * *iscsi cid*
+ * @ofld_timer: offload timer to detect timeout
+ * @ofld_wait: wait queue
+ *
+ * Endpoint Structure - equivalent of tcp socket structure
+ */
+struct bnx2i_endpoint {
+ struct list_head link;
+ struct bnx2i_hba *hba;
+ struct bnx2i_conn *conn;
+ struct cnic_sock *cm_sk;
+ u32 hba_age;
+ u32 state;
+ unsigned long timestamp;
+ int num_active_cmds;
+
+ struct qp_info qp;
+ struct ep_handles ids;
+ #define ep_iscsi_cid ids.drv_iscsi_cid
+ #define ep_cid ids.fw_cid
+ #define ep_pg_cid ids.pg_cid
+ struct timer_list ofld_timer;
+ wait_queue_head_t ofld_wait;
+};
+
+
+
+/* Global variables */
+extern unsigned int error_mask1, error_mask2;
+extern u64 iscsi_error_mask;
+extern unsigned int en_tcp_dack;
+extern unsigned int event_coal_div;
+
+extern struct scsi_transport_template *bnx2i_scsi_xport_template;
+extern struct iscsi_transport bnx2i_iscsi_transport;
+extern struct cnic_ulp_ops bnx2i_cnic_cb;
+
+extern unsigned int sq_size;
+extern unsigned int rq_size;
+
+extern struct device_attribute *bnx2i_dev_attributes[];
+
+
+
+/*
+ * Function Prototypes
+ */
+extern void bnx2i_identify_device(struct bnx2i_hba *hba);
+extern void bnx2i_register_device(struct bnx2i_hba *hba);
+
+extern void bnx2i_ulp_init(struct cnic_dev *dev);
+extern void bnx2i_ulp_exit(struct cnic_dev *dev);
+extern void bnx2i_start(void *handle);
+extern void bnx2i_stop(void *handle);
+extern void bnx2i_reg_dev_all(void);
+extern void bnx2i_unreg_dev_all(void);
+extern struct bnx2i_hba *get_adapter_list_head(void);
+
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+ u16 iscsi_cid);
+
+int bnx2i_alloc_ep_pool(void);
+void bnx2i_release_ep_pool(void);
+struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba);
+struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba);
+
+struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic);
+
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic);
+void bnx2i_free_hba(struct bnx2i_hba *hba);
+
+void bnx2i_get_rq_buf(struct bnx2i_conn *conn, char *ptr, int len);
+void bnx2i_put_rq_buf(struct bnx2i_conn *conn, int count);
+
+void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd);
+
+void bnx2i_drop_session(struct iscsi_cls_session *session);
+
+extern int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba);
+extern int bnx2i_send_iscsi_login(struct bnx2i_conn *conn,
+ struct iscsi_task *mtask);
+extern int bnx2i_send_iscsi_tmf(struct bnx2i_conn *conn,
+ struct iscsi_task *mtask);
+extern int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *conn,
+ struct bnx2i_cmd *cmnd);
+extern int bnx2i_send_iscsi_nopout(struct bnx2i_conn *conn,
+ struct iscsi_task *mtask, u32 ttt,
+ char *datap, int data_len, int unsol);
+extern int bnx2i_send_iscsi_logout(struct bnx2i_conn *conn,
+ struct iscsi_task *mtask);
+extern void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba,
+ struct bnx2i_cmd *cmd);
+extern void bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep);
+extern void bnx2i_update_iscsi_conn(struct iscsi_conn *conn);
+extern void bnx2i_send_conn_destroy(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep);
+
+extern int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep);
+extern void bnx2i_free_qp_resc(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep);
+extern void bnx2i_ep_ofld_timer(unsigned long data);
+extern struct bnx2i_endpoint *bnx2i_find_ep_in_ofld_list(
+ struct bnx2i_hba *hba, u32 iscsi_cid);
+extern struct bnx2i_endpoint *bnx2i_find_ep_in_destroy_list(
+ struct bnx2i_hba *hba, u32 iscsi_cid);
+
+extern int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep);
+extern void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action);
+
+/* Debug related function prototypes */
+extern void bnx2i_print_pend_cmd_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_active_cmd_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_xmit_pdu_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_recv_state(struct bnx2i_conn *conn);
+
+#endif
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
new file mode 100644
index 000000000000..906cef5cda86
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -0,0 +1,2405 @@
+/* bnx2i_hwi.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/libiscsi.h>
+#include "bnx2i.h"
+
+/**
+ * bnx2i_get_cid_num - get cid from ep
+ * @ep: endpoint pointer
+ *
+ * Only applicable to 57710 family of devices
+ */
+static u32 bnx2i_get_cid_num(struct bnx2i_endpoint *ep)
+{
+ u32 cid;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+ cid = ep->ep_cid;
+ else
+ cid = GET_CID_NUM(ep->ep_cid);
+ return cid;
+}
+
+
+/**
+ * bnx2i_adjust_qp_size - Adjust SQ/RQ/CQ size for 57710 device type
+ * @hba: Adapter for which adjustments is to be made
+ *
+ * Only applicable to 57710 family of devices
+ */
+static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba)
+{
+ u32 num_elements_per_pg;
+
+ if (test_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type) ||
+ test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type) ||
+ test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) {
+ if (!is_power_of_2(hba->max_sqes))
+ hba->max_sqes = rounddown_pow_of_two(hba->max_sqes);
+
+ if (!is_power_of_2(hba->max_rqes))
+ hba->max_rqes = rounddown_pow_of_two(hba->max_rqes);
+ }
+
+ /* Adjust each queue size if the user selection does not
+ * yield integral num of page buffers
+ */
+ /* adjust SQ */
+ num_elements_per_pg = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+ if (hba->max_sqes < num_elements_per_pg)
+ hba->max_sqes = num_elements_per_pg;
+ else if (hba->max_sqes % num_elements_per_pg)
+ hba->max_sqes = (hba->max_sqes + num_elements_per_pg - 1) &
+ ~(num_elements_per_pg - 1);
+
+ /* adjust CQ */
+ num_elements_per_pg = PAGE_SIZE / BNX2I_CQE_SIZE;
+ if (hba->max_cqes < num_elements_per_pg)
+ hba->max_cqes = num_elements_per_pg;
+ else if (hba->max_cqes % num_elements_per_pg)
+ hba->max_cqes = (hba->max_cqes + num_elements_per_pg - 1) &
+ ~(num_elements_per_pg - 1);
+
+ /* adjust RQ */
+ num_elements_per_pg = PAGE_SIZE / BNX2I_RQ_WQE_SIZE;
+ if (hba->max_rqes < num_elements_per_pg)
+ hba->max_rqes = num_elements_per_pg;
+ else if (hba->max_rqes % num_elements_per_pg)
+ hba->max_rqes = (hba->max_rqes + num_elements_per_pg - 1) &
+ ~(num_elements_per_pg - 1);
+}
+
+
+/**
+ * bnx2i_get_link_state - get network interface link state
+ * @hba: adapter instance pointer
+ *
+ * updates adapter structure flag based on netdev state
+ */
+static void bnx2i_get_link_state(struct bnx2i_hba *hba)
+{
+ if (test_bit(__LINK_STATE_NOCARRIER, &hba->netdev->state))
+ set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
+ else
+ clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
+}
+
+
+/**
+ * bnx2i_iscsi_license_error - displays iscsi license related error message
+ * @hba: adapter instance pointer
+ * @error_code: error classification
+ *
+ * Puts out an error log when driver is unable to offload iscsi connection
+ * due to license restrictions
+ */
+static void bnx2i_iscsi_license_error(struct bnx2i_hba *hba, u32 error_code)
+{
+ if (error_code == ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED)
+ /* iSCSI offload not supported on this device */
+ printk(KERN_ERR "bnx2i: iSCSI not supported, dev=%s\n",
+ hba->netdev->name);
+ if (error_code == ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED)
+ /* iSCSI offload not supported on this LOM device */
+ printk(KERN_ERR "bnx2i: LOM is not enable to "
+ "offload iSCSI connections, dev=%s\n",
+ hba->netdev->name);
+ set_bit(ADAPTER_STATE_INIT_FAILED, &hba->adapter_state);
+}
+
+
+/**
+ * bnx2i_arm_cq_event_coalescing - arms CQ to enable EQ notification
+ * @ep: endpoint (transport indentifier) structure
+ * @action: action, ARM or DISARM. For now only ARM_CQE is used
+ *
+ * Arm'ing CQ will enable chip to generate global EQ events inorder to interrupt
+ * the driver. EQ event is generated CQ index is hit or at least 1 CQ is
+ * outstanding and on chip timer expires
+ */
+void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
+{
+ struct bnx2i_5771x_cq_db *cq_db;
+ u16 cq_index;
+
+ if (!test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+ return;
+
+ if (action == CNIC_ARM_CQE) {
+ cq_index = ep->qp.cqe_exp_seq_sn +
+ ep->num_active_cmds / event_coal_div;
+ cq_index %= (ep->qp.cqe_size * 2 + 1);
+ if (!cq_index) {
+ cq_index = 1;
+ cq_db = (struct bnx2i_5771x_cq_db *)
+ ep->qp.cq_pgtbl_virt;
+ cq_db->sqn[0] = cq_index;
+ }
+ }
+}
+
+
+/**
+ * bnx2i_get_rq_buf - copy RQ buffer contents to driver buffer
+ * @conn: iscsi connection on which RQ event occured
+ * @ptr: driver buffer to which RQ buffer contents is to
+ * be copied
+ * @len: length of valid data inside RQ buf
+ *
+ * Copies RQ buffer contents from shared (DMA'able) memory region to
+ * driver buffer. RQ is used to DMA unsolicitated iscsi pdu's and
+ * scsi sense info
+ */
+void bnx2i_get_rq_buf(struct bnx2i_conn *bnx2i_conn, char *ptr, int len)
+{
+ if (!bnx2i_conn->ep->qp.rqe_left)
+ return;
+
+ bnx2i_conn->ep->qp.rqe_left--;
+ memcpy(ptr, (u8 *) bnx2i_conn->ep->qp.rq_cons_qe, len);
+ if (bnx2i_conn->ep->qp.rq_cons_qe == bnx2i_conn->ep->qp.rq_last_qe) {
+ bnx2i_conn->ep->qp.rq_cons_qe = bnx2i_conn->ep->qp.rq_first_qe;
+ bnx2i_conn->ep->qp.rq_cons_idx = 0;
+ } else {
+ bnx2i_conn->ep->qp.rq_cons_qe++;
+ bnx2i_conn->ep->qp.rq_cons_idx++;
+ }
+}
+
+
+static void bnx2i_ring_577xx_doorbell(struct bnx2i_conn *conn)
+{
+ struct bnx2i_5771x_dbell dbell;
+ u32 msg;
+
+ memset(&dbell, 0, sizeof(dbell));
+ dbell.dbell.header = (B577XX_ISCSI_CONNECTION_TYPE <<
+ B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT);
+ msg = *((u32 *)&dbell);
+ /* TODO : get doorbell register mapping */
+ writel(cpu_to_le32(msg), conn->ep->qp.ctx_base);
+}
+
+
+/**
+ * bnx2i_put_rq_buf - Replenish RQ buffer, if required ring on chip doorbell
+ * @conn: iscsi connection on which event to post
+ * @count: number of RQ buffer being posted to chip
+ *
+ * No need to ring hardware doorbell for 57710 family of devices
+ */
+void bnx2i_put_rq_buf(struct bnx2i_conn *bnx2i_conn, int count)
+{
+ struct bnx2i_5771x_sq_rq_db *rq_db;
+ u16 hi_bit = (bnx2i_conn->ep->qp.rq_prod_idx & 0x8000);
+ struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+
+ ep->qp.rqe_left += count;
+ ep->qp.rq_prod_idx &= 0x7FFF;
+ ep->qp.rq_prod_idx += count;
+
+ if (ep->qp.rq_prod_idx > bnx2i_conn->hba->max_rqes) {
+ ep->qp.rq_prod_idx %= bnx2i_conn->hba->max_rqes;
+ if (!hi_bit)
+ ep->qp.rq_prod_idx |= 0x8000;
+ } else
+ ep->qp.rq_prod_idx |= hi_bit;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+ rq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.rq_pgtbl_virt;
+ rq_db->prod_idx = ep->qp.rq_prod_idx;
+ /* no need to ring hardware doorbell for 57710 */
+ } else {
+ writew(ep->qp.rq_prod_idx,
+ ep->qp.ctx_base + CNIC_RECV_DOORBELL);
+ }
+ mmiowb();
+}
+
+
+/**
+ * bnx2i_ring_sq_dbell - Ring SQ doorbell to wake-up the processing engine
+ * @conn: iscsi connection to which new SQ entries belong
+ * @count: number of SQ WQEs to post
+ *
+ * SQ DB is updated in host memory and TX Doorbell is rung for 57710 family
+ * of devices. For 5706/5708/5709 new SQ WQE count is written into the
+ * doorbell register
+ */
+static void bnx2i_ring_sq_dbell(struct bnx2i_conn *bnx2i_conn, int count)
+{
+ struct bnx2i_5771x_sq_rq_db *sq_db;
+ struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+
+ ep->num_active_cmds++;
+ wmb(); /* flush SQ WQE memory before the doorbell is rung */
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+ sq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.sq_pgtbl_virt;
+ sq_db->prod_idx = ep->qp.sq_prod_idx;
+ bnx2i_ring_577xx_doorbell(bnx2i_conn);
+ } else
+ writew(count, ep->qp.ctx_base + CNIC_SEND_DOORBELL);
+
+ mmiowb(); /* flush posted PCI writes */
+}
+
+
+/**
+ * bnx2i_ring_dbell_update_sq_params - update SQ driver parameters
+ * @conn: iscsi connection to which new SQ entries belong
+ * @count: number of SQ WQEs to post
+ *
+ * this routine will update SQ driver parameters and ring the doorbell
+ */
+static void bnx2i_ring_dbell_update_sq_params(struct bnx2i_conn *bnx2i_conn,
+ int count)
+{
+ int tmp_cnt;
+
+ if (count == 1) {
+ if (bnx2i_conn->ep->qp.sq_prod_qe ==
+ bnx2i_conn->ep->qp.sq_last_qe)
+ bnx2i_conn->ep->qp.sq_prod_qe =
+ bnx2i_conn->ep->qp.sq_first_qe;
+ else
+ bnx2i_conn->ep->qp.sq_prod_qe++;
+ } else {
+ if ((bnx2i_conn->ep->qp.sq_prod_qe + count) <=
+ bnx2i_conn->ep->qp.sq_last_qe)
+ bnx2i_conn->ep->qp.sq_prod_qe += count;
+ else {
+ tmp_cnt = bnx2i_conn->ep->qp.sq_last_qe -
+ bnx2i_conn->ep->qp.sq_prod_qe;
+ bnx2i_conn->ep->qp.sq_prod_qe =
+ &bnx2i_conn->ep->qp.sq_first_qe[count -
+ (tmp_cnt + 1)];
+ }
+ }
+ bnx2i_conn->ep->qp.sq_prod_idx += count;
+ /* Ring the doorbell */
+ bnx2i_ring_sq_dbell(bnx2i_conn, bnx2i_conn->ep->qp.sq_prod_idx);
+}
+
+
+/**
+ * bnx2i_send_iscsi_login - post iSCSI login request MP WQE to hardware
+ * @conn: iscsi connection
+ * @cmd: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI Login request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn,
+ struct iscsi_task *task)
+{
+ struct bnx2i_cmd *bnx2i_cmd;
+ struct bnx2i_login_request *login_wqe;
+ struct iscsi_login *login_hdr;
+ u32 dword;
+
+ bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+ login_hdr = (struct iscsi_login *)task->hdr;
+ login_wqe = (struct bnx2i_login_request *)
+ bnx2i_conn->ep->qp.sq_prod_qe;
+
+ login_wqe->op_code = login_hdr->opcode;
+ login_wqe->op_attr = login_hdr->flags;
+ login_wqe->version_max = login_hdr->max_version;
+ login_wqe->version_min = login_hdr->min_version;
+ login_wqe->data_length = ntoh24(login_hdr->dlength);
+ login_wqe->isid_lo = *((u32 *) login_hdr->isid);
+ login_wqe->isid_hi = *((u16 *) login_hdr->isid + 2);
+ login_wqe->tsih = login_hdr->tsih;
+ login_wqe->itt = task->itt |
+ (ISCSI_TASK_TYPE_MPATH << ISCSI_LOGIN_REQUEST_TYPE_SHIFT);
+ login_wqe->cid = login_hdr->cid;
+
+ login_wqe->cmd_sn = be32_to_cpu(login_hdr->cmdsn);
+ login_wqe->exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn);
+
+ login_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma;
+ login_wqe->resp_bd_list_addr_hi =
+ (u32) ((u64) bnx2i_conn->gen_pdu.resp_bd_dma >> 32);
+
+ dword = ((1 << ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT) |
+ (bnx2i_conn->gen_pdu.resp_buf_size <<
+ ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT));
+ login_wqe->resp_buffer = dword;
+ login_wqe->flags = 0;
+ login_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma;
+ login_wqe->bd_list_addr_hi =
+ (u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32);
+ login_wqe->num_bds = 1;
+ login_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+ return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_tmf - post iSCSI task management request MP WQE to hardware
+ * @conn: iscsi connection
+ * @mtask: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI Login request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
+ struct iscsi_task *mtask)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_tm *tmfabort_hdr;
+ struct scsi_cmnd *ref_sc;
+ struct iscsi_task *ctask;
+ struct bnx2i_cmd *bnx2i_cmd;
+ struct bnx2i_tmf_request *tmfabort_wqe;
+ u32 dword;
+
+ bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data;
+ tmfabort_hdr = (struct iscsi_tm *)mtask->hdr;
+ tmfabort_wqe = (struct bnx2i_tmf_request *)
+ bnx2i_conn->ep->qp.sq_prod_qe;
+
+ tmfabort_wqe->op_code = tmfabort_hdr->opcode;
+ tmfabort_wqe->op_attr = 0;
+ tmfabort_wqe->op_attr =
+ ISCSI_TMF_REQUEST_ALWAYS_ONE | ISCSI_TM_FUNC_ABORT_TASK;
+ tmfabort_wqe->lun[0] = be32_to_cpu(tmfabort_hdr->lun[0]);
+ tmfabort_wqe->lun[1] = be32_to_cpu(tmfabort_hdr->lun[1]);
+
+ tmfabort_wqe->itt = (mtask->itt | (ISCSI_TASK_TYPE_MPATH << 14));
+ tmfabort_wqe->reserved2 = 0;
+ tmfabort_wqe->cmd_sn = be32_to_cpu(tmfabort_hdr->cmdsn);
+
+ ctask = iscsi_itt_to_task(conn, tmfabort_hdr->rtt);
+ if (!ctask || ctask->sc)
+ /*
+ * the iscsi layer must have completed the cmd while this
+ * was starting up.
+ */
+ return 0;
+ ref_sc = ctask->sc;
+
+ if (ref_sc->sc_data_direction == DMA_TO_DEVICE)
+ dword = (ISCSI_TASK_TYPE_WRITE << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ else
+ dword = (ISCSI_TASK_TYPE_READ << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ tmfabort_wqe->ref_itt = (dword | tmfabort_hdr->rtt);
+ tmfabort_wqe->ref_cmd_sn = be32_to_cpu(tmfabort_hdr->refcmdsn);
+
+ tmfabort_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma;
+ tmfabort_wqe->bd_list_addr_hi = (u32)
+ ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+ tmfabort_wqe->num_bds = 1;
+ tmfabort_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+ return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_scsicmd - post iSCSI scsicmd request WQE to hardware
+ * @conn: iscsi connection
+ * @cmd: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI SCSI-CMD request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *bnx2i_conn,
+ struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_cmd_request *scsi_cmd_wqe;
+
+ scsi_cmd_wqe = (struct bnx2i_cmd_request *)
+ bnx2i_conn->ep->qp.sq_prod_qe;
+ memcpy(scsi_cmd_wqe, &cmd->req, sizeof(struct bnx2i_cmd_request));
+ scsi_cmd_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+ return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_nopout - post iSCSI NOPOUT request WQE to hardware
+ * @conn: iscsi connection
+ * @cmd: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ * @ttt: TTT to be used when building pdu header
+ * @datap: payload buffer pointer
+ * @data_len: payload data length
+ * @unsol: indicated whether nopout pdu is unsolicited pdu or
+ * in response to target's NOPIN w/ TTT != FFFFFFFF
+ *
+ * prepare and post a nopout request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
+ struct iscsi_task *task, u32 ttt,
+ char *datap, int data_len, int unsol)
+{
+ struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+ struct bnx2i_cmd *bnx2i_cmd;
+ struct bnx2i_nop_out_request *nopout_wqe;
+ struct iscsi_nopout *nopout_hdr;
+
+ bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+ nopout_hdr = (struct iscsi_nopout *)task->hdr;
+ nopout_wqe = (struct bnx2i_nop_out_request *)ep->qp.sq_prod_qe;
+ nopout_wqe->op_code = nopout_hdr->opcode;
+ nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL;
+ memcpy(nopout_wqe->lun, nopout_hdr->lun, 8);
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+ u32 tmp = nopout_hdr->lun[0];
+ /* 57710 requires LUN field to be swapped */
+ nopout_hdr->lun[0] = nopout_hdr->lun[1];
+ nopout_hdr->lun[1] = tmp;
+ }
+
+ nopout_wqe->itt = ((u16)task->itt |
+ (ISCSI_TASK_TYPE_MPATH <<
+ ISCSI_TMF_REQUEST_TYPE_SHIFT));
+ nopout_wqe->ttt = ttt;
+ nopout_wqe->flags = 0;
+ if (!unsol)
+ nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION;
+ else if (nopout_hdr->itt == RESERVED_ITT)
+ nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION;
+
+ nopout_wqe->cmd_sn = be32_to_cpu(nopout_hdr->cmdsn);
+ nopout_wqe->data_length = data_len;
+ if (data_len) {
+ /* handle payload data, not required in first release */
+ printk(KERN_ALERT "NOPOUT: WARNING!! payload len != 0\n");
+ } else {
+ nopout_wqe->bd_list_addr_lo = (u32)
+ bnx2i_conn->hba->mp_bd_dma;
+ nopout_wqe->bd_list_addr_hi =
+ (u32) ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+ nopout_wqe->num_bds = 1;
+ }
+ nopout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+ return 0;
+}
+
+
+/**
+ * bnx2i_send_iscsi_logout - post iSCSI logout request WQE to hardware
+ * @conn: iscsi connection
+ * @cmd: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ *
+ * prepare and post logout request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_logout(struct bnx2i_conn *bnx2i_conn,
+ struct iscsi_task *task)
+{
+ struct bnx2i_cmd *bnx2i_cmd;
+ struct bnx2i_logout_request *logout_wqe;
+ struct iscsi_logout *logout_hdr;
+
+ bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+ logout_hdr = (struct iscsi_logout *)task->hdr;
+
+ logout_wqe = (struct bnx2i_logout_request *)
+ bnx2i_conn->ep->qp.sq_prod_qe;
+ memset(logout_wqe, 0x00, sizeof(struct bnx2i_logout_request));
+
+ logout_wqe->op_code = logout_hdr->opcode;
+ logout_wqe->cmd_sn = be32_to_cpu(logout_hdr->cmdsn);
+ logout_wqe->op_attr =
+ logout_hdr->flags | ISCSI_LOGOUT_REQUEST_ALWAYS_ONE;
+ logout_wqe->itt = ((u16)task->itt |
+ (ISCSI_TASK_TYPE_MPATH <<
+ ISCSI_LOGOUT_REQUEST_TYPE_SHIFT));
+ logout_wqe->data_length = 0;
+ logout_wqe->cid = 0;
+
+ logout_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma;
+ logout_wqe->bd_list_addr_hi = (u32)
+ ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+ logout_wqe->num_bds = 1;
+ logout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+ return 0;
+}
+
+
+/**
+ * bnx2i_update_iscsi_conn - post iSCSI logout request WQE to hardware
+ * @conn: iscsi connection which requires iscsi parameter update
+ *
+ * sends down iSCSI Conn Update request to move iSCSI conn to FFP
+ */
+void bnx2i_update_iscsi_conn(struct iscsi_conn *conn)
+{
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct bnx2i_hba *hba = bnx2i_conn->hba;
+ struct kwqe *kwqe_arr[2];
+ struct iscsi_kwqe_conn_update *update_wqe;
+ struct iscsi_kwqe_conn_update conn_update_kwqe;
+
+ update_wqe = &conn_update_kwqe;
+
+ update_wqe->hdr.op_code = ISCSI_KWQE_OPCODE_UPDATE_CONN;
+ update_wqe->hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ /* 5771x requires conn context id to be passed as is */
+ if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_conn->ep->hba->cnic_dev_type))
+ update_wqe->context_id = bnx2i_conn->ep->ep_cid;
+ else
+ update_wqe->context_id = (bnx2i_conn->ep->ep_cid >> 7);
+ update_wqe->conn_flags = 0;
+ if (conn->hdrdgst_en)
+ update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST;
+ if (conn->datadgst_en)
+ update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST;
+ if (conn->session->initial_r2t_en)
+ update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T;
+ if (conn->session->imm_data_en)
+ update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA;
+
+ update_wqe->max_send_pdu_length = conn->max_xmit_dlength;
+ update_wqe->max_recv_pdu_length = conn->max_recv_dlength;
+ update_wqe->first_burst_length = conn->session->first_burst;
+ update_wqe->max_burst_length = conn->session->max_burst;
+ update_wqe->exp_stat_sn = conn->exp_statsn;
+ update_wqe->max_outstanding_r2ts = conn->session->max_r2t;
+ update_wqe->session_error_recovery_level = conn->session->erl;
+ iscsi_conn_printk(KERN_ALERT, conn,
+ "bnx2i: conn update - MBL 0x%x FBL 0x%x"
+ "MRDSL_I 0x%x MRDSL_T 0x%x \n",
+ update_wqe->max_burst_length,
+ update_wqe->first_burst_length,
+ update_wqe->max_recv_pdu_length,
+ update_wqe->max_send_pdu_length);
+
+ kwqe_arr[0] = (struct kwqe *) update_wqe;
+ if (hba->cnic && hba->cnic->submit_kwqes)
+ hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1);
+}
+
+
+/**
+ * bnx2i_ep_ofld_timer - post iSCSI logout request WQE to hardware
+ * @data: endpoint (transport handle) structure pointer
+ *
+ * routine to handle connection offload/destroy request timeout
+ */
+void bnx2i_ep_ofld_timer(unsigned long data)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) data;
+
+ if (ep->state == EP_STATE_OFLD_START) {
+ printk(KERN_ALERT "ofld_timer: CONN_OFLD timeout\n");
+ ep->state = EP_STATE_OFLD_FAILED;
+ } else if (ep->state == EP_STATE_DISCONN_START) {
+ printk(KERN_ALERT "ofld_timer: CONN_DISCON timeout\n");
+ ep->state = EP_STATE_DISCONN_TIMEDOUT;
+ } else if (ep->state == EP_STATE_CLEANUP_START) {
+ printk(KERN_ALERT "ofld_timer: CONN_CLEANUP timeout\n");
+ ep->state = EP_STATE_CLEANUP_FAILED;
+ }
+
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+static int bnx2i_power_of2(u32 val)
+{
+ u32 power = 0;
+ if (val & (val - 1))
+ return power;
+ val--;
+ while (val) {
+ val = val >> 1;
+ power++;
+ }
+ return power;
+}
+
+
+/**
+ * bnx2i_send_cmd_cleanup_req - send iscsi cmd context clean-up request
+ * @hba: adapter structure pointer
+ * @cmd: driver command structure which is requesting
+ * a WQE to sent to chip for further processing
+ *
+ * prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_cleanup_request *cmd_cleanup;
+
+ cmd_cleanup =
+ (struct bnx2i_cleanup_request *)cmd->conn->ep->qp.sq_prod_qe;
+ memset(cmd_cleanup, 0x00, sizeof(struct bnx2i_cleanup_request));
+
+ cmd_cleanup->op_code = ISCSI_OPCODE_CLEANUP_REQUEST;
+ cmd_cleanup->itt = cmd->req.itt;
+ cmd_cleanup->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+ bnx2i_ring_dbell_update_sq_params(cmd->conn, 1);
+}
+
+
+/**
+ * bnx2i_send_conn_destroy - initiates iscsi connection teardown process
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE to initiate
+ * iscsi connection context clean-up process
+ */
+void bnx2i_send_conn_destroy(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+ struct kwqe *kwqe_arr[2];
+ struct iscsi_kwqe_conn_destroy conn_cleanup;
+
+ memset(&conn_cleanup, 0x00, sizeof(struct iscsi_kwqe_conn_destroy));
+
+ conn_cleanup.hdr.op_code = ISCSI_KWQE_OPCODE_DESTROY_CONN;
+ conn_cleanup.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+ /* 5771x requires conn context id to be passed as is */
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+ conn_cleanup.context_id = ep->ep_cid;
+ else
+ conn_cleanup.context_id = (ep->ep_cid >> 7);
+
+ conn_cleanup.reserved0 = (u16)ep->ep_iscsi_cid;
+
+ kwqe_arr[0] = (struct kwqe *) &conn_cleanup;
+ if (hba->cnic && hba->cnic->submit_kwqes)
+ hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1);
+}
+
+
+/**
+ * bnx2i_570x_send_conn_ofld_req - initiates iscsi conn context setup process
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * 5706/5708/5709 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+static void bnx2i_570x_send_conn_ofld_req(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ struct kwqe *kwqe_arr[2];
+ struct iscsi_kwqe_conn_offload1 ofld_req1;
+ struct iscsi_kwqe_conn_offload2 ofld_req2;
+ dma_addr_t dma_addr;
+ int num_kwqes = 2;
+ u32 *ptbl;
+
+ ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1;
+ ofld_req1.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid;
+
+ dma_addr = ep->qp.sq_pgtbl_phys;
+ ofld_req1.sq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ dma_addr = ep->qp.cq_pgtbl_phys;
+ ofld_req1.cq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2;
+ ofld_req2.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ dma_addr = ep->qp.rq_pgtbl_phys;
+ ofld_req2.rq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ ptbl = (u32 *) ep->qp.sq_pgtbl_virt;
+
+ ofld_req2.sq_first_pte.hi = *ptbl++;
+ ofld_req2.sq_first_pte.lo = *ptbl;
+
+ ptbl = (u32 *) ep->qp.cq_pgtbl_virt;
+ ofld_req2.cq_first_pte.hi = *ptbl++;
+ ofld_req2.cq_first_pte.lo = *ptbl;
+
+ kwqe_arr[0] = (struct kwqe *) &ofld_req1;
+ kwqe_arr[1] = (struct kwqe *) &ofld_req2;
+ ofld_req2.num_additional_wqes = 0;
+
+ if (hba->cnic && hba->cnic->submit_kwqes)
+ hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes);
+}
+
+
+/**
+ * bnx2i_5771x_send_conn_ofld_req - initiates iscsi connection context creation
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * 57710 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+static void bnx2i_5771x_send_conn_ofld_req(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ struct kwqe *kwqe_arr[5];
+ struct iscsi_kwqe_conn_offload1 ofld_req1;
+ struct iscsi_kwqe_conn_offload2 ofld_req2;
+ struct iscsi_kwqe_conn_offload3 ofld_req3[1];
+ dma_addr_t dma_addr;
+ int num_kwqes = 2;
+ u32 *ptbl;
+
+ ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1;
+ ofld_req1.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid;
+
+ dma_addr = ep->qp.sq_pgtbl_phys + ISCSI_SQ_DB_SIZE;
+ ofld_req1.sq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ dma_addr = ep->qp.cq_pgtbl_phys + ISCSI_CQ_DB_SIZE;
+ ofld_req1.cq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2;
+ ofld_req2.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ dma_addr = ep->qp.rq_pgtbl_phys + ISCSI_RQ_DB_SIZE;
+ ofld_req2.rq_page_table_addr_lo = (u32) dma_addr;
+ ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+ ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE);
+ ofld_req2.sq_first_pte.hi = *ptbl++;
+ ofld_req2.sq_first_pte.lo = *ptbl;
+
+ ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE);
+ ofld_req2.cq_first_pte.hi = *ptbl++;
+ ofld_req2.cq_first_pte.lo = *ptbl;
+
+ kwqe_arr[0] = (struct kwqe *) &ofld_req1;
+ kwqe_arr[1] = (struct kwqe *) &ofld_req2;
+
+ ofld_req2.num_additional_wqes = 1;
+ memset(ofld_req3, 0x00, sizeof(ofld_req3[0]));
+ ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE);
+ ofld_req3[0].qp_first_pte[0].hi = *ptbl++;
+ ofld_req3[0].qp_first_pte[0].lo = *ptbl;
+
+ kwqe_arr[2] = (struct kwqe *) ofld_req3;
+ /* need if we decide to go with multiple KCQE's per conn */
+ num_kwqes += 1;
+
+ if (hba->cnic && hba->cnic->submit_kwqes)
+ hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes);
+}
+
+/**
+ * bnx2i_send_conn_ofld_req - initiates iscsi connection context setup process
+ *
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+void bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+ if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+ bnx2i_5771x_send_conn_ofld_req(hba, ep);
+ else
+ bnx2i_570x_send_conn_ofld_req(hba, ep);
+}
+
+
+/**
+ * setup_qp_page_tables - iscsi QP page table setup function
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * Sets up page tables for SQ/RQ/CQ, 1G/sec (5706/5708/5709) devices requires
+ * 64-bit address in big endian format. Whereas 10G/sec (57710) requires
+ * PT in little endian format
+ */
+static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
+{
+ int num_pages;
+ u32 *ptbl;
+ dma_addr_t page;
+ int cnic_dev_10g;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+ cnic_dev_10g = 1;
+ else
+ cnic_dev_10g = 0;
+
+ /* SQ page table */
+ memset(ep->qp.sq_pgtbl_virt, 0, ep->qp.sq_pgtbl_size);
+ num_pages = ep->qp.sq_mem_size / PAGE_SIZE;
+ page = ep->qp.sq_phys;
+
+ if (cnic_dev_10g)
+ ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE);
+ else
+ ptbl = (u32 *) ep->qp.sq_pgtbl_virt;
+ while (num_pages--) {
+ if (cnic_dev_10g) {
+ /* PTE is written in little endian format for 57710 */
+ *ptbl = (u32) page;
+ ptbl++;
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ page += PAGE_SIZE;
+ } else {
+ /* PTE is written in big endian format for
+ * 5706/5708/5709 devices */
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ *ptbl = (u32) page;
+ ptbl++;
+ page += PAGE_SIZE;
+ }
+ }
+
+ /* RQ page table */
+ memset(ep->qp.rq_pgtbl_virt, 0, ep->qp.rq_pgtbl_size);
+ num_pages = ep->qp.rq_mem_size / PAGE_SIZE;
+ page = ep->qp.rq_phys;
+
+ if (cnic_dev_10g)
+ ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE);
+ else
+ ptbl = (u32 *) ep->qp.rq_pgtbl_virt;
+ while (num_pages--) {
+ if (cnic_dev_10g) {
+ /* PTE is written in little endian format for 57710 */
+ *ptbl = (u32) page;
+ ptbl++;
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ page += PAGE_SIZE;
+ } else {
+ /* PTE is written in big endian format for
+ * 5706/5708/5709 devices */
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ *ptbl = (u32) page;
+ ptbl++;
+ page += PAGE_SIZE;
+ }
+ }
+
+ /* CQ page table */
+ memset(ep->qp.cq_pgtbl_virt, 0, ep->qp.cq_pgtbl_size);
+ num_pages = ep->qp.cq_mem_size / PAGE_SIZE;
+ page = ep->qp.cq_phys;
+
+ if (cnic_dev_10g)
+ ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE);
+ else
+ ptbl = (u32 *) ep->qp.cq_pgtbl_virt;
+ while (num_pages--) {
+ if (cnic_dev_10g) {
+ /* PTE is written in little endian format for 57710 */
+ *ptbl = (u32) page;
+ ptbl++;
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ page += PAGE_SIZE;
+ } else {
+ /* PTE is written in big endian format for
+ * 5706/5708/5709 devices */
+ *ptbl = (u32) ((u64) page >> 32);
+ ptbl++;
+ *ptbl = (u32) page;
+ ptbl++;
+ page += PAGE_SIZE;
+ }
+ }
+}
+
+
+/**
+ * bnx2i_alloc_qp_resc - allocates required resources for QP.
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * Allocate QP (transport layer for iSCSI connection) resources, DMA'able
+ * memory for SQ/RQ/CQ and page tables. EP structure elements such
+ * as producer/consumer indexes/pointers, queue sizes and page table
+ * contents are setup
+ */
+int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+ struct bnx2i_5771x_cq_db *cq_db;
+
+ ep->hba = hba;
+ ep->conn = NULL;
+ ep->ep_cid = ep->ep_iscsi_cid = ep->ep_pg_cid = 0;
+
+ /* Allocate page table memory for SQ which is page aligned */
+ ep->qp.sq_mem_size = hba->max_sqes * BNX2I_SQ_WQE_SIZE;
+ ep->qp.sq_mem_size =
+ (ep->qp.sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ ep->qp.sq_pgtbl_size =
+ (ep->qp.sq_mem_size / PAGE_SIZE) * sizeof(void *);
+ ep->qp.sq_pgtbl_size =
+ (ep->qp.sq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ ep->qp.sq_pgtbl_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size,
+ &ep->qp.sq_pgtbl_phys, GFP_KERNEL);
+ if (!ep->qp.sq_pgtbl_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc SQ PT mem (%d)\n",
+ ep->qp.sq_pgtbl_size);
+ goto mem_alloc_err;
+ }
+
+ /* Allocate memory area for actual SQ element */
+ ep->qp.sq_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size,
+ &ep->qp.sq_phys, GFP_KERNEL);
+ if (!ep->qp.sq_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc SQ BD memory %d\n",
+ ep->qp.sq_mem_size);
+ goto mem_alloc_err;
+ }
+
+ memset(ep->qp.sq_virt, 0x00, ep->qp.sq_mem_size);
+ ep->qp.sq_first_qe = ep->qp.sq_virt;
+ ep->qp.sq_prod_qe = ep->qp.sq_first_qe;
+ ep->qp.sq_cons_qe = ep->qp.sq_first_qe;
+ ep->qp.sq_last_qe = &ep->qp.sq_first_qe[hba->max_sqes - 1];
+ ep->qp.sq_prod_idx = 0;
+ ep->qp.sq_cons_idx = 0;
+ ep->qp.sqe_left = hba->max_sqes;
+
+ /* Allocate page table memory for CQ which is page aligned */
+ ep->qp.cq_mem_size = hba->max_cqes * BNX2I_CQE_SIZE;
+ ep->qp.cq_mem_size =
+ (ep->qp.cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ ep->qp.cq_pgtbl_size =
+ (ep->qp.cq_mem_size / PAGE_SIZE) * sizeof(void *);
+ ep->qp.cq_pgtbl_size =
+ (ep->qp.cq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ ep->qp.cq_pgtbl_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size,
+ &ep->qp.cq_pgtbl_phys, GFP_KERNEL);
+ if (!ep->qp.cq_pgtbl_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc CQ PT memory %d\n",
+ ep->qp.cq_pgtbl_size);
+ goto mem_alloc_err;
+ }
+
+ /* Allocate memory area for actual CQ element */
+ ep->qp.cq_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size,
+ &ep->qp.cq_phys, GFP_KERNEL);
+ if (!ep->qp.cq_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc CQ BD memory %d\n",
+ ep->qp.cq_mem_size);
+ goto mem_alloc_err;
+ }
+ memset(ep->qp.cq_virt, 0x00, ep->qp.cq_mem_size);
+
+ ep->qp.cq_first_qe = ep->qp.cq_virt;
+ ep->qp.cq_prod_qe = ep->qp.cq_first_qe;
+ ep->qp.cq_cons_qe = ep->qp.cq_first_qe;
+ ep->qp.cq_last_qe = &ep->qp.cq_first_qe[hba->max_cqes - 1];
+ ep->qp.cq_prod_idx = 0;
+ ep->qp.cq_cons_idx = 0;
+ ep->qp.cqe_left = hba->max_cqes;
+ ep->qp.cqe_exp_seq_sn = ISCSI_INITIAL_SN;
+ ep->qp.cqe_size = hba->max_cqes;
+
+ /* Invalidate all EQ CQE index, req only for 57710 */
+ cq_db = (struct bnx2i_5771x_cq_db *) ep->qp.cq_pgtbl_virt;
+ memset(cq_db->sqn, 0xFF, sizeof(cq_db->sqn[0]) * BNX2X_MAX_CQS);
+
+ /* Allocate page table memory for RQ which is page aligned */
+ ep->qp.rq_mem_size = hba->max_rqes * BNX2I_RQ_WQE_SIZE;
+ ep->qp.rq_mem_size =
+ (ep->qp.rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ ep->qp.rq_pgtbl_size =
+ (ep->qp.rq_mem_size / PAGE_SIZE) * sizeof(void *);
+ ep->qp.rq_pgtbl_size =
+ (ep->qp.rq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ ep->qp.rq_pgtbl_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size,
+ &ep->qp.rq_pgtbl_phys, GFP_KERNEL);
+ if (!ep->qp.rq_pgtbl_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc RQ PT mem %d\n",
+ ep->qp.rq_pgtbl_size);
+ goto mem_alloc_err;
+ }
+
+ /* Allocate memory area for actual RQ element */
+ ep->qp.rq_virt =
+ dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size,
+ &ep->qp.rq_phys, GFP_KERNEL);
+ if (!ep->qp.rq_virt) {
+ printk(KERN_ALERT "bnx2i: unable to alloc RQ BD memory %d\n",
+ ep->qp.rq_mem_size);
+ goto mem_alloc_err;
+ }
+
+ ep->qp.rq_first_qe = ep->qp.rq_virt;
+ ep->qp.rq_prod_qe = ep->qp.rq_first_qe;
+ ep->qp.rq_cons_qe = ep->qp.rq_first_qe;
+ ep->qp.rq_last_qe = &ep->qp.rq_first_qe[hba->max_rqes - 1];
+ ep->qp.rq_prod_idx = 0x8000;
+ ep->qp.rq_cons_idx = 0;
+ ep->qp.rqe_left = hba->max_rqes;
+
+ setup_qp_page_tables(ep);
+
+ return 0;
+
+mem_alloc_err:
+ bnx2i_free_qp_resc(hba, ep);
+ return -ENOMEM;
+}
+
+
+
+/**
+ * bnx2i_free_qp_resc - free memory resources held by QP
+ * @hba: adapter structure pointer
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * Free QP resources - SQ/RQ/CQ memory and page tables.
+ */
+void bnx2i_free_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+ if (ep->qp.ctx_base) {
+ iounmap(ep->qp.ctx_base);
+ ep->qp.ctx_base = NULL;
+ }
+ /* Free SQ mem */
+ if (ep->qp.sq_pgtbl_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size,
+ ep->qp.sq_pgtbl_virt, ep->qp.sq_pgtbl_phys);
+ ep->qp.sq_pgtbl_virt = NULL;
+ ep->qp.sq_pgtbl_phys = 0;
+ }
+ if (ep->qp.sq_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size,
+ ep->qp.sq_virt, ep->qp.sq_phys);
+ ep->qp.sq_virt = NULL;
+ ep->qp.sq_phys = 0;
+ }
+
+ /* Free RQ mem */
+ if (ep->qp.rq_pgtbl_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size,
+ ep->qp.rq_pgtbl_virt, ep->qp.rq_pgtbl_phys);
+ ep->qp.rq_pgtbl_virt = NULL;
+ ep->qp.rq_pgtbl_phys = 0;
+ }
+ if (ep->qp.rq_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size,
+ ep->qp.rq_virt, ep->qp.rq_phys);
+ ep->qp.rq_virt = NULL;
+ ep->qp.rq_phys = 0;
+ }
+
+ /* Free CQ mem */
+ if (ep->qp.cq_pgtbl_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size,
+ ep->qp.cq_pgtbl_virt, ep->qp.cq_pgtbl_phys);
+ ep->qp.cq_pgtbl_virt = NULL;
+ ep->qp.cq_pgtbl_phys = 0;
+ }
+ if (ep->qp.cq_virt) {
+ dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size,
+ ep->qp.cq_virt, ep->qp.cq_phys);
+ ep->qp.cq_virt = NULL;
+ ep->qp.cq_phys = 0;
+ }
+}
+
+
+/**
+ * bnx2i_send_fw_iscsi_init_msg - initiates initial handshake with iscsi f/w
+ * @hba: adapter structure pointer
+ *
+ * Send down iscsi_init KWQEs which initiates the initial handshake with the f/w
+ * This results in iSCSi support validation and on-chip context manager
+ * initialization. Firmware completes this handshake with a CQE carrying
+ * the result of iscsi support validation. Parameter carried by
+ * iscsi init request determines the number of offloaded connection and
+ * tolerance level for iscsi protocol violation this hba/chip can support
+ */
+int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
+{
+ struct kwqe *kwqe_arr[3];
+ struct iscsi_kwqe_init1 iscsi_init;
+ struct iscsi_kwqe_init2 iscsi_init2;
+ int rc = 0;
+ u64 mask64;
+
+ bnx2i_adjust_qp_size(hba);
+
+ iscsi_init.flags =
+ ISCSI_PAGE_SIZE_4K << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT;
+ if (en_tcp_dack)
+ iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE;
+ iscsi_init.reserved0 = 0;
+ iscsi_init.num_cqs = 1;
+ iscsi_init.hdr.op_code = ISCSI_KWQE_OPCODE_INIT1;
+ iscsi_init.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+ iscsi_init.dummy_buffer_addr_lo = (u32) hba->dummy_buf_dma;
+ iscsi_init.dummy_buffer_addr_hi =
+ (u32) ((u64) hba->dummy_buf_dma >> 32);
+
+ hba->ctx_ccell_tasks =
+ ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16));
+ iscsi_init.num_ccells_per_conn = hba->num_ccell;
+ iscsi_init.num_tasks_per_conn = hba->max_sqes;
+ iscsi_init.sq_wqes_per_page = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+ iscsi_init.sq_num_wqes = hba->max_sqes;
+ iscsi_init.cq_log_wqes_per_page =
+ (u8) bnx2i_power_of2(PAGE_SIZE / BNX2I_CQE_SIZE);
+ iscsi_init.cq_num_wqes = hba->max_cqes;
+ iscsi_init.cq_num_pages = (hba->max_cqes * BNX2I_CQE_SIZE +
+ (PAGE_SIZE - 1)) / PAGE_SIZE;
+ iscsi_init.sq_num_pages = (hba->max_sqes * BNX2I_SQ_WQE_SIZE +
+ (PAGE_SIZE - 1)) / PAGE_SIZE;
+ iscsi_init.rq_buffer_size = BNX2I_RQ_WQE_SIZE;
+ iscsi_init.rq_num_wqes = hba->max_rqes;
+
+
+ iscsi_init2.hdr.op_code = ISCSI_KWQE_OPCODE_INIT2;
+ iscsi_init2.hdr.flags =
+ (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+ iscsi_init2.max_cq_sqn = hba->max_cqes * 2 + 1;
+ mask64 = 0x0ULL;
+ mask64 |= (
+ /* CISCO MDS */
+ (1UL <<
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV) |
+ /* HP MSA1510i */
+ (1UL <<
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN) |
+ /* EMC */
+ (1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN));
+ if (error_mask1)
+ iscsi_init2.error_bit_map[0] = error_mask1;
+ else
+ iscsi_init2.error_bit_map[0] = (u32) mask64;
+
+ if (error_mask2)
+ iscsi_init2.error_bit_map[1] = error_mask2;
+ else
+ iscsi_init2.error_bit_map[1] = (u32) (mask64 >> 32);
+
+ iscsi_error_mask = mask64;
+
+ kwqe_arr[0] = (struct kwqe *) &iscsi_init;
+ kwqe_arr[1] = (struct kwqe *) &iscsi_init2;
+
+ if (hba->cnic && hba->cnic->submit_kwqes)
+ rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 2);
+ return rc;
+}
+
+
+/**
+ * bnx2i_process_scsi_cmd_resp - this function handles scsi cmd completion.
+ * @conn: iscsi connection
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process SCSI CMD Response CQE & complete the request to SCSI-ML
+ */
+static int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct bnx2i_cmd_response *resp_cqe;
+ struct bnx2i_cmd *bnx2i_cmd;
+ struct iscsi_task *task;
+ struct iscsi_cmd_rsp *hdr;
+ u32 datalen = 0;
+
+ resp_cqe = (struct bnx2i_cmd_response *)cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ resp_cqe->itt & ISCSI_CMD_RESPONSE_INDEX);
+ if (!task)
+ goto fail;
+
+ bnx2i_cmd = task->dd_data;
+
+ if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) {
+ conn->datain_pdus_cnt +=
+ resp_cqe->task_stat.read_stat.num_data_outs;
+ conn->rxdata_octets +=
+ bnx2i_cmd->req.total_data_transfer_length;
+ } else {
+ conn->dataout_pdus_cnt +=
+ resp_cqe->task_stat.read_stat.num_data_outs;
+ conn->r2t_pdus_cnt +=
+ resp_cqe->task_stat.read_stat.num_r2ts;
+ conn->txdata_octets +=
+ bnx2i_cmd->req.total_data_transfer_length;
+ }
+ bnx2i_iscsi_unmap_sg_list(bnx2i_cmd);
+
+ hdr = (struct iscsi_cmd_rsp *)task->hdr;
+ resp_cqe = (struct bnx2i_cmd_response *)cqe;
+ hdr->opcode = resp_cqe->op_code;
+ hdr->max_cmdsn = cpu_to_be32(resp_cqe->max_cmd_sn);
+ hdr->exp_cmdsn = cpu_to_be32(resp_cqe->exp_cmd_sn);
+ hdr->response = resp_cqe->response;
+ hdr->cmd_status = resp_cqe->status;
+ hdr->flags = resp_cqe->response_flags;
+ hdr->residual_count = cpu_to_be32(resp_cqe->residual_count);
+
+ if (resp_cqe->op_code == ISCSI_OP_SCSI_DATA_IN)
+ goto done;
+
+ if (resp_cqe->status == SAM_STAT_CHECK_CONDITION) {
+ datalen = resp_cqe->data_length;
+ if (datalen < 2)
+ goto done;
+
+ if (datalen > BNX2I_RQ_WQE_SIZE) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "sense data len %d > RQ sz\n",
+ datalen);
+ datalen = BNX2I_RQ_WQE_SIZE;
+ } else if (datalen > ISCSI_DEF_MAX_RECV_SEG_LEN) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "sense data len %d > conn data\n",
+ datalen);
+ datalen = ISCSI_DEF_MAX_RECV_SEG_LEN;
+ }
+
+ bnx2i_get_rq_buf(bnx2i_cmd->conn, conn->data, datalen);
+ bnx2i_put_rq_buf(bnx2i_cmd->conn, 1);
+ }
+
+done:
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr,
+ conn->data, datalen);
+fail:
+ spin_unlock(&session->lock);
+ return 0;
+}
+
+
+/**
+ * bnx2i_process_login_resp - this function handles iscsi login response
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process Login Response CQE & complete it to open-iscsi user daemon
+ */
+static int bnx2i_process_login_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_task *task;
+ struct bnx2i_login_response *login;
+ struct iscsi_login_rsp *resp_hdr;
+ int pld_len;
+ int pad_len;
+
+ login = (struct bnx2i_login_response *) cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ login->itt & ISCSI_LOGIN_RESPONSE_INDEX);
+ if (!task)
+ goto done;
+
+ resp_hdr = (struct iscsi_login_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+ memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+ resp_hdr->opcode = login->op_code;
+ resp_hdr->flags = login->response_flags;
+ resp_hdr->max_version = login->version_max;
+ resp_hdr->active_version = login->version_active;;
+ resp_hdr->hlength = 0;
+
+ hton24(resp_hdr->dlength, login->data_length);
+ memcpy(resp_hdr->isid, &login->isid_lo, 6);
+ resp_hdr->tsih = cpu_to_be16(login->tsih);
+ resp_hdr->itt = task->hdr->itt;
+ resp_hdr->statsn = cpu_to_be32(login->stat_sn);
+ resp_hdr->exp_cmdsn = cpu_to_be32(login->exp_cmd_sn);
+ resp_hdr->max_cmdsn = cpu_to_be32(login->max_cmd_sn);
+ resp_hdr->status_class = login->status_class;
+ resp_hdr->status_detail = login->status_detail;
+ pld_len = login->data_length;
+ bnx2i_conn->gen_pdu.resp_wr_ptr =
+ bnx2i_conn->gen_pdu.resp_buf + pld_len;
+
+ pad_len = 0;
+ if (pld_len & 0x3)
+ pad_len = 4 - (pld_len % 4);
+
+ if (pad_len) {
+ int i = 0;
+ for (i = 0; i < pad_len; i++) {
+ bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0;
+ bnx2i_conn->gen_pdu.resp_wr_ptr++;
+ }
+ }
+
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr,
+ bnx2i_conn->gen_pdu.resp_buf,
+ bnx2i_conn->gen_pdu.resp_wr_ptr - bnx2i_conn->gen_pdu.resp_buf);
+done:
+ spin_unlock(&session->lock);
+ return 0;
+}
+
+/**
+ * bnx2i_process_tmf_resp - this function handles iscsi TMF response
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI TMF Response CQE and wake up the driver eh thread.
+ */
+static int bnx2i_process_tmf_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_task *task;
+ struct bnx2i_tmf_response *tmf_cqe;
+ struct iscsi_tm_rsp *resp_hdr;
+
+ tmf_cqe = (struct bnx2i_tmf_response *)cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ tmf_cqe->itt & ISCSI_TMF_RESPONSE_INDEX);
+ if (!task)
+ goto done;
+
+ resp_hdr = (struct iscsi_tm_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+ memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+ resp_hdr->opcode = tmf_cqe->op_code;
+ resp_hdr->max_cmdsn = cpu_to_be32(tmf_cqe->max_cmd_sn);
+ resp_hdr->exp_cmdsn = cpu_to_be32(tmf_cqe->exp_cmd_sn);
+ resp_hdr->itt = task->hdr->itt;
+ resp_hdr->response = tmf_cqe->response;
+
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
+done:
+ spin_unlock(&session->lock);
+ return 0;
+}
+
+/**
+ * bnx2i_process_logout_resp - this function handles iscsi logout response
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI Logout Response CQE & make function call to
+ * notify the user daemon.
+ */
+static int bnx2i_process_logout_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_task *task;
+ struct bnx2i_logout_response *logout;
+ struct iscsi_logout_rsp *resp_hdr;
+
+ logout = (struct bnx2i_logout_response *) cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ logout->itt & ISCSI_LOGOUT_RESPONSE_INDEX);
+ if (!task)
+ goto done;
+
+ resp_hdr = (struct iscsi_logout_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+ memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+ resp_hdr->opcode = logout->op_code;
+ resp_hdr->flags = logout->response;
+ resp_hdr->hlength = 0;
+
+ resp_hdr->itt = task->hdr->itt;
+ resp_hdr->statsn = task->hdr->exp_statsn;
+ resp_hdr->exp_cmdsn = cpu_to_be32(logout->exp_cmd_sn);
+ resp_hdr->max_cmdsn = cpu_to_be32(logout->max_cmd_sn);
+
+ resp_hdr->t2wait = cpu_to_be32(logout->time_to_wait);
+ resp_hdr->t2retain = cpu_to_be32(logout->time_to_retain);
+
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
+done:
+ spin_unlock(&session->lock);
+ return 0;
+}
+
+/**
+ * bnx2i_process_nopin_local_cmpl - this function handles iscsi nopin CQE
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI NOPIN local completion CQE, frees IIT and command structures
+ */
+static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct bnx2i_nop_in_msg *nop_in;
+ struct iscsi_task *task;
+
+ nop_in = (struct bnx2i_nop_in_msg *)cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ nop_in->itt & ISCSI_NOP_IN_MSG_INDEX);
+ if (task)
+ iscsi_put_task(task);
+ spin_unlock(&session->lock);
+}
+
+/**
+ * bnx2i_unsol_pdu_adjust_rq - makes adjustments to RQ after unsol pdu is recvd
+ * @conn: iscsi connection
+ *
+ * Firmware advances RQ producer index for every unsolicited PDU even if
+ * payload data length is '0'. This function makes corresponding
+ * adjustments on the driver side to match this f/w behavior
+ */
+static void bnx2i_unsol_pdu_adjust_rq(struct bnx2i_conn *bnx2i_conn)
+{
+ char dummy_rq_data[2];
+ bnx2i_get_rq_buf(bnx2i_conn, dummy_rq_data, 1);
+ bnx2i_put_rq_buf(bnx2i_conn, 1);
+}
+
+
+/**
+ * bnx2i_process_nopin_mesg - this function handles iscsi nopin CQE
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI target's proactive iSCSI NOPIN request
+ */
+static int bnx2i_process_nopin_mesg(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_task *task;
+ struct bnx2i_nop_in_msg *nop_in;
+ struct iscsi_nopin *hdr;
+ u32 itt;
+ int tgt_async_nop = 0;
+
+ nop_in = (struct bnx2i_nop_in_msg *)cqe;
+ itt = nop_in->itt & ISCSI_NOP_IN_MSG_INDEX;
+
+ spin_lock(&session->lock);
+ hdr = (struct iscsi_nopin *)&bnx2i_conn->gen_pdu.resp_hdr;
+ memset(hdr, 0, sizeof(struct iscsi_hdr));
+ hdr->opcode = nop_in->op_code;
+ hdr->max_cmdsn = cpu_to_be32(nop_in->max_cmd_sn);
+ hdr->exp_cmdsn = cpu_to_be32(nop_in->exp_cmd_sn);
+ hdr->ttt = cpu_to_be32(nop_in->ttt);
+
+ if (itt == (u16) RESERVED_ITT) {
+ bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+ hdr->itt = RESERVED_ITT;
+ tgt_async_nop = 1;
+ goto done;
+ }
+
+ /* this is a response to one of our nop-outs */
+ task = iscsi_itt_to_task(conn, itt);
+ if (task) {
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ hdr->itt = task->hdr->itt;
+ hdr->ttt = cpu_to_be32(nop_in->ttt);
+ memcpy(hdr->lun, nop_in->lun, 8);
+ }
+done:
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
+ spin_unlock(&session->lock);
+
+ return tgt_async_nop;
+}
+
+
+/**
+ * bnx2i_process_async_mesg - this function handles iscsi async message
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI ASYNC Message
+ */
+static void bnx2i_process_async_mesg(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct bnx2i_async_msg *async_cqe;
+ struct iscsi_async *resp_hdr;
+ u8 async_event;
+
+ bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+
+ async_cqe = (struct bnx2i_async_msg *)cqe;
+ async_event = async_cqe->async_event;
+
+ if (async_event == ISCSI_ASYNC_MSG_SCSI_EVENT) {
+ iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+ "async: scsi events not supported\n");
+ return;
+ }
+
+ spin_lock(&session->lock);
+ resp_hdr = (struct iscsi_async *) &bnx2i_conn->gen_pdu.resp_hdr;
+ memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+ resp_hdr->opcode = async_cqe->op_code;
+ resp_hdr->flags = 0x80;
+
+ memcpy(resp_hdr->lun, async_cqe->lun, 8);
+ resp_hdr->exp_cmdsn = cpu_to_be32(async_cqe->exp_cmd_sn);
+ resp_hdr->max_cmdsn = cpu_to_be32(async_cqe->max_cmd_sn);
+
+ resp_hdr->async_event = async_cqe->async_event;
+ resp_hdr->async_vcode = async_cqe->async_vcode;
+
+ resp_hdr->param1 = cpu_to_be16(async_cqe->param1);
+ resp_hdr->param2 = cpu_to_be16(async_cqe->param2);
+ resp_hdr->param3 = cpu_to_be16(async_cqe->param3);
+
+ __iscsi_complete_pdu(bnx2i_conn->cls_conn->dd_data,
+ (struct iscsi_hdr *)resp_hdr, NULL, 0);
+ spin_unlock(&session->lock);
+}
+
+
+/**
+ * bnx2i_process_reject_mesg - process iscsi reject pdu
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI REJECT message
+ */
+static void bnx2i_process_reject_mesg(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct bnx2i_reject_msg *reject;
+ struct iscsi_reject *hdr;
+
+ reject = (struct bnx2i_reject_msg *) cqe;
+ if (reject->data_length) {
+ bnx2i_get_rq_buf(bnx2i_conn, conn->data, reject->data_length);
+ bnx2i_put_rq_buf(bnx2i_conn, 1);
+ } else
+ bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+
+ spin_lock(&session->lock);
+ hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr;
+ memset(hdr, 0, sizeof(struct iscsi_hdr));
+ hdr->opcode = reject->op_code;
+ hdr->reason = reject->reason;
+ hton24(hdr->dlength, reject->data_length);
+ hdr->max_cmdsn = cpu_to_be32(reject->max_cmd_sn);
+ hdr->exp_cmdsn = cpu_to_be32(reject->exp_cmd_sn);
+ hdr->ffffffff = cpu_to_be32(RESERVED_ITT);
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, conn->data,
+ reject->data_length);
+ spin_unlock(&session->lock);
+}
+
+/**
+ * bnx2i_process_cmd_cleanup_resp - process scsi command clean-up completion
+ * @session: iscsi session pointer
+ * @bnx2i_conn: iscsi connection pointer
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * process command cleanup response CQE during conn shutdown or error recovery
+ */
+static void bnx2i_process_cmd_cleanup_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
+{
+ struct bnx2i_cleanup_response *cmd_clean_rsp;
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_task *task;
+
+ cmd_clean_rsp = (struct bnx2i_cleanup_response *)cqe;
+ spin_lock(&session->lock);
+ task = iscsi_itt_to_task(conn,
+ cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
+ if (!task)
+ printk(KERN_ALERT "bnx2i: cmd clean ITT %x not active\n",
+ cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
+ spin_unlock(&session->lock);
+ complete(&bnx2i_conn->cmd_cleanup_cmpl);
+}
+
+
+
+/**
+ * bnx2i_process_new_cqes - process newly DMA'ed CQE's
+ * @bnx2i_conn: iscsi connection
+ *
+ * this function is called by generic KCQ handler to process all pending CQE's
+ */
+static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
+{
+ struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ struct qp_info *qp = &bnx2i_conn->ep->qp;
+ struct bnx2i_nop_in_msg *nopin;
+ int tgt_async_msg;
+
+ while (1) {
+ nopin = (struct bnx2i_nop_in_msg *) qp->cq_cons_qe;
+ if (nopin->cq_req_sn != qp->cqe_exp_seq_sn)
+ break;
+
+ if (unlikely(test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx)))
+ break;
+
+ tgt_async_msg = 0;
+
+ switch (nopin->op_code) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ case ISCSI_OP_SCSI_DATA_IN:
+ bnx2i_process_scsi_cmd_resp(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OP_LOGIN_RSP:
+ bnx2i_process_login_resp(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ bnx2i_process_tmf_resp(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OP_LOGOUT_RSP:
+ bnx2i_process_logout_resp(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OP_NOOP_IN:
+ if (bnx2i_process_nopin_mesg(session, bnx2i_conn,
+ qp->cq_cons_qe))
+ tgt_async_msg = 1;
+ break;
+ case ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION:
+ bnx2i_process_nopin_local_cmpl(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OP_ASYNC_EVENT:
+ bnx2i_process_async_mesg(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ tgt_async_msg = 1;
+ break;
+ case ISCSI_OP_REJECT:
+ bnx2i_process_reject_mesg(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ case ISCSI_OPCODE_CLEANUP_RESPONSE:
+ bnx2i_process_cmd_cleanup_resp(session, bnx2i_conn,
+ qp->cq_cons_qe);
+ break;
+ default:
+ printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n",
+ nopin->op_code);
+ }
+
+ if (!tgt_async_msg)
+ bnx2i_conn->ep->num_active_cmds--;
+
+ /* clear out in production version only, till beta keep opcode
+ * field intact, will be helpful in debugging (context dump)
+ * nopin->op_code = 0;
+ */
+ qp->cqe_exp_seq_sn++;
+ if (qp->cqe_exp_seq_sn == (qp->cqe_size * 2 + 1))
+ qp->cqe_exp_seq_sn = ISCSI_INITIAL_SN;
+
+ if (qp->cq_cons_qe == qp->cq_last_qe) {
+ qp->cq_cons_qe = qp->cq_first_qe;
+ qp->cq_cons_idx = 0;
+ } else {
+ qp->cq_cons_qe++;
+ qp->cq_cons_idx++;
+ }
+ }
+ bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+}
+
+/**
+ * bnx2i_fastpath_notification - process global event queue (KCQ)
+ * @hba: adapter structure pointer
+ * @new_cqe_kcqe: pointer to newly DMA'ed KCQE entry
+ *
+ * Fast path event notification handler, KCQ entry carries context id
+ * of the connection that has 1 or more pending CQ entries
+ */
+static void bnx2i_fastpath_notification(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *new_cqe_kcqe)
+{
+ struct bnx2i_conn *conn;
+ u32 iscsi_cid;
+
+ iscsi_cid = new_cqe_kcqe->iscsi_conn_id;
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "cid #%x not valid\n", iscsi_cid);
+ return;
+ }
+ if (!conn->ep) {
+ printk(KERN_ALERT "cid #%x - ep not bound\n", iscsi_cid);
+ return;
+ }
+
+ bnx2i_process_new_cqes(conn);
+}
+
+
+/**
+ * bnx2i_process_update_conn_cmpl - process iscsi conn update completion KCQE
+ * @hba: adapter structure pointer
+ * @update_kcqe: kcqe pointer
+ *
+ * CONN_UPDATE completion handler, this completes iSCSI connection FFP migration
+ */
+static void bnx2i_process_update_conn_cmpl(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *update_kcqe)
+{
+ struct bnx2i_conn *conn;
+ u32 iscsi_cid;
+
+ iscsi_cid = update_kcqe->iscsi_conn_id;
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "conn_update: cid %x not valid\n", iscsi_cid);
+ return;
+ }
+ if (!conn->ep) {
+ printk(KERN_ALERT "cid %x does not have ep bound\n", iscsi_cid);
+ return;
+ }
+
+ if (update_kcqe->completion_status) {
+ printk(KERN_ALERT "request failed cid %x\n", iscsi_cid);
+ conn->ep->state = EP_STATE_ULP_UPDATE_FAILED;
+ } else
+ conn->ep->state = EP_STATE_ULP_UPDATE_COMPL;
+
+ wake_up_interruptible(&conn->ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_recovery_que_add_conn - add connection to recovery queue
+ * @hba: adapter structure pointer
+ * @bnx2i_conn: iscsi connection
+ *
+ * Add connection to recovery queue and schedule adapter eh worker
+ */
+static void bnx2i_recovery_que_add_conn(struct bnx2i_hba *hba,
+ struct bnx2i_conn *bnx2i_conn)
+{
+ iscsi_conn_failure(bnx2i_conn->cls_conn->dd_data,
+ ISCSI_ERR_CONN_FAILED);
+}
+
+
+/**
+ * bnx2i_process_tcp_error - process error notification on a given connection
+ *
+ * @hba: adapter structure pointer
+ * @tcp_err: tcp error kcqe pointer
+ *
+ * handles tcp level error notifications from FW.
+ */
+static void bnx2i_process_tcp_error(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *tcp_err)
+{
+ struct bnx2i_conn *bnx2i_conn;
+ u32 iscsi_cid;
+
+ iscsi_cid = tcp_err->iscsi_conn_id;
+ bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!bnx2i_conn) {
+ printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid);
+ return;
+ }
+
+ printk(KERN_ALERT "bnx2i - cid 0x%x had TCP errors, error code 0x%x\n",
+ iscsi_cid, tcp_err->completion_status);
+ bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn);
+}
+
+
+/**
+ * bnx2i_process_iscsi_error - process error notification on a given connection
+ * @hba: adapter structure pointer
+ * @iscsi_err: iscsi error kcqe pointer
+ *
+ * handles iscsi error notifications from the FW. Firmware based in initial
+ * handshake classifies iscsi protocol / TCP rfc violation into either
+ * warning or error indications. If indication is of "Error" type, driver
+ * will initiate session recovery for that connection/session. For
+ * "Warning" type indication, driver will put out a system log message
+ * (there will be only one message for each type for the life of the
+ * session, this is to avoid un-necessarily overloading the system)
+ */
+static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *iscsi_err)
+{
+ struct bnx2i_conn *bnx2i_conn;
+ u32 iscsi_cid;
+ char warn_notice[] = "iscsi_warning";
+ char error_notice[] = "iscsi_error";
+ char additional_notice[64];
+ char *message;
+ int need_recovery;
+ u64 err_mask64;
+
+ iscsi_cid = iscsi_err->iscsi_conn_id;
+ bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+ if (!bnx2i_conn) {
+ printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid);
+ return;
+ }
+
+ err_mask64 = (0x1ULL << iscsi_err->completion_status);
+
+ if (err_mask64 & iscsi_error_mask) {
+ need_recovery = 0;
+ message = warn_notice;
+ } else {
+ need_recovery = 1;
+ message = error_notice;
+ }
+
+ switch (iscsi_err->completion_status) {
+ case ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR:
+ strcpy(additional_notice, "hdr digest err");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR:
+ strcpy(additional_notice, "data digest err");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE:
+ strcpy(additional_notice, "wrong opcode rcvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN:
+ strcpy(additional_notice, "AHS len > 0 rcvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT:
+ strcpy(additional_notice, "invalid ITT rcvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN:
+ strcpy(additional_notice, "wrong StatSN rcvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN:
+ strcpy(additional_notice, "wrong DataSN rcvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T:
+ strcpy(additional_notice, "pend R2T violation");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0:
+ strcpy(additional_notice, "ERL0, UO");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1:
+ strcpy(additional_notice, "ERL0, U1");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2:
+ strcpy(additional_notice, "ERL0, U2");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3:
+ strcpy(additional_notice, "ERL0, U3");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4:
+ strcpy(additional_notice, "ERL0, U4");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5:
+ strcpy(additional_notice, "ERL0, U5");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6:
+ strcpy(additional_notice, "ERL0, U6");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN:
+ strcpy(additional_notice, "invalid resi len");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN:
+ strcpy(additional_notice, "MRDSL violation");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO:
+ strcpy(additional_notice, "F-bit not set");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV:
+ strcpy(additional_notice, "invalid TTT");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN:
+ strcpy(additional_notice, "invalid DataSN");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN:
+ strcpy(additional_notice, "burst len violation");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF:
+ strcpy(additional_notice, "buf offset violation");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN:
+ strcpy(additional_notice, "invalid LUN field");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN:
+ strcpy(additional_notice, "invalid R2TSN field");
+ break;
+#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0 \
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0
+ case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0:
+ strcpy(additional_notice, "invalid cmd len1");
+ break;
+#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1 \
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1
+ case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1:
+ strcpy(additional_notice, "invalid cmd len2");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED:
+ strcpy(additional_notice,
+ "pend r2t exceeds MaxOutstandingR2T value");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV:
+ strcpy(additional_notice, "TTT is rsvd");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN:
+ strcpy(additional_notice, "MBL violation");
+ break;
+#define BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO \
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO
+ case BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO:
+ strcpy(additional_notice, "data seg len != 0");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN:
+ strcpy(additional_notice, "reject pdu len error");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN:
+ strcpy(additional_notice, "async pdu len error");
+ break;
+ case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN:
+ strcpy(additional_notice, "nopin pdu len error");
+ break;
+#define BNX2_ERR_PEND_R2T_IN_CLEANUP \
+ ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP
+ case BNX2_ERR_PEND_R2T_IN_CLEANUP:
+ strcpy(additional_notice, "pend r2t in cleanup");
+ break;
+
+ case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT:
+ strcpy(additional_notice, "IP fragments rcvd");
+ break;
+ case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS:
+ strcpy(additional_notice, "IP options error");
+ break;
+ case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG:
+ strcpy(additional_notice, "urgent flag error");
+ break;
+ default:
+ printk(KERN_ALERT "iscsi_err - unknown err %x\n",
+ iscsi_err->completion_status);
+ }
+
+ if (need_recovery) {
+ iscsi_conn_printk(KERN_ALERT,
+ bnx2i_conn->cls_conn->dd_data,
+ "bnx2i: %s - %s\n",
+ message, additional_notice);
+
+ iscsi_conn_printk(KERN_ALERT,
+ bnx2i_conn->cls_conn->dd_data,
+ "conn_err - hostno %d conn %p, "
+ "iscsi_cid %x cid %x\n",
+ bnx2i_conn->hba->shost->host_no,
+ bnx2i_conn, bnx2i_conn->ep->ep_iscsi_cid,
+ bnx2i_conn->ep->ep_cid);
+ bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn);
+ } else
+ if (!test_and_set_bit(iscsi_err->completion_status,
+ (void *) &bnx2i_conn->violation_notified))
+ iscsi_conn_printk(KERN_ALERT,
+ bnx2i_conn->cls_conn->dd_data,
+ "bnx2i: %s - %s\n",
+ message, additional_notice);
+}
+
+
+/**
+ * bnx2i_process_conn_destroy_cmpl - process iscsi conn destroy completion
+ * @hba: adapter structure pointer
+ * @conn_destroy: conn destroy kcqe pointer
+ *
+ * handles connection destroy completion request.
+ */
+static void bnx2i_process_conn_destroy_cmpl(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *conn_destroy)
+{
+ struct bnx2i_endpoint *ep;
+
+ ep = bnx2i_find_ep_in_destroy_list(hba, conn_destroy->iscsi_conn_id);
+ if (!ep) {
+ printk(KERN_ALERT "bnx2i_conn_destroy_cmpl: no pending "
+ "offload request, unexpected complection\n");
+ return;
+ }
+
+ if (hba != ep->hba) {
+ printk(KERN_ALERT "conn destroy- error hba mis-match\n");
+ return;
+ }
+
+ if (conn_destroy->completion_status) {
+ printk(KERN_ALERT "conn_destroy_cmpl: op failed\n");
+ ep->state = EP_STATE_CLEANUP_FAILED;
+ } else
+ ep->state = EP_STATE_CLEANUP_CMPL;
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_process_ofld_cmpl - process initial iscsi conn offload completion
+ * @hba: adapter structure pointer
+ * @ofld_kcqe: conn offload kcqe pointer
+ *
+ * handles initial connection offload completion, ep_connect() thread is
+ * woken-up to continue with LLP connect process
+ */
+static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
+ struct iscsi_kcqe *ofld_kcqe)
+{
+ u32 cid_addr;
+ struct bnx2i_endpoint *ep;
+ u32 cid_num;
+
+ ep = bnx2i_find_ep_in_ofld_list(hba, ofld_kcqe->iscsi_conn_id);
+ if (!ep) {
+ printk(KERN_ALERT "ofld_cmpl: no pend offload request\n");
+ return;
+ }
+
+ if (hba != ep->hba) {
+ printk(KERN_ALERT "ofld_cmpl: error hba mis-match\n");
+ return;
+ }
+
+ if (ofld_kcqe->completion_status) {
+ if (ofld_kcqe->completion_status ==
+ ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE)
+ printk(KERN_ALERT "bnx2i: unable to allocate"
+ " iSCSI context resources\n");
+ ep->state = EP_STATE_OFLD_FAILED;
+ } else {
+ ep->state = EP_STATE_OFLD_COMPL;
+ cid_addr = ofld_kcqe->iscsi_conn_context_id;
+ cid_num = bnx2i_get_cid_num(ep);
+ ep->ep_cid = cid_addr;
+ ep->qp.ctx_base = NULL;
+ }
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+/**
+ * bnx2i_indicate_kcqe - process iscsi conn update completion KCQE
+ * @hba: adapter structure pointer
+ * @update_kcqe: kcqe pointer
+ *
+ * Generic KCQ event handler/dispatcher
+ */
+static void bnx2i_indicate_kcqe(void *context, struct kcqe *kcqe[],
+ u32 num_cqe)
+{
+ struct bnx2i_hba *hba = context;
+ int i = 0;
+ struct iscsi_kcqe *ikcqe = NULL;
+
+ while (i < num_cqe) {
+ ikcqe = (struct iscsi_kcqe *) kcqe[i++];
+
+ if (ikcqe->op_code ==
+ ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION)
+ bnx2i_fastpath_notification(hba, ikcqe);
+ else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_OFFLOAD_CONN)
+ bnx2i_process_ofld_cmpl(hba, ikcqe);
+ else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_UPDATE_CONN)
+ bnx2i_process_update_conn_cmpl(hba, ikcqe);
+ else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_INIT) {
+ if (ikcqe->completion_status !=
+ ISCSI_KCQE_COMPLETION_STATUS_SUCCESS)
+ bnx2i_iscsi_license_error(hba, ikcqe->\
+ completion_status);
+ else {
+ set_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+ bnx2i_get_link_state(hba);
+ printk(KERN_INFO "bnx2i [%.2x:%.2x.%.2x]: "
+ "ISCSI_INIT passed\n",
+ (u8)hba->pcidev->bus->number,
+ hba->pci_devno,
+ (u8)hba->pci_func);
+
+
+ }
+ } else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_DESTROY_CONN)
+ bnx2i_process_conn_destroy_cmpl(hba, ikcqe);
+ else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_ISCSI_ERROR)
+ bnx2i_process_iscsi_error(hba, ikcqe);
+ else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_TCP_ERROR)
+ bnx2i_process_tcp_error(hba, ikcqe);
+ else
+ printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n",
+ ikcqe->op_code);
+ }
+}
+
+
+/**
+ * bnx2i_indicate_netevent - Generic netdev event handler
+ * @context: adapter structure pointer
+ * @event: event type
+ *
+ * Handles four netdev events, NETDEV_UP, NETDEV_DOWN,
+ * NETDEV_GOING_DOWN and NETDEV_CHANGE
+ */
+static void bnx2i_indicate_netevent(void *context, unsigned long event)
+{
+ struct bnx2i_hba *hba = context;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
+ bnx2i_send_fw_iscsi_init_msg(hba);
+ break;
+ case NETDEV_DOWN:
+ clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+ clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+ break;
+ case NETDEV_GOING_DOWN:
+ set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+ iscsi_host_for_each_session(hba->shost,
+ bnx2i_drop_session);
+ break;
+ case NETDEV_CHANGE:
+ bnx2i_get_link_state(hba);
+ break;
+ default:
+ ;
+ }
+}
+
+
+/**
+ * bnx2i_cm_connect_cmpl - process iscsi conn establishment completion
+ * @cm_sk: cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ * indicate completion of option-2 TCP connect request.
+ */
+static void bnx2i_cm_connect_cmpl(struct cnic_sock *cm_sk)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+ if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state))
+ ep->state = EP_STATE_CONNECT_FAILED;
+ else if (test_bit(SK_F_OFFLD_COMPLETE, &cm_sk->flags))
+ ep->state = EP_STATE_CONNECT_COMPL;
+ else
+ ep->state = EP_STATE_CONNECT_FAILED;
+
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_close_cmpl - process tcp conn close completion
+ * @cm_sk: cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ * indicate completion of option-2 graceful TCP connect shutdown
+ */
+static void bnx2i_cm_close_cmpl(struct cnic_sock *cm_sk)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+ ep->state = EP_STATE_DISCONN_COMPL;
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_abort_cmpl - process abortive tcp conn teardown completion
+ * @cm_sk: cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ * indicate completion of option-2 abortive TCP connect termination
+ */
+static void bnx2i_cm_abort_cmpl(struct cnic_sock *cm_sk)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+ ep->state = EP_STATE_DISCONN_COMPL;
+ wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_remote_close - process received TCP FIN
+ * @hba: adapter structure pointer
+ * @update_kcqe: kcqe pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to indicate
+ * async TCP events such as FIN
+ */
+static void bnx2i_cm_remote_close(struct cnic_sock *cm_sk)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+ ep->state = EP_STATE_TCP_FIN_RCVD;
+ if (ep->conn)
+ bnx2i_recovery_que_add_conn(ep->hba, ep->conn);
+}
+
+/**
+ * bnx2i_cm_remote_abort - process TCP RST and start conn cleanup
+ * @hba: adapter structure pointer
+ * @update_kcqe: kcqe pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ * indicate async TCP events (RST) sent by the peer.
+ */
+static void bnx2i_cm_remote_abort(struct cnic_sock *cm_sk)
+{
+ struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+ ep->state = EP_STATE_TCP_RST_RCVD;
+ if (ep->conn)
+ bnx2i_recovery_que_add_conn(ep->hba, ep->conn);
+}
+
+
+static void bnx2i_send_nl_mesg(struct cnic_dev *dev, u32 msg_type,
+ char *buf, u16 buflen)
+{
+ struct bnx2i_hba *hba;
+
+ hba = bnx2i_find_hba_for_cnic(dev);
+ if (!hba)
+ return;
+
+ if (iscsi_offload_mesg(hba->shost, &bnx2i_iscsi_transport,
+ msg_type, buf, buflen))
+ printk(KERN_ALERT "bnx2i: private nl message send error\n");
+
+}
+
+
+/**
+ * bnx2i_cnic_cb - global template of bnx2i - cnic driver interface structure
+ * carrying callback function pointers
+ *
+ */
+struct cnic_ulp_ops bnx2i_cnic_cb = {
+ .cnic_init = bnx2i_ulp_init,
+ .cnic_exit = bnx2i_ulp_exit,
+ .cnic_start = bnx2i_start,
+ .cnic_stop = bnx2i_stop,
+ .indicate_kcqes = bnx2i_indicate_kcqe,
+ .indicate_netevent = bnx2i_indicate_netevent,
+ .cm_connect_complete = bnx2i_cm_connect_cmpl,
+ .cm_close_complete = bnx2i_cm_close_cmpl,
+ .cm_abort_complete = bnx2i_cm_abort_cmpl,
+ .cm_remote_close = bnx2i_cm_remote_close,
+ .cm_remote_abort = bnx2i_cm_remote_abort,
+ .iscsi_nl_send_msg = bnx2i_send_nl_mesg,
+ .owner = THIS_MODULE
+};
+
+
+/**
+ * bnx2i_map_ep_dbell_regs - map connection doorbell registers
+ * @ep: bnx2i endpoint
+ *
+ * maps connection's SQ and RQ doorbell registers, 5706/5708/5709 hosts these
+ * register in BAR #0. Whereas in 57710 these register are accessed by
+ * mapping BAR #1
+ */
+int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep)
+{
+ u32 cid_num;
+ u32 reg_off;
+ u32 first_l4l5;
+ u32 ctx_sz;
+ u32 config2;
+ resource_size_t reg_base;
+
+ cid_num = bnx2i_get_cid_num(ep);
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+ reg_base = pci_resource_start(ep->hba->pcidev,
+ BNX2X_DOORBELL_PCI_BAR);
+ reg_off = PAGE_SIZE * (cid_num & 0x1FFFF) + DPM_TRIGER_TYPE;
+ ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off, 4);
+ goto arm_cq;
+ }
+
+ reg_base = ep->hba->netdev->base_addr;
+ if ((test_bit(BNX2I_NX2_DEV_5709, &ep->hba->cnic_dev_type)) &&
+ (ep->hba->mail_queue_access == BNX2I_MQ_BIN_MODE)) {
+ config2 = REG_RD(ep->hba, BNX2_MQ_CONFIG2);
+ first_l4l5 = config2 & BNX2_MQ_CONFIG2_FIRST_L4L5;
+ ctx_sz = (config2 & BNX2_MQ_CONFIG2_CONT_SZ) >> 3;
+ if (ctx_sz)
+ reg_off = CTX_OFFSET + MAX_CID_CNT * MB_KERNEL_CTX_SIZE
+ + PAGE_SIZE *
+ (((cid_num - first_l4l5) / ctx_sz) + 256);
+ else
+ reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num);
+ } else
+ /* 5709 device in normal node and 5706/5708 devices */
+ reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num);
+
+ ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off,
+ MB_KERNEL_CTX_SIZE);
+ if (!ep->qp.ctx_base)
+ return -ENOMEM;
+
+arm_cq:
+ bnx2i_arm_cq_event_coalescing(ep, CNIC_ARM_CQE);
+ return 0;
+}
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
new file mode 100644
index 000000000000..ae4b2d588fd3
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -0,0 +1,438 @@
+/* bnx2i.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list);
+static u32 adapter_count;
+static int bnx2i_reg_device;
+
+#define DRV_MODULE_NAME "bnx2i"
+#define DRV_MODULE_VERSION "2.0.1d"
+#define DRV_MODULE_RELDATE "Mar 25, 2009"
+
+static char version[] __devinitdata =
+ "Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+
+MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709 iSCSI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static DEFINE_RWLOCK(bnx2i_dev_lock);
+
+unsigned int event_coal_div = 1;
+module_param(event_coal_div, int, 0664);
+MODULE_PARM_DESC(event_coal_div, "Event Coalescing Divide Factor");
+
+unsigned int en_tcp_dack = 1;
+module_param(en_tcp_dack, int, 0664);
+MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK");
+
+unsigned int error_mask1 = 0x00;
+module_param(error_mask1, int, 0664);
+MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1");
+
+unsigned int error_mask2 = 0x00;
+module_param(error_mask2, int, 0664);
+MODULE_PARM_DESC(error_mask2, "Config FW iSCSI Error Mask #2");
+
+unsigned int sq_size;
+module_param(sq_size, int, 0664);
+MODULE_PARM_DESC(sq_size, "Configure SQ size");
+
+unsigned int rq_size = BNX2I_RQ_WQES_DEFAULT;
+module_param(rq_size, int, 0664);
+MODULE_PARM_DESC(rq_size, "Configure RQ size");
+
+u64 iscsi_error_mask = 0x00;
+
+static void bnx2i_unreg_one_device(struct bnx2i_hba *hba) ;
+
+
+/**
+ * bnx2i_identify_device - identifies NetXtreme II device type
+ * @hba: Adapter structure pointer
+ *
+ * This function identifies the NX2 device type and sets appropriate
+ * queue mailbox register access method, 5709 requires driver to
+ * access MBOX regs using *bin* mode
+ */
+void bnx2i_identify_device(struct bnx2i_hba *hba)
+{
+ hba->cnic_dev_type = 0;
+ if ((hba->pci_did == PCI_DEVICE_ID_NX2_5706) ||
+ (hba->pci_did == PCI_DEVICE_ID_NX2_5706S))
+ set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
+ else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5708) ||
+ (hba->pci_did == PCI_DEVICE_ID_NX2_5708S))
+ set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
+ else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5709) ||
+ (hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) {
+ set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
+ hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
+ } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_57711)
+ set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type);
+}
+
+
+/**
+ * get_adapter_list_head - returns head of adapter list
+ */
+struct bnx2i_hba *get_adapter_list_head(void)
+{
+ struct bnx2i_hba *hba = NULL;
+ struct bnx2i_hba *tmp_hba;
+
+ if (!adapter_count)
+ goto hba_not_found;
+
+ read_lock(&bnx2i_dev_lock);
+ list_for_each_entry(tmp_hba, &adapter_list, link) {
+ if (tmp_hba->cnic && tmp_hba->cnic->cm_select_dev) {
+ hba = tmp_hba;
+ break;
+ }
+ }
+ read_unlock(&bnx2i_dev_lock);
+hba_not_found:
+ return hba;
+}
+
+
+/**
+ * bnx2i_find_hba_for_cnic - maps cnic device instance to bnx2i adapter instance
+ * @cnic: pointer to cnic device instance
+ *
+ */
+struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic)
+{
+ struct bnx2i_hba *hba, *temp;
+
+ read_lock(&bnx2i_dev_lock);
+ list_for_each_entry_safe(hba, temp, &adapter_list, link) {
+ if (hba->cnic == cnic) {
+ read_unlock(&bnx2i_dev_lock);
+ return hba;
+ }
+ }
+ read_unlock(&bnx2i_dev_lock);
+ return NULL;
+}
+
+
+/**
+ * bnx2i_start - cnic callback to initialize & start adapter instance
+ * @handle: transparent handle pointing to adapter structure
+ *
+ * This function maps adapter structure to pcidev structure and initiates
+ * firmware handshake to enable/initialize on chip iscsi components
+ * This bnx2i - cnic interface api callback is issued after following
+ * 2 conditions are met -
+ * a) underlying network interface is up (marked by event 'NETDEV_UP'
+ * from netdev
+ * b) bnx2i adapter instance is registered
+ */
+void bnx2i_start(void *handle)
+{
+#define BNX2I_INIT_POLL_TIME (1000 / HZ)
+ struct bnx2i_hba *hba = handle;
+ int i = HZ;
+
+ bnx2i_send_fw_iscsi_init_msg(hba);
+ while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && i--)
+ msleep(BNX2I_INIT_POLL_TIME);
+}
+
+
+/**
+ * bnx2i_stop - cnic callback to shutdown adapter instance
+ * @handle: transparent handle pointing to adapter structure
+ *
+ * driver checks if adapter is already in shutdown mode, if not start
+ * the shutdown process
+ */
+void bnx2i_stop(void *handle)
+{
+ struct bnx2i_hba *hba = handle;
+
+ /* check if cleanup happened in GOING_DOWN context */
+ clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+ if (!test_and_clear_bit(ADAPTER_STATE_GOING_DOWN,
+ &hba->adapter_state))
+ iscsi_host_for_each_session(hba->shost,
+ bnx2i_drop_session);
+}
+
+/**
+ * bnx2i_register_device - register bnx2i adapter instance with the cnic driver
+ * @hba: Adapter instance to register
+ *
+ * registers bnx2i adapter instance with the cnic driver while holding the
+ * adapter structure lock
+ */
+void bnx2i_register_device(struct bnx2i_hba *hba)
+{
+ if (test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) ||
+ test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ return;
+ }
+
+ hba->cnic->register_device(hba->cnic, CNIC_ULP_ISCSI, hba);
+
+ spin_lock(&hba->lock);
+ bnx2i_reg_device++;
+ spin_unlock(&hba->lock);
+
+ set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+}
+
+
+/**
+ * bnx2i_reg_dev_all - registers all adapter instances with the cnic driver
+ *
+ * registers all bnx2i adapter instances with the cnic driver while holding
+ * the global resource lock
+ */
+void bnx2i_reg_dev_all(void)
+{
+ struct bnx2i_hba *hba, *temp;
+
+ read_lock(&bnx2i_dev_lock);
+ list_for_each_entry_safe(hba, temp, &adapter_list, link)
+ bnx2i_register_device(hba);
+ read_unlock(&bnx2i_dev_lock);
+}
+
+
+/**
+ * bnx2i_unreg_one_device - unregister adapter instance with the cnic driver
+ * @hba: Adapter instance to unregister
+ *
+ * registers bnx2i adapter instance with the cnic driver while holding
+ * the adapter structure lock
+ */
+static void bnx2i_unreg_one_device(struct bnx2i_hba *hba)
+{
+ if (hba->ofld_conns_active ||
+ !test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic) ||
+ test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
+ return;
+
+ hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+
+ spin_lock(&hba->lock);
+ bnx2i_reg_device--;
+ spin_unlock(&hba->lock);
+
+ /* ep_disconnect could come before NETDEV_DOWN, driver won't
+ * see NETDEV_DOWN as it already unregistered itself.
+ */
+ hba->adapter_state = 0;
+ clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+}
+
+/**
+ * bnx2i_unreg_dev_all - unregisters all bnx2i instances with the cnic driver
+ *
+ * unregisters all bnx2i adapter instances with the cnic driver while holding
+ * the global resource lock
+ */
+void bnx2i_unreg_dev_all(void)
+{
+ struct bnx2i_hba *hba, *temp;
+
+ read_lock(&bnx2i_dev_lock);
+ list_for_each_entry_safe(hba, temp, &adapter_list, link)
+ bnx2i_unreg_one_device(hba);
+ read_unlock(&bnx2i_dev_lock);
+}
+
+
+/**
+ * bnx2i_init_one - initialize an adapter instance and allocate memory resources
+ * @hba: bnx2i adapter instance
+ * @cnic: cnic device handle
+ *
+ * Global resource lock and host adapter lock is held during critical sections
+ * below. This routine is called from cnic_register_driver() context and
+ * work horse thread which does majority of device specific initialization
+ */
+static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic)
+{
+ int rc;
+
+ read_lock(&bnx2i_dev_lock);
+ if (bnx2i_reg_device &&
+ !test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba);
+ if (rc) /* duplicate registration */
+ printk(KERN_ERR "bnx2i- dev reg failed\n");
+
+ spin_lock(&hba->lock);
+ bnx2i_reg_device++;
+ hba->age++;
+ spin_unlock(&hba->lock);
+
+ set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+ }
+ read_unlock(&bnx2i_dev_lock);
+
+ write_lock(&bnx2i_dev_lock);
+ list_add_tail(&hba->link, &adapter_list);
+ adapter_count++;
+ write_unlock(&bnx2i_dev_lock);
+ return 0;
+}
+
+
+/**
+ * bnx2i_ulp_init - initialize an adapter instance
+ * @dev: cnic device handle
+ *
+ * Called from cnic_register_driver() context to initialize all enumerated
+ * cnic devices. This routine allocate adapter structure and other
+ * device specific resources.
+ */
+void bnx2i_ulp_init(struct cnic_dev *dev)
+{
+ struct bnx2i_hba *hba;
+
+ /* Allocate a HBA structure for this device */
+ hba = bnx2i_alloc_hba(dev);
+ if (!hba) {
+ printk(KERN_ERR "bnx2i init: hba initialization failed\n");
+ return;
+ }
+
+ /* Get PCI related information and update hba struct members */
+ clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+ if (bnx2i_init_one(hba, dev)) {
+ printk(KERN_ERR "bnx2i - hba %p init failed\n", hba);
+ bnx2i_free_hba(hba);
+ } else
+ hba->cnic = dev;
+}
+
+
+/**
+ * bnx2i_ulp_exit - shuts down adapter instance and frees all resources
+ * @dev: cnic device handle
+ *
+ */
+void bnx2i_ulp_exit(struct cnic_dev *dev)
+{
+ struct bnx2i_hba *hba;
+
+ hba = bnx2i_find_hba_for_cnic(dev);
+ if (!hba) {
+ printk(KERN_INFO "bnx2i_ulp_exit: hba not "
+ "found, dev 0x%p\n", dev);
+ return;
+ }
+ write_lock(&bnx2i_dev_lock);
+ list_del_init(&hba->link);
+ adapter_count--;
+
+ if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+ clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+
+ spin_lock(&hba->lock);
+ bnx2i_reg_device--;
+ spin_unlock(&hba->lock);
+ }
+ write_unlock(&bnx2i_dev_lock);
+
+ bnx2i_free_hba(hba);
+}
+
+
+/**
+ * bnx2i_mod_init - module init entry point
+ *
+ * initialize any driver wide global data structures such as endpoint pool,
+ * tcp port manager/queue, sysfs. finally driver will register itself
+ * with the cnic module
+ */
+static int __init bnx2i_mod_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "%s", version);
+
+ if (!is_power_of_2(sq_size))
+ sq_size = roundup_pow_of_two(sq_size);
+
+ bnx2i_scsi_xport_template =
+ iscsi_register_transport(&bnx2i_iscsi_transport);
+ if (!bnx2i_scsi_xport_template) {
+ printk(KERN_ERR "Could not register bnx2i transport.\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = cnic_register_driver(CNIC_ULP_ISCSI, &bnx2i_cnic_cb);
+ if (err) {
+ printk(KERN_ERR "Could not register bnx2i cnic driver.\n");
+ goto unreg_xport;
+ }
+
+ return 0;
+
+unreg_xport:
+ iscsi_unregister_transport(&bnx2i_iscsi_transport);
+out:
+ return err;
+}
+
+
+/**
+ * bnx2i_mod_exit - module cleanup/exit entry point
+ *
+ * Global resource lock and host adapter lock is held during critical sections
+ * in this function. Driver will browse through the adapter list, cleans-up
+ * each instance, unregisters iscsi transport name and finally driver will
+ * unregister itself with the cnic module
+ */
+static void __exit bnx2i_mod_exit(void)
+{
+ struct bnx2i_hba *hba;
+
+ write_lock(&bnx2i_dev_lock);
+ while (!list_empty(&adapter_list)) {
+ hba = list_entry(adapter_list.next, struct bnx2i_hba, link);
+ list_del(&hba->link);
+ adapter_count--;
+
+ if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+ clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+ bnx2i_reg_device--;
+ }
+
+ write_unlock(&bnx2i_dev_lock);
+ bnx2i_free_hba(hba);
+ write_lock(&bnx2i_dev_lock);
+ }
+ write_unlock(&bnx2i_dev_lock);
+
+ iscsi_unregister_transport(&bnx2i_iscsi_transport);
+ cnic_unregister_driver(CNIC_ULP_ISCSI);
+}
+
+module_init(bnx2i_mod_init);
+module_exit(bnx2i_mod_exit);
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
new file mode 100644
index 000000000000..f7412196f2f8
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -0,0 +1,2064 @@
+/*
+ * bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/libiscsi.h>
+#include "bnx2i.h"
+
+struct scsi_transport_template *bnx2i_scsi_xport_template;
+struct iscsi_transport bnx2i_iscsi_transport;
+static struct scsi_host_template bnx2i_host_template;
+
+/*
+ * Global endpoint resource info
+ */
+static DEFINE_SPINLOCK(bnx2i_resc_lock); /* protects global resources */
+
+
+static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
+{
+ int retval = 0;
+
+ if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
+ test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) ||
+ test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state))
+ retval = -EPERM;
+ return retval;
+}
+
+/**
+ * bnx2i_get_write_cmd_bd_idx - identifies various BD bookmarks
+ * @cmd: iscsi cmd struct pointer
+ * @buf_off: absolute buffer offset
+ * @start_bd_off: u32 pointer to return the offset within the BD
+ * indicated by 'start_bd_idx' on which 'buf_off' falls
+ * @start_bd_idx: index of the BD on which 'buf_off' falls
+ *
+ * identifies & marks various bd info for scsi command's imm data,
+ * unsolicited data and the first solicited data seq.
+ */
+static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
+ u32 *start_bd_off, u32 *start_bd_idx)
+{
+ struct iscsi_bd *bd_tbl = cmd->io_tbl.bd_tbl;
+ u32 cur_offset = 0;
+ u32 cur_bd_idx = 0;
+
+ if (buf_off) {
+ while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
+ cur_offset += bd_tbl->buffer_length;
+ cur_bd_idx++;
+ bd_tbl++;
+ }
+ }
+
+ *start_bd_off = buf_off - cur_offset;
+ *start_bd_idx = cur_bd_idx;
+}
+
+/**
+ * bnx2i_setup_write_cmd_bd_info - sets up BD various information
+ * @task: transport layer's cmd struct pointer
+ *
+ * identifies & marks various bd info for scsi command's immediate data,
+ * unsolicited data and first solicited data seq which includes BD start
+ * index & BD buf off. his function takes into account iscsi parameter such
+ * as immediate data and unsolicited data is support on this connection.
+ */
+static void bnx2i_setup_write_cmd_bd_info(struct iscsi_task *task)
+{
+ struct bnx2i_cmd *cmd = task->dd_data;
+ u32 start_bd_offset;
+ u32 start_bd_idx;
+ u32 buffer_offset = 0;
+ u32 cmd_len = cmd->req.total_data_transfer_length;
+
+ /* if ImmediateData is turned off & IntialR2T is turned on,
+ * there will be no immediate or unsolicited data, just return.
+ */
+ if (!iscsi_task_has_unsol_data(task) && !task->imm_count)
+ return;
+
+ /* Immediate data */
+ buffer_offset += task->imm_count;
+ if (task->imm_count == cmd_len)
+ return;
+
+ if (iscsi_task_has_unsol_data(task)) {
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ cmd->req.ud_buffer_offset = start_bd_offset;
+ cmd->req.ud_start_bd_index = start_bd_idx;
+ buffer_offset += task->unsol_r2t.data_length;
+ }
+
+ if (buffer_offset != cmd_len) {
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ if ((start_bd_offset > task->conn->session->first_burst) ||
+ (start_bd_idx > scsi_sg_count(cmd->scsi_cmd))) {
+ int i = 0;
+
+ iscsi_conn_printk(KERN_ALERT, task->conn,
+ "bnx2i- error, buf offset 0x%x "
+ "bd_valid %d use_sg %d\n",
+ buffer_offset, cmd->io_tbl.bd_valid,
+ scsi_sg_count(cmd->scsi_cmd));
+ for (i = 0; i < cmd->io_tbl.bd_valid; i++)
+ iscsi_conn_printk(KERN_ALERT, task->conn,
+ "bnx2i err, bd[%d]: len %x\n",
+ i, cmd->io_tbl.bd_tbl[i].\
+ buffer_length);
+ }
+ cmd->req.sd_buffer_offset = start_bd_offset;
+ cmd->req.sd_start_bd_index = start_bd_idx;
+ }
+}
+
+
+
+/**
+ * bnx2i_map_scsi_sg - maps IO buffer and prepares the BD table
+ * @hba: adapter instance
+ * @cmd: iscsi cmd struct pointer
+ *
+ * map SG list
+ */
+static int bnx2i_map_scsi_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct iscsi_bd *bd = cmd->io_tbl.bd_tbl;
+ struct scatterlist *sg;
+ int byte_count = 0;
+ int bd_count = 0;
+ int sg_count;
+ int sg_len;
+ u64 addr;
+ int i;
+
+ BUG_ON(scsi_sg_count(sc) > ISCSI_MAX_BDS_PER_CMD);
+
+ sg_count = scsi_dma_map(sc);
+
+ scsi_for_each_sg(sc, sg, sg_count, i) {
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
+ bd[bd_count].buffer_addr_hi = addr >> 32;
+ bd[bd_count].buffer_length = sg_len;
+ bd[bd_count].flags = 0;
+ if (bd_count == 0)
+ bd[bd_count].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+ byte_count += sg_len;
+ bd_count++;
+ }
+
+ if (bd_count)
+ bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+ BUG_ON(byte_count != scsi_bufflen(sc));
+ return bd_count;
+}
+
+/**
+ * bnx2i_iscsi_map_sg_list - maps SG list
+ * @cmd: iscsi cmd struct pointer
+ *
+ * creates BD list table for the command
+ */
+static void bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
+{
+ int bd_count;
+
+ bd_count = bnx2i_map_scsi_sg(cmd->conn->hba, cmd);
+ if (!bd_count) {
+ struct iscsi_bd *bd = cmd->io_tbl.bd_tbl;
+
+ bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
+ bd[0].buffer_length = bd[0].flags = 0;
+ }
+ cmd->io_tbl.bd_valid = bd_count;
+}
+
+
+/**
+ * bnx2i_iscsi_unmap_sg_list - unmaps SG list
+ * @cmd: iscsi cmd struct pointer
+ *
+ * unmap IO buffers and invalidate the BD table
+ */
+void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+
+ if (cmd->io_tbl.bd_valid && sc) {
+ scsi_dma_unmap(sc);
+ cmd->io_tbl.bd_valid = 0;
+ }
+}
+
+static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
+{
+ memset(&cmd->req, 0x00, sizeof(cmd->req));
+ cmd->req.op_code = 0xFF;
+ cmd->req.bd_list_addr_lo = (u32) cmd->io_tbl.bd_tbl_dma;
+ cmd->req.bd_list_addr_hi =
+ (u32) ((u64) cmd->io_tbl.bd_tbl_dma >> 32);
+
+}
+
+
+/**
+ * bnx2i_bind_conn_to_iscsi_cid - bind conn structure to 'iscsi_cid'
+ * @hba: pointer to adapter instance
+ * @conn: pointer to iscsi connection
+ * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1)
+ *
+ * update iscsi cid table entry with connection pointer. This enables
+ * driver to quickly get hold of connection structure pointer in
+ * completion/interrupt thread using iscsi context ID
+ */
+static int bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_hba *hba,
+ struct bnx2i_conn *bnx2i_conn,
+ u32 iscsi_cid)
+{
+ if (hba && hba->cid_que.conn_cid_tbl[iscsi_cid]) {
+ iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+ "conn bind - entry #%d not free\n", iscsi_cid);
+ return -EBUSY;
+ }
+
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = bnx2i_conn;
+ return 0;
+}
+
+
+/**
+ * bnx2i_get_conn_from_id - maps an iscsi cid to corresponding conn ptr
+ * @hba: pointer to adapter instance
+ * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1)
+ */
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+ u16 iscsi_cid)
+{
+ if (!hba->cid_que.conn_cid_tbl) {
+ printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
+ return NULL;
+
+ } else if (iscsi_cid >= hba->max_active_conns) {
+ printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
+ return NULL;
+ }
+ return hba->cid_que.conn_cid_tbl[iscsi_cid];
+}
+
+
+/**
+ * bnx2i_alloc_iscsi_cid - allocates a iscsi_cid from free pool
+ * @hba: pointer to adapter instance
+ */
+static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
+{
+ int idx;
+
+ if (!hba->cid_que.cid_free_cnt)
+ return -1;
+
+ idx = hba->cid_que.cid_q_cons_idx;
+ hba->cid_que.cid_q_cons_idx++;
+ if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx)
+ hba->cid_que.cid_q_cons_idx = 0;
+
+ hba->cid_que.cid_free_cnt--;
+ return hba->cid_que.cid_que[idx];
+}
+
+
+/**
+ * bnx2i_free_iscsi_cid - returns tcp port to free list
+ * @hba: pointer to adapter instance
+ * @iscsi_cid: iscsi context ID to free
+ */
+static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
+{
+ int idx;
+
+ if (iscsi_cid == (u16) -1)
+ return;
+
+ hba->cid_que.cid_free_cnt++;
+
+ idx = hba->cid_que.cid_q_prod_idx;
+ hba->cid_que.cid_que[idx] = iscsi_cid;
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
+ hba->cid_que.cid_q_prod_idx++;
+ if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx)
+ hba->cid_que.cid_q_prod_idx = 0;
+}
+
+
+/**
+ * bnx2i_setup_free_cid_que - sets up free iscsi cid queue
+ * @hba: pointer to adapter instance
+ *
+ * allocates memory for iscsi cid queue & 'cid - conn ptr' mapping table,
+ * and initialize table attributes
+ */
+static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
+{
+ int mem_size;
+ int i;
+
+ mem_size = hba->max_active_conns * sizeof(u32);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.cid_que_base)
+ return -ENOMEM;
+
+ mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ hba->cid_que.conn_cid_tbl = kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.conn_cid_tbl) {
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+ return -ENOMEM;
+ }
+
+ hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
+ hba->cid_que.cid_q_prod_idx = 0;
+ hba->cid_que.cid_q_cons_idx = 0;
+ hba->cid_que.cid_q_max_idx = hba->max_active_conns;
+ hba->cid_que.cid_free_cnt = hba->max_active_conns;
+
+ for (i = 0; i < hba->max_active_conns; i++) {
+ hba->cid_que.cid_que[i] = i;
+ hba->cid_que.conn_cid_tbl[i] = NULL;
+ }
+ return 0;
+}
+
+
+/**
+ * bnx2i_release_free_cid_que - releases 'iscsi_cid' queue resources
+ * @hba: pointer to adapter instance
+ */
+static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
+{
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+
+ kfree(hba->cid_que.conn_cid_tbl);
+ hba->cid_que.conn_cid_tbl = NULL;
+}
+
+
+/**
+ * bnx2i_alloc_ep - allocates ep structure from global pool
+ * @hba: pointer to adapter instance
+ *
+ * routine allocates a free endpoint structure from global pool and
+ * a tcp port to be used for this connection. Global resource lock,
+ * 'bnx2i_resc_lock' is held while accessing shared global data structures
+ */
+static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba)
+{
+ struct iscsi_endpoint *ep;
+ struct bnx2i_endpoint *bnx2i_ep;
+
+ ep = iscsi_create_endpoint(sizeof(*bnx2i_ep));
+ if (!ep) {
+ printk(KERN_ERR "bnx2i: Could not allocate ep\n");
+ return NULL;
+ }
+
+ bnx2i_ep = ep->dd_data;
+ INIT_LIST_HEAD(&bnx2i_ep->link);
+ bnx2i_ep->state = EP_STATE_IDLE;
+ bnx2i_ep->hba = hba;
+ bnx2i_ep->hba_age = hba->age;
+ hba->ofld_conns_active++;
+ init_waitqueue_head(&bnx2i_ep->ofld_wait);
+ return ep;
+}
+
+
+/**
+ * bnx2i_free_ep - free endpoint
+ * @ep: pointer to iscsi endpoint structure
+ */
+static void bnx2i_free_ep(struct iscsi_endpoint *ep)
+{
+ struct bnx2i_endpoint *bnx2i_ep = ep->dd_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bnx2i_resc_lock, flags);
+ bnx2i_ep->state = EP_STATE_IDLE;
+ bnx2i_ep->hba->ofld_conns_active--;
+
+ bnx2i_free_iscsi_cid(bnx2i_ep->hba, bnx2i_ep->ep_iscsi_cid);
+ if (bnx2i_ep->conn) {
+ bnx2i_ep->conn->ep = NULL;
+ bnx2i_ep->conn = NULL;
+ }
+
+ bnx2i_ep->hba = NULL;
+ spin_unlock_irqrestore(&bnx2i_resc_lock, flags);
+ iscsi_destroy_endpoint(ep);
+}
+
+
+/**
+ * bnx2i_alloc_bdt - allocates buffer descriptor (BD) table for the command
+ * @hba: adapter instance pointer
+ * @session: iscsi session pointer
+ * @cmd: iscsi command structure
+ */
+static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session,
+ struct bnx2i_cmd *cmd)
+{
+ struct io_bdt *io = &cmd->io_tbl;
+ struct iscsi_bd *bd;
+
+ io->bd_tbl = dma_alloc_coherent(&hba->pcidev->dev,
+ ISCSI_MAX_BDS_PER_CMD * sizeof(*bd),
+ &io->bd_tbl_dma, GFP_KERNEL);
+ if (!io->bd_tbl) {
+ iscsi_session_printk(KERN_ERR, session, "Could not "
+ "allocate bdt.\n");
+ return -ENOMEM;
+ }
+ io->bd_valid = 0;
+ return 0;
+}
+
+/**
+ * bnx2i_destroy_cmd_pool - destroys iscsi command pool and release BD table
+ * @hba: adapter instance pointer
+ * @session: iscsi session pointer
+ * @cmd: iscsi command structure
+ */
+static void bnx2i_destroy_cmd_pool(struct bnx2i_hba *hba,
+ struct iscsi_session *session)
+{
+ int i;
+
+ for (i = 0; i < session->cmds_max; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct bnx2i_cmd *cmd = task->dd_data;
+
+ if (cmd->io_tbl.bd_tbl)
+ dma_free_coherent(&hba->pcidev->dev,
+ ISCSI_MAX_BDS_PER_CMD *
+ sizeof(struct iscsi_bd),
+ cmd->io_tbl.bd_tbl,
+ cmd->io_tbl.bd_tbl_dma);
+ }
+
+}
+
+
+/**
+ * bnx2i_setup_cmd_pool - sets up iscsi command pool for the session
+ * @hba: adapter instance pointer
+ * @session: iscsi session pointer
+ */
+static int bnx2i_setup_cmd_pool(struct bnx2i_hba *hba,
+ struct iscsi_session *session)
+{
+ int i;
+
+ for (i = 0; i < session->cmds_max; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct bnx2i_cmd *cmd = task->dd_data;
+
+ /* Anil */
+ task->hdr = &cmd->hdr;
+ task->hdr_max = sizeof(struct iscsi_hdr);
+
+ if (bnx2i_alloc_bdt(hba, session, cmd))
+ goto free_bdts;
+ }
+
+ return 0;
+
+free_bdts:
+ bnx2i_destroy_cmd_pool(hba, session);
+ return -ENOMEM;
+}
+
+
+/**
+ * bnx2i_setup_mp_bdt - allocate BD table resources
+ * @hba: pointer to adapter structure
+ *
+ * Allocate memory for dummy buffer and associated BD
+ * table to be used by middle path (MP) requests
+ */
+static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
+{
+ int rc = 0;
+ struct iscsi_bd *mp_bdt;
+ u64 addr;
+
+ hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ &hba->mp_bd_dma, GFP_KERNEL);
+ if (!hba->mp_bd_tbl) {
+ printk(KERN_ERR "unable to allocate Middle Path BDT\n");
+ rc = -1;
+ goto out;
+ }
+
+ hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ &hba->dummy_buf_dma, GFP_KERNEL);
+ if (!hba->dummy_buffer) {
+ printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ rc = -1;
+ goto out;
+ }
+
+ mp_bdt = (struct iscsi_bd *) hba->mp_bd_tbl;
+ addr = (unsigned long) hba->dummy_buf_dma;
+ mp_bdt->buffer_addr_lo = addr & 0xffffffff;
+ mp_bdt->buffer_addr_hi = addr >> 32;
+ mp_bdt->buffer_length = PAGE_SIZE;
+ mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+out:
+ return rc;
+}
+
+
+/**
+ * bnx2i_free_mp_bdt - releases ITT back to free pool
+ * @hba: pointer to adapter instance
+ *
+ * free MP dummy buffer and associated BD table
+ */
+static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
+{
+ if (hba->mp_bd_tbl) {
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ }
+ if (hba->dummy_buffer) {
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ hba->dummy_buffer, hba->dummy_buf_dma);
+ hba->dummy_buffer = NULL;
+ }
+ return;
+}
+
+/**
+ * bnx2i_drop_session - notifies iscsid of connection error.
+ * @hba: adapter instance pointer
+ * @session: iscsi session pointer
+ *
+ * This notifies iscsid that there is a error, so it can initiate
+ * recovery.
+ *
+ * This relies on caller using the iscsi class iterator so the object
+ * is refcounted and does not disapper from under us.
+ */
+void bnx2i_drop_session(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+/**
+ * bnx2i_ep_destroy_list_add - add an entry to EP destroy list
+ * @hba: pointer to adapter instance
+ * @ep: pointer to endpoint (transport indentifier) structure
+ *
+ * EP destroy queue manager
+ */
+static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ write_lock_bh(&hba->ep_rdwr_lock);
+ list_add_tail(&ep->link, &hba->ep_destroy_list);
+ write_unlock_bh(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+/**
+ * bnx2i_ep_destroy_list_del - add an entry to EP destroy list
+ *
+ * @hba: pointer to adapter instance
+ * @ep: pointer to endpoint (transport indentifier) structure
+ *
+ * EP destroy queue manager
+ */
+static int bnx2i_ep_destroy_list_del(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ write_lock_bh(&hba->ep_rdwr_lock);
+ list_del_init(&ep->link);
+ write_unlock_bh(&hba->ep_rdwr_lock);
+
+ return 0;
+}
+
+/**
+ * bnx2i_ep_ofld_list_add - add an entry to ep offload pending list
+ * @hba: pointer to adapter instance
+ * @ep: pointer to endpoint (transport indentifier) structure
+ *
+ * pending conn offload completion queue manager
+ */
+static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ write_lock_bh(&hba->ep_rdwr_lock);
+ list_add_tail(&ep->link, &hba->ep_ofld_list);
+ write_unlock_bh(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+/**
+ * bnx2i_ep_ofld_list_del - add an entry to ep offload pending list
+ * @hba: pointer to adapter instance
+ * @ep: pointer to endpoint (transport indentifier) structure
+ *
+ * pending conn offload completion queue manager
+ */
+static int bnx2i_ep_ofld_list_del(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ write_lock_bh(&hba->ep_rdwr_lock);
+ list_del_init(&ep->link);
+ write_unlock_bh(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+
+/**
+ * bnx2i_find_ep_in_ofld_list - find iscsi_cid in pending list of endpoints
+ *
+ * @hba: pointer to adapter instance
+ * @iscsi_cid: iscsi context ID to find
+ *
+ */
+struct bnx2i_endpoint *
+bnx2i_find_ep_in_ofld_list(struct bnx2i_hba *hba, u32 iscsi_cid)
+{
+ struct list_head *list;
+ struct list_head *tmp;
+ struct bnx2i_endpoint *ep;
+
+ read_lock_bh(&hba->ep_rdwr_lock);
+ list_for_each_safe(list, tmp, &hba->ep_ofld_list) {
+ ep = (struct bnx2i_endpoint *)list;
+
+ if (ep->ep_iscsi_cid == iscsi_cid)
+ break;
+ ep = NULL;
+ }
+ read_unlock_bh(&hba->ep_rdwr_lock);
+
+ if (!ep)
+ printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid);
+ return ep;
+}
+
+
+/**
+ * bnx2i_find_ep_in_destroy_list - find iscsi_cid in destroy list
+ * @hba: pointer to adapter instance
+ * @iscsi_cid: iscsi context ID to find
+ *
+ */
+struct bnx2i_endpoint *
+bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid)
+{
+ struct list_head *list;
+ struct list_head *tmp;
+ struct bnx2i_endpoint *ep;
+
+ read_lock_bh(&hba->ep_rdwr_lock);
+ list_for_each_safe(list, tmp, &hba->ep_destroy_list) {
+ ep = (struct bnx2i_endpoint *)list;
+
+ if (ep->ep_iscsi_cid == iscsi_cid)
+ break;
+ ep = NULL;
+ }
+ read_unlock_bh(&hba->ep_rdwr_lock);
+
+ if (!ep)
+ printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid);
+
+ return ep;
+}
+
+/**
+ * bnx2i_setup_host_queue_size - assigns shost->can_queue param
+ * @hba: pointer to adapter instance
+ * @shost: scsi host pointer
+ *
+ * Initializes 'can_queue' parameter based on how many outstanding commands
+ * the device can handle. Each device 5708/5709/57710 has different
+ * capabilities
+ */
+static void bnx2i_setup_host_queue_size(struct bnx2i_hba *hba,
+ struct Scsi_Host *shost)
+{
+ if (test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type))
+ shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708;
+ else if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type))
+ shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5709;
+ else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+ shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_57710;
+ else
+ shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708;
+}
+
+
+/**
+ * bnx2i_alloc_hba - allocate and init adapter instance
+ * @cnic: cnic device pointer
+ *
+ * allocate & initialize adapter structure and call other
+ * support routines to do per adapter initialization
+ */
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
+{
+ struct Scsi_Host *shost;
+ struct bnx2i_hba *hba;
+
+ shost = iscsi_host_alloc(&bnx2i_host_template, sizeof(*hba), 0);
+ if (!shost)
+ return NULL;
+ shost->dma_boundary = cnic->pcidev->dma_mask;
+ shost->transportt = bnx2i_scsi_xport_template;
+ shost->max_id = ISCSI_MAX_CONNS_PER_HBA;
+ shost->max_channel = 0;
+ shost->max_lun = 512;
+ shost->max_cmd_len = 16;
+
+ hba = iscsi_host_priv(shost);
+ hba->shost = shost;
+ hba->netdev = cnic->netdev;
+ /* Get PCI related information and update hba struct members */
+ hba->pcidev = cnic->pcidev;
+ pci_dev_get(hba->pcidev);
+ hba->pci_did = hba->pcidev->device;
+ hba->pci_vid = hba->pcidev->vendor;
+ hba->pci_sdid = hba->pcidev->subsystem_device;
+ hba->pci_svid = hba->pcidev->subsystem_vendor;
+ hba->pci_func = PCI_FUNC(hba->pcidev->devfn);
+ hba->pci_devno = PCI_SLOT(hba->pcidev->devfn);
+ bnx2i_identify_device(hba);
+
+ bnx2i_identify_device(hba);
+ bnx2i_setup_host_queue_size(hba, shost);
+
+ if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) {
+ hba->regview = ioremap_nocache(hba->netdev->base_addr,
+ BNX2_MQ_CONFIG2);
+ if (!hba->regview)
+ goto ioreg_map_err;
+ } else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+ hba->regview = ioremap_nocache(hba->netdev->base_addr, 4096);
+ if (!hba->regview)
+ goto ioreg_map_err;
+ }
+
+ if (bnx2i_setup_mp_bdt(hba))
+ goto mp_bdt_mem_err;
+
+ INIT_LIST_HEAD(&hba->ep_ofld_list);
+ INIT_LIST_HEAD(&hba->ep_destroy_list);
+ rwlock_init(&hba->ep_rdwr_lock);
+
+ hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
+
+ /* different values for 5708/5709/57710 */
+ hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
+
+ if (bnx2i_setup_free_cid_que(hba))
+ goto cid_que_err;
+
+ /* SQ/RQ/CQ size can be changed via sysfx interface */
+ if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+ if (sq_size && sq_size <= BNX2I_5770X_SQ_WQES_MAX)
+ hba->max_sqes = sq_size;
+ else
+ hba->max_sqes = BNX2I_5770X_SQ_WQES_DEFAULT;
+ } else { /* 5706/5708/5709 */
+ if (sq_size && sq_size <= BNX2I_570X_SQ_WQES_MAX)
+ hba->max_sqes = sq_size;
+ else
+ hba->max_sqes = BNX2I_570X_SQ_WQES_DEFAULT;
+ }
+
+ hba->max_rqes = rq_size;
+ hba->max_cqes = hba->max_sqes + rq_size;
+ if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+ if (hba->max_cqes > BNX2I_5770X_CQ_WQES_MAX)
+ hba->max_cqes = BNX2I_5770X_CQ_WQES_MAX;
+ } else if (hba->max_cqes > BNX2I_570X_CQ_WQES_MAX)
+ hba->max_cqes = BNX2I_570X_CQ_WQES_MAX;
+
+ hba->num_ccell = hba->max_sqes / 2;
+
+ spin_lock_init(&hba->lock);
+ mutex_init(&hba->net_dev_lock);
+
+ if (iscsi_host_add(shost, &hba->pcidev->dev))
+ goto free_dump_mem;
+ return hba;
+
+free_dump_mem:
+ bnx2i_release_free_cid_que(hba);
+cid_que_err:
+ bnx2i_free_mp_bdt(hba);
+mp_bdt_mem_err:
+ if (hba->regview) {
+ iounmap(hba->regview);
+ hba->regview = NULL;
+ }
+ioreg_map_err:
+ pci_dev_put(hba->pcidev);
+ scsi_host_put(shost);
+ return NULL;
+}
+
+/**
+ * bnx2i_free_hba- releases hba structure and resources held by the adapter
+ * @hba: pointer to adapter instance
+ *
+ * free adapter structure and call various cleanup routines.
+ */
+void bnx2i_free_hba(struct bnx2i_hba *hba)
+{
+ struct Scsi_Host *shost = hba->shost;
+
+ iscsi_host_remove(shost);
+ INIT_LIST_HEAD(&hba->ep_ofld_list);
+ INIT_LIST_HEAD(&hba->ep_destroy_list);
+ pci_dev_put(hba->pcidev);
+
+ if (hba->regview) {
+ iounmap(hba->regview);
+ hba->regview = NULL;
+ }
+ bnx2i_free_mp_bdt(hba);
+ bnx2i_release_free_cid_que(hba);
+ iscsi_host_free(shost);
+}
+
+/**
+ * bnx2i_conn_free_login_resources - free DMA resources used for login process
+ * @hba: pointer to adapter instance
+ * @bnx2i_conn: iscsi connection pointer
+ *
+ * Login related resources, mostly BDT & payload DMA memory is freed
+ */
+static void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *bnx2i_conn)
+{
+ if (bnx2i_conn->gen_pdu.resp_bd_tbl) {
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ bnx2i_conn->gen_pdu.resp_bd_tbl,
+ bnx2i_conn->gen_pdu.resp_bd_dma);
+ bnx2i_conn->gen_pdu.resp_bd_tbl = NULL;
+ }
+
+ if (bnx2i_conn->gen_pdu.req_bd_tbl) {
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ bnx2i_conn->gen_pdu.req_bd_tbl,
+ bnx2i_conn->gen_pdu.req_bd_dma);
+ bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
+ }
+
+ if (bnx2i_conn->gen_pdu.resp_buf) {
+ dma_free_coherent(&hba->pcidev->dev,
+ ISCSI_DEF_MAX_RECV_SEG_LEN,
+ bnx2i_conn->gen_pdu.resp_buf,
+ bnx2i_conn->gen_pdu.resp_dma_addr);
+ bnx2i_conn->gen_pdu.resp_buf = NULL;
+ }
+
+ if (bnx2i_conn->gen_pdu.req_buf) {
+ dma_free_coherent(&hba->pcidev->dev,
+ ISCSI_DEF_MAX_RECV_SEG_LEN,
+ bnx2i_conn->gen_pdu.req_buf,
+ bnx2i_conn->gen_pdu.req_dma_addr);
+ bnx2i_conn->gen_pdu.req_buf = NULL;
+ }
+}
+
+/**
+ * bnx2i_conn_alloc_login_resources - alloc DMA resources for login/nop.
+ * @hba: pointer to adapter instance
+ * @bnx2i_conn: iscsi connection pointer
+ *
+ * Mgmt task DNA resources are allocated in this routine.
+ */
+static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *bnx2i_conn)
+{
+ /* Allocate memory for login request/response buffers */
+ bnx2i_conn->gen_pdu.req_buf =
+ dma_alloc_coherent(&hba->pcidev->dev,
+ ISCSI_DEF_MAX_RECV_SEG_LEN,
+ &bnx2i_conn->gen_pdu.req_dma_addr,
+ GFP_KERNEL);
+ if (bnx2i_conn->gen_pdu.req_buf == NULL)
+ goto login_req_buf_failure;
+
+ bnx2i_conn->gen_pdu.req_buf_size = 0;
+ bnx2i_conn->gen_pdu.req_wr_ptr = bnx2i_conn->gen_pdu.req_buf;
+
+ bnx2i_conn->gen_pdu.resp_buf =
+ dma_alloc_coherent(&hba->pcidev->dev,
+ ISCSI_DEF_MAX_RECV_SEG_LEN,
+ &bnx2i_conn->gen_pdu.resp_dma_addr,
+ GFP_KERNEL);
+ if (bnx2i_conn->gen_pdu.resp_buf == NULL)
+ goto login_resp_buf_failure;
+
+ bnx2i_conn->gen_pdu.resp_buf_size = ISCSI_DEF_MAX_RECV_SEG_LEN;
+ bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf;
+
+ bnx2i_conn->gen_pdu.req_bd_tbl =
+ dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ &bnx2i_conn->gen_pdu.req_bd_dma, GFP_KERNEL);
+ if (bnx2i_conn->gen_pdu.req_bd_tbl == NULL)
+ goto login_req_bd_tbl_failure;
+
+ bnx2i_conn->gen_pdu.resp_bd_tbl =
+ dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ &bnx2i_conn->gen_pdu.resp_bd_dma,
+ GFP_KERNEL);
+ if (bnx2i_conn->gen_pdu.resp_bd_tbl == NULL)
+ goto login_resp_bd_tbl_failure;
+
+ return 0;
+
+login_resp_bd_tbl_failure:
+ dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ bnx2i_conn->gen_pdu.req_bd_tbl,
+ bnx2i_conn->gen_pdu.req_bd_dma);
+ bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
+
+login_req_bd_tbl_failure:
+ dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN,
+ bnx2i_conn->gen_pdu.resp_buf,
+ bnx2i_conn->gen_pdu.resp_dma_addr);
+ bnx2i_conn->gen_pdu.resp_buf = NULL;
+login_resp_buf_failure:
+ dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN,
+ bnx2i_conn->gen_pdu.req_buf,
+ bnx2i_conn->gen_pdu.req_dma_addr);
+ bnx2i_conn->gen_pdu.req_buf = NULL;
+login_req_buf_failure:
+ iscsi_conn_printk(KERN_ERR, bnx2i_conn->cls_conn->dd_data,
+ "login resource alloc failed!!\n");
+ return -ENOMEM;
+
+}
+
+
+/**
+ * bnx2i_iscsi_prep_generic_pdu_bd - prepares BD table.
+ * @bnx2i_conn: iscsi connection pointer
+ *
+ * Allocates buffers and BD tables before shipping requests to cnic
+ * for PDUs prepared by 'iscsid' daemon
+ */
+static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *bnx2i_conn)
+{
+ struct iscsi_bd *bd_tbl;
+
+ bd_tbl = (struct iscsi_bd *) bnx2i_conn->gen_pdu.req_bd_tbl;
+
+ bd_tbl->buffer_addr_hi =
+ (u32) ((u64) bnx2i_conn->gen_pdu.req_dma_addr >> 32);
+ bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.req_dma_addr;
+ bd_tbl->buffer_length = bnx2i_conn->gen_pdu.req_wr_ptr -
+ bnx2i_conn->gen_pdu.req_buf;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+ bd_tbl = (struct iscsi_bd *) bnx2i_conn->gen_pdu.resp_bd_tbl;
+ bd_tbl->buffer_addr_hi = (u64) bnx2i_conn->gen_pdu.resp_dma_addr >> 32;
+ bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_dma_addr;
+ bd_tbl->buffer_length = ISCSI_DEF_MAX_RECV_SEG_LEN;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+}
+
+
+/**
+ * bnx2i_iscsi_send_generic_request - called to send mgmt tasks.
+ * @task: transport layer task pointer
+ *
+ * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
+ * Nop-out and Logout requests flow through this path.
+ */
+static int bnx2i_iscsi_send_generic_request(struct iscsi_task *task)
+{
+ struct bnx2i_cmd *cmd = task->dd_data;
+ struct bnx2i_conn *bnx2i_conn = cmd->conn;
+ int rc = 0;
+ char *buf;
+ int data_len;
+
+ bnx2i_iscsi_prep_generic_pdu_bd(bnx2i_conn);
+ switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ bnx2i_send_iscsi_login(bnx2i_conn, task);
+ break;
+ case ISCSI_OP_NOOP_OUT:
+ data_len = bnx2i_conn->gen_pdu.req_buf_size;
+ buf = bnx2i_conn->gen_pdu.req_buf;
+ if (data_len)
+ rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task,
+ RESERVED_ITT,
+ buf, data_len, 1);
+ else
+ rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task,
+ RESERVED_ITT,
+ NULL, 0, 1);
+ break;
+ case ISCSI_OP_LOGOUT:
+ rc = bnx2i_send_iscsi_logout(bnx2i_conn, task);
+ break;
+ case ISCSI_OP_SCSI_TMFUNC:
+ rc = bnx2i_send_iscsi_tmf(bnx2i_conn, task);
+ break;
+ default:
+ iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+ "send_gen: unsupported op 0x%x\n",
+ task->hdr->opcode);
+ }
+ return rc;
+}
+
+
+/**********************************************************************
+ * SCSI-ML Interface
+ **********************************************************************/
+
+/**
+ * bnx2i_cpy_scsi_cdb - copies LUN & CDB fields in required format to sq wqe
+ * @sc: SCSI-ML command pointer
+ * @cmd: iscsi cmd pointer
+ */
+static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd)
+{
+ u32 dword;
+ int lpcnt;
+ u8 *srcp;
+ u32 *dstp;
+ u32 scsi_lun[2];
+
+ int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
+ cmd->req.lun[0] = be32_to_cpu(scsi_lun[0]);
+ cmd->req.lun[1] = be32_to_cpu(scsi_lun[1]);
+
+ lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
+ srcp = (u8 *) sc->cmnd;
+ dstp = (u32 *) cmd->req.cdb;
+ while (lpcnt--) {
+ memcpy(&dword, (const void *) srcp, 4);
+ *dstp = cpu_to_be32(dword);
+ srcp += 4;
+ dstp++;
+ }
+ if (sc->cmd_len & 0x3) {
+ dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
+ *dstp = cpu_to_be32(dword);
+ }
+}
+
+static void bnx2i_cleanup_task(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct bnx2i_hba *hba = bnx2i_conn->hba;
+
+ /*
+ * mgmt task or cmd was never sent to us to transmit.
+ */
+ if (!task->sc || task->state == ISCSI_TASK_PENDING)
+ return;
+ /*
+ * need to clean-up task context to claim dma buffers
+ */
+ if (task->state == ISCSI_TASK_ABRT_TMF) {
+ bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
+
+ spin_unlock_bh(&conn->session->lock);
+ wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
+ msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
+ spin_lock_bh(&conn->session->lock);
+ }
+ bnx2i_iscsi_unmap_sg_list(task->dd_data);
+}
+
+/**
+ * bnx2i_mtask_xmit - transmit mtask to chip for further processing
+ * @conn: transport layer conn structure pointer
+ * @task: transport layer command structure pointer
+ */
+static int
+bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct bnx2i_cmd *cmd = task->dd_data;
+
+ memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN);
+
+ bnx2i_setup_cmd_wqe_template(cmd);
+ bnx2i_conn->gen_pdu.req_buf_size = task->data_count;
+ if (task->data_count) {
+ memcpy(bnx2i_conn->gen_pdu.req_buf, task->data,
+ task->data_count);
+ bnx2i_conn->gen_pdu.req_wr_ptr =
+ bnx2i_conn->gen_pdu.req_buf + task->data_count;
+ }
+ cmd->conn = conn->dd_data;
+ cmd->scsi_cmd = NULL;
+ return bnx2i_iscsi_send_generic_request(task);
+}
+
+/**
+ * bnx2i_task_xmit - transmit iscsi command to chip for further processing
+ * @task: transport layer command structure pointer
+ *
+ * maps SG buffers and send request to chip/firmware in the form of SQ WQE
+ */
+static int bnx2i_task_xmit(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = conn->session;
+ struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct scsi_cmnd *sc = task->sc;
+ struct bnx2i_cmd *cmd = task->dd_data;
+ struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
+
+ if (test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state))
+ return -ENOTCONN;
+
+ if (!bnx2i_conn->is_bound)
+ return -ENOTCONN;
+
+ /*
+ * If there is no scsi_cmnd this must be a mgmt task
+ */
+ if (!sc)
+ return bnx2i_mtask_xmit(conn, task);
+
+ bnx2i_setup_cmd_wqe_template(cmd);
+ cmd->req.op_code = ISCSI_OP_SCSI_CMD;
+ cmd->conn = bnx2i_conn;
+ cmd->scsi_cmd = sc;
+ cmd->req.total_data_transfer_length = scsi_bufflen(sc);
+ cmd->req.cmd_sn = be32_to_cpu(hdr->cmdsn);
+
+ bnx2i_iscsi_map_sg_list(cmd);
+ bnx2i_cpy_scsi_cdb(sc, cmd);
+
+ cmd->req.op_attr = ISCSI_ATTR_SIMPLE;
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ cmd->req.op_attr |= ISCSI_CMD_REQUEST_WRITE;
+ cmd->req.itt = task->itt |
+ (ISCSI_TASK_TYPE_WRITE << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ bnx2i_setup_write_cmd_bd_info(task);
+ } else {
+ if (scsi_bufflen(sc))
+ cmd->req.op_attr |= ISCSI_CMD_REQUEST_READ;
+ cmd->req.itt = task->itt |
+ (ISCSI_TASK_TYPE_READ << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ }
+
+ cmd->req.num_bds = cmd->io_tbl.bd_valid;
+ if (!cmd->io_tbl.bd_valid) {
+ cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
+ cmd->req.bd_list_addr_hi = (u32) ((u64) hba->mp_bd_dma >> 32);
+ cmd->req.num_bds = 1;
+ }
+
+ bnx2i_send_iscsi_scsicmd(bnx2i_conn, cmd);
+ return 0;
+}
+
+/**
+ * bnx2i_session_create - create a new iscsi session
+ * @cmds_max: max commands supported
+ * @qdepth: scsi queue depth to support
+ * @initial_cmdsn: initial iscsi CMDSN to be used for this session
+ *
+ * Creates a new iSCSI session instance on given device.
+ */
+static struct iscsi_cls_session *
+bnx2i_session_create(struct iscsi_endpoint *ep,
+ uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_cls_session *cls_session;
+ struct bnx2i_hba *hba;
+ struct bnx2i_endpoint *bnx2i_ep;
+
+ if (!ep) {
+ printk(KERN_ERR "bnx2i: missing ep.\n");
+ return NULL;
+ }
+
+ bnx2i_ep = ep->dd_data;
+ shost = bnx2i_ep->hba->shost;
+ hba = iscsi_host_priv(shost);
+ if (bnx2i_adapter_ready(hba))
+ return NULL;
+
+ /*
+ * user can override hw limit as long as it is within
+ * the min/max.
+ */
+ if (cmds_max > hba->max_sqes)
+ cmds_max = hba->max_sqes;
+ else if (cmds_max < BNX2I_SQ_WQES_MIN)
+ cmds_max = BNX2I_SQ_WQES_MIN;
+
+ cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost,
+ cmds_max, sizeof(struct bnx2i_cmd),
+ initial_cmdsn, ISCSI_MAX_TARGET);
+ if (!cls_session)
+ return NULL;
+
+ if (bnx2i_setup_cmd_pool(hba, cls_session->dd_data))
+ goto session_teardown;
+ return cls_session;
+
+session_teardown:
+ iscsi_session_teardown(cls_session);
+ return NULL;
+}
+
+
+/**
+ * bnx2i_session_destroy - destroys iscsi session
+ * @cls_session: pointer to iscsi cls session
+ *
+ * Destroys previously created iSCSI session instance and releases
+ * all resources held by it
+ */
+static void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = cls_session->dd_data;
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+
+ bnx2i_destroy_cmd_pool(hba, session);
+ iscsi_session_teardown(cls_session);
+}
+
+
+/**
+ * bnx2i_conn_create - create iscsi connection instance
+ * @cls_session: pointer to iscsi cls session
+ * @cid: iscsi cid as per rfc (not NX2's CID terminology)
+ *
+ * Creates a new iSCSI connection instance for a given session
+ */
+static struct iscsi_cls_conn *
+bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+ struct bnx2i_conn *bnx2i_conn;
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_conn *conn;
+
+ cls_conn = iscsi_conn_setup(cls_session, sizeof(*bnx2i_conn),
+ cid);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+
+ bnx2i_conn = conn->dd_data;
+ bnx2i_conn->cls_conn = cls_conn;
+ bnx2i_conn->hba = hba;
+ /* 'ep' ptr will be assigned in bind() call */
+ bnx2i_conn->ep = NULL;
+ init_completion(&bnx2i_conn->cmd_cleanup_cmpl);
+
+ if (bnx2i_conn_alloc_login_resources(hba, bnx2i_conn)) {
+ iscsi_conn_printk(KERN_ALERT, conn,
+ "conn_new: login resc alloc failed!!\n");
+ goto free_conn;
+ }
+
+ return cls_conn;
+
+free_conn:
+ iscsi_conn_teardown(cls_conn);
+ return NULL;
+}
+
+/**
+ * bnx2i_conn_bind - binds iscsi sess, conn and ep objects together
+ * @cls_session: pointer to iscsi cls session
+ * @cls_conn: pointer to iscsi cls conn
+ * @transport_fd: 64-bit EP handle
+ * @is_leading: leading connection on this session?
+ *
+ * Binds together iSCSI session instance, iSCSI connection instance
+ * and the TCP connection. This routine returns error code if
+ * TCP connection does not belong on the device iSCSI sess/conn
+ * is bound
+ */
+static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn,
+ uint64_t transport_fd, int is_leading)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+ struct bnx2i_endpoint *bnx2i_ep;
+ struct iscsi_endpoint *ep;
+ int ret_code;
+
+ ep = iscsi_lookup_endpoint(transport_fd);
+ if (!ep)
+ return -EINVAL;
+
+ bnx2i_ep = ep->dd_data;
+ if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) ||
+ (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD))
+ /* Peer disconnect via' FIN or RST */
+ return -EINVAL;
+
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
+ return -EINVAL;
+
+ if (bnx2i_ep->hba != hba) {
+ /* Error - TCP connection does not belong to this device
+ */
+ iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
+ "conn bind, ep=0x%p (%s) does not",
+ bnx2i_ep, bnx2i_ep->hba->netdev->name);
+ iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
+ "belong to hba (%s)\n",
+ hba->netdev->name);
+ return -EEXIST;
+ }
+
+ bnx2i_ep->conn = bnx2i_conn;
+ bnx2i_conn->ep = bnx2i_ep;
+ bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid;
+ bnx2i_conn->fw_cid = bnx2i_ep->ep_cid;
+ bnx2i_conn->is_bound = 1;
+
+ ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn,
+ bnx2i_ep->ep_iscsi_cid);
+
+ /* 5706/5708/5709 FW takes RQ as full when initiated, but for 57710
+ * driver needs to explicitly replenish RQ index during setup.
+ */
+ if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type))
+ bnx2i_put_rq_buf(bnx2i_conn, 0);
+
+ bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+ return ret_code;
+}
+
+
+/**
+ * bnx2i_conn_destroy - destroy iscsi connection instance & release resources
+ * @cls_conn: pointer to iscsi cls conn
+ *
+ * Destroy an iSCSI connection instance and release memory resources held by
+ * this connection
+ */
+static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct Scsi_Host *shost;
+ struct bnx2i_hba *hba;
+
+ shost = iscsi_session_to_shost(iscsi_conn_to_session(cls_conn));
+ hba = iscsi_host_priv(shost);
+
+ bnx2i_conn_free_login_resources(hba, bnx2i_conn);
+ iscsi_conn_teardown(cls_conn);
+}
+
+
+/**
+ * bnx2i_conn_get_param - return iscsi connection parameter to caller
+ * @cls_conn: pointer to iscsi cls conn
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ *
+ * returns iSCSI connection parameters
+ */
+static int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ int len = 0;
+
+ switch (param) {
+ case ISCSI_PARAM_CONN_PORT:
+ if (bnx2i_conn->ep)
+ len = sprintf(buf, "%hu\n",
+ bnx2i_conn->ep->cm_sk->dst_port);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ if (bnx2i_conn->ep)
+ len = sprintf(buf, NIPQUAD_FMT "\n",
+ NIPQUAD(bnx2i_conn->ep->cm_sk->dst_ip));
+ break;
+ default:
+ return iscsi_conn_get_param(cls_conn, param, buf);
+ }
+
+ return len;
+}
+
+/**
+ * bnx2i_host_get_param - returns host (adapter) related parameters
+ * @shost: scsi host pointer
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ */
+static int bnx2i_host_get_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param, char *buf)
+{
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+ int len = 0;
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ len = sysfs_format_mac(buf, hba->cnic->mac_addr, 6);
+ break;
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ len = sprintf(buf, "%s\n", hba->netdev->name);
+ break;
+ default:
+ return iscsi_host_get_param(shost, param, buf);
+ }
+ return len;
+}
+
+/**
+ * bnx2i_conn_start - completes iscsi connection migration to FFP
+ * @cls_conn: pointer to iscsi cls conn
+ *
+ * last call in FFP migration to handover iscsi conn to the driver
+ */
+static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+
+ bnx2i_conn->ep->state = EP_STATE_ULP_UPDATE_START;
+ bnx2i_update_iscsi_conn(conn);
+
+ /*
+ * this should normally not sleep for a long time so it should
+ * not disrupt the caller.
+ */
+ bnx2i_conn->ep->ofld_timer.expires = 1 * HZ + jiffies;
+ bnx2i_conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ bnx2i_conn->ep->ofld_timer.data = (unsigned long) bnx2i_conn->ep;
+ add_timer(&bnx2i_conn->ep->ofld_timer);
+ /* update iSCSI context for this conn, wait for CNIC to complete */
+ wait_event_interruptible(bnx2i_conn->ep->ofld_wait,
+ bnx2i_conn->ep->state != EP_STATE_ULP_UPDATE_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&bnx2i_conn->ep->ofld_timer);
+
+ iscsi_conn_start(cls_conn);
+ return 0;
+}
+
+
+/**
+ * bnx2i_conn_get_stats - returns iSCSI stats
+ * @cls_conn: pointer to iscsi cls conn
+ * @stats: pointer to iscsi statistic struct
+ */
+static void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+ stats->custom_length = 3;
+ strcpy(stats->custom[2].desc, "eh_abort_cnt");
+ stats->custom[2].value = conn->eh_abort_cnt;
+ stats->digest_err = 0;
+ stats->timeout_err = 0;
+ stats->custom_length = 0;
+}
+
+
+/**
+ * bnx2i_check_route - checks if target IP route belongs to one of NX2 devices
+ * @dst_addr: target IP address
+ *
+ * check if route resolves to BNX2 device
+ */
+static struct bnx2i_hba *bnx2i_check_route(struct sockaddr *dst_addr)
+{
+ struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+ struct bnx2i_hba *hba;
+ struct cnic_dev *cnic = NULL;
+
+ bnx2i_reg_dev_all();
+
+ hba = get_adapter_list_head();
+ if (hba && hba->cnic)
+ cnic = hba->cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
+ if (!cnic) {
+ printk(KERN_ALERT "bnx2i: no route,"
+ "can't connect using cnic\n");
+ goto no_nx2_route;
+ }
+ hba = bnx2i_find_hba_for_cnic(cnic);
+ if (!hba)
+ goto no_nx2_route;
+
+ if (bnx2i_adapter_ready(hba)) {
+ printk(KERN_ALERT "bnx2i: check route, hba not found\n");
+ goto no_nx2_route;
+ }
+ if (hba->netdev->mtu > hba->mtu_supported) {
+ printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
+ hba->netdev->name, hba->netdev->mtu);
+ printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
+ hba->mtu_supported);
+ goto no_nx2_route;
+ }
+ return hba;
+no_nx2_route:
+ return NULL;
+}
+
+
+/**
+ * bnx2i_tear_down_conn - tear down iscsi/tcp connection and free resources
+ * @hba: pointer to adapter instance
+ * @ep: endpoint (transport indentifier) structure
+ *
+ * destroys cm_sock structure and on chip iscsi context
+ */
+static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic))
+ hba->cnic->cm_destroy(ep->cm_sk);
+
+ if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state))
+ ep->state = EP_STATE_DISCONN_COMPL;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type) &&
+ ep->state == EP_STATE_DISCONN_TIMEDOUT) {
+ printk(KERN_ALERT "bnx2i - ERROR - please submit GRC Dump,"
+ " NW/PCIe trace, driver msgs to developers"
+ " for analysis\n");
+ return 1;
+ }
+
+ ep->state = EP_STATE_CLEANUP_START;
+ init_timer(&ep->ofld_timer);
+ ep->ofld_timer.expires = 10*HZ + jiffies;
+ ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ ep->ofld_timer.data = (unsigned long) ep;
+ add_timer(&ep->ofld_timer);
+
+ bnx2i_ep_destroy_list_add(hba, ep);
+
+ /* destroy iSCSI context, wait for it to complete */
+ bnx2i_send_conn_destroy(hba, ep);
+ wait_event_interruptible(ep->ofld_wait,
+ (ep->state != EP_STATE_CLEANUP_START));
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&ep->ofld_timer);
+
+ bnx2i_ep_destroy_list_del(hba, ep);
+
+ if (ep->state != EP_STATE_CLEANUP_CMPL)
+ /* should never happen */
+ printk(KERN_ALERT "bnx2i - conn destroy failed\n");
+
+ return 0;
+}
+
+
+/**
+ * bnx2i_ep_connect - establish TCP connection to target portal
+ * @shost: scsi host
+ * @dst_addr: target IP address
+ * @non_blocking: blocking or non-blocking call
+ *
+ * this routine initiates the TCP/IP connection by invoking Option-2 i/f
+ * with l5_core and the CNIC. This is a multi-step process of resolving
+ * route to target, create a iscsi connection context, handshaking with
+ * CNIC module to create/initialize the socket struct and finally
+ * sending down option-2 request to complete TCP 3-way handshake
+ */
+static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
+ struct sockaddr *dst_addr,
+ int non_blocking)
+{
+ u32 iscsi_cid = BNX2I_CID_RESERVED;
+ struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+ struct sockaddr_in6 *desti6;
+ struct bnx2i_endpoint *bnx2i_ep;
+ struct bnx2i_hba *hba;
+ struct cnic_dev *cnic;
+ struct cnic_sockaddr saddr;
+ struct iscsi_endpoint *ep;
+ int rc = 0;
+
+ if (shost)
+ /* driver is given scsi host to work with */
+ hba = iscsi_host_priv(shost);
+ else
+ /*
+ * check if the given destination can be reached through
+ * a iscsi capable NetXtreme2 device
+ */
+ hba = bnx2i_check_route(dst_addr);
+ if (!hba) {
+ rc = -ENOMEM;
+ goto check_busy;
+ }
+
+ cnic = hba->cnic;
+ ep = bnx2i_alloc_ep(hba);
+ if (!ep) {
+ rc = -ENOMEM;
+ goto check_busy;
+ }
+ bnx2i_ep = ep->dd_data;
+
+ mutex_lock(&hba->net_dev_lock);
+ if (bnx2i_adapter_ready(hba)) {
+ rc = -EPERM;
+ goto net_if_down;
+ }
+
+ bnx2i_ep->state = EP_STATE_IDLE;
+ bnx2i_ep->ep_iscsi_cid = (u16) -1;
+ bnx2i_ep->num_active_cmds = 0;
+ iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
+ if (iscsi_cid == -1) {
+ printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
+ rc = -ENOMEM;
+ goto iscsi_cid_err;
+ }
+ bnx2i_ep->hba_age = hba->age;
+
+ rc = bnx2i_alloc_qp_resc(hba, bnx2i_ep);
+ if (rc != 0) {
+ printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
+ rc = -ENOMEM;
+ goto qp_resc_err;
+ }
+
+ bnx2i_ep->ep_iscsi_cid = (u16)iscsi_cid;
+ bnx2i_ep->state = EP_STATE_OFLD_START;
+ bnx2i_ep_ofld_list_add(hba, bnx2i_ep);
+
+ init_timer(&bnx2i_ep->ofld_timer);
+ bnx2i_ep->ofld_timer.expires = 2 * HZ + jiffies;
+ bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
+ add_timer(&bnx2i_ep->ofld_timer);
+
+ bnx2i_send_conn_ofld_req(hba, bnx2i_ep);
+
+ /* Wait for CNIC hardware to setup conn context and return 'cid' */
+ wait_event_interruptible(bnx2i_ep->ofld_wait,
+ bnx2i_ep->state != EP_STATE_OFLD_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&bnx2i_ep->ofld_timer);
+
+ bnx2i_ep_ofld_list_del(hba, bnx2i_ep);
+
+ if (bnx2i_ep->state != EP_STATE_OFLD_COMPL) {
+ rc = -ENOSPC;
+ goto conn_failed;
+ }
+
+ rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, bnx2i_ep->ep_cid,
+ iscsi_cid, &bnx2i_ep->cm_sk, bnx2i_ep);
+ if (rc) {
+ rc = -EINVAL;
+ goto conn_failed;
+ }
+
+ bnx2i_ep->cm_sk->rcv_buf = 256 * 1024;
+ bnx2i_ep->cm_sk->snd_buf = 256 * 1024;
+ clear_bit(SK_TCP_TIMESTAMP, &bnx2i_ep->cm_sk->tcp_flags);
+
+ memset(&saddr, 0, sizeof(saddr));
+ if (dst_addr->sa_family == AF_INET) {
+ desti = (struct sockaddr_in *) dst_addr;
+ saddr.remote.v4 = *desti;
+ saddr.local.v4.sin_family = desti->sin_family;
+ } else if (dst_addr->sa_family == AF_INET6) {
+ desti6 = (struct sockaddr_in6 *) dst_addr;
+ saddr.remote.v6 = *desti6;
+ saddr.local.v6.sin6_family = desti6->sin6_family;
+ }
+
+ bnx2i_ep->timestamp = jiffies;
+ bnx2i_ep->state = EP_STATE_CONNECT_START;
+ if (!test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ rc = -EINVAL;
+ goto conn_failed;
+ } else
+ rc = cnic->cm_connect(bnx2i_ep->cm_sk, &saddr);
+
+ if (rc)
+ goto release_ep;
+
+ if (bnx2i_map_ep_dbell_regs(bnx2i_ep))
+ goto release_ep;
+ mutex_unlock(&hba->net_dev_lock);
+ return ep;
+
+release_ep:
+ if (bnx2i_tear_down_conn(hba, bnx2i_ep)) {
+ mutex_unlock(&hba->net_dev_lock);
+ return ERR_PTR(rc);
+ }
+conn_failed:
+net_if_down:
+iscsi_cid_err:
+ bnx2i_free_qp_resc(hba, bnx2i_ep);
+qp_resc_err:
+ bnx2i_free_ep(ep);
+ mutex_unlock(&hba->net_dev_lock);
+check_busy:
+ bnx2i_unreg_dev_all();
+ return ERR_PTR(rc);
+}
+
+
+/**
+ * bnx2i_ep_poll - polls for TCP connection establishement
+ * @ep: TCP connection (endpoint) handle
+ * @timeout_ms: timeout value in milli secs
+ *
+ * polls for TCP connect request to complete
+ */
+static int bnx2i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
+{
+ struct bnx2i_endpoint *bnx2i_ep;
+ int rc = 0;
+
+ bnx2i_ep = ep->dd_data;
+ if ((bnx2i_ep->state == EP_STATE_IDLE) ||
+ (bnx2i_ep->state == EP_STATE_CONNECT_FAILED) ||
+ (bnx2i_ep->state == EP_STATE_OFLD_FAILED))
+ return -1;
+ if (bnx2i_ep->state == EP_STATE_CONNECT_COMPL)
+ return 1;
+
+ rc = wait_event_interruptible_timeout(bnx2i_ep->ofld_wait,
+ ((bnx2i_ep->state ==
+ EP_STATE_OFLD_FAILED) ||
+ (bnx2i_ep->state ==
+ EP_STATE_CONNECT_FAILED) ||
+ (bnx2i_ep->state ==
+ EP_STATE_CONNECT_COMPL)),
+ msecs_to_jiffies(timeout_ms));
+ if (!rc || (bnx2i_ep->state == EP_STATE_OFLD_FAILED))
+ rc = -1;
+
+ if (rc > 0)
+ return 1;
+ else if (!rc)
+ return 0; /* timeout */
+ else
+ return rc;
+}
+
+
+/**
+ * bnx2i_ep_tcp_conn_active - check EP state transition
+ * @ep: endpoint pointer
+ *
+ * check if underlying TCP connection is active
+ */
+static int bnx2i_ep_tcp_conn_active(struct bnx2i_endpoint *bnx2i_ep)
+{
+ int ret;
+ int cnic_dev_10g = 0;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type))
+ cnic_dev_10g = 1;
+
+ switch (bnx2i_ep->state) {
+ case EP_STATE_CONNECT_START:
+ case EP_STATE_CLEANUP_FAILED:
+ case EP_STATE_OFLD_FAILED:
+ case EP_STATE_DISCONN_TIMEDOUT:
+ ret = 0;
+ break;
+ case EP_STATE_CONNECT_COMPL:
+ case EP_STATE_ULP_UPDATE_START:
+ case EP_STATE_ULP_UPDATE_COMPL:
+ case EP_STATE_TCP_FIN_RCVD:
+ case EP_STATE_ULP_UPDATE_FAILED:
+ ret = 1;
+ break;
+ case EP_STATE_TCP_RST_RCVD:
+ ret = 0;
+ break;
+ case EP_STATE_CONNECT_FAILED:
+ if (cnic_dev_10g)
+ ret = 1;
+ else
+ ret = 0;
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+
+/**
+ * bnx2i_ep_disconnect - executes TCP connection teardown process
+ * @ep: TCP connection (endpoint) handle
+ *
+ * executes TCP connection teardown process
+ */
+static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
+{
+ struct bnx2i_endpoint *bnx2i_ep;
+ struct bnx2i_conn *bnx2i_conn = NULL;
+ struct iscsi_session *session = NULL;
+ struct iscsi_conn *conn;
+ struct cnic_dev *cnic;
+ struct bnx2i_hba *hba;
+
+ bnx2i_ep = ep->dd_data;
+
+ /* driver should not attempt connection cleanup untill TCP_CONNECT
+ * completes either successfully or fails. Timeout is 9-secs, so
+ * wait for it to complete
+ */
+ while ((bnx2i_ep->state == EP_STATE_CONNECT_START) &&
+ !time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ)))
+ msleep(250);
+
+ if (bnx2i_ep->conn) {
+ bnx2i_conn = bnx2i_ep->conn;
+ conn = bnx2i_conn->cls_conn->dd_data;
+ session = conn->session;
+
+ spin_lock_bh(&session->lock);
+ bnx2i_conn->is_bound = 0;
+ spin_unlock_bh(&session->lock);
+ }
+
+ hba = bnx2i_ep->hba;
+ if (bnx2i_ep->state == EP_STATE_IDLE)
+ goto return_bnx2i_ep;
+ cnic = hba->cnic;
+
+ mutex_lock(&hba->net_dev_lock);
+
+ if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
+ goto free_resc;
+ if (bnx2i_ep->hba_age != hba->age)
+ goto free_resc;
+
+ if (!bnx2i_ep_tcp_conn_active(bnx2i_ep))
+ goto destory_conn;
+
+ bnx2i_ep->state = EP_STATE_DISCONN_START;
+
+ init_timer(&bnx2i_ep->ofld_timer);
+ bnx2i_ep->ofld_timer.expires = 10*HZ + jiffies;
+ bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
+ add_timer(&bnx2i_ep->ofld_timer);
+
+ if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+ int close = 0;
+
+ if (session) {
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGING_OUT)
+ close = 1;
+ spin_unlock_bh(&session->lock);
+ }
+ if (close)
+ cnic->cm_close(bnx2i_ep->cm_sk);
+ else
+ cnic->cm_abort(bnx2i_ep->cm_sk);
+ } else
+ goto free_resc;
+
+ /* wait for option-2 conn teardown */
+ wait_event_interruptible(bnx2i_ep->ofld_wait,
+ bnx2i_ep->state != EP_STATE_DISCONN_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&bnx2i_ep->ofld_timer);
+
+destory_conn:
+ if (bnx2i_tear_down_conn(hba, bnx2i_ep)) {
+ mutex_unlock(&hba->net_dev_lock);
+ return;
+ }
+free_resc:
+ mutex_unlock(&hba->net_dev_lock);
+ bnx2i_free_qp_resc(hba, bnx2i_ep);
+return_bnx2i_ep:
+ if (bnx2i_conn)
+ bnx2i_conn->ep = NULL;
+
+ bnx2i_free_ep(ep);
+
+ if (!hba->ofld_conns_active)
+ bnx2i_unreg_dev_all();
+}
+
+
+/**
+ * bnx2i_nl_set_path - ISCSI_UEVENT_PATH_UPDATE user message handler
+ * @buf: pointer to buffer containing iscsi path message
+ *
+ */
+static int bnx2i_nl_set_path(struct Scsi_Host *shost, struct iscsi_path *params)
+{
+ struct bnx2i_hba *hba = iscsi_host_priv(shost);
+ char *buf = (char *) params;
+ u16 len = sizeof(*params);
+
+ /* handled by cnic driver */
+ hba->cnic->iscsi_nl_msg_recv(hba->cnic, ISCSI_UEVENT_PATH_UPDATE, buf,
+ len);
+
+ return 0;
+}
+
+
+/*
+ * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
+ * used while registering with the scsi host and iSCSI transport module.
+ */
+static struct scsi_host_template bnx2i_host_template = {
+ .module = THIS_MODULE,
+ .name = "Broadcom Offload iSCSI Initiator",
+ .proc_name = "bnx2i",
+ .queuecommand = iscsi_queuecommand,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_device_reset_handler = iscsi_eh_device_reset,
+ .eh_target_reset_handler = iscsi_eh_target_reset,
+ .can_queue = 1024,
+ .max_sectors = 127,
+ .cmd_per_lun = 32,
+ .this_id = -1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
+ .shost_attrs = bnx2i_dev_attributes,
+};
+
+struct iscsi_transport bnx2i_iscsi_transport = {
+ .owner = THIS_MODULE,
+ .name = "bnx2i",
+ .caps = CAP_RECOVERY_L0 | CAP_HDRDGST |
+ CAP_MULTI_R2T | CAP_DATADGST |
+ CAP_DATA_PATH_OFFLOAD,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME | ISCSI_TPGT |
+ ISCSI_USERNAME | ISCSI_PASSWORD |
+ ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_LU_RESET_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO |
+ ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
+ .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME,
+ .create_session = bnx2i_session_create,
+ .destroy_session = bnx2i_session_destroy,
+ .create_conn = bnx2i_conn_create,
+ .bind_conn = bnx2i_conn_bind,
+ .destroy_conn = bnx2i_conn_destroy,
+ .set_param = iscsi_set_param,
+ .get_conn_param = bnx2i_conn_get_param,
+ .get_session_param = iscsi_session_get_param,
+ .get_host_param = bnx2i_host_get_param,
+ .start_conn = bnx2i_conn_start,
+ .stop_conn = iscsi_conn_stop,
+ .send_pdu = iscsi_conn_send_pdu,
+ .xmit_task = bnx2i_task_xmit,
+ .get_stats = bnx2i_conn_get_stats,
+ /* TCP connect - disconnect - option-2 interface calls */
+ .ep_connect = bnx2i_ep_connect,
+ .ep_poll = bnx2i_ep_poll,
+ .ep_disconnect = bnx2i_ep_disconnect,
+ .set_path = bnx2i_nl_set_path,
+ /* Error recovery timeout call */
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
+ .cleanup_task = bnx2i_cleanup_task,
+};
diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
new file mode 100644
index 000000000000..96426b751eb2
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
@@ -0,0 +1,142 @@
+/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2004 - 2009 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+/**
+ * bnx2i_dev_to_hba - maps dev pointer to adapter struct
+ * @dev: device pointer
+ *
+ * Map device to hba structure
+ */
+static inline struct bnx2i_hba *bnx2i_dev_to_hba(struct device *dev)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ return iscsi_host_priv(shost);
+}
+
+
+/**
+ * bnx2i_show_sq_info - return(s currently configured send queue (SQ) size
+ * @dev: device pointer
+ * @buf: buffer to return current SQ size parameter
+ *
+ * Returns current SQ size parameter, this paramater determines the number
+ * outstanding iSCSI commands supported on a connection
+ */
+static ssize_t bnx2i_show_sq_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_sqes);
+}
+
+
+/**
+ * bnx2i_set_sq_info - update send queue (SQ) size parameter
+ * @dev: device pointer
+ * @buf: buffer to return current SQ size parameter
+ * @count: parameter buffer size
+ *
+ * Interface for user to change shared queue size allocated for each conn
+ * Must be within SQ limits and a power of 2. For the latter this is needed
+ * because of how libiscsi preallocates tasks.
+ */
+static ssize_t bnx2i_set_sq_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+ u32 val;
+ int max_sq_size;
+
+ if (hba->ofld_conns_active)
+ goto skip_config;
+
+ if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+ max_sq_size = BNX2I_5770X_SQ_WQES_MAX;
+ else
+ max_sq_size = BNX2I_570X_SQ_WQES_MAX;
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_SQ_WQES_MIN) && (val <= max_sq_size) &&
+ (is_power_of_2(val)))
+ hba->max_sqes = val;
+ }
+
+ return count;
+
+skip_config:
+ printk(KERN_ERR "bnx2i: device busy, cannot change SQ size\n");
+ return 0;
+}
+
+
+/**
+ * bnx2i_show_ccell_info - returns command cell (HQ) size
+ * @dev: device pointer
+ * @buf: buffer to return current SQ size parameter
+ *
+ * returns per-connection TCP history queue size parameter
+ */
+static ssize_t bnx2i_show_ccell_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+ return sprintf(buf, "0x%x\n", hba->num_ccell);
+}
+
+
+/**
+ * bnx2i_get_link_state - set command cell (HQ) size
+ * @dev: device pointer
+ * @buf: buffer to return current SQ size parameter
+ * @count: parameter buffer size
+ *
+ * updates per-connection TCP history queue size parameter
+ */
+static ssize_t bnx2i_set_ccell_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+ if (hba->ofld_conns_active)
+ goto skip_config;
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_CCELLS_MIN) &&
+ (val <= BNX2I_CCELLS_MAX)) {
+ hba->num_ccell = val;
+ }
+ }
+
+ return count;
+
+skip_config:
+ printk(KERN_ERR "bnx2i: device busy, cannot change CCELL size\n");
+ return 0;
+}
+
+
+static DEVICE_ATTR(sq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_sq_info, bnx2i_set_sq_info);
+static DEVICE_ATTR(num_ccell, S_IRUGO | S_IWUSR,
+ bnx2i_show_ccell_info, bnx2i_set_ccell_info);
+
+struct device_attribute *bnx2i_dev_attributes[] = {
+ &dev_attr_sq_size,
+ &dev_attr_num_ccell,
+ NULL
+};
diff --git a/drivers/scsi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgb3i/cxgb3i.h
index d362860e7504..e3133b58e594 100644
--- a/drivers/scsi/cxgb3i/cxgb3i.h
+++ b/drivers/scsi/cxgb3i/cxgb3i.h
@@ -34,7 +34,7 @@
#include "cxgb3i_offload.h"
#include "cxgb3i_ddp.h"
-#define CXGB3I_SCSI_QDEPTH_DFLT 128
+#define CXGB3I_SCSI_HOST_QDEPTH 1024
#define CXGB3I_MAX_TARGET CXGB3I_MAX_CONN
#define CXGB3I_MAX_LUN 512
#define ISCSI_PDU_NONPAYLOAD_MAX \
@@ -144,7 +144,6 @@ struct cxgb3i_adapter *cxgb3i_adapter_find_by_tdev(struct t3cdev *);
void cxgb3i_adapter_open(struct t3cdev *);
void cxgb3i_adapter_close(struct t3cdev *);
-struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *);
struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *,
struct net_device *);
void cxgb3i_hba_host_remove(struct cxgb3i_hba *);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
index d06a661c209f..99c912547902 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_ddp.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
@@ -120,20 +120,26 @@ static void clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int tag,
}
static inline int ddp_find_unused_entries(struct cxgb3i_ddp_info *ddp,
- int start, int max, int count,
+ unsigned int start, unsigned int max,
+ unsigned int count,
struct cxgb3i_gather_list *gl)
{
- unsigned int i, j;
+ unsigned int i, j, k;
+ /* not enough entries */
+ if ((max - start) < count)
+ return -EBUSY;
+
+ max -= count;
spin_lock(&ddp->map_lock);
- for (i = start; i <= max;) {
- for (j = 0; j < count; j++) {
- if (ddp->gl_map[i + j])
+ for (i = start; i < max;) {
+ for (j = 0, k = i; j < count; j++, k++) {
+ if (ddp->gl_map[k])
break;
}
if (j == count) {
- for (j = 0; j < count; j++)
- ddp->gl_map[i + j] = gl;
+ for (j = 0, k = i; j < count; j++, k++)
+ ddp->gl_map[k] = gl;
spin_unlock(&ddp->map_lock);
return i;
}
@@ -354,7 +360,7 @@ int cxgb3i_ddp_tag_reserve(struct t3cdev *tdev, unsigned int tid,
struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi;
struct pagepod_hdr hdr;
unsigned int npods;
- int idx = -1, idx_max;
+ int idx = -1;
int err = -ENOMEM;
u32 sw_tag = *tagp;
u32 tag;
@@ -367,17 +373,17 @@ int cxgb3i_ddp_tag_reserve(struct t3cdev *tdev, unsigned int tid,
}
npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
- idx_max = ddp->nppods - npods + 1;
if (ddp->idx_last == ddp->nppods)
- idx = ddp_find_unused_entries(ddp, 0, idx_max, npods, gl);
+ idx = ddp_find_unused_entries(ddp, 0, ddp->nppods, npods, gl);
else {
idx = ddp_find_unused_entries(ddp, ddp->idx_last + 1,
- idx_max, npods, gl);
- if (idx < 0 && ddp->idx_last >= npods)
+ ddp->nppods, npods, gl);
+ if (idx < 0 && ddp->idx_last >= npods) {
idx = ddp_find_unused_entries(ddp, 0,
- ddp->idx_last - npods + 1,
+ min(ddp->idx_last + npods, ddp->nppods),
npods, gl);
+ }
}
if (idx < 0) {
ddp_log_debug("xferlen %u, gl %u, npods %u NO DDP.\n",
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index fff8e4327644..74369a3f963b 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -13,6 +13,7 @@
#include <linux/inet.h>
#include <linux/crypto.h>
+#include <net/dst.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -178,7 +179,7 @@ void cxgb3i_adapter_close(struct t3cdev *t3dev)
* cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure via net_device
* @t3dev: t3cdev adapter
*/
-struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
+static struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
{
struct cxgb3i_adapter *snic;
int i;
@@ -261,20 +262,27 @@ void cxgb3i_hba_host_remove(struct cxgb3i_hba *hba)
/**
* cxgb3i_ep_connect - establish TCP connection to target portal
+ * @shost: scsi host to use
* @dst_addr: target IP address
* @non_blocking: blocking or non-blocking call
*
* Initiates a TCP/IP connection to the dst_addr
*/
-static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
+static struct iscsi_endpoint *cxgb3i_ep_connect(struct Scsi_Host *shost,
+ struct sockaddr *dst_addr,
int non_blocking)
{
struct iscsi_endpoint *ep;
struct cxgb3i_endpoint *cep;
- struct cxgb3i_hba *hba;
+ struct cxgb3i_hba *hba = NULL;
struct s3_conn *c3cn = NULL;
int err = 0;
+ if (shost)
+ hba = iscsi_host_priv(shost);
+
+ cxgb3i_api_debug("shost 0x%p, hba 0x%p.\n", shost, hba);
+
c3cn = cxgb3i_c3cn_create();
if (!c3cn) {
cxgb3i_log_info("ep connect OOM.\n");
@@ -282,17 +290,27 @@ static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
goto release_conn;
}
- err = cxgb3i_c3cn_connect(c3cn, (struct sockaddr_in *)dst_addr);
+ err = cxgb3i_c3cn_connect(hba ? hba->ndev : NULL, c3cn,
+ (struct sockaddr_in *)dst_addr);
if (err < 0) {
cxgb3i_log_info("ep connect failed.\n");
goto release_conn;
}
+
hba = cxgb3i_hba_find_by_netdev(c3cn->dst_cache->dev);
if (!hba) {
err = -ENOSPC;
cxgb3i_log_info("NOT going through cxgbi device.\n");
goto release_conn;
}
+
+ if (shost && hba != iscsi_host_priv(shost)) {
+ err = -ENOSPC;
+ cxgb3i_log_info("Could not connect through request host%u\n",
+ shost->host_no);
+ goto release_conn;
+ }
+
if (c3cn_is_closing(c3cn)) {
err = -ENOSPC;
cxgb3i_log_info("ep connect unable to connect.\n");
@@ -876,13 +894,14 @@ static struct scsi_host_template cxgb3i_host_template = {
.proc_name = "cxgb3i",
.queuecommand = iscsi_queuecommand,
.change_queue_depth = iscsi_change_queue_depth,
- .can_queue = CXGB3I_SCSI_QDEPTH_DFLT - 1,
+ .can_queue = CXGB3I_SCSI_HOST_QDEPTH,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
- .cmd_per_lun = CXGB3I_SCSI_QDEPTH_DFLT,
+ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_target_reset,
+ .target_alloc = iscsi_target_alloc,
.use_clustering = DISABLE_CLUSTERING,
.this_id = -1,
};
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c
index 4d8654cdbdae..c1d5be4adf9c 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_offload.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.c
@@ -1479,12 +1479,13 @@ static struct net_device *cxgb3_egress_dev(struct net_device *root_dev,
return NULL;
}
-static struct rtable *find_route(__be32 saddr, __be32 daddr,
+static struct rtable *find_route(struct net_device *dev,
+ __be32 saddr, __be32 daddr,
__be16 sport, __be16 dport)
{
struct rtable *rt;
struct flowi fl = {
- .oif = 0,
+ .oif = dev ? dev->ifindex : 0,
.nl_u = {
.ip4_u = {
.daddr = daddr,
@@ -1573,36 +1574,40 @@ out_err:
*
* return 0 if active open request is sent, < 0 otherwise.
*/
-int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
+int cxgb3i_c3cn_connect(struct net_device *dev, struct s3_conn *c3cn,
+ struct sockaddr_in *usin)
{
struct rtable *rt;
- struct net_device *dev;
struct cxgb3i_sdev_data *cdata;
struct t3cdev *cdev;
__be32 sipv4;
int err;
+ c3cn_conn_debug("c3cn 0x%p, dev 0x%p.\n", c3cn, dev);
+
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
c3cn->daddr.sin_port = usin->sin_port;
c3cn->daddr.sin_addr.s_addr = usin->sin_addr.s_addr;
- rt = find_route(c3cn->saddr.sin_addr.s_addr,
+ rt = find_route(dev, c3cn->saddr.sin_addr.s_addr,
c3cn->daddr.sin_addr.s_addr,
c3cn->saddr.sin_port,
c3cn->daddr.sin_port);
if (rt == NULL) {
- c3cn_conn_debug("NO route to 0x%x, port %u.\n",
+ c3cn_conn_debug("NO route to 0x%x, port %u, dev %s.\n",
c3cn->daddr.sin_addr.s_addr,
- ntohs(c3cn->daddr.sin_port));
+ ntohs(c3cn->daddr.sin_port),
+ dev ? dev->name : "any");
return -ENETUNREACH;
}
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
- c3cn_conn_debug("multi-cast route to 0x%x, port %u.\n",
+ c3cn_conn_debug("multi-cast route to 0x%x, port %u, dev %s.\n",
c3cn->daddr.sin_addr.s_addr,
- ntohs(c3cn->daddr.sin_port));
+ ntohs(c3cn->daddr.sin_port),
+ dev ? dev->name : "any");
ip_rt_put(rt);
return -ENETUNREACH;
}
@@ -1737,7 +1742,7 @@ int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb)
c3cn_tx_debug("c3cn 0x%p, snd %u - %u > %u.\n",
c3cn, c3cn->write_seq, c3cn->snd_una,
cxgb3_snd_win);
- err = -EAGAIN;
+ err = -ENOBUFS;
goto out_err;
}
@@ -1775,6 +1780,8 @@ done:
out_err:
if (copied == 0 && err == -EPIPE)
copied = c3cn->err ? c3cn->err : -EPIPE;
+ else
+ copied = err;
goto done;
}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.h b/drivers/scsi/cxgb3i/cxgb3i_offload.h
index ebfca960c0a9..6a1d86b1fafe 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_offload.h
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.h
@@ -169,7 +169,8 @@ void cxgb3i_sdev_add(struct t3cdev *, struct cxgb3_client *);
void cxgb3i_sdev_remove(struct t3cdev *);
struct s3_conn *cxgb3i_c3cn_create(void);
-int cxgb3i_c3cn_connect(struct s3_conn *, struct sockaddr_in *);
+int cxgb3i_c3cn_connect(struct net_device *, struct s3_conn *,
+ struct sockaddr_in *);
void cxgb3i_c3cn_rx_credits(struct s3_conn *, int);
int cxgb3i_c3cn_send_pdus(struct s3_conn *, struct sk_buff *);
void cxgb3i_c3cn_release(struct s3_conn *);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
index 7eebc9a7cb35..709105071177 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_pdu.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
@@ -400,17 +400,18 @@ int cxgb3i_conn_xmit_pdu(struct iscsi_task *task)
return 0;
}
- if (err < 0 && err != -EAGAIN) {
- kfree_skb(skb);
- cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
- task->itt, skb, skb->len, skb->data_len, err);
- iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
- iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
+ if (err == -EAGAIN || err == -ENOBUFS) {
+ /* reset skb to send when we are called again */
+ tdata->skb = skb;
return err;
}
- /* reset skb to send when we are called again */
- tdata->skb = skb;
- return -EAGAIN;
+
+ kfree_skb(skb);
+ cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
+ task->itt, skb, skb->len, skb->data_len, err);
+ iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
+ iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
+ return err;
}
int cxgb3i_pdu_init(void)
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 43b8c51e98d0..fd0544f7da81 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -561,6 +561,12 @@ static int rdac_check_sense(struct scsi_device *sdev,
struct rdac_dh_data *h = get_rdac_data(sdev);
switch (sense_hdr->sense_key) {
case NOT_READY:
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x01)
+ /* LUN Not Ready - Logical Unit Not Ready and is in
+ * the process of becoming ready
+ * Just retry.
+ */
+ return ADD_TO_MLQUEUE;
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
/* LUN Not Ready - Storage firmware incompatible
* Manual code synchonisation required.
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 94e1e3189773..e606b4829d44 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -54,10 +54,9 @@ MODULE_LICENSE("GPL v2");
/* fcoe host list */
LIST_HEAD(fcoe_hostlist);
DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
-/* Function Prototyes */
+/* Function Prototypes */
static int fcoe_reset(struct Scsi_Host *shost);
static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
static int fcoe_rcv(struct sk_buff *, struct net_device *,
@@ -71,7 +70,7 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
static int fcoe_hostlist_remove(const struct fc_lport *);
-static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
static void fcoe_dev_cleanup(void);
@@ -138,7 +137,6 @@ static struct scsi_host_template fcoe_shost_template = {
/**
* fcoe_lport_config() - sets up the fc_lport
* @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
*
* Returns: 0 for success
*/
@@ -147,6 +145,7 @@ static int fcoe_lport_config(struct fc_lport *lp)
lp->link_up = 0;
lp->qfull = 0;
lp->max_retry_count = 3;
+ lp->max_rport_retry_count = 3;
lp->e_d_tov = 2 * 1000; /* FC-FS default */
lp->r_a_tov = 2 * 2 * 1000;
lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
@@ -168,6 +167,18 @@ static int fcoe_lport_config(struct fc_lport *lp)
}
/**
+ * fcoe_queue_timer() - fcoe queue timer
+ * @lp: the fc_lport pointer
+ *
+ * Calls fcoe_check_wait_queue on timeout
+ *
+ */
+static void fcoe_queue_timer(ulong lp)
+{
+ fcoe_check_wait_queue((struct fc_lport *)lp, NULL);
+}
+
+/**
* fcoe_netdev_config() - Set up netdev for SW FCoE
* @lp : ptr to the fc_lport
* @netdev : ptr to the associated netdevice struct
@@ -237,6 +248,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
}
skb_queue_head_init(&fc->fcoe_pending_queue);
fc->fcoe_pending_queue_active = 0;
+ setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp);
/* setup Source Mac Address */
memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr,
@@ -256,6 +268,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
rtnl_lock();
memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+ dev_mc_add(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
rtnl_unlock();
/*
@@ -380,12 +393,15 @@ static int fcoe_if_destroy(struct net_device *netdev)
dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
rtnl_unlock();
- /* Free the per-CPU revieve threads */
+ /* Free the per-CPU receive threads */
fcoe_percpu_clean(lp);
/* Free existing skbs */
fcoe_clean_pending_queue(lp);
+ /* Stop the timer */
+ del_timer_sync(&fc->timer);
+
/* Free memory used by statistical counters */
fc_lport_free_stats(lp);
@@ -720,7 +736,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
}
#else
/*
- * This a non-SMP scenario where the singluar Rx thread is
+ * This a non-SMP scenario where the singular Rx thread is
* being removed. Free all skbs and stop the thread.
*/
spin_lock_bh(&p->fcoe_rx_list.lock);
@@ -777,7 +793,7 @@ static struct notifier_block fcoe_cpu_notifier = {
* @skb: the receive skb
* @dev: associated net device
* @ptype: context
- * @odldev: last device
+ * @olddev: last device
*
* this function will receive the packet and build fc frame and pass it up
*
@@ -884,7 +900,6 @@ err2:
kfree_skb(skb);
return -1;
}
-EXPORT_SYMBOL_GPL(fcoe_rcv);
/**
* fcoe_start_io() - pass to netdev to start xmit for fcoe
@@ -905,7 +920,7 @@ static inline int fcoe_start_io(struct sk_buff *skb)
}
/**
- * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
+ * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof
* @skb: the skb to be xmitted
* @tlen: total len
*
@@ -947,7 +962,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
/**
* fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containg data to be checksummed
+ * @fp: the fc_frame containing data to be checksummed
*
* This uses crc32() to calculate the crc for fc frame
* Return : 32 bit crc
@@ -989,7 +1004,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
*/
int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
{
- int wlen, rc = 0;
+ int wlen;
u32 crc;
struct ethhdr *eh;
struct fcoe_crc_eof *cp;
@@ -1011,7 +1026,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
wlen = skb->len / FCOE_WORD_TO_BYTE;
if (!lp->link_up) {
- kfree(skb);
+ kfree_skb(skb);
return 0;
}
@@ -1022,8 +1037,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
sof = fr_sof(fp);
eof = fr_eof(fp);
- elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
- sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+ elen = sizeof(struct ethhdr);
hlen = sizeof(struct fcoe_hdr);
tlen = sizeof(struct fcoe_crc_eof);
wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
@@ -1062,7 +1076,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
cp = NULL;
}
- /* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+ /* adjust skb network/transport offsets to match mac/fcoe/fc */
skb_push(skb, elen + hlen);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
@@ -1108,22 +1122,12 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
/* send down to lld */
fr_dev(fp) = lp;
if (fc->fcoe_pending_queue.qlen)
- rc = fcoe_check_wait_queue(lp);
-
- if (rc == 0)
- rc = fcoe_start_io(skb);
-
- if (rc) {
- spin_lock_bh(&fc->fcoe_pending_queue.lock);
- __skb_queue_tail(&fc->fcoe_pending_queue, skb);
- spin_unlock_bh(&fc->fcoe_pending_queue.lock);
- if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- lp->qfull = 1;
- }
+ fcoe_check_wait_queue(lp, skb);
+ else if (fcoe_start_io(skb))
+ fcoe_check_wait_queue(lp, skb);
return 0;
}
-EXPORT_SYMBOL_GPL(fcoe_xmit);
/**
* fcoe_percpu_receive_thread() - recv thread per cpu
@@ -1270,54 +1274,28 @@ int fcoe_percpu_receive_thread(void *arg)
}
/**
- * fcoe_watchdog() - fcoe timer callback
- * @vp:
- *
- * This checks the pending queue length for fcoe and set lport qfull
- * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
- * fcoe_hostlist.
- *
- * Returns: 0 for success
- */
-void fcoe_watchdog(ulong vp)
-{
- struct fcoe_softc *fc;
-
- read_lock(&fcoe_hostlist_lock);
- list_for_each_entry(fc, &fcoe_hostlist, list) {
- if (fc->ctlr.lp)
- fcoe_check_wait_queue(fc->ctlr.lp);
- }
- read_unlock(&fcoe_hostlist_lock);
-
- fcoe_timer.expires = jiffies + (1 * HZ);
- add_timer(&fcoe_timer);
-}
-
-
-/**
- * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
- * @lp: the fc_port for this skb
- * @skb: the associated skb to be xmitted
+ * fcoe_check_wait_queue() - attempt to clear the transmit backlog
+ * @lp: the fc_lport
*
* This empties the wait_queue, dequeue the head of the wait_queue queue
* and calls fcoe_start_io() for each packet, if all skb have been
* transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and try again later.
+ * wait_queue and try again later.
*
* The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the time function OR
+ * in the wait_queue which will be emptied by the timer function or
* by the next skb transmit.
- *
- * Returns: 0 for success
*/
-static int fcoe_check_wait_queue(struct fc_lport *lp)
+static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
{
struct fcoe_softc *fc = lport_priv(lp);
- struct sk_buff *skb;
- int rc = -1;
+ int rc;
spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+ if (skb)
+ __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+
if (fc->fcoe_pending_queue_active)
goto out;
fc->fcoe_pending_queue_active = 1;
@@ -1343,27 +1321,26 @@ static int fcoe_check_wait_queue(struct fc_lport *lp)
if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
lp->qfull = 0;
+ if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer))
+ mod_timer(&fc->timer, jiffies + 2);
fc->fcoe_pending_queue_active = 0;
- rc = fc->fcoe_pending_queue.qlen;
out:
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ lp->qfull = 1;
spin_unlock_bh(&fc->fcoe_pending_queue.lock);
- return rc;
+ return;
}
/**
* fcoe_dev_setup() - setup link change notification interface
*/
-static void fcoe_dev_setup()
+static void fcoe_dev_setup(void)
{
- /*
- * here setup a interface specific wd time to
- * monitor the link state
- */
register_netdevice_notifier(&fcoe_notifier);
}
/**
- * fcoe_dev_setup() - cleanup link change notification interface
+ * fcoe_dev_cleanup() - cleanup link change notification interface
*/
static void fcoe_dev_cleanup(void)
{
@@ -1437,10 +1414,9 @@ out:
/**
* fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @ifname: fixed array for output parsed ifname
* @buffer: incoming buffer to be copied
*
- * Returns: NULL or ptr to netdeive
+ * Returns: NULL or ptr to net_device
*/
static struct net_device *fcoe_if_to_netdev(const char *buffer)
{
@@ -1458,7 +1434,7 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
}
/**
- * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
+ * fcoe_netdev_to_module_owner() - finds out the driver module of the netdev
* @netdev: the target netdev
*
* Returns: ptr to the struct module, NULL for failure
@@ -1488,7 +1464,7 @@ fcoe_netdev_to_module_owner(const struct net_device *netdev)
* Holds the Ethernet driver module by try_module_get() for
* the corresponding netdev.
*
- * Returns: 0 for succsss
+ * Returns: 0 for success
*/
static int fcoe_ethdrv_get(const struct net_device *netdev)
{
@@ -1510,7 +1486,7 @@ static int fcoe_ethdrv_get(const struct net_device *netdev)
* Releases the Ethernet driver module by module_put for
* the corresponding netdev.
*
- * Returns: 0 for succsss
+ * Returns: 0 for success
*/
static int fcoe_ethdrv_put(const struct net_device *netdev)
{
@@ -1528,7 +1504,7 @@ static int fcoe_ethdrv_put(const struct net_device *netdev)
/**
* fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expcted to be a eth if name
+ * @buffer: expected to be an eth if name
* @kp: associated kernel param
*
* Returns: 0 for success
@@ -1565,7 +1541,7 @@ out_nodev:
/**
* fcoe_create() - Handles the create call from sysfs
- * @buffer: expcted to be a eth if name
+ * @buffer: expected to be an eth if name
* @kp: associated kernel param
*
* Returns: 0 for success
@@ -1652,7 +1628,6 @@ int fcoe_link_ok(struct fc_lport *lp)
return rc;
}
-EXPORT_SYMBOL_GPL(fcoe_link_ok);
/**
* fcoe_percpu_clean() - Clear the pending skbs for an lport
@@ -1684,7 +1659,6 @@ void fcoe_percpu_clean(struct fc_lport *lp)
spin_unlock_bh(&pp->fcoe_rx_list.lock);
}
}
-EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
/**
* fcoe_clean_pending_queue() - Dequeue a skb and free it
@@ -1705,7 +1679,6 @@ void fcoe_clean_pending_queue(struct fc_lport *lp)
}
spin_unlock_bh(&fc->fcoe_pending_queue.lock);
}
-EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
/**
* fcoe_reset() - Resets the fcoe
@@ -1719,11 +1692,10 @@ int fcoe_reset(struct Scsi_Host *shost)
fc_lport_reset(lport);
return 0;
}
-EXPORT_SYMBOL_GPL(fcoe_reset);
/**
* fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
- * @device: this is currently ptr to net_device
+ * @dev: this is currently ptr to net_device
*
* Returns: NULL or the located fcoe_softc
*/
@@ -1757,11 +1729,10 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
return (fc) ? fc->ctlr.lp : NULL;
}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
/**
* fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to badded
+ * @lp: ptr to the fc_lport to be added
*
* Returns: 0 for success
*/
@@ -1778,11 +1749,10 @@ int fcoe_hostlist_add(const struct fc_lport *lp)
}
return 0;
}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
/**
* fcoe_hostlist_remove() - remove a lport from lports list
- * @lp: ptr to the fc_lport to badded
+ * @lp: ptr to the fc_lport to be removed
*
* Returns: 0 for success
*/
@@ -1798,7 +1768,6 @@ int fcoe_hostlist_remove(const struct fc_lport *lp)
return 0;
}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
/**
* fcoe_init() - fcoe module loading initialization
@@ -1830,10 +1799,6 @@ static int __init fcoe_init(void)
/* Setup link change notification */
fcoe_dev_setup();
- setup_timer(&fcoe_timer, fcoe_watchdog, 0);
-
- mod_timer(&fcoe_timer, jiffies + (10 * HZ));
-
fcoe_if_init();
return 0;
@@ -1859,9 +1824,6 @@ static void __exit fcoe_exit(void)
fcoe_dev_cleanup();
- /* Stop the timer */
- del_timer_sync(&fcoe_timer);
-
/* releases the associated fcoe hosts */
list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
fcoe_if_destroy(fc->real_dev);
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index 917aae886897..a1eb8c1988b0 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -61,6 +61,7 @@ struct fcoe_softc {
struct packet_type fip_packet_type;
struct sk_buff_head fcoe_pending_queue;
u8 fcoe_pending_queue_active;
+ struct timer_list timer; /* queue timer */
struct fcoe_ctlr ctlr;
};
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index f410f4abb548..929411880e4b 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -122,7 +122,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctrl_destroy() - Disable and tear-down the FCoE controller.
+ * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller.
* @fip: FCoE controller.
*
* This is called by FCoE drivers before freeing the &fcoe_ctlr.
@@ -213,7 +213,7 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
sol->desc.size.fd_size = htons(fcoe_size);
skb_put(skb, sizeof(*sol));
- skb->protocol = htons(ETH_P_802_3);
+ skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
fip->send(fip, skb);
@@ -365,7 +365,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
}
skb_put(skb, len);
- skb->protocol = htons(ETH_P_802_3);
+ skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
fip->send(fip, skb);
@@ -424,7 +424,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
if (dtype != ELS_FLOGI)
memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
- skb->protocol = htons(ETH_P_802_3);
+ skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
return 0;
@@ -447,14 +447,10 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
u16 old_xid;
u8 op;
- if (fip->state == FIP_ST_NON_FIP)
- return 0;
-
fh = (struct fc_frame_header *)skb->data;
op = *(u8 *)(fh + 1);
- switch (op) {
- case ELS_FLOGI:
+ if (op == ELS_FLOGI) {
old_xid = fip->flogi_oxid;
fip->flogi_oxid = ntohs(fh->fh_ox_id);
if (fip->state == FIP_ST_AUTO) {
@@ -466,6 +462,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
fip->map_dest = 1;
return 0;
}
+ if (fip->state == FIP_ST_NON_FIP)
+ fip->map_dest = 1;
+ }
+
+ if (fip->state == FIP_ST_NON_FIP)
+ return 0;
+
+ switch (op) {
+ case ELS_FLOGI:
op = FIP_DT_FLOGI;
break;
case ELS_FDISC:
diff --git a/drivers/scsi/fnic/Makefile b/drivers/scsi/fnic/Makefile
new file mode 100644
index 000000000000..37c3440bc17c
--- /dev/null
+++ b/drivers/scsi/fnic/Makefile
@@ -0,0 +1,15 @@
+obj-$(CONFIG_FCOE_FNIC) += fnic.o
+
+fnic-y := \
+ fnic_attrs.o \
+ fnic_isr.o \
+ fnic_main.o \
+ fnic_res.o \
+ fnic_fcs.o \
+ fnic_scsi.o \
+ vnic_cq.o \
+ vnic_dev.o \
+ vnic_intr.o \
+ vnic_rq.o \
+ vnic_wq_copy.o \
+ vnic_wq.o
diff --git a/drivers/scsi/fnic/cq_desc.h b/drivers/scsi/fnic/cq_desc.h
new file mode 100644
index 000000000000..d1225cf6320e
--- /dev/null
+++ b/drivers/scsi/fnic/cq_desc.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _CQ_DESC_H_
+#define _CQ_DESC_H_
+
+/*
+ * Completion queue descriptor types
+ */
+enum cq_desc_types {
+ CQ_DESC_TYPE_WQ_ENET = 0,
+ CQ_DESC_TYPE_DESC_COPY = 1,
+ CQ_DESC_TYPE_WQ_EXCH = 2,
+ CQ_DESC_TYPE_RQ_ENET = 3,
+ CQ_DESC_TYPE_RQ_FCP = 4,
+};
+
+/* Completion queue descriptor: 16B
+ *
+ * All completion queues have this basic layout. The
+ * type_specfic area is unique for each completion
+ * queue type.
+ */
+struct cq_desc {
+ __le16 completed_index;
+ __le16 q_number;
+ u8 type_specfic[11];
+ u8 type_color;
+};
+
+#define CQ_DESC_TYPE_BITS 4
+#define CQ_DESC_TYPE_MASK ((1 << CQ_DESC_TYPE_BITS) - 1)
+#define CQ_DESC_COLOR_MASK 1
+#define CQ_DESC_COLOR_SHIFT 7
+#define CQ_DESC_Q_NUM_BITS 10
+#define CQ_DESC_Q_NUM_MASK ((1 << CQ_DESC_Q_NUM_BITS) - 1)
+#define CQ_DESC_COMP_NDX_BITS 12
+#define CQ_DESC_COMP_NDX_MASK ((1 << CQ_DESC_COMP_NDX_BITS) - 1)
+
+static inline void cq_desc_dec(const struct cq_desc *desc_arg,
+ u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+ const struct cq_desc *desc = desc_arg;
+ const u8 type_color = desc->type_color;
+
+ *color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
+
+ /*
+ * Make sure color bit is read from desc *before* other fields
+ * are read from desc. Hardware guarantees color bit is last
+ * bit (byte) written. Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which would potentially
+ * result in reading stale values.
+ */
+
+ rmb();
+
+ *type = type_color & CQ_DESC_TYPE_MASK;
+ *q_number = le16_to_cpu(desc->q_number) & CQ_DESC_Q_NUM_MASK;
+ *completed_index = le16_to_cpu(desc->completed_index) &
+ CQ_DESC_COMP_NDX_MASK;
+}
+
+#endif /* _CQ_DESC_H_ */
diff --git a/drivers/scsi/fnic/cq_enet_desc.h b/drivers/scsi/fnic/cq_enet_desc.h
new file mode 100644
index 000000000000..a9fa26f82ddd
--- /dev/null
+++ b/drivers/scsi/fnic/cq_enet_desc.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _CQ_ENET_DESC_H_
+#define _CQ_ENET_DESC_H_
+
+#include "cq_desc.h"
+
+/* Ethernet completion queue descriptor: 16B */
+struct cq_enet_wq_desc {
+ __le16 completed_index;
+ __le16 q_number;
+ u8 reserved[11];
+ u8 type_color;
+};
+
+static inline void cq_enet_wq_desc_dec(struct cq_enet_wq_desc *desc,
+ u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+ cq_desc_dec((struct cq_desc *)desc, type,
+ color, q_number, completed_index);
+}
+
+/* Completion queue descriptor: Ethernet receive queue, 16B */
+struct cq_enet_rq_desc {
+ __le16 completed_index_flags;
+ __le16 q_number_rss_type_flags;
+ __le32 rss_hash;
+ __le16 bytes_written_flags;
+ __le16 vlan;
+ __le16 checksum_fcoe;
+ u8 flags;
+ u8 type_color;
+};
+
+#define CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT (0x1 << 12)
+#define CQ_ENET_RQ_DESC_FLAGS_FCOE (0x1 << 13)
+#define CQ_ENET_RQ_DESC_FLAGS_EOP (0x1 << 14)
+#define CQ_ENET_RQ_DESC_FLAGS_SOP (0x1 << 15)
+
+#define CQ_ENET_RQ_DESC_RSS_TYPE_BITS 4
+#define CQ_ENET_RQ_DESC_RSS_TYPE_MASK \
+ ((1 << CQ_ENET_RQ_DESC_RSS_TYPE_BITS) - 1)
+#define CQ_ENET_RQ_DESC_RSS_TYPE_NONE 0
+#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv4 1
+#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv4 2
+#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv6 3
+#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6 4
+#define CQ_ENET_RQ_DESC_RSS_TYPE_IPv6_EX 5
+#define CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6_EX 6
+
+#define CQ_ENET_RQ_DESC_FLAGS_CSUM_NOT_CALC (0x1 << 14)
+
+#define CQ_ENET_RQ_DESC_BYTES_WRITTEN_BITS 14
+#define CQ_ENET_RQ_DESC_BYTES_WRITTEN_MASK \
+ ((1 << CQ_ENET_RQ_DESC_BYTES_WRITTEN_BITS) - 1)
+#define CQ_ENET_RQ_DESC_FLAGS_TRUNCATED (0x1 << 14)
+#define CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED (0x1 << 15)
+
+#define CQ_ENET_RQ_DESC_FCOE_SOF_BITS 4
+#define CQ_ENET_RQ_DESC_FCOE_SOF_MASK \
+ ((1 << CQ_ENET_RQ_DESC_FCOE_SOF_BITS) - 1)
+#define CQ_ENET_RQ_DESC_FCOE_EOF_BITS 8
+#define CQ_ENET_RQ_DESC_FCOE_EOF_MASK \
+ ((1 << CQ_ENET_RQ_DESC_FCOE_EOF_BITS) - 1)
+#define CQ_ENET_RQ_DESC_FCOE_EOF_SHIFT 8
+
+#define CQ_ENET_RQ_DESC_FLAGS_TCP_UDP_CSUM_OK (0x1 << 0)
+#define CQ_ENET_RQ_DESC_FCOE_FC_CRC_OK (0x1 << 0)
+#define CQ_ENET_RQ_DESC_FLAGS_UDP (0x1 << 1)
+#define CQ_ENET_RQ_DESC_FCOE_ENC_ERROR (0x1 << 1)
+#define CQ_ENET_RQ_DESC_FLAGS_TCP (0x1 << 2)
+#define CQ_ENET_RQ_DESC_FLAGS_IPV4_CSUM_OK (0x1 << 3)
+#define CQ_ENET_RQ_DESC_FLAGS_IPV6 (0x1 << 4)
+#define CQ_ENET_RQ_DESC_FLAGS_IPV4 (0x1 << 5)
+#define CQ_ENET_RQ_DESC_FLAGS_IPV4_FRAGMENT (0x1 << 6)
+#define CQ_ENET_RQ_DESC_FLAGS_FCS_OK (0x1 << 7)
+
+static inline void cq_enet_rq_desc_dec(struct cq_enet_rq_desc *desc,
+ u8 *type, u8 *color, u16 *q_number, u16 *completed_index,
+ u8 *ingress_port, u8 *fcoe, u8 *eop, u8 *sop, u8 *rss_type,
+ u8 *csum_not_calc, u32 *rss_hash, u16 *bytes_written, u8 *packet_error,
+ u8 *vlan_stripped, u16 *vlan, u16 *checksum, u8 *fcoe_sof,
+ u8 *fcoe_fc_crc_ok, u8 *fcoe_enc_error, u8 *fcoe_eof,
+ u8 *tcp_udp_csum_ok, u8 *udp, u8 *tcp, u8 *ipv4_csum_ok,
+ u8 *ipv6, u8 *ipv4, u8 *ipv4_fragment, u8 *fcs_ok)
+{
+ u16 completed_index_flags = le16_to_cpu(desc->completed_index_flags);
+ u16 q_number_rss_type_flags =
+ le16_to_cpu(desc->q_number_rss_type_flags);
+ u16 bytes_written_flags = le16_to_cpu(desc->bytes_written_flags);
+
+ cq_desc_dec((struct cq_desc *)desc, type,
+ color, q_number, completed_index);
+
+ *ingress_port = (completed_index_flags &
+ CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT) ? 1 : 0;
+ *fcoe = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_FCOE) ?
+ 1 : 0;
+ *eop = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_EOP) ?
+ 1 : 0;
+ *sop = (completed_index_flags & CQ_ENET_RQ_DESC_FLAGS_SOP) ?
+ 1 : 0;
+
+ *rss_type = (u8)((q_number_rss_type_flags >> CQ_DESC_Q_NUM_BITS) &
+ CQ_ENET_RQ_DESC_RSS_TYPE_MASK);
+ *csum_not_calc = (q_number_rss_type_flags &
+ CQ_ENET_RQ_DESC_FLAGS_CSUM_NOT_CALC) ? 1 : 0;
+
+ *rss_hash = le32_to_cpu(desc->rss_hash);
+
+ *bytes_written = bytes_written_flags &
+ CQ_ENET_RQ_DESC_BYTES_WRITTEN_MASK;
+ *packet_error = (bytes_written_flags &
+ CQ_ENET_RQ_DESC_FLAGS_TRUNCATED) ? 1 : 0;
+ *vlan_stripped = (bytes_written_flags &
+ CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED) ? 1 : 0;
+
+ *vlan = le16_to_cpu(desc->vlan);
+
+ if (*fcoe) {
+ *fcoe_sof = (u8)(le16_to_cpu(desc->checksum_fcoe) &
+ CQ_ENET_RQ_DESC_FCOE_SOF_MASK);
+ *fcoe_fc_crc_ok = (desc->flags &
+ CQ_ENET_RQ_DESC_FCOE_FC_CRC_OK) ? 1 : 0;
+ *fcoe_enc_error = (desc->flags &
+ CQ_ENET_RQ_DESC_FCOE_ENC_ERROR) ? 1 : 0;
+ *fcoe_eof = (u8)((desc->checksum_fcoe >>
+ CQ_ENET_RQ_DESC_FCOE_EOF_SHIFT) &
+ CQ_ENET_RQ_DESC_FCOE_EOF_MASK);
+ *checksum = 0;
+ } else {
+ *fcoe_sof = 0;
+ *fcoe_fc_crc_ok = 0;
+ *fcoe_enc_error = 0;
+ *fcoe_eof = 0;
+ *checksum = le16_to_cpu(desc->checksum_fcoe);
+ }
+
+ *tcp_udp_csum_ok =
+ (desc->flags & CQ_ENET_RQ_DESC_FLAGS_TCP_UDP_CSUM_OK) ? 1 : 0;
+ *udp = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_UDP) ? 1 : 0;
+ *tcp = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_TCP) ? 1 : 0;
+ *ipv4_csum_ok =
+ (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4_CSUM_OK) ? 1 : 0;
+ *ipv6 = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV6) ? 1 : 0;
+ *ipv4 = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4) ? 1 : 0;
+ *ipv4_fragment =
+ (desc->flags & CQ_ENET_RQ_DESC_FLAGS_IPV4_FRAGMENT) ? 1 : 0;
+ *fcs_ok = (desc->flags & CQ_ENET_RQ_DESC_FLAGS_FCS_OK) ? 1 : 0;
+}
+
+#endif /* _CQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/fnic/cq_exch_desc.h b/drivers/scsi/fnic/cq_exch_desc.h
new file mode 100644
index 000000000000..501660cfe228
--- /dev/null
+++ b/drivers/scsi/fnic/cq_exch_desc.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _CQ_EXCH_DESC_H_
+#define _CQ_EXCH_DESC_H_
+
+#include "cq_desc.h"
+
+/* Exchange completion queue descriptor: 16B */
+struct cq_exch_wq_desc {
+ u16 completed_index;
+ u16 q_number;
+ u16 exchange_id;
+ u8 tmpl;
+ u8 reserved0;
+ u32 reserved1;
+ u8 exch_status;
+ u8 reserved2[2];
+ u8 type_color;
+};
+
+#define CQ_EXCH_WQ_STATUS_BITS 2
+#define CQ_EXCH_WQ_STATUS_MASK ((1 << CQ_EXCH_WQ_STATUS_BITS) - 1)
+
+enum cq_exch_status_types {
+ CQ_EXCH_WQ_STATUS_TYPE_COMPLETE = 0,
+ CQ_EXCH_WQ_STATUS_TYPE_ABORT = 1,
+ CQ_EXCH_WQ_STATUS_TYPE_SGL_EOF = 2,
+ CQ_EXCH_WQ_STATUS_TYPE_TMPL_ERR = 3,
+};
+
+static inline void cq_exch_wq_desc_dec(struct cq_exch_wq_desc *desc_ptr,
+ u8 *type,
+ u8 *color,
+ u16 *q_number,
+ u16 *completed_index,
+ u8 *exch_status)
+{
+ cq_desc_dec((struct cq_desc *)desc_ptr, type,
+ color, q_number, completed_index);
+ *exch_status = desc_ptr->exch_status & CQ_EXCH_WQ_STATUS_MASK;
+}
+
+struct cq_fcp_rq_desc {
+ u16 completed_index_eop_sop_prt;
+ u16 q_number;
+ u16 exchange_id;
+ u16 tmpl;
+ u16 bytes_written;
+ u16 vlan;
+ u8 sof;
+ u8 eof;
+ u8 fcs_fer_fck;
+ u8 type_color;
+};
+
+#define CQ_FCP_RQ_DESC_FLAGS_SOP (1 << 15)
+#define CQ_FCP_RQ_DESC_FLAGS_EOP (1 << 14)
+#define CQ_FCP_RQ_DESC_FLAGS_PRT (1 << 12)
+#define CQ_FCP_RQ_DESC_TMPL_MASK 0x1f
+#define CQ_FCP_RQ_DESC_BYTES_WRITTEN_MASK 0x3fff
+#define CQ_FCP_RQ_DESC_PACKET_ERR_SHIFT 14
+#define CQ_FCP_RQ_DESC_PACKET_ERR_MASK (1 << CQ_FCP_RQ_DESC_PACKET_ERR_SHIFT)
+#define CQ_FCP_RQ_DESC_VS_STRIPPED_SHIFT 15
+#define CQ_FCP_RQ_DESC_VS_STRIPPED_MASK (1 << CQ_FCP_RQ_DESC_VS_STRIPPED_SHIFT)
+#define CQ_FCP_RQ_DESC_FC_CRC_OK_MASK 0x1
+#define CQ_FCP_RQ_DESC_FCOE_ERR_SHIFT 1
+#define CQ_FCP_RQ_DESC_FCOE_ERR_MASK (1 << CQ_FCP_RQ_DESC_FCOE_ERR_SHIFT)
+#define CQ_FCP_RQ_DESC_FCS_OK_SHIFT 7
+#define CQ_FCP_RQ_DESC_FCS_OK_MASK (1 << CQ_FCP_RQ_DESC_FCS_OK_SHIFT)
+
+static inline void cq_fcp_rq_desc_dec(struct cq_fcp_rq_desc *desc_ptr,
+ u8 *type,
+ u8 *color,
+ u16 *q_number,
+ u16 *completed_index,
+ u8 *eop,
+ u8 *sop,
+ u8 *fck,
+ u16 *exchange_id,
+ u16 *tmpl,
+ u32 *bytes_written,
+ u8 *sof,
+ u8 *eof,
+ u8 *ingress_port,
+ u8 *packet_err,
+ u8 *fcoe_err,
+ u8 *fcs_ok,
+ u8 *vlan_stripped,
+ u16 *vlan)
+{
+ cq_desc_dec((struct cq_desc *)desc_ptr, type,
+ color, q_number, completed_index);
+ *eop = (desc_ptr->completed_index_eop_sop_prt &
+ CQ_FCP_RQ_DESC_FLAGS_EOP) ? 1 : 0;
+ *sop = (desc_ptr->completed_index_eop_sop_prt &
+ CQ_FCP_RQ_DESC_FLAGS_SOP) ? 1 : 0;
+ *ingress_port =
+ (desc_ptr->completed_index_eop_sop_prt &
+ CQ_FCP_RQ_DESC_FLAGS_PRT) ? 1 : 0;
+ *exchange_id = desc_ptr->exchange_id;
+ *tmpl = desc_ptr->tmpl & CQ_FCP_RQ_DESC_TMPL_MASK;
+ *bytes_written =
+ desc_ptr->bytes_written & CQ_FCP_RQ_DESC_BYTES_WRITTEN_MASK;
+ *packet_err =
+ (desc_ptr->bytes_written & CQ_FCP_RQ_DESC_PACKET_ERR_MASK) >>
+ CQ_FCP_RQ_DESC_PACKET_ERR_SHIFT;
+ *vlan_stripped =
+ (desc_ptr->bytes_written & CQ_FCP_RQ_DESC_VS_STRIPPED_MASK) >>
+ CQ_FCP_RQ_DESC_VS_STRIPPED_SHIFT;
+ *vlan = desc_ptr->vlan;
+ *sof = desc_ptr->sof;
+ *fck = desc_ptr->fcs_fer_fck & CQ_FCP_RQ_DESC_FC_CRC_OK_MASK;
+ *fcoe_err = (desc_ptr->fcs_fer_fck & CQ_FCP_RQ_DESC_FCOE_ERR_MASK) >>
+ CQ_FCP_RQ_DESC_FCOE_ERR_SHIFT;
+ *eof = desc_ptr->eof;
+ *fcs_ok =
+ (desc_ptr->fcs_fer_fck & CQ_FCP_RQ_DESC_FCS_OK_MASK) >>
+ CQ_FCP_RQ_DESC_FCS_OK_SHIFT;
+}
+
+struct cq_sgl_desc {
+ u16 exchange_id;
+ u16 q_number;
+ u32 active_burst_offset;
+ u32 tot_data_bytes;
+ u16 tmpl;
+ u8 sgl_err;
+ u8 type_color;
+};
+
+enum cq_sgl_err_types {
+ CQ_SGL_ERR_NO_ERROR = 0,
+ CQ_SGL_ERR_OVERFLOW, /* data ran beyond end of SGL */
+ CQ_SGL_ERR_SGL_LCL_ADDR_ERR, /* sgl access to local vnic addr illegal*/
+ CQ_SGL_ERR_ADDR_RSP_ERR, /* sgl address error */
+ CQ_SGL_ERR_DATA_RSP_ERR, /* sgl data rsp error */
+ CQ_SGL_ERR_CNT_ZERO_ERR, /* SGL count is 0 */
+ CQ_SGL_ERR_CNT_MAX_ERR, /* SGL count is larger than supported */
+ CQ_SGL_ERR_ORDER_ERR, /* frames recv on both ports, order err */
+ CQ_SGL_ERR_DATA_LCL_ADDR_ERR,/* sgl data buf to local vnic addr ill */
+ CQ_SGL_ERR_HOST_CQ_ERR, /* host cq entry to local vnic addr ill */
+};
+
+#define CQ_SGL_SGL_ERR_MASK 0x1f
+#define CQ_SGL_TMPL_MASK 0x1f
+
+static inline void cq_sgl_desc_dec(struct cq_sgl_desc *desc_ptr,
+ u8 *type,
+ u8 *color,
+ u16 *q_number,
+ u16 *exchange_id,
+ u32 *active_burst_offset,
+ u32 *tot_data_bytes,
+ u16 *tmpl,
+ u8 *sgl_err)
+{
+ /* Cheat a little by assuming exchange_id is the same as completed
+ index */
+ cq_desc_dec((struct cq_desc *)desc_ptr, type, color, q_number,
+ exchange_id);
+ *active_burst_offset = desc_ptr->active_burst_offset;
+ *tot_data_bytes = desc_ptr->tot_data_bytes;
+ *tmpl = desc_ptr->tmpl & CQ_SGL_TMPL_MASK;
+ *sgl_err = desc_ptr->sgl_err & CQ_SGL_SGL_ERR_MASK;
+}
+
+#endif /* _CQ_EXCH_DESC_H_ */
diff --git a/drivers/scsi/fnic/fcpio.h b/drivers/scsi/fnic/fcpio.h
new file mode 100644
index 000000000000..12d770d885c5
--- /dev/null
+++ b/drivers/scsi/fnic/fcpio.h
@@ -0,0 +1,780 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FCPIO_H_
+#define _FCPIO_H_
+
+#include <linux/if_ether.h>
+
+/*
+ * This header file includes all of the data structures used for
+ * communication by the host driver to the fcp firmware.
+ */
+
+/*
+ * Exchange and sequence id space allocated to the host driver
+ */
+#define FCPIO_HOST_EXCH_RANGE_START 0x1000
+#define FCPIO_HOST_EXCH_RANGE_END 0x1fff
+#define FCPIO_HOST_SEQ_ID_RANGE_START 0x80
+#define FCPIO_HOST_SEQ_ID_RANGE_END 0xff
+
+/*
+ * Command entry type
+ */
+enum fcpio_type {
+ /*
+ * Initiator request types
+ */
+ FCPIO_ICMND_16 = 0x1,
+ FCPIO_ICMND_32,
+ FCPIO_ICMND_CMPL,
+ FCPIO_ITMF,
+ FCPIO_ITMF_CMPL,
+
+ /*
+ * Target request types
+ */
+ FCPIO_TCMND_16 = 0x11,
+ FCPIO_TCMND_32,
+ FCPIO_TDATA,
+ FCPIO_TXRDY,
+ FCPIO_TRSP,
+ FCPIO_TDRSP_CMPL,
+ FCPIO_TTMF,
+ FCPIO_TTMF_ACK,
+ FCPIO_TABORT,
+ FCPIO_TABORT_CMPL,
+
+ /*
+ * Misc request types
+ */
+ FCPIO_ACK = 0x20,
+ FCPIO_RESET,
+ FCPIO_RESET_CMPL,
+ FCPIO_FLOGI_REG,
+ FCPIO_FLOGI_REG_CMPL,
+ FCPIO_ECHO,
+ FCPIO_ECHO_CMPL,
+ FCPIO_LUNMAP_CHNG,
+ FCPIO_LUNMAP_REQ,
+ FCPIO_LUNMAP_REQ_CMPL,
+ FCPIO_FLOGI_FIP_REG,
+ FCPIO_FLOGI_FIP_REG_CMPL,
+};
+
+/*
+ * Header status codes from the firmware
+ */
+enum fcpio_status {
+ FCPIO_SUCCESS = 0, /* request was successful */
+
+ /*
+ * If a request to the firmware is rejected, the original request
+ * header will be returned with the status set to one of the following:
+ */
+ FCPIO_INVALID_HEADER, /* header contains invalid data */
+ FCPIO_OUT_OF_RESOURCE, /* out of resources to complete request */
+ FCPIO_INVALID_PARAM, /* some parameter in request is invalid */
+ FCPIO_REQ_NOT_SUPPORTED, /* request type is not supported */
+ FCPIO_IO_NOT_FOUND, /* requested I/O was not found */
+
+ /*
+ * Once a request is processed, the firmware will usually return
+ * a cmpl message type. In cases where errors occurred,
+ * the header status field would be filled in with one of the following:
+ */
+ FCPIO_ABORTED = 0x41, /* request was aborted */
+ FCPIO_TIMEOUT, /* request was timed out */
+ FCPIO_SGL_INVALID, /* request was aborted due to sgl error */
+ FCPIO_MSS_INVALID, /* request was aborted due to mss error */
+ FCPIO_DATA_CNT_MISMATCH, /* recv/sent more/less data than exp. */
+ FCPIO_FW_ERR, /* request was terminated due to fw error */
+ FCPIO_ITMF_REJECTED, /* itmf req was rejected by remote node */
+ FCPIO_ITMF_FAILED, /* itmf req was failed by remote node */
+ FCPIO_ITMF_INCORRECT_LUN, /* itmf req targeted incorrect LUN */
+ FCPIO_CMND_REJECTED, /* request was invalid and rejected */
+ FCPIO_NO_PATH_AVAIL, /* no paths to the lun was available */
+ FCPIO_PATH_FAILED, /* i/o sent to current path failed */
+ FCPIO_LUNMAP_CHNG_PEND, /* i/o rejected due to lunmap change */
+};
+
+/*
+ * The header command tag. All host requests will use the "tag" field
+ * to mark commands with a unique tag. When the firmware responds to
+ * a host request, it will copy the tag field into the response.
+ *
+ * The only firmware requests that will use the rx_id/ox_id fields instead
+ * of the tag field will be the target command and target task management
+ * requests. These two requests do not have corresponding host requests
+ * since they come directly from the FC initiator on the network.
+ */
+struct fcpio_tag {
+ union {
+ u32 req_id;
+ struct {
+ u16 rx_id;
+ u16 ox_id;
+ } ex_id;
+ } u;
+};
+
+static inline void
+fcpio_tag_id_enc(struct fcpio_tag *tag, u32 id)
+{
+ tag->u.req_id = id;
+}
+
+static inline void
+fcpio_tag_id_dec(struct fcpio_tag *tag, u32 *id)
+{
+ *id = tag->u.req_id;
+}
+
+static inline void
+fcpio_tag_exid_enc(struct fcpio_tag *tag, u16 ox_id, u16 rx_id)
+{
+ tag->u.ex_id.rx_id = rx_id;
+ tag->u.ex_id.ox_id = ox_id;
+}
+
+static inline void
+fcpio_tag_exid_dec(struct fcpio_tag *tag, u16 *ox_id, u16 *rx_id)
+{
+ *rx_id = tag->u.ex_id.rx_id;
+ *ox_id = tag->u.ex_id.ox_id;
+}
+
+/*
+ * The header for an fcpio request, whether from the firmware or from the
+ * host driver
+ */
+struct fcpio_header {
+ u8 type; /* enum fcpio_type */
+ u8 status; /* header status entry */
+ u16 _resvd; /* reserved */
+ struct fcpio_tag tag; /* header tag */
+};
+
+static inline void
+fcpio_header_enc(struct fcpio_header *hdr,
+ u8 type, u8 status,
+ struct fcpio_tag tag)
+{
+ hdr->type = type;
+ hdr->status = status;
+ hdr->_resvd = 0;
+ hdr->tag = tag;
+}
+
+static inline void
+fcpio_header_dec(struct fcpio_header *hdr,
+ u8 *type, u8 *status,
+ struct fcpio_tag *tag)
+{
+ *type = hdr->type;
+ *status = hdr->status;
+ *tag = hdr->tag;
+}
+
+#define CDB_16 16
+#define CDB_32 32
+#define LUN_ADDRESS 8
+
+/*
+ * fcpio_icmnd_16: host -> firmware request
+ *
+ * used for sending out an initiator SCSI 16-byte command
+ */
+struct fcpio_icmnd_16 {
+ u32 lunmap_id; /* index into lunmap table */
+ u8 special_req_flags; /* special exchange request flags */
+ u8 _resvd0[3]; /* reserved */
+ u32 sgl_cnt; /* scatter-gather list count */
+ u32 sense_len; /* sense buffer length */
+ u64 sgl_addr; /* scatter-gather list addr */
+ u64 sense_addr; /* sense buffer address */
+ u8 crn; /* SCSI Command Reference No. */
+ u8 pri_ta; /* SCSI Priority and Task attribute */
+ u8 _resvd1; /* reserved: should be 0 */
+ u8 flags; /* command flags */
+ u8 scsi_cdb[CDB_16]; /* SCSI Cmnd Descriptor Block */
+ u32 data_len; /* length of data expected */
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 _resvd2; /* reserved */
+ u8 d_id[3]; /* FC vNIC only: Target D_ID */
+ u16 mss; /* FC vNIC only: max burst */
+ u16 _resvd3; /* reserved */
+ u32 r_a_tov; /* FC vNIC only: Res. Alloc Timeout */
+ u32 e_d_tov; /* FC vNIC only: Err Detect Timeout */
+};
+
+/*
+ * Special request flags
+ */
+#define FCPIO_ICMND_SRFLAG_RETRY 0x01 /* Enable Retry handling on exchange */
+
+/*
+ * Priority/Task Attribute settings
+ */
+#define FCPIO_ICMND_PTA_SIMPLE 0 /* simple task attribute */
+#define FCPIO_ICMND_PTA_HEADQ 1 /* head of queue task attribute */
+#define FCPIO_ICMND_PTA_ORDERED 2 /* ordered task attribute */
+#define FCPIO_ICMND_PTA_ACA 4 /* auto contingent allegiance */
+#define FCPIO_ICMND_PRI_SHIFT 3 /* priority field starts in bit 3 */
+
+/*
+ * Command flags
+ */
+#define FCPIO_ICMND_RDDATA 0x02 /* read data */
+#define FCPIO_ICMND_WRDATA 0x01 /* write data */
+
+/*
+ * fcpio_icmnd_32: host -> firmware request
+ *
+ * used for sending out an initiator SCSI 32-byte command
+ */
+struct fcpio_icmnd_32 {
+ u32 lunmap_id; /* index into lunmap table */
+ u8 special_req_flags; /* special exchange request flags */
+ u8 _resvd0[3]; /* reserved */
+ u32 sgl_cnt; /* scatter-gather list count */
+ u32 sense_len; /* sense buffer length */
+ u64 sgl_addr; /* scatter-gather list addr */
+ u64 sense_addr; /* sense buffer address */
+ u8 crn; /* SCSI Command Reference No. */
+ u8 pri_ta; /* SCSI Priority and Task attribute */
+ u8 _resvd1; /* reserved: should be 0 */
+ u8 flags; /* command flags */
+ u8 scsi_cdb[CDB_32]; /* SCSI Cmnd Descriptor Block */
+ u32 data_len; /* length of data expected */
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 _resvd2; /* reserved */
+ u8 d_id[3]; /* FC vNIC only: Target D_ID */
+ u16 mss; /* FC vNIC only: max burst */
+ u16 _resvd3; /* reserved */
+ u32 r_a_tov; /* FC vNIC only: Res. Alloc Timeout */
+ u32 e_d_tov; /* FC vNIC only: Error Detect Timeout */
+};
+
+/*
+ * fcpio_itmf: host -> firmware request
+ *
+ * used for requesting the firmware to abort a request and/or send out
+ * a task management function
+ *
+ * The t_tag field is only needed when the request type is ABT_TASK.
+ */
+struct fcpio_itmf {
+ u32 lunmap_id; /* index into lunmap table */
+ u32 tm_req; /* SCSI Task Management request */
+ u32 t_tag; /* header tag of fcpio to be aborted */
+ u32 _resvd; /* _reserved */
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 _resvd1; /* reserved */
+ u8 d_id[3]; /* FC vNIC only: Target D_ID */
+ u32 r_a_tov; /* FC vNIC only: R_A_TOV in msec */
+ u32 e_d_tov; /* FC vNIC only: E_D_TOV in msec */
+};
+
+/*
+ * Task Management request
+ */
+enum fcpio_itmf_tm_req_type {
+ FCPIO_ITMF_ABT_TASK_TERM = 0x01, /* abort task and terminate */
+ FCPIO_ITMF_ABT_TASK, /* abort task and issue abts */
+ FCPIO_ITMF_ABT_TASK_SET, /* abort task set */
+ FCPIO_ITMF_CLR_TASK_SET, /* clear task set */
+ FCPIO_ITMF_LUN_RESET, /* logical unit reset task mgmt */
+ FCPIO_ITMF_CLR_ACA, /* Clear ACA condition */
+};
+
+/*
+ * fcpio_tdata: host -> firmware request
+ *
+ * used for requesting the firmware to send out a read data transfer for a
+ * target command
+ */
+struct fcpio_tdata {
+ u16 rx_id; /* FC rx_id of target command */
+ u16 flags; /* command flags */
+ u32 rel_offset; /* data sequence relative offset */
+ u32 sgl_cnt; /* scatter-gather list count */
+ u32 data_len; /* length of data expected to send */
+ u64 sgl_addr; /* scatter-gather list address */
+};
+
+/*
+ * Command flags
+ */
+#define FCPIO_TDATA_SCSI_RSP 0x01 /* send a scsi resp. after last frame */
+
+/*
+ * fcpio_txrdy: host -> firmware request
+ *
+ * used for requesting the firmware to send out a write data transfer for a
+ * target command
+ */
+struct fcpio_txrdy {
+ u16 rx_id; /* FC rx_id of target command */
+ u16 _resvd0; /* reserved */
+ u32 rel_offset; /* data sequence relative offset */
+ u32 sgl_cnt; /* scatter-gather list count */
+ u32 data_len; /* length of data expected to send */
+ u64 sgl_addr; /* scatter-gather list address */
+};
+
+/*
+ * fcpio_trsp: host -> firmware request
+ *
+ * used for requesting the firmware to send out a response for a target
+ * command
+ */
+struct fcpio_trsp {
+ u16 rx_id; /* FC rx_id of target command */
+ u16 _resvd0; /* reserved */
+ u32 sense_len; /* sense data buffer length */
+ u64 sense_addr; /* sense data buffer address */
+ u16 _resvd1; /* reserved */
+ u8 flags; /* response request flags */
+ u8 scsi_status; /* SCSI status */
+ u32 residual; /* SCSI data residual value of I/O */
+};
+
+/*
+ * resposnse request flags
+ */
+#define FCPIO_TRSP_RESID_UNDER 0x08 /* residual is valid and is underflow */
+#define FCPIO_TRSP_RESID_OVER 0x04 /* residual is valid and is overflow */
+
+/*
+ * fcpio_ttmf_ack: host -> firmware response
+ *
+ * used by the host to indicate to the firmware it has received and processed
+ * the target tmf request
+ */
+struct fcpio_ttmf_ack {
+ u16 rx_id; /* FC rx_id of target command */
+ u16 _resvd0; /* reserved */
+ u32 tmf_status; /* SCSI task management status */
+};
+
+/*
+ * fcpio_tabort: host -> firmware request
+ *
+ * used by the host to request the firmware to abort a target request that was
+ * received by the firmware
+ */
+struct fcpio_tabort {
+ u16 rx_id; /* rx_id of the target request */
+};
+
+/*
+ * fcpio_reset: host -> firmware request
+ *
+ * used by the host to signal a reset of the driver to the firmware
+ * and to request firmware to clean up all outstanding I/O
+ */
+struct fcpio_reset {
+ u32 _resvd;
+};
+
+enum fcpio_flogi_reg_format_type {
+ FCPIO_FLOGI_REG_DEF_DEST = 0, /* Use the oui | s_id mac format */
+ FCPIO_FLOGI_REG_GW_DEST, /* Use the fixed gateway mac */
+};
+
+/*
+ * fcpio_flogi_reg: host -> firmware request
+ *
+ * fc vnic only
+ * used by the host to notify the firmware of the lif's s_id
+ * and destination mac address format
+ */
+struct fcpio_flogi_reg {
+ u8 format;
+ u8 s_id[3]; /* FC vNIC only: Source S_ID */
+ u8 gateway_mac[ETH_ALEN]; /* Destination gateway mac */
+ u16 _resvd;
+ u32 r_a_tov; /* R_A_TOV in msec */
+ u32 e_d_tov; /* E_D_TOV in msec */
+};
+
+/*
+ * fcpio_echo: host -> firmware request
+ *
+ * sends a heartbeat echo request to the firmware
+ */
+struct fcpio_echo {
+ u32 _resvd;
+};
+
+/*
+ * fcpio_lunmap_req: host -> firmware request
+ *
+ * scsi vnic only
+ * sends a request to retrieve the lunmap table for scsi vnics
+ */
+struct fcpio_lunmap_req {
+ u64 addr; /* address of the buffer */
+ u32 len; /* len of the buffer */
+};
+
+/*
+ * fcpio_flogi_fip_reg: host -> firmware request
+ *
+ * fc vnic only
+ * used by the host to notify the firmware of the lif's s_id
+ * and destination mac address format
+ */
+struct fcpio_flogi_fip_reg {
+ u8 _resvd0;
+ u8 s_id[3]; /* FC vNIC only: Source S_ID */
+ u8 fcf_mac[ETH_ALEN]; /* FCF Target destination mac */
+ u16 _resvd1;
+ u32 r_a_tov; /* R_A_TOV in msec */
+ u32 e_d_tov; /* E_D_TOV in msec */
+ u8 ha_mac[ETH_ALEN]; /* Host adapter source mac */
+ u16 _resvd2;
+};
+
+/*
+ * Basic structure for all fcpio structures that are sent from the host to the
+ * firmware. They are 128 bytes per structure.
+ */
+#define FCPIO_HOST_REQ_LEN 128 /* expected length of host requests */
+
+struct fcpio_host_req {
+ struct fcpio_header hdr;
+
+ union {
+ /*
+ * Defines space needed for request
+ */
+ u8 buf[FCPIO_HOST_REQ_LEN - sizeof(struct fcpio_header)];
+
+ /*
+ * Initiator host requests
+ */
+ struct fcpio_icmnd_16 icmnd_16;
+ struct fcpio_icmnd_32 icmnd_32;
+ struct fcpio_itmf itmf;
+
+ /*
+ * Target host requests
+ */
+ struct fcpio_tdata tdata;
+ struct fcpio_txrdy txrdy;
+ struct fcpio_trsp trsp;
+ struct fcpio_ttmf_ack ttmf_ack;
+ struct fcpio_tabort tabort;
+
+ /*
+ * Misc requests
+ */
+ struct fcpio_reset reset;
+ struct fcpio_flogi_reg flogi_reg;
+ struct fcpio_echo echo;
+ struct fcpio_lunmap_req lunmap_req;
+ struct fcpio_flogi_fip_reg flogi_fip_reg;
+ } u;
+};
+
+/*
+ * fcpio_icmnd_cmpl: firmware -> host response
+ *
+ * used for sending the host a response to an initiator command
+ */
+struct fcpio_icmnd_cmpl {
+ u8 _resvd0[6]; /* reserved */
+ u8 flags; /* response flags */
+ u8 scsi_status; /* SCSI status */
+ u32 residual; /* SCSI data residual length */
+ u32 sense_len; /* SCSI sense length */
+};
+
+/*
+ * response flags
+ */
+#define FCPIO_ICMND_CMPL_RESID_UNDER 0x08 /* resid under and valid */
+#define FCPIO_ICMND_CMPL_RESID_OVER 0x04 /* resid over and valid */
+
+/*
+ * fcpio_itmf_cmpl: firmware -> host response
+ *
+ * used for sending the host a response for a itmf request
+ */
+struct fcpio_itmf_cmpl {
+ u32 _resvd; /* reserved */
+};
+
+/*
+ * fcpio_tcmnd_16: firmware -> host request
+ *
+ * used by the firmware to notify the host of an incoming target SCSI 16-Byte
+ * request
+ */
+struct fcpio_tcmnd_16 {
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 crn; /* SCSI Command Reference No. */
+ u8 pri_ta; /* SCSI Priority and Task attribute */
+ u8 _resvd2; /* reserved: should be 0 */
+ u8 flags; /* command flags */
+ u8 scsi_cdb[CDB_16]; /* SCSI Cmnd Descriptor Block */
+ u32 data_len; /* length of data expected */
+ u8 _resvd1; /* reserved */
+ u8 s_id[3]; /* FC vNIC only: Source S_ID */
+};
+
+/*
+ * Priority/Task Attribute settings
+ */
+#define FCPIO_TCMND_PTA_SIMPLE 0 /* simple task attribute */
+#define FCPIO_TCMND_PTA_HEADQ 1 /* head of queue task attribute */
+#define FCPIO_TCMND_PTA_ORDERED 2 /* ordered task attribute */
+#define FCPIO_TCMND_PTA_ACA 4 /* auto contingent allegiance */
+#define FCPIO_TCMND_PRI_SHIFT 3 /* priority field starts in bit 3 */
+
+/*
+ * Command flags
+ */
+#define FCPIO_TCMND_RDDATA 0x02 /* read data */
+#define FCPIO_TCMND_WRDATA 0x01 /* write data */
+
+/*
+ * fcpio_tcmnd_32: firmware -> host request
+ *
+ * used by the firmware to notify the host of an incoming target SCSI 32-Byte
+ * request
+ */
+struct fcpio_tcmnd_32 {
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 crn; /* SCSI Command Reference No. */
+ u8 pri_ta; /* SCSI Priority and Task attribute */
+ u8 _resvd2; /* reserved: should be 0 */
+ u8 flags; /* command flags */
+ u8 scsi_cdb[CDB_32]; /* SCSI Cmnd Descriptor Block */
+ u32 data_len; /* length of data expected */
+ u8 _resvd0; /* reserved */
+ u8 s_id[3]; /* FC vNIC only: Source S_ID */
+};
+
+/*
+ * fcpio_tdrsp_cmpl: firmware -> host response
+ *
+ * used by the firmware to notify the host of a response to a host target
+ * command
+ */
+struct fcpio_tdrsp_cmpl {
+ u16 rx_id; /* rx_id of the target request */
+ u16 _resvd0; /* reserved */
+};
+
+/*
+ * fcpio_ttmf: firmware -> host request
+ *
+ * used by the firmware to notify the host of an incoming task management
+ * function request
+ */
+struct fcpio_ttmf {
+ u8 _resvd0; /* reserved */
+ u8 s_id[3]; /* FC vNIC only: Source S_ID */
+ u8 lun[LUN_ADDRESS]; /* FC vNIC only: LUN address */
+ u8 crn; /* SCSI Command Reference No. */
+ u8 _resvd2[3]; /* reserved */
+ u32 tmf_type; /* task management request type */
+};
+
+/*
+ * Task Management request
+ */
+#define FCPIO_TTMF_CLR_ACA 0x40 /* Clear ACA condition */
+#define FCPIO_TTMF_LUN_RESET 0x10 /* logical unit reset task mgmt */
+#define FCPIO_TTMF_CLR_TASK_SET 0x04 /* clear task set */
+#define FCPIO_TTMF_ABT_TASK_SET 0x02 /* abort task set */
+#define FCPIO_TTMF_ABT_TASK 0x01 /* abort task */
+
+/*
+ * fcpio_tabort_cmpl: firmware -> host response
+ *
+ * used by the firmware to respond to a host's tabort request
+ */
+struct fcpio_tabort_cmpl {
+ u16 rx_id; /* rx_id of the target request */
+ u16 _resvd0; /* reserved */
+};
+
+/*
+ * fcpio_ack: firmware -> host response
+ *
+ * used by firmware to notify the host of the last work request received
+ */
+struct fcpio_ack {
+ u16 request_out; /* last host entry received */
+ u16 _resvd;
+};
+
+/*
+ * fcpio_reset_cmpl: firmware -> host response
+ *
+ * use by firmware to respond to the host's reset request
+ */
+struct fcpio_reset_cmpl {
+ u16 vnic_id;
+};
+
+/*
+ * fcpio_flogi_reg_cmpl: firmware -> host response
+ *
+ * fc vnic only
+ * response to the fcpio_flogi_reg request
+ */
+struct fcpio_flogi_reg_cmpl {
+ u32 _resvd;
+};
+
+/*
+ * fcpio_echo_cmpl: firmware -> host response
+ *
+ * response to the fcpio_echo request
+ */
+struct fcpio_echo_cmpl {
+ u32 _resvd;
+};
+
+/*
+ * fcpio_lunmap_chng: firmware -> host notification
+ *
+ * scsi vnic only
+ * notifies the host that the lunmap tables have changed
+ */
+struct fcpio_lunmap_chng {
+ u32 _resvd;
+};
+
+/*
+ * fcpio_lunmap_req_cmpl: firmware -> host response
+ *
+ * scsi vnic only
+ * response for lunmap table request from the host
+ */
+struct fcpio_lunmap_req_cmpl {
+ u32 _resvd;
+};
+
+/*
+ * Basic structure for all fcpio structures that are sent from the firmware to
+ * the host. They are 64 bytes per structure.
+ */
+#define FCPIO_FW_REQ_LEN 64 /* expected length of fw requests */
+struct fcpio_fw_req {
+ struct fcpio_header hdr;
+
+ union {
+ /*
+ * Defines space needed for request
+ */
+ u8 buf[FCPIO_FW_REQ_LEN - sizeof(struct fcpio_header)];
+
+ /*
+ * Initiator firmware responses
+ */
+ struct fcpio_icmnd_cmpl icmnd_cmpl;
+ struct fcpio_itmf_cmpl itmf_cmpl;
+
+ /*
+ * Target firmware new requests
+ */
+ struct fcpio_tcmnd_16 tcmnd_16;
+ struct fcpio_tcmnd_32 tcmnd_32;
+
+ /*
+ * Target firmware responses
+ */
+ struct fcpio_tdrsp_cmpl tdrsp_cmpl;
+ struct fcpio_ttmf ttmf;
+ struct fcpio_tabort_cmpl tabort_cmpl;
+
+ /*
+ * Firmware response to work received
+ */
+ struct fcpio_ack ack;
+
+ /*
+ * Misc requests
+ */
+ struct fcpio_reset_cmpl reset_cmpl;
+ struct fcpio_flogi_reg_cmpl flogi_reg_cmpl;
+ struct fcpio_echo_cmpl echo_cmpl;
+ struct fcpio_lunmap_chng lunmap_chng;
+ struct fcpio_lunmap_req_cmpl lunmap_req_cmpl;
+ } u;
+};
+
+/*
+ * Access routines to encode and decode the color bit, which is the most
+ * significant bit of the MSB of the structure
+ */
+static inline void fcpio_color_enc(struct fcpio_fw_req *fw_req, u8 color)
+{
+ u8 *c = ((u8 *) fw_req) + sizeof(struct fcpio_fw_req) - 1;
+
+ if (color)
+ *c |= 0x80;
+ else
+ *c &= ~0x80;
+}
+
+static inline void fcpio_color_dec(struct fcpio_fw_req *fw_req, u8 *color)
+{
+ u8 *c = ((u8 *) fw_req) + sizeof(struct fcpio_fw_req) - 1;
+
+ *color = *c >> 7;
+
+ /*
+ * Make sure color bit is read from desc *before* other fields
+ * are read from desc. Hardware guarantees color bit is last
+ * bit (byte) written. Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which would potentially
+ * result in reading stale values.
+ */
+
+ rmb();
+
+}
+
+/*
+ * Lunmap table entry for scsi vnics
+ */
+#define FCPIO_LUNMAP_TABLE_SIZE 256
+#define FCPIO_FLAGS_LUNMAP_VALID 0x80
+#define FCPIO_FLAGS_BOOT 0x01
+struct fcpio_lunmap_entry {
+ u8 bus;
+ u8 target;
+ u8 lun;
+ u8 path_cnt;
+ u16 flags;
+ u16 update_cnt;
+};
+
+struct fcpio_lunmap_tbl {
+ u32 update_cnt;
+ struct fcpio_lunmap_entry lunmaps[FCPIO_LUNMAP_TABLE_SIZE];
+};
+
+#endif /* _FCPIO_H_ */
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
new file mode 100644
index 000000000000..e4c0a3d7d87b
--- /dev/null
+++ b/drivers/scsi/fnic/fnic.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FNIC_H_
+#define _FNIC_H_
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <scsi/libfc.h>
+#include "fnic_io.h"
+#include "fnic_res.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_rq.h"
+#include "vnic_cq.h"
+#include "vnic_wq_copy.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "vnic_scsi.h"
+
+#define DRV_NAME "fnic"
+#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
+#define DRV_VERSION "1.0.0.1121"
+#define PFX DRV_NAME ": "
+#define DFX DRV_NAME "%d: "
+
+#define DESC_CLEAN_LOW_WATERMARK 8
+#define FNIC_MAX_IO_REQ 2048 /* scsi_cmnd tag map entries */
+#define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */
+#define FNIC_DFLT_QUEUE_DEPTH 32
+#define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */
+
+/*
+ * Tag bits used for special requests.
+ */
+#define BIT(nr) (1UL << (nr))
+#define FNIC_TAG_ABORT BIT(30) /* tag bit indicating abort */
+#define FNIC_TAG_DEV_RST BIT(29) /* indicates device reset */
+#define FNIC_TAG_MASK (BIT(24) - 1) /* mask for lookup */
+#define FNIC_NO_TAG -1
+
+/*
+ * Usage of the scsi_cmnd scratchpad.
+ * These fields are locked by the hashed io_req_lock.
+ */
+#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
+#define CMD_STATE(Cmnd) ((Cmnd)->SCp.phase)
+#define CMD_ABTS_STATUS(Cmnd) ((Cmnd)->SCp.Message)
+#define CMD_LR_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
+#define CMD_TAG(Cmnd) ((Cmnd)->SCp.sent_command)
+
+#define FCPIO_INVALID_CODE 0x100 /* hdr_status value unused by firmware */
+
+#define FNIC_LUN_RESET_TIMEOUT 10000 /* mSec */
+#define FNIC_HOST_RESET_TIMEOUT 10000 /* mSec */
+#define FNIC_RMDEVICE_TIMEOUT 1000 /* mSec */
+#define FNIC_HOST_RESET_SETTLE_TIME 30 /* Sec */
+
+#define FNIC_MAX_FCP_TARGET 256
+
+extern unsigned int fnic_log_level;
+
+#define FNIC_MAIN_LOGGING 0x01
+#define FNIC_FCS_LOGGING 0x02
+#define FNIC_SCSI_LOGGING 0x04
+#define FNIC_ISR_LOGGING 0x08
+
+#define FNIC_CHECK_LOGGING(LEVEL, CMD) \
+do { \
+ if (unlikely(fnic_log_level & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+} while (0)
+
+#define FNIC_MAIN_DBG(kern_level, host, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \
+ shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_FCS_DBG(kern_level, host, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \
+ shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_SCSI_DBG(kern_level, host, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \
+ shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_ISR_DBG(kern_level, host, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \
+ shost_printk(kern_level, host, fmt, ##args);)
+
+extern const char *fnic_state_str[];
+
+enum fnic_intx_intr_index {
+ FNIC_INTX_WQ_RQ_COPYWQ,
+ FNIC_INTX_ERR,
+ FNIC_INTX_NOTIFY,
+ FNIC_INTX_INTR_MAX,
+};
+
+enum fnic_msix_intr_index {
+ FNIC_MSIX_RQ,
+ FNIC_MSIX_WQ,
+ FNIC_MSIX_WQ_COPY,
+ FNIC_MSIX_ERR_NOTIFY,
+ FNIC_MSIX_INTR_MAX,
+};
+
+struct fnic_msix_entry {
+ int requested;
+ char devname[IFNAMSIZ];
+ irqreturn_t (*isr)(int, void *);
+ void *devid;
+};
+
+enum fnic_state {
+ FNIC_IN_FC_MODE = 0,
+ FNIC_IN_FC_TRANS_ETH_MODE,
+ FNIC_IN_ETH_MODE,
+ FNIC_IN_ETH_TRANS_FC_MODE,
+};
+
+#define FNIC_WQ_COPY_MAX 1
+#define FNIC_WQ_MAX 1
+#define FNIC_RQ_MAX 1
+#define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
+
+struct mempool;
+
+/* Per-instance private data structure */
+struct fnic {
+ struct fc_lport *lport;
+ struct vnic_dev_bar bar0;
+
+ struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX];
+ struct fnic_msix_entry msix[FNIC_MSIX_INTR_MAX];
+
+ struct vnic_stats *stats;
+ unsigned long stats_time; /* time of stats update */
+ struct vnic_nic_cfg *nic_cfg;
+ char name[IFNAMSIZ];
+ struct timer_list notify_timer; /* used for MSI interrupts */
+
+ unsigned int err_intr_offset;
+ unsigned int link_intr_offset;
+
+ unsigned int wq_count;
+ unsigned int cq_count;
+
+ u32 fcoui_mode:1; /* use fcoui address*/
+ u32 vlan_hw_insert:1; /* let hw insert the tag */
+ u32 in_remove:1; /* fnic device in removal */
+ u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */
+
+ struct completion *remove_wait; /* device remove thread blocks */
+
+ struct fc_frame *flogi;
+ struct fc_frame *flogi_resp;
+ u16 flogi_oxid;
+ unsigned long s_id;
+ enum fnic_state state;
+ spinlock_t fnic_lock;
+
+ u16 vlan_id; /* VLAN tag including priority */
+ u8 mac_addr[ETH_ALEN];
+ u8 dest_addr[ETH_ALEN];
+ u8 data_src_addr[ETH_ALEN];
+ u64 fcp_input_bytes; /* internal statistic */
+ u64 fcp_output_bytes; /* internal statistic */
+ u32 link_down_cnt;
+ int link_status;
+
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct vnic_fc_config config;
+ struct vnic_dev *vdev;
+ unsigned int raw_wq_count;
+ unsigned int wq_copy_count;
+ unsigned int rq_count;
+ int fw_ack_index[FNIC_WQ_COPY_MAX];
+ unsigned short fw_ack_recd[FNIC_WQ_COPY_MAX];
+ unsigned short wq_copy_desc_low[FNIC_WQ_COPY_MAX];
+ unsigned int intr_count;
+ u32 __iomem *legacy_pba;
+ struct fnic_host_tag *tags;
+ mempool_t *io_req_pool;
+ mempool_t *io_sgl_pool[FNIC_SGL_NUM_CACHES];
+ spinlock_t io_req_lock[FNIC_IO_LOCKS]; /* locks for scsi cmnds */
+
+ struct work_struct link_work;
+ struct work_struct frame_work;
+ struct sk_buff_head frame_queue;
+
+ /* copy work queue cache line section */
+ ____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
+ /* completion queue cache line section */
+ ____cacheline_aligned struct vnic_cq cq[FNIC_CQ_MAX];
+
+ spinlock_t wq_copy_lock[FNIC_WQ_COPY_MAX];
+
+ /* work queue cache line section */
+ ____cacheline_aligned struct vnic_wq wq[FNIC_WQ_MAX];
+ spinlock_t wq_lock[FNIC_WQ_MAX];
+
+ /* receive queue cache line section */
+ ____cacheline_aligned struct vnic_rq rq[FNIC_RQ_MAX];
+
+ /* interrupt resource cache line section */
+ ____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
+};
+
+extern struct workqueue_struct *fnic_event_queue;
+extern struct device_attribute *fnic_attrs[];
+
+void fnic_clear_intr_mode(struct fnic *fnic);
+int fnic_set_intr_mode(struct fnic *fnic);
+void fnic_free_intr(struct fnic *fnic);
+int fnic_request_intr(struct fnic *fnic);
+
+int fnic_send(struct fc_lport *, struct fc_frame *);
+void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf);
+void fnic_handle_frame(struct work_struct *work);
+void fnic_handle_link(struct work_struct *work);
+int fnic_rq_cmpl_handler(struct fnic *fnic, int);
+int fnic_alloc_rq_frame(struct vnic_rq *rq);
+void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
+int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+
+int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
+int fnic_abort_cmd(struct scsi_cmnd *);
+int fnic_device_reset(struct scsi_cmnd *);
+int fnic_host_reset(struct scsi_cmnd *);
+int fnic_reset(struct Scsi_Host *);
+void fnic_scsi_cleanup(struct fc_lport *);
+void fnic_scsi_abort_io(struct fc_lport *);
+void fnic_empty_scsi_cleanup(struct fc_lport *);
+void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
+int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
+int fnic_wq_cmpl_handler(struct fnic *fnic, int);
+int fnic_flogi_reg_handler(struct fnic *fnic);
+void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
+ struct fcpio_host_req *desc);
+int fnic_fw_reset_handler(struct fnic *fnic);
+void fnic_terminate_rport_io(struct fc_rport *);
+const char *fnic_state_to_str(unsigned int state);
+
+void fnic_log_q_error(struct fnic *fnic);
+void fnic_handle_link_event(struct fnic *fnic);
+
+#endif /* _FNIC_H_ */
diff --git a/drivers/scsi/fnic/fnic_attrs.c b/drivers/scsi/fnic/fnic_attrs.c
new file mode 100644
index 000000000000..aea0c3becfd4
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_attrs.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/string.h>
+#include <linux/device.h>
+#include <scsi/scsi_host.h>
+#include "fnic.h"
+
+static ssize_t fnic_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fc_lport *lp = shost_priv(class_to_shost(dev));
+ struct fnic *fnic = lport_priv(lp);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", fnic_state_str[fnic->state]);
+}
+
+static ssize_t fnic_show_drv_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", DRV_VERSION);
+}
+
+static ssize_t fnic_show_link_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fc_lport *lp = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", (lp->link_up)
+ ? "Link Up" : "Link Down");
+}
+
+static DEVICE_ATTR(fnic_state, S_IRUGO, fnic_show_state, NULL);
+static DEVICE_ATTR(drv_version, S_IRUGO, fnic_show_drv_version, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO, fnic_show_link_state, NULL);
+
+struct device_attribute *fnic_attrs[] = {
+ &dev_attr_fnic_state,
+ &dev_attr_drv_version,
+ &dev_attr_link_state,
+ NULL,
+};
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
new file mode 100644
index 000000000000..07e6eedb83ce
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/workqueue.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fcoe.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfc.h>
+#include "fnic_io.h"
+#include "fnic.h"
+#include "cq_enet_desc.h"
+#include "cq_exch_desc.h"
+
+struct workqueue_struct *fnic_event_queue;
+
+void fnic_handle_link(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, link_work);
+ unsigned long flags;
+ int old_link_status;
+ u32 old_link_down_cnt;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ old_link_down_cnt = fnic->link_down_cnt;
+ old_link_status = fnic->link_status;
+ fnic->link_status = vnic_dev_link_status(fnic->vdev);
+ fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
+
+ if (old_link_status == fnic->link_status) {
+ if (!fnic->link_status)
+ /* DOWN -> DOWN */
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ else {
+ if (old_link_down_cnt != fnic->link_down_cnt) {
+ /* UP -> DOWN -> UP */
+ fnic->lport->host_stats.link_failure_count++;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "link down\n");
+ fc_linkdown(fnic->lport);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "link up\n");
+ fc_linkup(fnic->lport);
+ } else
+ /* UP -> UP */
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ }
+ } else if (fnic->link_status) {
+ /* DOWN -> UP */
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
+ fc_linkup(fnic->lport);
+ } else {
+ /* UP -> DOWN */
+ fnic->lport->host_stats.link_failure_count++;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
+ fc_linkdown(fnic->lport);
+ }
+
+}
+
+/*
+ * This function passes incoming fabric frames to libFC
+ */
+void fnic_handle_frame(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, frame_work);
+ struct fc_lport *lp = fnic->lport;
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct fc_frame *fp;
+
+ while ((skb = skb_dequeue(&fnic->frame_queue))) {
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ dev_kfree_skb(skb);
+ return;
+ }
+ fp = (struct fc_frame *)skb;
+ /* if Flogi resp frame, register the address */
+ if (fr_flags(fp)) {
+ vnic_dev_add_addr(fnic->vdev,
+ fnic->data_src_addr);
+ fr_flags(fp) = 0;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ fc_exch_recv(lp, lp->emp, fp);
+ }
+
+}
+
+static inline void fnic_import_rq_fc_frame(struct sk_buff *skb,
+ u32 len, u8 sof, u8 eof)
+{
+ struct fc_frame *fp = (struct fc_frame *)skb;
+
+ skb_trim(skb, len);
+ fr_eof(fp) = eof;
+ fr_sof(fp) = sof;
+}
+
+
+static inline int fnic_import_rq_eth_pkt(struct sk_buff *skb, u32 len)
+{
+ struct fc_frame *fp;
+ struct ethhdr *eh;
+ struct vlan_ethhdr *vh;
+ struct fcoe_hdr *fcoe_hdr;
+ struct fcoe_crc_eof *ft;
+ u32 transport_len = 0;
+
+ eh = (struct ethhdr *)skb->data;
+ vh = (struct vlan_ethhdr *)skb->data;
+ if (vh->h_vlan_proto == htons(ETH_P_8021Q) &&
+ vh->h_vlan_encapsulated_proto == htons(ETH_P_FCOE)) {
+ skb_pull(skb, sizeof(struct vlan_ethhdr));
+ transport_len += sizeof(struct vlan_ethhdr);
+ } else if (eh->h_proto == htons(ETH_P_FCOE)) {
+ transport_len += sizeof(struct ethhdr);
+ skb_pull(skb, sizeof(struct ethhdr));
+ } else
+ return -1;
+
+ fcoe_hdr = (struct fcoe_hdr *)skb->data;
+ if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
+ return -1;
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_sof(fp) = fcoe_hdr->fcoe_sof;
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ transport_len += sizeof(struct fcoe_hdr);
+
+ ft = (struct fcoe_crc_eof *)(skb->data + len -
+ transport_len - sizeof(*ft));
+ fr_eof(fp) = ft->fcoe_eof;
+ skb_trim(skb, len - transport_len - sizeof(*ft));
+ return 0;
+}
+
+static inline int fnic_handle_flogi_resp(struct fnic *fnic,
+ struct fc_frame *fp)
+{
+ u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
+ struct ethhdr *eth_hdr;
+ struct fc_frame_header *fh;
+ int ret = 0;
+ unsigned long flags;
+ struct fc_frame *old_flogi_resp = NULL;
+
+ fh = (struct fc_frame_header *)fr_hdr(fp);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ if (fnic->state == FNIC_IN_ETH_MODE) {
+
+ /*
+ * Check if oxid matches on taking the lock. A new Flogi
+ * issued by libFC might have changed the fnic cached oxid
+ */
+ if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Flogi response oxid not"
+ " matching cached oxid, dropping frame"
+ "\n");
+ ret = -1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ dev_kfree_skb_irq(fp_skb(fp));
+ goto handle_flogi_resp_end;
+ }
+
+ /* Drop older cached flogi response frame, cache this frame */
+ old_flogi_resp = fnic->flogi_resp;
+ fnic->flogi_resp = fp;
+ fnic->flogi_oxid = FC_XID_UNKNOWN;
+
+ /*
+ * this frame is part of flogi get the src mac addr from this
+ * frame if the src mac is fcoui based then we mark the
+ * address mode flag to use fcoui base for dst mac addr
+ * otherwise we have to store the fcoe gateway addr
+ */
+ eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp));
+ memcpy(mac, eth_hdr->h_source, ETH_ALEN);
+
+ if (ntoh24(mac) == FC_FCOE_OUI)
+ fnic->fcoui_mode = 1;
+ else {
+ fnic->fcoui_mode = 0;
+ memcpy(fnic->dest_addr, mac, ETH_ALEN);
+ }
+
+ /*
+ * Except for Flogi frame, all outbound frames from us have the
+ * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
+ * the vnic MAC address as the Eth Src address
+ */
+ fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id);
+
+ /* We get our s_id from the d_id of the flogi resp frame */
+ fnic->s_id = ntoh24(fh->fh_d_id);
+
+ /* Change state to reflect transition from Eth to FC mode */
+ fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
+
+ } else {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Unexpected fnic state %s while"
+ " processing flogi resp\n",
+ fnic_state_to_str(fnic->state));
+ ret = -1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ dev_kfree_skb_irq(fp_skb(fp));
+ goto handle_flogi_resp_end;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ /* Drop older cached frame */
+ if (old_flogi_resp)
+ dev_kfree_skb_irq(fp_skb(old_flogi_resp));
+
+ /*
+ * send flogi reg request to firmware, this will put the fnic in
+ * in FC mode
+ */
+ ret = fnic_flogi_reg_handler(fnic);
+
+ if (ret < 0) {
+ int free_fp = 1;
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ /*
+ * free the frame is some other thread is not
+ * pointing to it
+ */
+ if (fnic->flogi_resp != fp)
+ free_fp = 0;
+ else
+ fnic->flogi_resp = NULL;
+
+ if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
+ fnic->state = FNIC_IN_ETH_MODE;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (free_fp)
+ dev_kfree_skb_irq(fp_skb(fp));
+ }
+
+ handle_flogi_resp_end:
+ return ret;
+}
+
+/* Returns 1 for a response that matches cached flogi oxid */
+static inline int is_matching_flogi_resp_frame(struct fnic *fnic,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ int ret = 0;
+ u32 f_ctl;
+
+ fh = fc_frame_header_get(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+
+ if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) &&
+ fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+ (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX &&
+ fh->fh_type == FC_TYPE_ELS)
+ ret = 1;
+
+ return ret;
+}
+
+static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
+ *cq_desc, struct vnic_rq_buf *buf,
+ int skipped __attribute__((unused)),
+ void *opaque)
+{
+ struct fnic *fnic = vnic_dev_priv(rq->vdev);
+ struct sk_buff *skb;
+ struct fc_frame *fp;
+ unsigned int eth_hdrs_stripped;
+ u8 type, color, eop, sop, ingress_port, vlan_stripped;
+ u8 fcoe = 0, fcoe_sof, fcoe_eof;
+ u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0;
+ u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok;
+ u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc;
+ u8 fcs_ok = 1, packet_error = 0;
+ u16 q_number, completed_index, bytes_written = 0, vlan, checksum;
+ u32 rss_hash;
+ u16 exchange_id, tmpl;
+ u8 sof = 0;
+ u8 eof = 0;
+ u32 fcp_bytes_written = 0;
+ unsigned long flags;
+
+ pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
+ PCI_DMA_FROMDEVICE);
+ skb = buf->os_buf;
+ buf->os_buf = NULL;
+
+ cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
+ if (type == CQ_DESC_TYPE_RQ_FCP) {
+ cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc,
+ &type, &color, &q_number, &completed_index,
+ &eop, &sop, &fcoe_fc_crc_ok, &exchange_id,
+ &tmpl, &fcp_bytes_written, &sof, &eof,
+ &ingress_port, &packet_error,
+ &fcoe_enc_error, &fcs_ok, &vlan_stripped,
+ &vlan);
+ eth_hdrs_stripped = 1;
+
+ } else if (type == CQ_DESC_TYPE_RQ_ENET) {
+ cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
+ &type, &color, &q_number, &completed_index,
+ &ingress_port, &fcoe, &eop, &sop,
+ &rss_type, &csum_not_calc, &rss_hash,
+ &bytes_written, &packet_error,
+ &vlan_stripped, &vlan, &checksum,
+ &fcoe_sof, &fcoe_fc_crc_ok,
+ &fcoe_enc_error, &fcoe_eof,
+ &tcp_udp_csum_ok, &udp, &tcp,
+ &ipv4_csum_ok, &ipv6, &ipv4,
+ &ipv4_fragment, &fcs_ok);
+ eth_hdrs_stripped = 0;
+
+ } else {
+ /* wrong CQ type*/
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "fnic rq_cmpl wrong cq type x%x\n", type);
+ goto drop;
+ }
+
+ if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "fnic rq_cmpl fcoe x%x fcsok x%x"
+ " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
+ " x%x\n",
+ fcoe, fcs_ok, packet_error,
+ fcoe_fc_crc_ok, fcoe_enc_error);
+ goto drop;
+ }
+
+ if (eth_hdrs_stripped)
+ fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof);
+ else if (fnic_import_rq_eth_pkt(skb, bytes_written))
+ goto drop;
+
+ fp = (struct fc_frame *)skb;
+
+ /*
+ * If frame is an ELS response that matches the cached FLOGI OX_ID,
+ * and is accept, issue flogi_reg_request copy wq request to firmware
+ * to register the S_ID and determine whether FC_OUI mode or GW mode.
+ */
+ if (is_matching_flogi_resp_frame(fnic, fp)) {
+ if (!eth_hdrs_stripped) {
+ if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
+ fnic_handle_flogi_resp(fnic, fp);
+ return;
+ }
+ /*
+ * Recd. Flogi reject. No point registering
+ * with fw, but forward to libFC
+ */
+ goto forward;
+ }
+ goto drop;
+ }
+ if (!eth_hdrs_stripped)
+ goto drop;
+
+forward:
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto drop;
+ }
+ /* Use fr_flags to indicate whether succ. flogi resp or not */
+ fr_flags(fp) = 0;
+ fr_dev(fp) = fnic->lport;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ skb_queue_tail(&fnic->frame_queue, skb);
+ queue_work(fnic_event_queue, &fnic->frame_work);
+
+ return;
+drop:
+ dev_kfree_skb_irq(skb);
+}
+
+static int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev,
+ struct cq_desc *cq_desc, u8 type,
+ u16 q_number, u16 completed_index,
+ void *opaque)
+{
+ struct fnic *fnic = vnic_dev_priv(vdev);
+
+ vnic_rq_service(&fnic->rq[q_number], cq_desc, completed_index,
+ VNIC_RQ_RETURN_DESC, fnic_rq_cmpl_frame_recv,
+ NULL);
+ return 0;
+}
+
+int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
+{
+ unsigned int tot_rq_work_done = 0, cur_work_done;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < fnic->rq_count; i++) {
+ cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do,
+ fnic_rq_cmpl_handler_cont,
+ NULL);
+ if (cur_work_done) {
+ err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
+ if (err)
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "fnic_alloc_rq_frame cant alloc"
+ " frame\n");
+ }
+ tot_rq_work_done += cur_work_done;
+ }
+
+ return tot_rq_work_done;
+}
+
+/*
+ * This function is called once at init time to allocate and fill RQ
+ * buffers. Subsequently, it is called in the interrupt context after RQ
+ * buffer processing to replenish the buffers in the RQ
+ */
+int fnic_alloc_rq_frame(struct vnic_rq *rq)
+{
+ struct fnic *fnic = vnic_dev_priv(rq->vdev);
+ struct sk_buff *skb;
+ u16 len;
+ dma_addr_t pa;
+
+ len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Unable to allocate RQ sk_buff\n");
+ return -ENOMEM;
+ }
+ skb_reset_mac_header(skb);
+ skb_reset_transport_header(skb);
+ skb_reset_network_header(skb);
+ skb_put(skb, len);
+ pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE);
+ fnic_queue_rq_desc(rq, skb, pa, len);
+ return 0;
+}
+
+void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
+{
+ struct fc_frame *fp = buf->os_buf;
+ struct fnic *fnic = vnic_dev_priv(rq->vdev);
+
+ pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
+ PCI_DMA_FROMDEVICE);
+
+ dev_kfree_skb(fp_skb(fp));
+ buf->os_buf = NULL;
+}
+
+static inline int is_flogi_frame(struct fc_frame_header *fh)
+{
+ return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI;
+}
+
+int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
+{
+ struct vnic_wq *wq = &fnic->wq[0];
+ struct sk_buff *skb;
+ dma_addr_t pa;
+ struct ethhdr *eth_hdr;
+ struct vlan_ethhdr *vlan_hdr;
+ struct fcoe_hdr *fcoe_hdr;
+ struct fc_frame_header *fh;
+ u32 tot_len, eth_hdr_len;
+ int ret = 0;
+ unsigned long flags;
+
+ fh = fc_frame_header_get(fp);
+ skb = fp_skb(fp);
+
+ if (!fnic->vlan_hw_insert) {
+ eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
+ vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len);
+ eth_hdr = (struct ethhdr *)vlan_hdr;
+ vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+ vlan_hdr->h_vlan_encapsulated_proto = htons(ETH_P_FCOE);
+ vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
+ fcoe_hdr = (struct fcoe_hdr *)(vlan_hdr + 1);
+ } else {
+ eth_hdr_len = sizeof(*eth_hdr) + sizeof(*fcoe_hdr);
+ eth_hdr = (struct ethhdr *)skb_push(skb, eth_hdr_len);
+ eth_hdr->h_proto = htons(ETH_P_FCOE);
+ fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
+ }
+
+ if (is_flogi_frame(fh)) {
+ fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
+ memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN);
+ } else {
+ if (fnic->fcoui_mode)
+ fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
+ else
+ memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN);
+ memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
+ }
+
+ tot_len = skb->len;
+ BUG_ON(tot_len % 4);
+
+ memset(fcoe_hdr, 0, sizeof(*fcoe_hdr));
+ fcoe_hdr->fcoe_sof = fr_sof(fp);
+ if (FC_FCOE_VER)
+ FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER);
+
+ pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE);
+
+ spin_lock_irqsave(&fnic->wq_lock[0], flags);
+
+ if (!vnic_wq_desc_avail(wq)) {
+ pci_unmap_single(fnic->pdev, pa,
+ tot_len, PCI_DMA_TODEVICE);
+ ret = -1;
+ goto fnic_send_frame_end;
+ }
+
+ fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp),
+ fnic->vlan_hw_insert, fnic->vlan_id, 1, 1, 1);
+fnic_send_frame_end:
+ spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
+
+ if (ret)
+ dev_kfree_skb_any(fp_skb(fp));
+
+ return ret;
+}
+
+/*
+ * fnic_send
+ * Routine to send a raw frame
+ */
+int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
+{
+ struct fnic *fnic = lport_priv(lp);
+ struct fc_frame_header *fh;
+ int ret = 0;
+ enum fnic_state old_state;
+ unsigned long flags;
+ struct fc_frame *old_flogi = NULL;
+ struct fc_frame *old_flogi_resp = NULL;
+
+ if (fnic->in_remove) {
+ dev_kfree_skb(fp_skb(fp));
+ ret = -1;
+ goto fnic_send_end;
+ }
+
+ fh = fc_frame_header_get(fp);
+ /* if not an Flogi frame, send it out, this is the common case */
+ if (!is_flogi_frame(fh))
+ return fnic_send_frame(fnic, fp);
+
+ /* Flogi frame, now enter the state machine */
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+again:
+ /* Get any old cached frames, free them after dropping lock */
+ old_flogi = fnic->flogi;
+ fnic->flogi = NULL;
+ old_flogi_resp = fnic->flogi_resp;
+ fnic->flogi_resp = NULL;
+
+ fnic->flogi_oxid = FC_XID_UNKNOWN;
+
+ old_state = fnic->state;
+ switch (old_state) {
+ case FNIC_IN_FC_MODE:
+ case FNIC_IN_ETH_TRANS_FC_MODE:
+ default:
+ fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
+ vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (old_flogi) {
+ dev_kfree_skb(fp_skb(old_flogi));
+ old_flogi = NULL;
+ }
+ if (old_flogi_resp) {
+ dev_kfree_skb(fp_skb(old_flogi_resp));
+ old_flogi_resp = NULL;
+ }
+
+ ret = fnic_fw_reset_handler(fnic);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
+ goto again;
+ if (ret) {
+ fnic->state = old_state;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ dev_kfree_skb(fp_skb(fp));
+ goto fnic_send_end;
+ }
+ old_flogi = fnic->flogi;
+ fnic->flogi = fp;
+ fnic->flogi_oxid = ntohs(fh->fh_ox_id);
+ old_flogi_resp = fnic->flogi_resp;
+ fnic->flogi_resp = NULL;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ break;
+
+ case FNIC_IN_FC_TRANS_ETH_MODE:
+ /*
+ * A reset is pending with the firmware. Store the flogi
+ * and its oxid. The transition out of this state happens
+ * only when Firmware completes the reset, either with
+ * success or failed. If success, transition to
+ * FNIC_IN_ETH_MODE, if fail, then transition to
+ * FNIC_IN_FC_MODE
+ */
+ fnic->flogi = fp;
+ fnic->flogi_oxid = ntohs(fh->fh_ox_id);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ break;
+
+ case FNIC_IN_ETH_MODE:
+ /*
+ * The fw/hw is already in eth mode. Store the oxid,
+ * and send the flogi frame out. The transition out of this
+ * state happens only we receive flogi response from the
+ * network, and the oxid matches the cached oxid when the
+ * flogi frame was sent out. If they match, then we issue
+ * a flogi_reg request and transition to state
+ * FNIC_IN_ETH_TRANS_FC_MODE
+ */
+ fnic->flogi_oxid = ntohs(fh->fh_ox_id);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ ret = fnic_send_frame(fnic, fp);
+ break;
+ }
+
+fnic_send_end:
+ if (old_flogi)
+ dev_kfree_skb(fp_skb(old_flogi));
+ if (old_flogi_resp)
+ dev_kfree_skb(fp_skb(old_flogi_resp));
+ return ret;
+}
+
+static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
+ struct cq_desc *cq_desc,
+ struct vnic_wq_buf *buf, void *opaque)
+{
+ struct sk_buff *skb = buf->os_buf;
+ struct fc_frame *fp = (struct fc_frame *)skb;
+ struct fnic *fnic = vnic_dev_priv(wq->vdev);
+
+ pci_unmap_single(fnic->pdev, buf->dma_addr,
+ buf->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(fp_skb(fp));
+ buf->os_buf = NULL;
+}
+
+static int fnic_wq_cmpl_handler_cont(struct vnic_dev *vdev,
+ struct cq_desc *cq_desc, u8 type,
+ u16 q_number, u16 completed_index,
+ void *opaque)
+{
+ struct fnic *fnic = vnic_dev_priv(vdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->wq_lock[q_number], flags);
+ vnic_wq_service(&fnic->wq[q_number], cq_desc, completed_index,
+ fnic_wq_complete_frame_send, NULL);
+ spin_unlock_irqrestore(&fnic->wq_lock[q_number], flags);
+
+ return 0;
+}
+
+int fnic_wq_cmpl_handler(struct fnic *fnic, int work_to_do)
+{
+ unsigned int wq_work_done = 0;
+ unsigned int i;
+
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ wq_work_done += vnic_cq_service(&fnic->cq[fnic->rq_count+i],
+ work_to_do,
+ fnic_wq_cmpl_handler_cont,
+ NULL);
+ }
+
+ return wq_work_done;
+}
+
+
+void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
+{
+ struct fc_frame *fp = buf->os_buf;
+ struct fnic *fnic = vnic_dev_priv(wq->vdev);
+
+ pci_unmap_single(fnic->pdev, buf->dma_addr,
+ buf->len, PCI_DMA_TODEVICE);
+
+ dev_kfree_skb(fp_skb(fp));
+ buf->os_buf = NULL;
+}
diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h
new file mode 100644
index 000000000000..f0b896988cd5
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_io.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FNIC_IO_H_
+#define _FNIC_IO_H_
+
+#include <scsi/fc/fc_fcp.h>
+
+#define FNIC_DFLT_SG_DESC_CNT 32
+#define FNIC_MAX_SG_DESC_CNT 1024 /* Maximum descriptors per sgl */
+#define FNIC_SG_DESC_ALIGN 16 /* Descriptor address alignment */
+
+struct host_sg_desc {
+ __le64 addr;
+ __le32 len;
+ u32 _resvd;
+};
+
+struct fnic_dflt_sgl_list {
+ struct host_sg_desc sg_desc[FNIC_DFLT_SG_DESC_CNT];
+};
+
+struct fnic_sgl_list {
+ struct host_sg_desc sg_desc[FNIC_MAX_SG_DESC_CNT];
+};
+
+enum fnic_sgl_list_type {
+ FNIC_SGL_CACHE_DFLT = 0, /* cache with default size sgl */
+ FNIC_SGL_CACHE_MAX, /* cache with max size sgl */
+ FNIC_SGL_NUM_CACHES /* number of sgl caches */
+};
+
+enum fnic_ioreq_state {
+ FNIC_IOREQ_CMD_PENDING = 0,
+ FNIC_IOREQ_ABTS_PENDING,
+ FNIC_IOREQ_ABTS_COMPLETE,
+ FNIC_IOREQ_CMD_COMPLETE,
+};
+
+struct fnic_io_req {
+ struct host_sg_desc *sgl_list; /* sgl list */
+ void *sgl_list_alloc; /* sgl list address used for free */
+ dma_addr_t sense_buf_pa; /* dma address for sense buffer*/
+ dma_addr_t sgl_list_pa; /* dma address for sgl list */
+ u16 sgl_cnt;
+ u8 sgl_type; /* device DMA descriptor list type */
+ u8 io_completed:1; /* set to 1 when fw completes IO */
+ u32 port_id; /* remote port DID */
+ struct completion *abts_done; /* completion for abts */
+ struct completion *dr_done; /* completion for device reset */
+};
+
+#endif /* _FNIC_IO_H_ */
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
new file mode 100644
index 000000000000..2b3064828aea
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "fnic_io.h"
+#include "fnic.h"
+
+static irqreturn_t fnic_isr_legacy(int irq, void *data)
+{
+ struct fnic *fnic = data;
+ u32 pba;
+ unsigned long work_done = 0;
+
+ pba = vnic_intr_legacy_pba(fnic->legacy_pba);
+ if (!pba)
+ return IRQ_NONE;
+
+ if (pba & (1 << FNIC_INTX_NOTIFY)) {
+ vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
+ fnic_handle_link_event(fnic);
+ }
+
+ if (pba & (1 << FNIC_INTX_ERR)) {
+ vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]);
+ fnic_log_q_error(fnic);
+ }
+
+ if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
+ work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
+ work_done += fnic_wq_cmpl_handler(fnic, 4);
+ work_done += fnic_rq_cmpl_handler(fnic, 4);
+
+ vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
+ work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fnic_isr_msi(int irq, void *data)
+{
+ struct fnic *fnic = data;
+ unsigned long work_done = 0;
+
+ work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
+ work_done += fnic_wq_cmpl_handler(fnic, 4);
+ work_done += fnic_rq_cmpl_handler(fnic, 4);
+
+ vnic_intr_return_credits(&fnic->intr[0],
+ work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
+{
+ struct fnic *fnic = data;
+ unsigned long rq_work_done = 0;
+
+ rq_work_done = fnic_rq_cmpl_handler(fnic, 4);
+ vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
+ rq_work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
+{
+ struct fnic *fnic = data;
+ unsigned long wq_work_done = 0;
+
+ wq_work_done = fnic_wq_cmpl_handler(fnic, 4);
+ vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
+ wq_work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
+{
+ struct fnic *fnic = data;
+ unsigned long wq_copy_work_done = 0;
+
+ wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8);
+ vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
+ wq_copy_work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
+{
+ struct fnic *fnic = data;
+
+ vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
+ fnic_log_q_error(fnic);
+ fnic_handle_link_event(fnic);
+
+ return IRQ_HANDLED;
+}
+
+void fnic_free_intr(struct fnic *fnic)
+{
+ int i;
+
+ switch (vnic_dev_get_intr_mode(fnic->vdev)) {
+ case VNIC_DEV_INTR_MODE_INTX:
+ case VNIC_DEV_INTR_MODE_MSI:
+ free_irq(fnic->pdev->irq, fnic);
+ break;
+
+ case VNIC_DEV_INTR_MODE_MSIX:
+ for (i = 0; i < ARRAY_SIZE(fnic->msix); i++)
+ if (fnic->msix[i].requested)
+ free_irq(fnic->msix_entry[i].vector,
+ fnic->msix[i].devid);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int fnic_request_intr(struct fnic *fnic)
+{
+ int err = 0;
+ int i;
+
+ switch (vnic_dev_get_intr_mode(fnic->vdev)) {
+
+ case VNIC_DEV_INTR_MODE_INTX:
+ err = request_irq(fnic->pdev->irq, &fnic_isr_legacy,
+ IRQF_SHARED, DRV_NAME, fnic);
+ break;
+
+ case VNIC_DEV_INTR_MODE_MSI:
+ err = request_irq(fnic->pdev->irq, &fnic_isr_msi,
+ 0, fnic->name, fnic);
+ break;
+
+ case VNIC_DEV_INTR_MODE_MSIX:
+
+ sprintf(fnic->msix[FNIC_MSIX_RQ].devname,
+ "%.11s-fcs-rq", fnic->name);
+ fnic->msix[FNIC_MSIX_RQ].isr = fnic_isr_msix_rq;
+ fnic->msix[FNIC_MSIX_RQ].devid = fnic;
+
+ sprintf(fnic->msix[FNIC_MSIX_WQ].devname,
+ "%.11s-fcs-wq", fnic->name);
+ fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq;
+ fnic->msix[FNIC_MSIX_WQ].devid = fnic;
+
+ sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname,
+ "%.11s-scsi-wq", fnic->name);
+ fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy;
+ fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic;
+
+ sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname,
+ "%.11s-err-notify", fnic->name);
+ fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr =
+ fnic_isr_msix_err_notify;
+ fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic;
+
+ for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) {
+ err = request_irq(fnic->msix_entry[i].vector,
+ fnic->msix[i].isr, 0,
+ fnic->msix[i].devname,
+ fnic->msix[i].devid);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "MSIX: request_irq"
+ " failed %d\n", err);
+ fnic_free_intr(fnic);
+ break;
+ }
+ fnic->msix[i].requested = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+int fnic_set_intr_mode(struct fnic *fnic)
+{
+ unsigned int n = ARRAY_SIZE(fnic->rq);
+ unsigned int m = ARRAY_SIZE(fnic->wq);
+ unsigned int o = ARRAY_SIZE(fnic->wq_copy);
+ unsigned int i;
+
+ /*
+ * Set interrupt mode (INTx, MSI, MSI-X) depending
+ * system capabilities.
+ *
+ * Try MSI-X first
+ *
+ * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
+ * (last INTR is used for WQ/RQ errors and notification area)
+ */
+
+ BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1);
+ for (i = 0; i < n + m + o + 1; i++)
+ fnic->msix_entry[i].entry = i;
+
+ if (fnic->rq_count >= n &&
+ fnic->raw_wq_count >= m &&
+ fnic->wq_copy_count >= o &&
+ fnic->cq_count >= n + m + o) {
+ if (!pci_enable_msix(fnic->pdev, fnic->msix_entry,
+ n + m + o + 1)) {
+ fnic->rq_count = n;
+ fnic->raw_wq_count = m;
+ fnic->wq_copy_count = o;
+ fnic->wq_count = m + o;
+ fnic->cq_count = n + m + o;
+ fnic->intr_count = n + m + o + 1;
+ fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
+
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ "Using MSI-X Interrupts\n");
+ vnic_dev_set_intr_mode(fnic->vdev,
+ VNIC_DEV_INTR_MODE_MSIX);
+ return 0;
+ }
+ }
+
+ /*
+ * Next try MSI
+ * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
+ */
+ if (fnic->rq_count >= 1 &&
+ fnic->raw_wq_count >= 1 &&
+ fnic->wq_copy_count >= 1 &&
+ fnic->cq_count >= 3 &&
+ fnic->intr_count >= 1 &&
+ !pci_enable_msi(fnic->pdev)) {
+
+ fnic->rq_count = 1;
+ fnic->raw_wq_count = 1;
+ fnic->wq_copy_count = 1;
+ fnic->wq_count = 2;
+ fnic->cq_count = 3;
+ fnic->intr_count = 1;
+ fnic->err_intr_offset = 0;
+
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ "Using MSI Interrupts\n");
+ vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
+
+ return 0;
+ }
+
+ /*
+ * Next try INTx
+ * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
+ * 1 INTR is used for all 3 queues, 1 INTR for queue errors
+ * 1 INTR for notification area
+ */
+
+ if (fnic->rq_count >= 1 &&
+ fnic->raw_wq_count >= 1 &&
+ fnic->wq_copy_count >= 1 &&
+ fnic->cq_count >= 3 &&
+ fnic->intr_count >= 3) {
+
+ fnic->rq_count = 1;
+ fnic->raw_wq_count = 1;
+ fnic->wq_copy_count = 1;
+ fnic->cq_count = 3;
+ fnic->intr_count = 3;
+
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ "Using Legacy Interrupts\n");
+ vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
+
+ return 0;
+ }
+
+ vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
+
+ return -EINVAL;
+}
+
+void fnic_clear_intr_mode(struct fnic *fnic)
+{
+ switch (vnic_dev_get_intr_mode(fnic->vdev)) {
+ case VNIC_DEV_INTR_MODE_MSIX:
+ pci_disable_msix(fnic->pdev);
+ break;
+ case VNIC_DEV_INTR_MODE_MSI:
+ pci_disable_msi(fnic->pdev);
+ break;
+ default:
+ break;
+ }
+
+ vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
+}
+
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
new file mode 100644
index 000000000000..a84072865fc2
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -0,0 +1,943 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "fnic_io.h"
+#include "fnic.h"
+
+#define PCI_DEVICE_ID_CISCO_FNIC 0x0045
+
+/* Timer to poll notification area for events. Used for MSI interrupts */
+#define FNIC_NOTIFY_TIMER_PERIOD (2 * HZ)
+
+static struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES];
+static struct kmem_cache *fnic_io_req_cache;
+LIST_HEAD(fnic_list);
+DEFINE_SPINLOCK(fnic_list_lock);
+
+/* Supported devices by fnic module */
+static struct pci_device_id fnic_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) },
+ { 0, }
+};
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("Abhijeet Joglekar <abjoglek@cisco.com>, "
+ "Joseph R. Eykholt <jeykholt@cisco.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, fnic_id_table);
+
+unsigned int fnic_log_level;
+module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
+
+
+static struct libfc_function_template fnic_transport_template = {
+ .frame_send = fnic_send,
+ .fcp_abort_io = fnic_empty_scsi_cleanup,
+ .fcp_cleanup = fnic_empty_scsi_cleanup,
+ .exch_mgr_reset = fnic_exch_mgr_reset
+};
+
+static int fnic_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct fc_lport *lp = shost_priv(sdev->host);
+ struct fnic *fnic = lport_priv(lp);
+
+ sdev->tagged_supported = 1;
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ scsi_activate_tcq(sdev, FNIC_DFLT_QUEUE_DEPTH);
+ rport->dev_loss_tmo = fnic->config.port_down_timeout / 1000;
+
+ return 0;
+}
+
+static struct scsi_host_template fnic_host_template = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = fnic_queuecommand,
+ .eh_abort_handler = fnic_abort_cmd,
+ .eh_device_reset_handler = fnic_device_reset,
+ .eh_host_reset_handler = fnic_host_reset,
+ .slave_alloc = fnic_slave_alloc,
+ .change_queue_depth = fc_change_queue_depth,
+ .change_queue_type = fc_change_queue_type,
+ .this_id = -1,
+ .cmd_per_lun = 3,
+ .can_queue = FNIC_MAX_IO_REQ,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = FNIC_MAX_SG_DESC_CNT,
+ .max_sectors = 0xffff,
+ .shost_attrs = fnic_attrs,
+};
+
+static void fnic_get_host_speed(struct Scsi_Host *shost);
+static struct scsi_transport_template *fnic_fc_transport;
+static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *);
+
+static struct fc_function_template fnic_fc_functions = {
+
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fnic_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .show_rport_dev_loss_tmo = 1,
+ .issue_fc_host_lip = fnic_reset,
+ .get_fc_host_stats = fnic_get_stats,
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .terminate_rport_io = fnic_terminate_rport_io,
+};
+
+static void fnic_get_host_speed(struct Scsi_Host *shost)
+{
+ struct fc_lport *lp = shost_priv(shost);
+ struct fnic *fnic = lport_priv(lp);
+ u32 port_speed = vnic_dev_port_speed(fnic->vdev);
+
+ /* Add in other values as they get defined in fw */
+ switch (port_speed) {
+ case 10000:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
+ }
+}
+
+static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
+{
+ int ret;
+ struct fc_lport *lp = shost_priv(host);
+ struct fnic *fnic = lport_priv(lp);
+ struct fc_host_statistics *stats = &lp->host_stats;
+ struct vnic_stats *vs;
+ unsigned long flags;
+
+ if (time_before(jiffies, fnic->stats_time + HZ / FNIC_STATS_RATE_LIMIT))
+ return stats;
+ fnic->stats_time = jiffies;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ ret = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (ret) {
+ FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
+ "fnic: Get vnic stats failed"
+ " 0x%x", ret);
+ return stats;
+ }
+ vs = fnic->stats;
+ stats->tx_frames = vs->tx.tx_unicast_frames_ok;
+ stats->tx_words = vs->tx.tx_unicast_bytes_ok / 4;
+ stats->rx_frames = vs->rx.rx_unicast_frames_ok;
+ stats->rx_words = vs->rx.rx_unicast_bytes_ok / 4;
+ stats->error_frames = vs->tx.tx_errors + vs->rx.rx_errors;
+ stats->dumped_frames = vs->tx.tx_drops + vs->rx.rx_drop;
+ stats->invalid_crc_count = vs->rx.rx_crc_errors;
+ stats->seconds_since_last_reset = (jiffies - lp->boot_time) / HZ;
+ stats->fcp_input_megabytes = div_u64(fnic->fcp_input_bytes, 1000000);
+ stats->fcp_output_megabytes = div_u64(fnic->fcp_output_bytes, 1000000);
+
+ return stats;
+}
+
+void fnic_log_q_error(struct fnic *fnic)
+{
+ unsigned int i;
+ u32 error_status;
+
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ error_status = ioread32(&fnic->wq[i].ctrl->error_status);
+ if (error_status)
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "WQ[%d] error_status"
+ " %d\n", i, error_status);
+ }
+
+ for (i = 0; i < fnic->rq_count; i++) {
+ error_status = ioread32(&fnic->rq[i].ctrl->error_status);
+ if (error_status)
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "RQ[%d] error_status"
+ " %d\n", i, error_status);
+ }
+
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status);
+ if (error_status)
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "CWQ[%d] error_status"
+ " %d\n", i, error_status);
+ }
+}
+
+void fnic_handle_link_event(struct fnic *fnic)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ queue_work(fnic_event_queue, &fnic->link_work);
+
+}
+
+static int fnic_notify_set(struct fnic *fnic)
+{
+ int err;
+
+ switch (vnic_dev_get_intr_mode(fnic->vdev)) {
+ case VNIC_DEV_INTR_MODE_INTX:
+ err = vnic_dev_notify_set(fnic->vdev, FNIC_INTX_NOTIFY);
+ break;
+ case VNIC_DEV_INTR_MODE_MSI:
+ err = vnic_dev_notify_set(fnic->vdev, -1);
+ break;
+ case VNIC_DEV_INTR_MODE_MSIX:
+ err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY);
+ break;
+ default:
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Interrupt mode should be set up"
+ " before devcmd notify set %d\n",
+ vnic_dev_get_intr_mode(fnic->vdev));
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static void fnic_notify_timer(unsigned long data)
+{
+ struct fnic *fnic = (struct fnic *)data;
+
+ fnic_handle_link_event(fnic);
+ mod_timer(&fnic->notify_timer,
+ round_jiffies(jiffies + FNIC_NOTIFY_TIMER_PERIOD));
+}
+
+static void fnic_notify_timer_start(struct fnic *fnic)
+{
+ switch (vnic_dev_get_intr_mode(fnic->vdev)) {
+ case VNIC_DEV_INTR_MODE_MSI:
+ /*
+ * Schedule first timeout immediately. The driver is
+ * initiatialized and ready to look for link up notification
+ */
+ mod_timer(&fnic->notify_timer, jiffies);
+ break;
+ default:
+ /* Using intr for notification for INTx/MSI-X */
+ break;
+ };
+}
+
+static int fnic_dev_wait(struct vnic_dev *vdev,
+ int (*start)(struct vnic_dev *, int),
+ int (*finished)(struct vnic_dev *, int *),
+ int arg)
+{
+ unsigned long time;
+ int done;
+ int err;
+
+ err = start(vdev, arg);
+ if (err)
+ return err;
+
+ /* Wait for func to complete...2 seconds max */
+ time = jiffies + (HZ * 2);
+ do {
+ err = finished(vdev, &done);
+ if (err)
+ return err;
+ if (done)
+ return 0;
+ schedule_timeout_uninterruptible(HZ / 10);
+ } while (time_after(time, jiffies));
+
+ return -ETIMEDOUT;
+}
+
+static int fnic_cleanup(struct fnic *fnic)
+{
+ unsigned int i;
+ int err;
+ unsigned long flags;
+ struct fc_frame *flogi = NULL;
+ struct fc_frame *flogi_resp = NULL;
+
+ vnic_dev_disable(fnic->vdev);
+ for (i = 0; i < fnic->intr_count; i++)
+ vnic_intr_mask(&fnic->intr[i]);
+
+ for (i = 0; i < fnic->rq_count; i++) {
+ err = vnic_rq_disable(&fnic->rq[i]);
+ if (err)
+ return err;
+ }
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ err = vnic_wq_disable(&fnic->wq[i]);
+ if (err)
+ return err;
+ }
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ err = vnic_wq_copy_disable(&fnic->wq_copy[i]);
+ if (err)
+ return err;
+ }
+
+ /* Clean up completed IOs and FCS frames */
+ fnic_wq_copy_cmpl_handler(fnic, -1);
+ fnic_wq_cmpl_handler(fnic, -1);
+ fnic_rq_cmpl_handler(fnic, -1);
+
+ /* Clean up the IOs and FCS frames that have not completed */
+ for (i = 0; i < fnic->raw_wq_count; i++)
+ vnic_wq_clean(&fnic->wq[i], fnic_free_wq_buf);
+ for (i = 0; i < fnic->rq_count; i++)
+ vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
+ for (i = 0; i < fnic->wq_copy_count; i++)
+ vnic_wq_copy_clean(&fnic->wq_copy[i],
+ fnic_wq_copy_cleanup_handler);
+
+ for (i = 0; i < fnic->cq_count; i++)
+ vnic_cq_clean(&fnic->cq[i]);
+ for (i = 0; i < fnic->intr_count; i++)
+ vnic_intr_clean(&fnic->intr[i]);
+
+ /*
+ * Remove cached flogi and flogi resp frames if any
+ * These frames are not in any queue, and therefore queue
+ * cleanup does not clean them. So clean them explicitly
+ */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ flogi = fnic->flogi;
+ fnic->flogi = NULL;
+ flogi_resp = fnic->flogi_resp;
+ fnic->flogi_resp = NULL;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (flogi)
+ dev_kfree_skb(fp_skb(flogi));
+
+ if (flogi_resp)
+ dev_kfree_skb(fp_skb(flogi_resp));
+
+ mempool_destroy(fnic->io_req_pool);
+ for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
+ mempool_destroy(fnic->io_sgl_pool[i]);
+
+ return 0;
+}
+
+static void fnic_iounmap(struct fnic *fnic)
+{
+ if (fnic->bar0.vaddr)
+ iounmap(fnic->bar0.vaddr);
+}
+
+/*
+ * Allocate element for mempools requiring GFP_DMA flag.
+ * Otherwise, checks in kmem_flagcheck() hit BUG_ON().
+ */
+static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data)
+{
+ struct kmem_cache *mem = pool_data;
+
+ return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA);
+}
+
+static int __devinit fnic_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct Scsi_Host *host;
+ struct fc_lport *lp;
+ struct fnic *fnic;
+ mempool_t *pool;
+ int err;
+ int i;
+ unsigned long flags;
+
+ /*
+ * Allocate SCSI Host and set up association between host,
+ * local port, and fnic
+ */
+ host = scsi_host_alloc(&fnic_host_template,
+ sizeof(struct fc_lport) + sizeof(struct fnic));
+ if (!host) {
+ printk(KERN_ERR PFX "Unable to alloc SCSI host\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+ lp = shost_priv(host);
+ lp->host = host;
+ fnic = lport_priv(lp);
+ fnic->lport = lp;
+
+ snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
+ host->host_no);
+
+ host->transportt = fnic_fc_transport;
+
+ err = scsi_init_shared_tag_map(host, FNIC_MAX_IO_REQ);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to alloc shared tag map\n");
+ goto err_out_free_hba;
+ }
+
+ /* Setup PCI resources */
+ pci_set_drvdata(pdev, fnic);
+
+ fnic->pdev = pdev;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Cannot enable PCI device, aborting.\n");
+ goto err_out_free_hba;
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Cannot enable PCI resources, aborting\n");
+ goto err_out_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ /* Query PCI controller on system for DMA addressing
+ * limitation for the device. Try 40-bit first, and
+ * fail to 32-bit.
+ */
+ err = pci_set_dma_mask(pdev, DMA_40BIT_MASK);
+ if (err) {
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "No usable DMA configuration "
+ "aborting\n");
+ goto err_out_release_regions;
+ }
+ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to obtain 32-bit DMA "
+ "for consistent allocations, aborting.\n");
+ goto err_out_release_regions;
+ }
+ } else {
+ err = pci_set_consistent_dma_mask(pdev, DMA_40BIT_MASK);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to obtain 40-bit DMA "
+ "for consistent allocations, aborting.\n");
+ goto err_out_release_regions;
+ }
+ }
+
+ /* Map vNIC resources from BAR0 */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "BAR0 not memory-map'able, aborting.\n");
+ err = -ENODEV;
+ goto err_out_release_regions;
+ }
+
+ fnic->bar0.vaddr = pci_iomap(pdev, 0, 0);
+ fnic->bar0.bus_addr = pci_resource_start(pdev, 0);
+ fnic->bar0.len = pci_resource_len(pdev, 0);
+
+ if (!fnic->bar0.vaddr) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Cannot memory-map BAR0 res hdr, "
+ "aborting.\n");
+ err = -ENODEV;
+ goto err_out_release_regions;
+ }
+
+ fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0);
+ if (!fnic->vdev) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vNIC registration failed, "
+ "aborting.\n");
+ err = -ENODEV;
+ goto err_out_iounmap;
+ }
+
+ err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
+ vnic_dev_open_done, 0);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vNIC dev open failed, aborting.\n");
+ goto err_out_vnic_unregister;
+ }
+
+ err = vnic_dev_init(fnic->vdev, 0);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vNIC dev init failed, aborting.\n");
+ goto err_out_dev_close;
+ }
+
+ err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vNIC get MAC addr failed \n");
+ goto err_out_dev_close;
+ }
+
+ /* Get vNIC configuration */
+ err = fnic_get_vnic_config(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Get vNIC configuration failed, "
+ "aborting.\n");
+ goto err_out_dev_close;
+ }
+ host->max_lun = fnic->config.luns_per_tgt;
+ host->max_id = FNIC_MAX_FCP_TARGET;
+
+ fnic_get_res_counts(fnic);
+
+ err = fnic_set_intr_mode(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Failed to set intr mode, "
+ "aborting.\n");
+ goto err_out_dev_close;
+ }
+
+ err = fnic_request_intr(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to request irq.\n");
+ goto err_out_clear_intr;
+ }
+
+ err = fnic_alloc_vnic_resources(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Failed to alloc vNIC resources, "
+ "aborting.\n");
+ goto err_out_free_intr;
+ }
+
+
+ /* initialize all fnic locks */
+ spin_lock_init(&fnic->fnic_lock);
+
+ for (i = 0; i < FNIC_WQ_MAX; i++)
+ spin_lock_init(&fnic->wq_lock[i]);
+
+ for (i = 0; i < FNIC_WQ_COPY_MAX; i++) {
+ spin_lock_init(&fnic->wq_copy_lock[i]);
+ fnic->wq_copy_desc_low[i] = DESC_CLEAN_LOW_WATERMARK;
+ fnic->fw_ack_recd[i] = 0;
+ fnic->fw_ack_index[i] = -1;
+ }
+
+ for (i = 0; i < FNIC_IO_LOCKS; i++)
+ spin_lock_init(&fnic->io_req_lock[i]);
+
+ fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache);
+ if (!fnic->io_req_pool)
+ goto err_out_free_resources;
+
+ pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
+ fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
+ if (!pool)
+ goto err_out_free_ioreq_pool;
+ fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT] = pool;
+
+ pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
+ fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
+ if (!pool)
+ goto err_out_free_dflt_pool;
+ fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX] = pool;
+
+ /* setup vlan config, hw inserts vlan header */
+ fnic->vlan_hw_insert = 1;
+ fnic->vlan_id = 0;
+
+ fnic->flogi_oxid = FC_XID_UNKNOWN;
+ fnic->flogi = NULL;
+ fnic->flogi_resp = NULL;
+ fnic->state = FNIC_IN_FC_MODE;
+
+ /* Enable hardware stripping of vlan header on ingress */
+ fnic_set_nic_config(fnic, 0, 0, 0, 0, 0, 0, 1);
+
+ /* Setup notification buffer area */
+ err = fnic_notify_set(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Failed to alloc notify buffer, aborting.\n");
+ goto err_out_free_max_pool;
+ }
+
+ /* Setup notify timer when using MSI interrupts */
+ if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
+ setup_timer(&fnic->notify_timer,
+ fnic_notify_timer, (unsigned long)fnic);
+
+ /* allocate RQ buffers and post them to RQ*/
+ for (i = 0; i < fnic->rq_count; i++) {
+ err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "fnic_alloc_rq_frame can't alloc "
+ "frame\n");
+ goto err_out_free_rq_buf;
+ }
+ }
+
+ /*
+ * Initialization done with PCI system, hardware, firmware.
+ * Add host to SCSI
+ */
+ err = scsi_add_host(lp->host, &pdev->dev);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "fnic: scsi_add_host failed...exiting\n");
+ goto err_out_free_rq_buf;
+ }
+
+ /* Start local port initiatialization */
+
+ lp->link_up = 0;
+ lp->tt = fnic_transport_template;
+
+ lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+ FCPIO_HOST_EXCH_RANGE_START,
+ FCPIO_HOST_EXCH_RANGE_END);
+ if (!lp->emp) {
+ err = -ENOMEM;
+ goto err_out_remove_scsi_host;
+ }
+
+ lp->max_retry_count = fnic->config.flogi_retries;
+ lp->max_rport_retry_count = fnic->config.plogi_retries;
+ lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_CONF_COMPL);
+ if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
+ lp->service_params |= FCP_SPPF_RETRY;
+
+ lp->boot_time = jiffies;
+ lp->e_d_tov = fnic->config.ed_tov;
+ lp->r_a_tov = fnic->config.ra_tov;
+ lp->link_supported_speeds = FC_PORTSPEED_10GBIT;
+ fc_set_wwnn(lp, fnic->config.node_wwn);
+ fc_set_wwpn(lp, fnic->config.port_wwn);
+
+ fc_exch_init(lp);
+ fc_lport_init(lp);
+ fc_elsct_init(lp);
+ fc_rport_init(lp);
+ fc_disc_init(lp);
+
+ fc_lport_config(lp);
+
+ if (fc_set_mfs(lp, fnic->config.maxdatafieldsize +
+ sizeof(struct fc_frame_header))) {
+ err = -EINVAL;
+ goto err_out_free_exch_mgr;
+ }
+ fc_host_maxframe_size(lp->host) = lp->mfs;
+
+ sprintf(fc_host_symbolic_name(lp->host),
+ DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
+
+ spin_lock_irqsave(&fnic_list_lock, flags);
+ list_add_tail(&fnic->list, &fnic_list);
+ spin_unlock_irqrestore(&fnic_list_lock, flags);
+
+ INIT_WORK(&fnic->link_work, fnic_handle_link);
+ INIT_WORK(&fnic->frame_work, fnic_handle_frame);
+ skb_queue_head_init(&fnic->frame_queue);
+
+ /* Enable all queues */
+ for (i = 0; i < fnic->raw_wq_count; i++)
+ vnic_wq_enable(&fnic->wq[i]);
+ for (i = 0; i < fnic->rq_count; i++)
+ vnic_rq_enable(&fnic->rq[i]);
+ for (i = 0; i < fnic->wq_copy_count; i++)
+ vnic_wq_copy_enable(&fnic->wq_copy[i]);
+
+ fc_fabric_login(lp);
+
+ vnic_dev_enable(fnic->vdev);
+ for (i = 0; i < fnic->intr_count; i++)
+ vnic_intr_unmask(&fnic->intr[i]);
+
+ fnic_notify_timer_start(fnic);
+
+ return 0;
+
+err_out_free_exch_mgr:
+ fc_exch_mgr_free(lp->emp);
+err_out_remove_scsi_host:
+ fc_remove_host(fnic->lport->host);
+ scsi_remove_host(fnic->lport->host);
+err_out_free_rq_buf:
+ for (i = 0; i < fnic->rq_count; i++)
+ vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
+ vnic_dev_notify_unset(fnic->vdev);
+err_out_free_max_pool:
+ mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX]);
+err_out_free_dflt_pool:
+ mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT]);
+err_out_free_ioreq_pool:
+ mempool_destroy(fnic->io_req_pool);
+err_out_free_resources:
+ fnic_free_vnic_resources(fnic);
+err_out_free_intr:
+ fnic_free_intr(fnic);
+err_out_clear_intr:
+ fnic_clear_intr_mode(fnic);
+err_out_dev_close:
+ vnic_dev_close(fnic->vdev);
+err_out_vnic_unregister:
+ vnic_dev_unregister(fnic->vdev);
+err_out_iounmap:
+ fnic_iounmap(fnic);
+err_out_release_regions:
+ pci_release_regions(pdev);
+err_out_disable_device:
+ pci_disable_device(pdev);
+err_out_free_hba:
+ scsi_host_put(lp->host);
+err_out:
+ return err;
+}
+
+static void __devexit fnic_remove(struct pci_dev *pdev)
+{
+ struct fnic *fnic = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ /*
+ * Mark state so that the workqueue thread stops forwarding
+ * received frames and link events to the local port. ISR and
+ * other threads that can queue work items will also stop
+ * creating work items on the fnic workqueue
+ */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->stop_rx_link_events = 1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
+ del_timer_sync(&fnic->notify_timer);
+
+ /*
+ * Flush the fnic event queue. After this call, there should
+ * be no event queued for this fnic device in the workqueue
+ */
+ flush_workqueue(fnic_event_queue);
+ skb_queue_purge(&fnic->frame_queue);
+
+ /*
+ * Log off the fabric. This stops all remote ports, dns port,
+ * logs off the fabric. This flushes all rport, disc, lport work
+ * before returning
+ */
+ fc_fabric_logoff(fnic->lport);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->in_remove = 1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ fc_lport_destroy(fnic->lport);
+
+ /*
+ * This stops the fnic device, masks all interrupts. Completed
+ * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are
+ * cleaned up
+ */
+ fnic_cleanup(fnic);
+
+ BUG_ON(!skb_queue_empty(&fnic->frame_queue));
+
+ spin_lock_irqsave(&fnic_list_lock, flags);
+ list_del(&fnic->list);
+ spin_unlock_irqrestore(&fnic_list_lock, flags);
+
+ fc_remove_host(fnic->lport->host);
+ scsi_remove_host(fnic->lport->host);
+ fc_exch_mgr_free(fnic->lport->emp);
+ vnic_dev_notify_unset(fnic->vdev);
+ fnic_free_vnic_resources(fnic);
+ fnic_free_intr(fnic);
+ fnic_clear_intr_mode(fnic);
+ vnic_dev_close(fnic->vdev);
+ vnic_dev_unregister(fnic->vdev);
+ fnic_iounmap(fnic);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ scsi_host_put(fnic->lport->host);
+}
+
+static struct pci_driver fnic_driver = {
+ .name = DRV_NAME,
+ .id_table = fnic_id_table,
+ .probe = fnic_probe,
+ .remove = __devexit_p(fnic_remove),
+};
+
+static int __init fnic_init_module(void)
+{
+ size_t len;
+ int err = 0;
+
+ printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION);
+
+ /* Create a cache for allocation of default size sgls */
+ len = sizeof(struct fnic_dflt_sgl_list);
+ fnic_sgl_cache[FNIC_SGL_CACHE_DFLT] = kmem_cache_create
+ ("fnic_sgl_dflt", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
+ SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
+ NULL);
+ if (!fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]) {
+ printk(KERN_ERR PFX "failed to create fnic dflt sgl slab\n");
+ err = -ENOMEM;
+ goto err_create_fnic_sgl_slab_dflt;
+ }
+
+ /* Create a cache for allocation of max size sgls*/
+ len = sizeof(struct fnic_sgl_list);
+ fnic_sgl_cache[FNIC_SGL_CACHE_MAX] = kmem_cache_create
+ ("fnic_sgl_max", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
+ SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
+ NULL);
+ if (!fnic_sgl_cache[FNIC_SGL_CACHE_MAX]) {
+ printk(KERN_ERR PFX "failed to create fnic max sgl slab\n");
+ err = -ENOMEM;
+ goto err_create_fnic_sgl_slab_max;
+ }
+
+ /* Create a cache of io_req structs for use via mempool */
+ fnic_io_req_cache = kmem_cache_create("fnic_io_req",
+ sizeof(struct fnic_io_req),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fnic_io_req_cache) {
+ printk(KERN_ERR PFX "failed to create fnic io_req slab\n");
+ err = -ENOMEM;
+ goto err_create_fnic_ioreq_slab;
+ }
+
+ fnic_event_queue = create_singlethread_workqueue("fnic_event_wq");
+ if (!fnic_event_queue) {
+ printk(KERN_ERR PFX "fnic work queue create failed\n");
+ err = -ENOMEM;
+ goto err_create_fnic_workq;
+ }
+
+ spin_lock_init(&fnic_list_lock);
+ INIT_LIST_HEAD(&fnic_list);
+
+ fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
+ if (!fnic_fc_transport) {
+ printk(KERN_ERR PFX "fc_attach_transport error\n");
+ err = -ENOMEM;
+ goto err_fc_transport;
+ }
+
+ /* register the driver with PCI system */
+ err = pci_register_driver(&fnic_driver);
+ if (err < 0) {
+ printk(KERN_ERR PFX "pci register error\n");
+ goto err_pci_register;
+ }
+ return err;
+
+err_pci_register:
+ fc_release_transport(fnic_fc_transport);
+err_fc_transport:
+ destroy_workqueue(fnic_event_queue);
+err_create_fnic_workq:
+ kmem_cache_destroy(fnic_io_req_cache);
+err_create_fnic_ioreq_slab:
+ kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
+err_create_fnic_sgl_slab_max:
+ kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
+err_create_fnic_sgl_slab_dflt:
+ return err;
+}
+
+static void __exit fnic_cleanup_module(void)
+{
+ pci_unregister_driver(&fnic_driver);
+ destroy_workqueue(fnic_event_queue);
+ kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
+ kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
+ kmem_cache_destroy(fnic_io_req_cache);
+ fc_release_transport(fnic_fc_transport);
+}
+
+module_init(fnic_init_module);
+module_exit(fnic_cleanup_module);
+
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
new file mode 100644
index 000000000000..7ba61ec715d2
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "wq_enet_desc.h"
+#include "rq_enet_desc.h"
+#include "cq_enet_desc.h"
+#include "vnic_resource.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_rq.h"
+#include "vnic_cq.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "vnic_nic.h"
+#include "fnic.h"
+
+int fnic_get_vnic_config(struct fnic *fnic)
+{
+ struct vnic_fc_config *c = &fnic->config;
+ int err;
+
+#define GET_CONFIG(m) \
+ do { \
+ err = vnic_dev_spec(fnic->vdev, \
+ offsetof(struct vnic_fc_config, m), \
+ sizeof(c->m), &c->m); \
+ if (err) { \
+ shost_printk(KERN_ERR, fnic->lport->host, \
+ "Error getting %s, %d\n", #m, \
+ err); \
+ return err; \
+ } \
+ } while (0);
+
+ GET_CONFIG(node_wwn);
+ GET_CONFIG(port_wwn);
+ GET_CONFIG(wq_enet_desc_count);
+ GET_CONFIG(wq_copy_desc_count);
+ GET_CONFIG(rq_desc_count);
+ GET_CONFIG(maxdatafieldsize);
+ GET_CONFIG(ed_tov);
+ GET_CONFIG(ra_tov);
+ GET_CONFIG(intr_timer);
+ GET_CONFIG(intr_timer_type);
+ GET_CONFIG(flags);
+ GET_CONFIG(flogi_retries);
+ GET_CONFIG(flogi_timeout);
+ GET_CONFIG(plogi_retries);
+ GET_CONFIG(plogi_timeout);
+ GET_CONFIG(io_throttle_count);
+ GET_CONFIG(link_down_timeout);
+ GET_CONFIG(port_down_timeout);
+ GET_CONFIG(port_down_io_retries);
+ GET_CONFIG(luns_per_tgt);
+
+ c->wq_enet_desc_count =
+ min_t(u32, VNIC_FNIC_WQ_DESCS_MAX,
+ max_t(u32, VNIC_FNIC_WQ_DESCS_MIN,
+ c->wq_enet_desc_count));
+ c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16);
+
+ c->wq_copy_desc_count =
+ min_t(u32, VNIC_FNIC_WQ_COPY_DESCS_MAX,
+ max_t(u32, VNIC_FNIC_WQ_COPY_DESCS_MIN,
+ c->wq_copy_desc_count));
+ c->wq_copy_desc_count = ALIGN(c->wq_copy_desc_count, 16);
+
+ c->rq_desc_count =
+ min_t(u32, VNIC_FNIC_RQ_DESCS_MAX,
+ max_t(u32, VNIC_FNIC_RQ_DESCS_MIN,
+ c->rq_desc_count));
+ c->rq_desc_count = ALIGN(c->rq_desc_count, 16);
+
+ c->maxdatafieldsize =
+ min_t(u16, VNIC_FNIC_MAXDATAFIELDSIZE_MAX,
+ max_t(u16, VNIC_FNIC_MAXDATAFIELDSIZE_MIN,
+ c->maxdatafieldsize));
+ c->ed_tov =
+ min_t(u32, VNIC_FNIC_EDTOV_MAX,
+ max_t(u32, VNIC_FNIC_EDTOV_MIN,
+ c->ed_tov));
+
+ c->ra_tov =
+ min_t(u32, VNIC_FNIC_RATOV_MAX,
+ max_t(u32, VNIC_FNIC_RATOV_MIN,
+ c->ra_tov));
+
+ c->flogi_retries =
+ min_t(u32, VNIC_FNIC_FLOGI_RETRIES_MAX, c->flogi_retries);
+
+ c->flogi_timeout =
+ min_t(u32, VNIC_FNIC_FLOGI_TIMEOUT_MAX,
+ max_t(u32, VNIC_FNIC_FLOGI_TIMEOUT_MIN,
+ c->flogi_timeout));
+
+ c->plogi_retries =
+ min_t(u32, VNIC_FNIC_PLOGI_RETRIES_MAX, c->plogi_retries);
+
+ c->plogi_timeout =
+ min_t(u32, VNIC_FNIC_PLOGI_TIMEOUT_MAX,
+ max_t(u32, VNIC_FNIC_PLOGI_TIMEOUT_MIN,
+ c->plogi_timeout));
+
+ c->io_throttle_count =
+ min_t(u32, VNIC_FNIC_IO_THROTTLE_COUNT_MAX,
+ max_t(u32, VNIC_FNIC_IO_THROTTLE_COUNT_MIN,
+ c->io_throttle_count));
+
+ c->link_down_timeout =
+ min_t(u32, VNIC_FNIC_LINK_DOWN_TIMEOUT_MAX,
+ c->link_down_timeout);
+
+ c->port_down_timeout =
+ min_t(u32, VNIC_FNIC_PORT_DOWN_TIMEOUT_MAX,
+ c->port_down_timeout);
+
+ c->port_down_io_retries =
+ min_t(u32, VNIC_FNIC_PORT_DOWN_IO_RETRIES_MAX,
+ c->port_down_io_retries);
+
+ c->luns_per_tgt =
+ min_t(u32, VNIC_FNIC_LUNS_PER_TARGET_MAX,
+ max_t(u32, VNIC_FNIC_LUNS_PER_TARGET_MIN,
+ c->luns_per_tgt));
+
+ c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer);
+ c->intr_timer_type = c->intr_timer_type;
+
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
+ "wq/wq_copy/rq %d/%d/%d\n",
+ fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
+ fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
+ c->wq_enet_desc_count, c->wq_copy_desc_count,
+ c->rq_desc_count);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC node wwn %llx port wwn %llx\n",
+ c->node_wwn, c->port_wwn);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC ed_tov %d ra_tov %d\n",
+ c->ed_tov, c->ra_tov);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC mtu %d intr timer %d\n",
+ c->maxdatafieldsize, c->intr_timer);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC flags 0x%x luns per tgt %d\n",
+ c->flags, c->luns_per_tgt);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC flogi_retries %d flogi timeout %d\n",
+ c->flogi_retries, c->flogi_timeout);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC plogi retries %d plogi timeout %d\n",
+ c->plogi_retries, c->plogi_timeout);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC io throttle count %d link dn timeout %d\n",
+ c->io_throttle_count, c->link_down_timeout);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "vNIC port dn io retries %d port dn timeout %d\n",
+ c->port_down_io_retries, c->port_down_timeout);
+
+ return 0;
+}
+
+int fnic_set_nic_config(struct fnic *fnic, u8 rss_default_cpu,
+ u8 rss_hash_type,
+ u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable,
+ u8 tso_ipid_split_en, u8 ig_vlan_strip_en)
+{
+ u64 a0, a1;
+ u32 nic_cfg;
+ int wait = 1000;
+
+ vnic_set_nic_cfg(&nic_cfg, rss_default_cpu,
+ rss_hash_type, rss_hash_bits, rss_base_cpu,
+ rss_enable, tso_ipid_split_en, ig_vlan_strip_en);
+
+ a0 = nic_cfg;
+ a1 = 0;
+
+ return vnic_dev_cmd(fnic->vdev, CMD_NIC_CFG, &a0, &a1, wait);
+}
+
+void fnic_get_res_counts(struct fnic *fnic)
+{
+ fnic->wq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_WQ);
+ fnic->raw_wq_count = fnic->wq_count - 1;
+ fnic->wq_copy_count = fnic->wq_count - fnic->raw_wq_count;
+ fnic->rq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_RQ);
+ fnic->cq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_CQ);
+ fnic->intr_count = vnic_dev_get_res_count(fnic->vdev,
+ RES_TYPE_INTR_CTRL);
+}
+
+void fnic_free_vnic_resources(struct fnic *fnic)
+{
+ unsigned int i;
+
+ for (i = 0; i < fnic->raw_wq_count; i++)
+ vnic_wq_free(&fnic->wq[i]);
+
+ for (i = 0; i < fnic->wq_copy_count; i++)
+ vnic_wq_copy_free(&fnic->wq_copy[i]);
+
+ for (i = 0; i < fnic->rq_count; i++)
+ vnic_rq_free(&fnic->rq[i]);
+
+ for (i = 0; i < fnic->cq_count; i++)
+ vnic_cq_free(&fnic->cq[i]);
+
+ for (i = 0; i < fnic->intr_count; i++)
+ vnic_intr_free(&fnic->intr[i]);
+}
+
+int fnic_alloc_vnic_resources(struct fnic *fnic)
+{
+ enum vnic_dev_intr_mode intr_mode;
+ unsigned int mask_on_assertion;
+ unsigned int interrupt_offset;
+ unsigned int error_interrupt_enable;
+ unsigned int error_interrupt_offset;
+ unsigned int i, cq_index;
+ unsigned int wq_copy_cq_desc_count;
+ int err;
+
+ intr_mode = vnic_dev_get_intr_mode(fnic->vdev);
+
+ shost_printk(KERN_INFO, fnic->lport->host, "vNIC interrupt mode: %s\n",
+ intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" :
+ intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" :
+ intr_mode == VNIC_DEV_INTR_MODE_MSIX ?
+ "MSI-X" : "unknown");
+
+ shost_printk(KERN_INFO, fnic->lport->host, "vNIC resources avail: "
+ "wq %d cp_wq %d raw_wq %d rq %d cq %d intr %d\n",
+ fnic->wq_count, fnic->wq_copy_count, fnic->raw_wq_count,
+ fnic->rq_count, fnic->cq_count, fnic->intr_count);
+
+ /* Allocate Raw WQ used for FCS frames */
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ err = vnic_wq_alloc(fnic->vdev, &fnic->wq[i], i,
+ fnic->config.wq_enet_desc_count,
+ sizeof(struct wq_enet_desc));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ /* Allocate Copy WQs used for SCSI IOs */
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ err = vnic_wq_copy_alloc(fnic->vdev, &fnic->wq_copy[i],
+ (fnic->raw_wq_count + i),
+ fnic->config.wq_copy_desc_count,
+ sizeof(struct fcpio_host_req));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ /* RQ for receiving FCS frames */
+ for (i = 0; i < fnic->rq_count; i++) {
+ err = vnic_rq_alloc(fnic->vdev, &fnic->rq[i], i,
+ fnic->config.rq_desc_count,
+ sizeof(struct rq_enet_desc));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ /* CQ for each RQ */
+ for (i = 0; i < fnic->rq_count; i++) {
+ cq_index = i;
+ err = vnic_cq_alloc(fnic->vdev,
+ &fnic->cq[cq_index], cq_index,
+ fnic->config.rq_desc_count,
+ sizeof(struct cq_enet_rq_desc));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ /* CQ for each WQ */
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ cq_index = fnic->rq_count + i;
+ err = vnic_cq_alloc(fnic->vdev, &fnic->cq[cq_index], cq_index,
+ fnic->config.wq_enet_desc_count,
+ sizeof(struct cq_enet_wq_desc));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ /* CQ for each COPY WQ */
+ wq_copy_cq_desc_count = (fnic->config.wq_copy_desc_count * 3);
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ cq_index = fnic->raw_wq_count + fnic->rq_count + i;
+ err = vnic_cq_alloc(fnic->vdev, &fnic->cq[cq_index],
+ cq_index,
+ wq_copy_cq_desc_count,
+ sizeof(struct fcpio_fw_req));
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ for (i = 0; i < fnic->intr_count; i++) {
+ err = vnic_intr_alloc(fnic->vdev, &fnic->intr[i], i);
+ if (err)
+ goto err_out_cleanup;
+ }
+
+ fnic->legacy_pba = vnic_dev_get_res(fnic->vdev,
+ RES_TYPE_INTR_PBA_LEGACY, 0);
+
+ if (!fnic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Failed to hook legacy pba resource\n");
+ err = -ENODEV;
+ goto err_out_cleanup;
+ }
+
+ /*
+ * Init RQ/WQ resources.
+ *
+ * RQ[0 to n-1] point to CQ[0 to n-1]
+ * WQ[0 to m-1] point to CQ[n to n+m-1]
+ * WQ_COPY[0 to k-1] points to CQ[n+m to n+m+k-1]
+ *
+ * Note for copy wq we always initialize with cq_index = 0
+ *
+ * Error interrupt is not enabled for MSI.
+ */
+
+ switch (intr_mode) {
+ case VNIC_DEV_INTR_MODE_INTX:
+ case VNIC_DEV_INTR_MODE_MSIX:
+ error_interrupt_enable = 1;
+ error_interrupt_offset = fnic->err_intr_offset;
+ break;
+ default:
+ error_interrupt_enable = 0;
+ error_interrupt_offset = 0;
+ break;
+ }
+
+ for (i = 0; i < fnic->rq_count; i++) {
+ cq_index = i;
+ vnic_rq_init(&fnic->rq[i],
+ cq_index,
+ error_interrupt_enable,
+ error_interrupt_offset);
+ }
+
+ for (i = 0; i < fnic->raw_wq_count; i++) {
+ cq_index = i + fnic->rq_count;
+ vnic_wq_init(&fnic->wq[i],
+ cq_index,
+ error_interrupt_enable,
+ error_interrupt_offset);
+ }
+
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ vnic_wq_copy_init(&fnic->wq_copy[i],
+ 0 /* cq_index 0 - always */,
+ error_interrupt_enable,
+ error_interrupt_offset);
+ }
+
+ for (i = 0; i < fnic->cq_count; i++) {
+
+ switch (intr_mode) {
+ case VNIC_DEV_INTR_MODE_MSIX:
+ interrupt_offset = i;
+ break;
+ default:
+ interrupt_offset = 0;
+ break;
+ }
+
+ vnic_cq_init(&fnic->cq[i],
+ 0 /* flow_control_enable */,
+ 1 /* color_enable */,
+ 0 /* cq_head */,
+ 0 /* cq_tail */,
+ 1 /* cq_tail_color */,
+ 1 /* interrupt_enable */,
+ 1 /* cq_entry_enable */,
+ 0 /* cq_message_enable */,
+ interrupt_offset,
+ 0 /* cq_message_addr */);
+ }
+
+ /*
+ * Init INTR resources
+ *
+ * mask_on_assertion is not used for INTx due to the level-
+ * triggered nature of INTx
+ */
+
+ switch (intr_mode) {
+ case VNIC_DEV_INTR_MODE_MSI:
+ case VNIC_DEV_INTR_MODE_MSIX:
+ mask_on_assertion = 1;
+ break;
+ default:
+ mask_on_assertion = 0;
+ break;
+ }
+
+ for (i = 0; i < fnic->intr_count; i++) {
+ vnic_intr_init(&fnic->intr[i],
+ fnic->config.intr_timer,
+ fnic->config.intr_timer_type,
+ mask_on_assertion);
+ }
+
+ /* init the stats memory by making the first call here */
+ err = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vnic_dev_stats_dump failed - x%x\n", err);
+ goto err_out_cleanup;
+ }
+
+ /* Clear LIF stats */
+ vnic_dev_stats_clear(fnic->vdev);
+
+ return 0;
+
+err_out_cleanup:
+ fnic_free_vnic_resources(fnic);
+
+ return err;
+}
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
new file mode 100644
index 000000000000..b6f310262534
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FNIC_RES_H_
+#define _FNIC_RES_H_
+
+#include "wq_enet_desc.h"
+#include "rq_enet_desc.h"
+#include "vnic_wq.h"
+#include "vnic_rq.h"
+#include "fnic_io.h"
+#include "fcpio.h"
+#include "vnic_wq_copy.h"
+#include "vnic_cq_copy.h"
+
+static inline void fnic_queue_wq_desc(struct vnic_wq *wq,
+ void *os_buf, dma_addr_t dma_addr,
+ unsigned int len, unsigned int fc_eof,
+ int vlan_tag_insert,
+ unsigned int vlan_tag,
+ int cq_entry, int sop, int eop)
+{
+ struct wq_enet_desc *desc = vnic_wq_next_desc(wq);
+
+ wq_enet_desc_enc(desc,
+ (u64)dma_addr | VNIC_PADDR_TARGET,
+ (u16)len,
+ 0, /* mss_or_csum_offset */
+ (u16)fc_eof,
+ 0, /* offload_mode */
+ (u8)eop, (u8)cq_entry,
+ 1, /* fcoe_encap */
+ (u8)vlan_tag_insert,
+ (u16)vlan_tag,
+ 0 /* loopback */);
+
+ vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop);
+}
+
+static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
+ u32 req_id,
+ u32 lunmap_id, u8 spl_flags,
+ u32 sgl_cnt, u32 sense_len,
+ u64 sgl_addr, u64 sns_addr,
+ u8 crn, u8 pri_ta,
+ u8 flags, u8 *scsi_cdb,
+ u32 data_len, u8 *lun,
+ u32 d_id, u16 mss,
+ u32 ratov, u32 edtov)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_ICMND_16; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.icmnd_16.lunmap_id = lunmap_id; /* index into lunmap table */
+ desc->u.icmnd_16.special_req_flags = spl_flags; /* exch req flags */
+ desc->u.icmnd_16._resvd0[0] = 0; /* reserved */
+ desc->u.icmnd_16._resvd0[1] = 0; /* reserved */
+ desc->u.icmnd_16._resvd0[2] = 0; /* reserved */
+ desc->u.icmnd_16.sgl_cnt = sgl_cnt; /* scatter-gather list count */
+ desc->u.icmnd_16.sense_len = sense_len; /* sense buffer length */
+ desc->u.icmnd_16.sgl_addr = sgl_addr; /* scatter-gather list addr */
+ desc->u.icmnd_16.sense_addr = sns_addr; /* sense buffer address */
+ desc->u.icmnd_16.crn = crn; /* SCSI Command Reference No.*/
+ desc->u.icmnd_16.pri_ta = pri_ta; /* SCSI Pri & Task attribute */
+ desc->u.icmnd_16._resvd1 = 0; /* reserved: should be 0 */
+ desc->u.icmnd_16.flags = flags; /* command flags */
+ memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, CDB_16); /* SCSI CDB */
+ desc->u.icmnd_16.data_len = data_len; /* length of data expected */
+ memcpy(desc->u.icmnd_16.lun, lun, LUN_ADDRESS); /* LUN address */
+ desc->u.icmnd_16._resvd2 = 0; /* reserved */
+ hton24(desc->u.icmnd_16.d_id, d_id); /* FC vNIC only: Target D_ID */
+ desc->u.icmnd_16.mss = mss; /* FC vNIC only: max burst */
+ desc->u.icmnd_16.r_a_tov = ratov; /*FC vNIC only: Res. Alloc Timeout */
+ desc->u.icmnd_16.e_d_tov = edtov; /*FC vNIC only: Err Detect Timeout */
+
+ vnic_wq_copy_post(wq);
+}
+
+static inline void fnic_queue_wq_copy_desc_itmf(struct vnic_wq_copy *wq,
+ u32 req_id, u32 lunmap_id,
+ u32 tm_req, u32 tm_id, u8 *lun,
+ u32 d_id, u32 r_a_tov,
+ u32 e_d_tov)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_ITMF; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.itmf.lunmap_id = lunmap_id; /* index into lunmap table */
+ desc->u.itmf.tm_req = tm_req; /* SCSI Task Management request */
+ desc->u.itmf.t_tag = tm_id; /* tag of fcpio to be aborted */
+ desc->u.itmf._resvd = 0;
+ memcpy(desc->u.itmf.lun, lun, LUN_ADDRESS); /* LUN address */
+ desc->u.itmf._resvd1 = 0;
+ hton24(desc->u.itmf.d_id, d_id); /* FC vNIC only: Target D_ID */
+ desc->u.itmf.r_a_tov = r_a_tov; /* FC vNIC only: R_A_TOV in msec */
+ desc->u.itmf.e_d_tov = e_d_tov; /* FC vNIC only: E_D_TOV in msec */
+
+ vnic_wq_copy_post(wq);
+}
+
+static inline void fnic_queue_wq_copy_desc_flogi_reg(struct vnic_wq_copy *wq,
+ u32 req_id, u8 format,
+ u32 s_id, u8 *gw_mac)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_FLOGI_REG; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.flogi_reg.format = format;
+ hton24(desc->u.flogi_reg.s_id, s_id);
+ memcpy(desc->u.flogi_reg.gateway_mac, gw_mac, ETH_ALEN);
+
+ vnic_wq_copy_post(wq);
+}
+
+static inline void fnic_queue_wq_copy_desc_fw_reset(struct vnic_wq_copy *wq,
+ u32 req_id)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_RESET; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ vnic_wq_copy_post(wq);
+}
+
+static inline void fnic_queue_wq_copy_desc_lunmap(struct vnic_wq_copy *wq,
+ u32 req_id, u64 lunmap_addr,
+ u32 lunmap_len)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_LUNMAP_REQ; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.lunmap_req.addr = lunmap_addr; /* address of the buffer */
+ desc->u.lunmap_req.len = lunmap_len; /* len of the buffer */
+
+ vnic_wq_copy_post(wq);
+}
+
+static inline void fnic_queue_rq_desc(struct vnic_rq *rq,
+ void *os_buf, dma_addr_t dma_addr,
+ u16 len)
+{
+ struct rq_enet_desc *desc = vnic_rq_next_desc(rq);
+
+ rq_enet_desc_enc(desc,
+ (u64)dma_addr | VNIC_PADDR_TARGET,
+ RQ_ENET_TYPE_ONLY_SOP,
+ (u16)len);
+
+ vnic_rq_post(rq, os_buf, 0, dma_addr, len);
+}
+
+
+struct fnic;
+
+int fnic_get_vnic_config(struct fnic *);
+int fnic_alloc_vnic_resources(struct fnic *);
+void fnic_free_vnic_resources(struct fnic *);
+void fnic_get_res_counts(struct fnic *);
+int fnic_set_nic_config(struct fnic *fnic, u8 rss_default_cpu,
+ u8 rss_hash_type, u8 rss_hash_bits, u8 rss_base_cpu,
+ u8 rss_enable, u8 tso_ipid_split_en,
+ u8 ig_vlan_strip_en);
+
+#endif /* _FNIC_RES_H_ */
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
new file mode 100644
index 000000000000..eabf36502856
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -0,0 +1,1850 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/delay.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fcoe.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include "fnic_io.h"
+#include "fnic.h"
+
+const char *fnic_state_str[] = {
+ [FNIC_IN_FC_MODE] = "FNIC_IN_FC_MODE",
+ [FNIC_IN_FC_TRANS_ETH_MODE] = "FNIC_IN_FC_TRANS_ETH_MODE",
+ [FNIC_IN_ETH_MODE] = "FNIC_IN_ETH_MODE",
+ [FNIC_IN_ETH_TRANS_FC_MODE] = "FNIC_IN_ETH_TRANS_FC_MODE",
+};
+
+static const char *fnic_ioreq_state_str[] = {
+ [FNIC_IOREQ_CMD_PENDING] = "FNIC_IOREQ_CMD_PENDING",
+ [FNIC_IOREQ_ABTS_PENDING] = "FNIC_IOREQ_ABTS_PENDING",
+ [FNIC_IOREQ_ABTS_COMPLETE] = "FNIC_IOREQ_ABTS_COMPLETE",
+ [FNIC_IOREQ_CMD_COMPLETE] = "FNIC_IOREQ_CMD_COMPLETE",
+};
+
+static const char *fcpio_status_str[] = {
+ [FCPIO_SUCCESS] = "FCPIO_SUCCESS", /*0x0*/
+ [FCPIO_INVALID_HEADER] = "FCPIO_INVALID_HEADER",
+ [FCPIO_OUT_OF_RESOURCE] = "FCPIO_OUT_OF_RESOURCE",
+ [FCPIO_INVALID_PARAM] = "FCPIO_INVALID_PARAM]",
+ [FCPIO_REQ_NOT_SUPPORTED] = "FCPIO_REQ_NOT_SUPPORTED",
+ [FCPIO_IO_NOT_FOUND] = "FCPIO_IO_NOT_FOUND",
+ [FCPIO_ABORTED] = "FCPIO_ABORTED", /*0x41*/
+ [FCPIO_TIMEOUT] = "FCPIO_TIMEOUT",
+ [FCPIO_SGL_INVALID] = "FCPIO_SGL_INVALID",
+ [FCPIO_MSS_INVALID] = "FCPIO_MSS_INVALID",
+ [FCPIO_DATA_CNT_MISMATCH] = "FCPIO_DATA_CNT_MISMATCH",
+ [FCPIO_FW_ERR] = "FCPIO_FW_ERR",
+ [FCPIO_ITMF_REJECTED] = "FCPIO_ITMF_REJECTED",
+ [FCPIO_ITMF_FAILED] = "FCPIO_ITMF_FAILED",
+ [FCPIO_ITMF_INCORRECT_LUN] = "FCPIO_ITMF_INCORRECT_LUN",
+ [FCPIO_CMND_REJECTED] = "FCPIO_CMND_REJECTED",
+ [FCPIO_NO_PATH_AVAIL] = "FCPIO_NO_PATH_AVAIL",
+ [FCPIO_PATH_FAILED] = "FCPIO_PATH_FAILED",
+ [FCPIO_LUNMAP_CHNG_PEND] = "FCPIO_LUNHMAP_CHNG_PEND",
+};
+
+const char *fnic_state_to_str(unsigned int state)
+{
+ if (state >= ARRAY_SIZE(fnic_state_str) || !fnic_state_str[state])
+ return "unknown";
+
+ return fnic_state_str[state];
+}
+
+static const char *fnic_ioreq_state_to_str(unsigned int state)
+{
+ if (state >= ARRAY_SIZE(fnic_ioreq_state_str) ||
+ !fnic_ioreq_state_str[state])
+ return "unknown";
+
+ return fnic_ioreq_state_str[state];
+}
+
+static const char *fnic_fcpio_status_to_str(unsigned int status)
+{
+ if (status >= ARRAY_SIZE(fcpio_status_str) || !fcpio_status_str[status])
+ return "unknown";
+
+ return fcpio_status_str[status];
+}
+
+static void fnic_cleanup_io(struct fnic *fnic, int exclude_id);
+
+static inline spinlock_t *fnic_io_lock_hash(struct fnic *fnic,
+ struct scsi_cmnd *sc)
+{
+ u32 hash = sc->request->tag & (FNIC_IO_LOCKS - 1);
+
+ return &fnic->io_req_lock[hash];
+}
+
+/*
+ * Unmap the data buffer and sense buffer for an io_req,
+ * also unmap and free the device-private scatter/gather list.
+ */
+static void fnic_release_ioreq_buf(struct fnic *fnic,
+ struct fnic_io_req *io_req,
+ struct scsi_cmnd *sc)
+{
+ if (io_req->sgl_list_pa)
+ pci_unmap_single(fnic->pdev, io_req->sgl_list_pa,
+ sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt,
+ PCI_DMA_TODEVICE);
+ scsi_dma_unmap(sc);
+
+ if (io_req->sgl_cnt)
+ mempool_free(io_req->sgl_list_alloc,
+ fnic->io_sgl_pool[io_req->sgl_type]);
+ if (io_req->sense_buf_pa)
+ pci_unmap_single(fnic->pdev, io_req->sense_buf_pa,
+ SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
+}
+
+/* Free up Copy Wq descriptors. Called with copy_wq lock held */
+static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq)
+{
+ /* if no Ack received from firmware, then nothing to clean */
+ if (!fnic->fw_ack_recd[0])
+ return 1;
+
+ /*
+ * Update desc_available count based on number of freed descriptors
+ * Account for wraparound
+ */
+ if (wq->to_clean_index <= fnic->fw_ack_index[0])
+ wq->ring.desc_avail += (fnic->fw_ack_index[0]
+ - wq->to_clean_index + 1);
+ else
+ wq->ring.desc_avail += (wq->ring.desc_count
+ - wq->to_clean_index
+ + fnic->fw_ack_index[0] + 1);
+
+ /*
+ * just bump clean index to ack_index+1 accounting for wraparound
+ * this will essentially free up all descriptors between
+ * to_clean_index and fw_ack_index, both inclusive
+ */
+ wq->to_clean_index =
+ (fnic->fw_ack_index[0] + 1) % wq->ring.desc_count;
+
+ /* we have processed the acks received so far */
+ fnic->fw_ack_recd[0] = 0;
+ return 0;
+}
+
+
+/*
+ * fnic_fw_reset_handler
+ * Routine to send reset msg to fw
+ */
+int fnic_fw_reset_handler(struct fnic *fnic)
+{
+ struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic, wq);
+
+ if (!vnic_wq_copy_desc_avail(wq))
+ ret = -EAGAIN;
+ else
+ fnic_queue_wq_copy_desc_fw_reset(wq, SCSI_NO_TAG);
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+
+ if (!ret)
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Issued fw reset\n");
+ else
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Failed to issue fw reset\n");
+ return ret;
+}
+
+
+/*
+ * fnic_flogi_reg_handler
+ * Routine to send flogi register msg to fw
+ */
+int fnic_flogi_reg_handler(struct fnic *fnic)
+{
+ struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ u8 gw_mac[ETH_ALEN];
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic, wq);
+
+ if (!vnic_wq_copy_desc_avail(wq)) {
+ ret = -EAGAIN;
+ goto flogi_reg_ioreq_end;
+ }
+
+ if (fnic->fcoui_mode)
+ memset(gw_mac, 0xff, ETH_ALEN);
+ else
+ memcpy(gw_mac, fnic->dest_addr, ETH_ALEN);
+
+ fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
+ FCPIO_FLOGI_REG_GW_DEST,
+ fnic->s_id,
+ gw_mac);
+
+flogi_reg_ioreq_end:
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+
+ if (!ret)
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "flog reg issued\n");
+
+ return ret;
+}
+
+/*
+ * fnic_queue_wq_copy_desc
+ * Routine to enqueue a wq copy desc
+ */
+static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
+ struct vnic_wq_copy *wq,
+ struct fnic_io_req *io_req,
+ struct scsi_cmnd *sc,
+ u32 sg_count)
+{
+ struct scatterlist *sg;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct host_sg_desc *desc;
+ u8 pri_tag = 0;
+ unsigned int i;
+ unsigned long intr_flags;
+ int flags;
+ u8 exch_flags;
+ struct scsi_lun fc_lun;
+ char msg[2];
+
+ if (sg_count) {
+ BUG_ON(sg_count < 0);
+ BUG_ON(sg_count > FNIC_MAX_SG_DESC_CNT);
+
+ /* For each SGE, create a device desc entry */
+ desc = io_req->sgl_list;
+ for_each_sg(scsi_sglist(sc), sg, sg_count, i) {
+ desc->addr = cpu_to_le64(sg_dma_address(sg));
+ desc->len = cpu_to_le32(sg_dma_len(sg));
+ desc->_resvd = 0;
+ desc++;
+ }
+
+ io_req->sgl_list_pa = pci_map_single
+ (fnic->pdev,
+ io_req->sgl_list,
+ sizeof(io_req->sgl_list[0]) * sg_count,
+ PCI_DMA_TODEVICE);
+ }
+
+ io_req->sense_buf_pa = pci_map_single(fnic->pdev,
+ sc->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ int_to_scsilun(sc->device->lun, &fc_lun);
+
+ pri_tag = FCPIO_ICMND_PTA_SIMPLE;
+ msg[0] = MSG_SIMPLE_TAG;
+ scsi_populate_tag_msg(sc, msg);
+ if (msg[0] == MSG_ORDERED_TAG)
+ pri_tag = FCPIO_ICMND_PTA_ORDERED;
+
+ /* Enqueue the descriptor in the Copy WQ */
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic, wq);
+
+ if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ flags = 0;
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ flags = FCPIO_ICMND_RDDATA;
+ else if (sc->sc_data_direction == DMA_TO_DEVICE)
+ flags = FCPIO_ICMND_WRDATA;
+
+ exch_flags = 0;
+ if ((fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) &&
+ (rp->flags & FC_RP_FLAGS_RETRY))
+ exch_flags |= FCPIO_ICMND_SRFLAG_RETRY;
+
+ fnic_queue_wq_copy_desc_icmnd_16(wq, sc->request->tag,
+ 0, exch_flags, io_req->sgl_cnt,
+ SCSI_SENSE_BUFFERSIZE,
+ io_req->sgl_list_pa,
+ io_req->sense_buf_pa,
+ 0, /* scsi cmd ref, always 0 */
+ pri_tag, /* scsi pri and tag */
+ flags, /* command flags */
+ sc->cmnd, scsi_bufflen(sc),
+ fc_lun.scsi_lun, io_req->port_id,
+ rport->maxframe_size, rp->r_a_tov,
+ rp->e_d_tov);
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
+ return 0;
+}
+
+/*
+ * fnic_queuecommand
+ * Routine to send a scsi cdb
+ * Called with host_lock held and interrupts disabled.
+ */
+int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+{
+ struct fc_lport *lp;
+ struct fc_rport *rport;
+ struct fnic_io_req *io_req;
+ struct fnic *fnic;
+ struct vnic_wq_copy *wq;
+ int ret;
+ u32 sg_count;
+ unsigned long flags;
+ unsigned long ptr;
+
+ rport = starget_to_rport(scsi_target(sc->device));
+ ret = fc_remote_port_chkready(rport);
+ if (ret) {
+ sc->result = ret;
+ done(sc);
+ return 0;
+ }
+
+ lp = shost_priv(sc->device->host);
+ if (lp->state != LPORT_ST_READY || !(lp->link_up))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ /*
+ * Release host lock, use driver resource specific locks from here.
+ * Don't re-enable interrupts in case they were disabled prior to the
+ * caller disabling them.
+ */
+ spin_unlock(lp->host->host_lock);
+
+ /* Get a new io_req for this SCSI IO */
+ fnic = lport_priv(lp);
+
+ io_req = mempool_alloc(fnic->io_req_pool, GFP_ATOMIC);
+ if (!io_req) {
+ ret = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+ memset(io_req, 0, sizeof(*io_req));
+
+ /* Map the data buffer */
+ sg_count = scsi_dma_map(sc);
+ if (sg_count < 0) {
+ mempool_free(io_req, fnic->io_req_pool);
+ goto out;
+ }
+
+ /* Determine the type of scatter/gather list we need */
+ io_req->sgl_cnt = sg_count;
+ io_req->sgl_type = FNIC_SGL_CACHE_DFLT;
+ if (sg_count > FNIC_DFLT_SG_DESC_CNT)
+ io_req->sgl_type = FNIC_SGL_CACHE_MAX;
+
+ if (sg_count) {
+ io_req->sgl_list =
+ mempool_alloc(fnic->io_sgl_pool[io_req->sgl_type],
+ GFP_ATOMIC | GFP_DMA);
+ if (!io_req->sgl_list) {
+ ret = SCSI_MLQUEUE_HOST_BUSY;
+ scsi_dma_unmap(sc);
+ mempool_free(io_req, fnic->io_req_pool);
+ goto out;
+ }
+
+ /* Cache sgl list allocated address before alignment */
+ io_req->sgl_list_alloc = io_req->sgl_list;
+ ptr = (unsigned long) io_req->sgl_list;
+ if (ptr % FNIC_SG_DESC_ALIGN) {
+ io_req->sgl_list = (struct host_sg_desc *)
+ (((unsigned long) ptr
+ + FNIC_SG_DESC_ALIGN - 1)
+ & ~(FNIC_SG_DESC_ALIGN - 1));
+ }
+ }
+
+ /* initialize rest of io_req */
+ io_req->port_id = rport->port_id;
+ CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
+ CMD_SP(sc) = (char *)io_req;
+ sc->scsi_done = done;
+
+ /* create copy wq desc and enqueue it */
+ wq = &fnic->wq_copy[0];
+ ret = fnic_queue_wq_copy_desc(fnic, wq, io_req, sc, sg_count);
+ if (ret) {
+ /*
+ * In case another thread cancelled the request,
+ * refetch the pointer under the lock.
+ */
+ spinlock_t *io_lock = fnic_io_lock_hash(fnic, sc);
+
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ CMD_SP(sc) = NULL;
+ CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
+ spin_unlock_irqrestore(io_lock, flags);
+ if (io_req) {
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+ }
+ }
+out:
+ /* acquire host lock before returning to SCSI */
+ spin_lock(lp->host->host_lock);
+ return ret;
+}
+
+/*
+ * fnic_fcpio_fw_reset_cmpl_handler
+ * Routine to handle fw reset completion
+ */
+static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
+ struct fcpio_fw_req *desc)
+{
+ u8 type;
+ u8 hdr_status;
+ struct fcpio_tag tag;
+ int ret = 0;
+ struct fc_frame *flogi;
+ unsigned long flags;
+
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
+
+ /* Clean up all outstanding io requests */
+ fnic_cleanup_io(fnic, SCSI_NO_TAG);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ flogi = fnic->flogi;
+ fnic->flogi = NULL;
+
+ /* fnic should be in FC_TRANS_ETH_MODE */
+ if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
+ /* Check status of reset completion */
+ if (!hdr_status) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "reset cmpl success\n");
+ /* Ready to send flogi out */
+ fnic->state = FNIC_IN_ETH_MODE;
+ } else {
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "fnic fw_reset : failed %s\n",
+ fnic_fcpio_status_to_str(hdr_status));
+
+ /*
+ * Unable to change to eth mode, cannot send out flogi
+ * Change state to fc mode, so that subsequent Flogi
+ * requests from libFC will cause more attempts to
+ * reset the firmware. Free the cached flogi
+ */
+ fnic->state = FNIC_IN_FC_MODE;
+ ret = -1;
+ }
+ } else {
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "Unexpected state %s while processing"
+ " reset cmpl\n", fnic_state_to_str(fnic->state));
+ ret = -1;
+ }
+
+ /* Thread removing device blocks till firmware reset is complete */
+ if (fnic->remove_wait)
+ complete(fnic->remove_wait);
+
+ /*
+ * If fnic is being removed, or fw reset failed
+ * free the flogi frame. Else, send it out
+ */
+ if (fnic->remove_wait || ret) {
+ fnic->flogi_oxid = FC_XID_UNKNOWN;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (flogi)
+ dev_kfree_skb_irq(fp_skb(flogi));
+ goto reset_cmpl_handler_end;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (flogi)
+ ret = fnic_send_frame(fnic, flogi);
+
+ reset_cmpl_handler_end:
+ return ret;
+}
+
+/*
+ * fnic_fcpio_flogi_reg_cmpl_handler
+ * Routine to handle flogi register completion
+ */
+static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
+ struct fcpio_fw_req *desc)
+{
+ u8 type;
+ u8 hdr_status;
+ struct fcpio_tag tag;
+ int ret = 0;
+ struct fc_frame *flogi_resp = NULL;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
+
+ /* Update fnic state based on status of flogi reg completion */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ flogi_resp = fnic->flogi_resp;
+ fnic->flogi_resp = NULL;
+
+ if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) {
+
+ /* Check flogi registration completion status */
+ if (!hdr_status) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "flog reg succeeded\n");
+ fnic->state = FNIC_IN_FC_MODE;
+ } else {
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "fnic flogi reg :failed %s\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ fnic->state = FNIC_IN_ETH_MODE;
+ ret = -1;
+ }
+ } else {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Unexpected fnic state %s while"
+ " processing flogi reg completion\n",
+ fnic_state_to_str(fnic->state));
+ ret = -1;
+ }
+
+ /* Successful flogi reg cmpl, pass frame to LibFC */
+ if (!ret && flogi_resp) {
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto reg_cmpl_handler_end;
+ }
+ skb = (struct sk_buff *)flogi_resp;
+ /* Use fr_flags to indicate whether flogi resp or not */
+ fr_flags(flogi_resp) = 1;
+ fr_dev(flogi_resp) = fnic->lport;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ skb_queue_tail(&fnic->frame_queue, skb);
+ queue_work(fnic_event_queue, &fnic->frame_work);
+
+ } else {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (flogi_resp)
+ dev_kfree_skb_irq(fp_skb(flogi_resp));
+ }
+
+reg_cmpl_handler_end:
+ return ret;
+}
+
+static inline int is_ack_index_in_range(struct vnic_wq_copy *wq,
+ u16 request_out)
+{
+ if (wq->to_clean_index <= wq->to_use_index) {
+ /* out of range, stale request_out index */
+ if (request_out < wq->to_clean_index ||
+ request_out >= wq->to_use_index)
+ return 0;
+ } else {
+ /* out of range, stale request_out index */
+ if (request_out < wq->to_clean_index &&
+ request_out >= wq->to_use_index)
+ return 0;
+ }
+ /* request_out index is in range */
+ return 1;
+}
+
+
+/*
+ * Mark that ack received and store the Ack index. If there are multiple
+ * acks received before Tx thread cleans it up, the latest value will be
+ * used which is correct behavior. This state should be in the copy Wq
+ * instead of in the fnic
+ */
+static inline void fnic_fcpio_ack_handler(struct fnic *fnic,
+ unsigned int cq_index,
+ struct fcpio_fw_req *desc)
+{
+ struct vnic_wq_copy *wq;
+ u16 request_out = desc->u.ack.request_out;
+ unsigned long flags;
+
+ /* mark the ack state */
+ wq = &fnic->wq_copy[cq_index - fnic->raw_wq_count - fnic->rq_count];
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+
+ if (is_ack_index_in_range(wq, request_out)) {
+ fnic->fw_ack_index[0] = request_out;
+ fnic->fw_ack_recd[0] = 1;
+ }
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+}
+
+/*
+ * fnic_fcpio_icmnd_cmpl_handler
+ * Routine to handle icmnd completions
+ */
+static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
+ struct fcpio_fw_req *desc)
+{
+ u8 type;
+ u8 hdr_status;
+ struct fcpio_tag tag;
+ u32 id;
+ u64 xfer_len = 0;
+ struct fcpio_icmnd_cmpl *icmnd_cmpl;
+ struct fnic_io_req *io_req;
+ struct scsi_cmnd *sc;
+ unsigned long flags;
+ spinlock_t *io_lock;
+
+ /* Decode the cmpl description to get the io_req id */
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
+ fcpio_tag_id_dec(&tag, &id);
+
+ if (id >= FNIC_MAX_IO_REQ)
+ return;
+
+ sc = scsi_host_find_tag(fnic->lport->host, id);
+ WARN_ON_ONCE(!sc);
+ if (!sc)
+ return;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ WARN_ON_ONCE(!io_req);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ return;
+ }
+
+ /* firmware completed the io */
+ io_req->io_completed = 1;
+
+ /*
+ * if SCSI-ML has already issued abort on this command,
+ * ignore completion of the IO. The abts path will clean it up
+ */
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ return;
+ }
+
+ /* Mark the IO as complete */
+ CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
+
+ icmnd_cmpl = &desc->u.icmnd_cmpl;
+
+ switch (hdr_status) {
+ case FCPIO_SUCCESS:
+ sc->result = (DID_OK << 16) | icmnd_cmpl->scsi_status;
+ xfer_len = scsi_bufflen(sc);
+ scsi_set_resid(sc, icmnd_cmpl->residual);
+
+ if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_UNDER)
+ xfer_len -= icmnd_cmpl->residual;
+
+ /*
+ * If queue_full, then try to reduce queue depth for all
+ * LUNS on the target. Todo: this should be accompanied
+ * by a periodic queue_depth rampup based on successful
+ * IO completion.
+ */
+ if (icmnd_cmpl->scsi_status == QUEUE_FULL) {
+ struct scsi_device *t_sdev;
+ int qd = 0;
+
+ shost_for_each_device(t_sdev, sc->device->host) {
+ if (t_sdev->id != sc->device->id)
+ continue;
+
+ if (t_sdev->queue_depth > 1) {
+ qd = scsi_track_queue_full
+ (t_sdev,
+ t_sdev->queue_depth - 1);
+ if (qd == -1)
+ qd = t_sdev->host->cmd_per_lun;
+ shost_printk(KERN_INFO,
+ fnic->lport->host,
+ "scsi[%d:%d:%d:%d"
+ "] queue full detected,"
+ "new depth = %d\n",
+ t_sdev->host->host_no,
+ t_sdev->channel,
+ t_sdev->id, t_sdev->lun,
+ t_sdev->queue_depth);
+ }
+ }
+ }
+ break;
+
+ case FCPIO_TIMEOUT: /* request was timed out */
+ sc->result = (DID_TIME_OUT << 16) | icmnd_cmpl->scsi_status;
+ break;
+
+ case FCPIO_ABORTED: /* request was aborted */
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
+ case FCPIO_DATA_CNT_MISMATCH: /* recv/sent more/less data than exp. */
+ scsi_set_resid(sc, icmnd_cmpl->residual);
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
+ case FCPIO_OUT_OF_RESOURCE: /* out of resources to complete request */
+ sc->result = (DID_REQUEUE << 16) | icmnd_cmpl->scsi_status;
+ break;
+ case FCPIO_INVALID_HEADER: /* header contains invalid data */
+ case FCPIO_INVALID_PARAM: /* some parameter in request invalid */
+ case FCPIO_REQ_NOT_SUPPORTED:/* request type is not supported */
+ case FCPIO_IO_NOT_FOUND: /* requested I/O was not found */
+ case FCPIO_SGL_INVALID: /* request was aborted due to sgl error */
+ case FCPIO_MSS_INVALID: /* request was aborted due to mss error */
+ case FCPIO_FW_ERR: /* request was terminated due fw error */
+ default:
+ shost_printk(KERN_ERR, fnic->lport->host, "hdr status = %s\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+ }
+
+ /* Break link with the SCSI command */
+ CMD_SP(sc) = NULL;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+
+ mempool_free(io_req, fnic->io_req_pool);
+
+ if (sc->sc_data_direction == DMA_FROM_DEVICE) {
+ fnic->lport->host_stats.fcp_input_requests++;
+ fnic->fcp_input_bytes += xfer_len;
+ } else if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ fnic->lport->host_stats.fcp_output_requests++;
+ fnic->fcp_output_bytes += xfer_len;
+ } else
+ fnic->lport->host_stats.fcp_control_requests++;
+
+ /* Call SCSI completion function to complete the IO */
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+
+}
+
+/* fnic_fcpio_itmf_cmpl_handler
+ * Routine to handle itmf completions
+ */
+static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
+ struct fcpio_fw_req *desc)
+{
+ u8 type;
+ u8 hdr_status;
+ struct fcpio_tag tag;
+ u32 id;
+ struct scsi_cmnd *sc;
+ struct fnic_io_req *io_req;
+ unsigned long flags;
+ spinlock_t *io_lock;
+
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
+ fcpio_tag_id_dec(&tag, &id);
+
+ if ((id & FNIC_TAG_MASK) >= FNIC_MAX_IO_REQ)
+ return;
+
+ sc = scsi_host_find_tag(fnic->lport->host, id & FNIC_TAG_MASK);
+ WARN_ON_ONCE(!sc);
+ if (!sc)
+ return;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ WARN_ON_ONCE(!io_req);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ return;
+ }
+
+ if (id & FNIC_TAG_ABORT) {
+ /* Completion of abort cmd */
+ if (CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING) {
+ /* This is a late completion. Ignore it */
+ spin_unlock_irqrestore(io_lock, flags);
+ return;
+ }
+ CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE;
+ CMD_ABTS_STATUS(sc) = hdr_status;
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "abts cmpl recd. id %d status %s\n",
+ (int)(id & FNIC_TAG_MASK),
+ fnic_fcpio_status_to_str(hdr_status));
+
+ /*
+ * If scsi_eh thread is blocked waiting for abts to complete,
+ * signal completion to it. IO will be cleaned in the thread
+ * else clean it in this context
+ */
+ if (io_req->abts_done) {
+ complete(io_req->abts_done);
+ spin_unlock_irqrestore(io_lock, flags);
+ } else {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "abts cmpl, completing IO\n");
+ CMD_SP(sc) = NULL;
+ sc->result = (DID_ERROR << 16);
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+ }
+
+ } else if (id & FNIC_TAG_DEV_RST) {
+ /* Completion of device reset */
+ CMD_LR_STATUS(sc) = hdr_status;
+ CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "dev reset cmpl recd. id %d status %s\n",
+ (int)(id & FNIC_TAG_MASK),
+ fnic_fcpio_status_to_str(hdr_status));
+ if (io_req->dr_done)
+ complete(io_req->dr_done);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ } else {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unexpected itmf io state %s tag %x\n",
+ fnic_ioreq_state_to_str(CMD_STATE(sc)), id);
+ spin_unlock_irqrestore(io_lock, flags);
+ }
+
+}
+
+/*
+ * fnic_fcpio_cmpl_handler
+ * Routine to service the cq for wq_copy
+ */
+static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
+ unsigned int cq_index,
+ struct fcpio_fw_req *desc)
+{
+ struct fnic *fnic = vnic_dev_priv(vdev);
+ int ret = 0;
+
+ switch (desc->hdr.type) {
+ case FCPIO_ACK: /* fw copied copy wq desc to its queue */
+ fnic_fcpio_ack_handler(fnic, cq_index, desc);
+ break;
+
+ case FCPIO_ICMND_CMPL: /* fw completed a command */
+ fnic_fcpio_icmnd_cmpl_handler(fnic, desc);
+ break;
+
+ case FCPIO_ITMF_CMPL: /* fw completed itmf (abort cmd, lun reset)*/
+ fnic_fcpio_itmf_cmpl_handler(fnic, desc);
+ break;
+
+ case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
+ ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
+ break;
+
+ case FCPIO_RESET_CMPL: /* fw completed reset */
+ ret = fnic_fcpio_fw_reset_cmpl_handler(fnic, desc);
+ break;
+
+ default:
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "firmware completion type %d\n",
+ desc->hdr.type);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * fnic_wq_copy_cmpl_handler
+ * Routine to process wq copy
+ */
+int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do)
+{
+ unsigned int wq_work_done = 0;
+ unsigned int i, cq_index;
+ unsigned int cur_work_done;
+
+ for (i = 0; i < fnic->wq_copy_count; i++) {
+ cq_index = i + fnic->raw_wq_count + fnic->rq_count;
+ cur_work_done = vnic_cq_copy_service(&fnic->cq[cq_index],
+ fnic_fcpio_cmpl_handler,
+ copy_work_to_do);
+ wq_work_done += cur_work_done;
+ }
+ return wq_work_done;
+}
+
+static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
+{
+ unsigned int i;
+ struct fnic_io_req *io_req;
+ unsigned long flags = 0;
+ struct scsi_cmnd *sc;
+ spinlock_t *io_lock;
+
+ for (i = 0; i < FNIC_MAX_IO_REQ; i++) {
+ if (i == exclude_id)
+ continue;
+
+ sc = scsi_host_find_tag(fnic->lport->host, i);
+ if (!sc)
+ continue;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto cleanup_scsi_cmd;
+ }
+
+ CMD_SP(sc) = NULL;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /*
+ * If there is a scsi_cmnd associated with this io_req, then
+ * free the corresponding state
+ */
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+
+cleanup_scsi_cmd:
+ sc->result = DID_TRANSPORT_DISRUPTED << 16;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "fnic_cleanup_io:"
+ " DID_TRANSPORT_DISRUPTED\n");
+
+ /* Complete the command to SCSI */
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+ }
+}
+
+void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
+ struct fcpio_host_req *desc)
+{
+ u32 id;
+ struct fnic *fnic = vnic_dev_priv(wq->vdev);
+ struct fnic_io_req *io_req;
+ struct scsi_cmnd *sc;
+ unsigned long flags;
+ spinlock_t *io_lock;
+
+ /* get the tag reference */
+ fcpio_tag_id_dec(&desc->hdr.tag, &id);
+ id &= FNIC_TAG_MASK;
+
+ if (id >= FNIC_MAX_IO_REQ)
+ return;
+
+ sc = scsi_host_find_tag(fnic->lport->host, id);
+ if (!sc)
+ return;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
+ /* Get the IO context which this desc refers to */
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+ /* fnic interrupts are turned off by now */
+
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto wq_copy_cleanup_scsi_cmd;
+ }
+
+ CMD_SP(sc) = NULL;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+
+wq_copy_cleanup_scsi_cmd:
+ sc->result = DID_NO_CONNECT << 16;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "wq_copy_cleanup_handler:"
+ " DID_NO_CONNECT\n");
+
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+}
+
+static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag,
+ u32 task_req, u8 *fc_lun,
+ struct fnic_io_req *io_req)
+{
+ struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic, wq);
+
+ if (!vnic_wq_copy_desc_avail(wq)) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+ return 1;
+ }
+ fnic_queue_wq_copy_desc_itmf(wq, tag | FNIC_TAG_ABORT,
+ 0, task_req, tag, fc_lun, io_req->port_id,
+ fnic->config.ra_tov, fnic->config.ed_tov);
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+ return 0;
+}
+
+void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
+{
+ int tag;
+ struct fnic_io_req *io_req;
+ spinlock_t *io_lock;
+ unsigned long flags;
+ struct scsi_cmnd *sc;
+ struct scsi_lun fc_lun;
+ enum fnic_ioreq_state old_ioreq_state;
+
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "fnic_rport_reset_exch called portid 0x%06x\n",
+ port_id);
+
+ if (fnic->in_remove)
+ return;
+
+ for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
+ sc = scsi_host_find_tag(fnic->lport->host, tag);
+ if (!sc)
+ continue;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+ if (!io_req || io_req->port_id != port_id) {
+ spin_unlock_irqrestore(io_lock, flags);
+ continue;
+ }
+
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to rport that went away
+ */
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ continue;
+ }
+ old_ioreq_state = CMD_STATE(sc);
+ CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
+ CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+
+ BUG_ON(io_req->abts_done);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "fnic_rport_reset_exch: Issuing abts\n");
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* Now queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
+
+ if (fnic_queue_abort_io_req(fnic, tag,
+ FCPIO_ITMF_ABT_TASK_TERM,
+ fc_lun.scsi_lun, io_req)) {
+ /*
+ * Revert the cmd state back to old state, if
+ * it hasnt changed in between. This cmd will get
+ * aborted later by scsi_eh, or cleaned up during
+ * lun reset
+ */
+ io_lock = fnic_io_lock_hash(fnic, sc);
+
+ spin_lock_irqsave(io_lock, flags);
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
+ CMD_STATE(sc) = old_ioreq_state;
+ spin_unlock_irqrestore(io_lock, flags);
+ }
+ }
+
+}
+
+void fnic_terminate_rport_io(struct fc_rport *rport)
+{
+ int tag;
+ struct fnic_io_req *io_req;
+ spinlock_t *io_lock;
+ unsigned long flags;
+ struct scsi_cmnd *sc;
+ struct scsi_lun fc_lun;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fnic *fnic = lport_priv(lport);
+ struct fc_rport *cmd_rport;
+ enum fnic_ioreq_state old_ioreq_state;
+
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host, "fnic_terminate_rport_io called"
+ " wwpn 0x%llx, wwnn0x%llx, portid 0x%06x\n",
+ rport->port_name, rport->node_name,
+ rport->port_id);
+
+ if (fnic->in_remove)
+ return;
+
+ for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
+ sc = scsi_host_find_tag(fnic->lport->host, tag);
+ if (!sc)
+ continue;
+
+ cmd_rport = starget_to_rport(scsi_target(sc->device));
+ if (rport != cmd_rport)
+ continue;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+ if (!io_req || rport != cmd_rport) {
+ spin_unlock_irqrestore(io_lock, flags);
+ continue;
+ }
+
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to rport that went away
+ */
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ continue;
+ }
+ old_ioreq_state = CMD_STATE(sc);
+ CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
+ CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+
+ BUG_ON(io_req->abts_done);
+
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "fnic_terminate_rport_io: Issuing abts\n");
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* Now queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
+
+ if (fnic_queue_abort_io_req(fnic, tag,
+ FCPIO_ITMF_ABT_TASK_TERM,
+ fc_lun.scsi_lun, io_req)) {
+ /*
+ * Revert the cmd state back to old state, if
+ * it hasnt changed in between. This cmd will get
+ * aborted later by scsi_eh, or cleaned up during
+ * lun reset
+ */
+ io_lock = fnic_io_lock_hash(fnic, sc);
+
+ spin_lock_irqsave(io_lock, flags);
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
+ CMD_STATE(sc) = old_ioreq_state;
+ spin_unlock_irqrestore(io_lock, flags);
+ }
+ }
+
+}
+
+static void fnic_block_error_handler(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *shost = sc->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (rport->port_state == FC_PORTSTATE_BLOCKED) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ msleep(1000);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+}
+
+/*
+ * This function is exported to SCSI for sending abort cmnds.
+ * A SCSI IO is represented by a io_req in the driver.
+ * The ioreq is linked to the SCSI Cmd, thus a link with the ULP's IO.
+ */
+int fnic_abort_cmd(struct scsi_cmnd *sc)
+{
+ struct fc_lport *lp;
+ struct fnic *fnic;
+ struct fnic_io_req *io_req;
+ struct fc_rport *rport;
+ spinlock_t *io_lock;
+ unsigned long flags;
+ int ret = SUCCESS;
+ u32 task_req;
+ struct scsi_lun fc_lun;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+
+ /* Wait for rport to unblock */
+ fnic_block_error_handler(sc);
+
+ /* Get local-port, check ready and link up */
+ lp = shost_priv(sc->device->host);
+
+ fnic = lport_priv(lp);
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
+ (starget_to_rport(scsi_target(sc->device)))->port_id,
+ sc->device->lun, sc->request->tag);
+
+ if (lp->state != LPORT_ST_READY || !(lp->link_up)) {
+ ret = FAILED;
+ goto fnic_abort_cmd_end;
+ }
+
+ /*
+ * Avoid a race between SCSI issuing the abort and the device
+ * completing the command.
+ *
+ * If the command is already completed by the fw cmpl code,
+ * we just return SUCCESS from here. This means that the abort
+ * succeeded. In the SCSI ML, since the timeout for command has
+ * happened, the completion wont actually complete the command
+ * and it will be considered as an aborted command
+ *
+ * The CMD_SP will not be cleared except while holding io_req_lock.
+ */
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto fnic_abort_cmd_end;
+ }
+
+ io_req->abts_done = &tm_done;
+
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto wait_pending;
+ }
+ /*
+ * Command is still pending, need to abort it
+ * If the firmware completes the command after this point,
+ * the completion wont be done till mid-layer, since abort
+ * has already started.
+ */
+ CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
+ CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /*
+ * Check readiness of the remote port. If the path to remote
+ * port is up, then send abts to the remote port to terminate
+ * the IO. Else, just locally terminate the IO in the firmware
+ */
+ rport = starget_to_rport(scsi_target(sc->device));
+ if (fc_remote_port_chkready(rport) == 0)
+ task_req = FCPIO_ITMF_ABT_TASK;
+ else
+ task_req = FCPIO_ITMF_ABT_TASK_TERM;
+
+ /* Now queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
+
+ if (fnic_queue_abort_io_req(fnic, sc->request->tag, task_req,
+ fc_lun.scsi_lun, io_req)) {
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (io_req)
+ io_req->abts_done = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+ goto fnic_abort_cmd_end;
+ }
+
+ /*
+ * We queued an abort IO, wait for its completion.
+ * Once the firmware completes the abort command, it will
+ * wake up this thread.
+ */
+ wait_pending:
+ wait_for_completion_timeout(&tm_done,
+ msecs_to_jiffies
+ (2 * fnic->config.ra_tov +
+ fnic->config.ed_tov));
+
+ /* Check the abort status */
+ spin_lock_irqsave(io_lock, flags);
+
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+ goto fnic_abort_cmd_end;
+ }
+ io_req->abts_done = NULL;
+
+ /* fw did not complete abort, timed out */
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+ goto fnic_abort_cmd_end;
+ }
+
+ /*
+ * firmware completed the abort, check the status,
+ * free the io_req irrespective of failure or success
+ */
+ if (CMD_ABTS_STATUS(sc) != FCPIO_SUCCESS)
+ ret = FAILED;
+
+ CMD_SP(sc) = NULL;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+
+fnic_abort_cmd_end:
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Returning from abort cmd %s\n",
+ (ret == SUCCESS) ?
+ "SUCCESS" : "FAILED");
+ return ret;
+}
+
+static inline int fnic_queue_dr_io_req(struct fnic *fnic,
+ struct scsi_cmnd *sc,
+ struct fnic_io_req *io_req)
+{
+ struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ struct scsi_lun fc_lun;
+ int ret = 0;
+ unsigned long intr_flags;
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic, wq);
+
+ if (!vnic_wq_copy_desc_avail(wq)) {
+ ret = -EAGAIN;
+ goto lr_io_req_end;
+ }
+
+ /* fill in the lun info */
+ int_to_scsilun(sc->device->lun, &fc_lun);
+
+ fnic_queue_wq_copy_desc_itmf(wq, sc->request->tag | FNIC_TAG_DEV_RST,
+ 0, FCPIO_ITMF_LUN_RESET, SCSI_NO_TAG,
+ fc_lun.scsi_lun, io_req->port_id,
+ fnic->config.ra_tov, fnic->config.ed_tov);
+
+lr_io_req_end:
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
+
+ return ret;
+}
+
+/*
+ * Clean up any pending aborts on the lun
+ * For each outstanding IO on this lun, whose abort is not completed by fw,
+ * issue a local abort. Wait for abort to complete. Return 0 if all commands
+ * successfully aborted, 1 otherwise
+ */
+static int fnic_clean_pending_aborts(struct fnic *fnic,
+ struct scsi_cmnd *lr_sc)
+{
+ int tag;
+ struct fnic_io_req *io_req;
+ spinlock_t *io_lock;
+ unsigned long flags;
+ int ret = 0;
+ struct scsi_cmnd *sc;
+ struct fc_rport *rport;
+ struct scsi_lun fc_lun;
+ struct scsi_device *lun_dev = lr_sc->device;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+
+ for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
+ sc = scsi_host_find_tag(fnic->lport->host, tag);
+ /*
+ * ignore this lun reset cmd or cmds that do not belong to
+ * this lun
+ */
+ if (!sc || sc == lr_sc || sc->device != lun_dev)
+ continue;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+ if (!io_req || sc->device != lun_dev) {
+ spin_unlock_irqrestore(io_lock, flags);
+ continue;
+ }
+
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to the LUN that we are resetting
+ */
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Found IO in %s on lun\n",
+ fnic_ioreq_state_to_str(CMD_STATE(sc)));
+
+ BUG_ON(CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING);
+
+ CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+ io_req->abts_done = &tm_done;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* Now queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
+ rport = starget_to_rport(scsi_target(sc->device));
+
+ if (fnic_queue_abort_io_req(fnic, tag,
+ FCPIO_ITMF_ABT_TASK_TERM,
+ fc_lun.scsi_lun, io_req)) {
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (io_req)
+ io_req->abts_done = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = 1;
+ goto clean_pending_aborts_end;
+ }
+
+ wait_for_completion_timeout(&tm_done,
+ msecs_to_jiffies
+ (fnic->config.ed_tov));
+
+ /* Recheck cmd state to check if it is now aborted */
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = 1;
+ goto clean_pending_aborts_end;
+ }
+
+ io_req->abts_done = NULL;
+
+ /* if abort is still pending with fw, fail */
+ if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = 1;
+ goto clean_pending_aborts_end;
+ }
+ CMD_SP(sc) = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+ }
+
+clean_pending_aborts_end:
+ return ret;
+}
+
+/*
+ * SCSI Eh thread issues a Lun Reset when one or more commands on a LUN
+ * fail to get aborted. It calls driver's eh_device_reset with a SCSI command
+ * on the LUN.
+ */
+int fnic_device_reset(struct scsi_cmnd *sc)
+{
+ struct fc_lport *lp;
+ struct fnic *fnic;
+ struct fnic_io_req *io_req;
+ struct fc_rport *rport;
+ int status;
+ int ret = FAILED;
+ spinlock_t *io_lock;
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+
+ /* Wait for rport to unblock */
+ fnic_block_error_handler(sc);
+
+ /* Get local-port, check ready and link up */
+ lp = shost_priv(sc->device->host);
+
+ fnic = lport_priv(lp);
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "Device reset called FCID 0x%x, LUN 0x%x\n",
+ (starget_to_rport(scsi_target(sc->device)))->port_id,
+ sc->device->lun);
+
+
+ if (lp->state != LPORT_ST_READY || !(lp->link_up))
+ goto fnic_device_reset_end;
+
+ /* Check if remote port up */
+ rport = starget_to_rport(scsi_target(sc->device));
+ if (fc_remote_port_chkready(rport))
+ goto fnic_device_reset_end;
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+ /*
+ * If there is a io_req attached to this command, then use it,
+ * else allocate a new one.
+ */
+ if (!io_req) {
+ io_req = mempool_alloc(fnic->io_req_pool, GFP_ATOMIC);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto fnic_device_reset_end;
+ }
+ memset(io_req, 0, sizeof(*io_req));
+ io_req->port_id = rport->port_id;
+ CMD_SP(sc) = (char *)io_req;
+ }
+ io_req->dr_done = &tm_done;
+ CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
+ CMD_LR_STATUS(sc) = FCPIO_INVALID_CODE;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "TAG %d\n",
+ sc->request->tag);
+
+ /*
+ * issue the device reset, if enqueue failed, clean up the ioreq
+ * and break assoc with scsi cmd
+ */
+ if (fnic_queue_dr_io_req(fnic, sc, io_req)) {
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (io_req)
+ io_req->dr_done = NULL;
+ goto fnic_device_reset_clean;
+ }
+
+ /*
+ * Wait on the local completion for LUN reset. The io_req may be
+ * freed while we wait since we hold no lock.
+ */
+ wait_for_completion_timeout(&tm_done,
+ msecs_to_jiffies(FNIC_LUN_RESET_TIMEOUT));
+
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (!io_req) {
+ spin_unlock_irqrestore(io_lock, flags);
+ goto fnic_device_reset_end;
+ }
+ io_req->dr_done = NULL;
+
+ status = CMD_LR_STATUS(sc);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /*
+ * If lun reset not completed, bail out with failed. io_req
+ * gets cleaned up during higher levels of EH
+ */
+ if (status == FCPIO_INVALID_CODE) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Device reset timed out\n");
+ goto fnic_device_reset_end;
+ }
+
+ /* Completed, but not successful, clean up the io_req, return fail */
+ if (status != FCPIO_SUCCESS) {
+ spin_lock_irqsave(io_lock, flags);
+ FNIC_SCSI_DBG(KERN_DEBUG,
+ fnic->lport->host,
+ "Device reset completed - failed\n");
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ goto fnic_device_reset_clean;
+ }
+
+ /*
+ * Clean up any aborts on this lun that have still not
+ * completed. If any of these fail, then LUN reset fails.
+ * clean_pending_aborts cleans all cmds on this lun except
+ * the lun reset cmd. If all cmds get cleaned, the lun reset
+ * succeeds
+ */
+ if (fnic_clean_pending_aborts(fnic, sc)) {
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Device reset failed"
+ " since could not abort all IOs\n");
+ goto fnic_device_reset_clean;
+ }
+
+ /* Clean lun reset command */
+ spin_lock_irqsave(io_lock, flags);
+ io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (io_req)
+ /* Completed, and successful */
+ ret = SUCCESS;
+
+fnic_device_reset_clean:
+ if (io_req)
+ CMD_SP(sc) = NULL;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (io_req) {
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+ }
+
+fnic_device_reset_end:
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Returning from device reset %s\n",
+ (ret == SUCCESS) ?
+ "SUCCESS" : "FAILED");
+ return ret;
+}
+
+/* Clean up all IOs, clean up libFC local port */
+int fnic_reset(struct Scsi_Host *shost)
+{
+ struct fc_lport *lp;
+ struct fnic *fnic;
+ int ret = SUCCESS;
+
+ lp = shost_priv(shost);
+ fnic = lport_priv(lp);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "fnic_reset called\n");
+
+ /*
+ * Reset local port, this will clean up libFC exchanges,
+ * reset remote port sessions, and if link is up, begin flogi
+ */
+ if (lp->tt.lport_reset(lp))
+ ret = FAILED;
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "Returning from fnic reset %s\n",
+ (ret == SUCCESS) ?
+ "SUCCESS" : "FAILED");
+
+ return ret;
+}
+
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED. If host reset completes
+ * successfully, and if link is up, then Fabric login begins.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ *
+ */
+int fnic_host_reset(struct scsi_cmnd *sc)
+{
+ int ret;
+ unsigned long wait_host_tmo;
+ struct Scsi_Host *shost = sc->device->host;
+ struct fc_lport *lp = shost_priv(shost);
+
+ /*
+ * If fnic_reset is successful, wait for fabric login to complete
+ * scsi-ml tries to send a TUR to every device if host reset is
+ * successful, so before returning to scsi, fabric should be up
+ */
+ ret = fnic_reset(shost);
+ if (ret == SUCCESS) {
+ wait_host_tmo = jiffies + FNIC_HOST_RESET_SETTLE_TIME * HZ;
+ ret = FAILED;
+ while (time_before(jiffies, wait_host_tmo)) {
+ if ((lp->state == LPORT_ST_READY) &&
+ (lp->link_up)) {
+ ret = SUCCESS;
+ break;
+ }
+ ssleep(1);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This fxn is called from libFC when host is removed
+ */
+void fnic_scsi_abort_io(struct fc_lport *lp)
+{
+ int err = 0;
+ unsigned long flags;
+ enum fnic_state old_state;
+ struct fnic *fnic = lport_priv(lp);
+ DECLARE_COMPLETION_ONSTACK(remove_wait);
+
+ /* Issue firmware reset for fnic, wait for reset to complete */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->remove_wait = &remove_wait;
+ old_state = fnic->state;
+ fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
+ vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ err = fnic_fw_reset_handler(fnic);
+ if (err) {
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)
+ fnic->state = old_state;
+ fnic->remove_wait = NULL;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ /* Wait for firmware reset to complete */
+ wait_for_completion_timeout(&remove_wait,
+ msecs_to_jiffies(FNIC_RMDEVICE_TIMEOUT));
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->remove_wait = NULL;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "fnic_scsi_abort_io %s\n",
+ (fnic->state == FNIC_IN_ETH_MODE) ?
+ "SUCCESS" : "FAILED");
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+}
+
+/*
+ * This fxn called from libFC to clean up driver IO state on link down
+ */
+void fnic_scsi_cleanup(struct fc_lport *lp)
+{
+ unsigned long flags;
+ enum fnic_state old_state;
+ struct fnic *fnic = lport_priv(lp);
+
+ /* issue fw reset */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ old_state = fnic->state;
+ fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
+ vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (fnic_fw_reset_handler(fnic)) {
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)
+ fnic->state = old_state;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ }
+
+}
+
+void fnic_empty_scsi_cleanup(struct fc_lport *lp)
+{
+}
+
+void fnic_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
+{
+ struct fnic *fnic = lport_priv(lp);
+
+ /* Non-zero sid, nothing to do */
+ if (sid)
+ goto call_fc_exch_mgr_reset;
+
+ if (did) {
+ fnic_rport_exch_reset(fnic, did);
+ goto call_fc_exch_mgr_reset;
+ }
+
+ /*
+ * sid = 0, did = 0
+ * link down or device being removed
+ */
+ if (!fnic->in_remove)
+ fnic_scsi_cleanup(lp);
+ else
+ fnic_scsi_abort_io(lp);
+
+ /* call libFC exch mgr reset to reset its exchanges */
+call_fc_exch_mgr_reset:
+ fc_exch_mgr_reset(lp, sid, did);
+
+}
diff --git a/drivers/scsi/fnic/rq_enet_desc.h b/drivers/scsi/fnic/rq_enet_desc.h
new file mode 100644
index 000000000000..92e80ae6b725
--- /dev/null
+++ b/drivers/scsi/fnic/rq_enet_desc.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _RQ_ENET_DESC_H_
+#define _RQ_ENET_DESC_H_
+
+/* Ethernet receive queue descriptor: 16B */
+struct rq_enet_desc {
+ __le64 address;
+ __le16 length_type;
+ u8 reserved[6];
+};
+
+enum rq_enet_type_types {
+ RQ_ENET_TYPE_ONLY_SOP = 0,
+ RQ_ENET_TYPE_NOT_SOP = 1,
+ RQ_ENET_TYPE_RESV2 = 2,
+ RQ_ENET_TYPE_RESV3 = 3,
+};
+
+#define RQ_ENET_ADDR_BITS 64
+#define RQ_ENET_LEN_BITS 14
+#define RQ_ENET_LEN_MASK ((1 << RQ_ENET_LEN_BITS) - 1)
+#define RQ_ENET_TYPE_BITS 2
+#define RQ_ENET_TYPE_MASK ((1 << RQ_ENET_TYPE_BITS) - 1)
+
+static inline void rq_enet_desc_enc(struct rq_enet_desc *desc,
+ u64 address, u8 type, u16 length)
+{
+ desc->address = cpu_to_le64(address);
+ desc->length_type = cpu_to_le16((length & RQ_ENET_LEN_MASK) |
+ ((type & RQ_ENET_TYPE_MASK) << RQ_ENET_LEN_BITS));
+}
+
+static inline void rq_enet_desc_dec(struct rq_enet_desc *desc,
+ u64 *address, u8 *type, u16 *length)
+{
+ *address = le64_to_cpu(desc->address);
+ *length = le16_to_cpu(desc->length_type) & RQ_ENET_LEN_MASK;
+ *type = (u8)((le16_to_cpu(desc->length_type) >> RQ_ENET_LEN_BITS) &
+ RQ_ENET_TYPE_MASK);
+}
+
+#endif /* _RQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/fnic/vnic_cq.c b/drivers/scsi/fnic/vnic_cq.c
new file mode 100644
index 000000000000..c5db32eda5ef
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_cq.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+void vnic_cq_free(struct vnic_cq *cq)
+{
+ vnic_dev_free_desc_ring(cq->vdev, &cq->ring);
+
+ cq->ctrl = NULL;
+}
+
+int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ cq->index = index;
+ cq->vdev = vdev;
+
+ cq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_CQ, index);
+ if (!cq->ctrl) {
+ printk(KERN_ERR "Failed to hook CQ[%d] resource\n", index);
+ return -EINVAL;
+ }
+
+ err = vnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+ unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+ unsigned int cq_tail_color, unsigned int interrupt_enable,
+ unsigned int cq_entry_enable, unsigned int cq_message_enable,
+ unsigned int interrupt_offset, u64 cq_message_addr)
+{
+ u64 paddr;
+
+ paddr = (u64)cq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &cq->ctrl->ring_base);
+ iowrite32(cq->ring.desc_count, &cq->ctrl->ring_size);
+ iowrite32(flow_control_enable, &cq->ctrl->flow_control_enable);
+ iowrite32(color_enable, &cq->ctrl->color_enable);
+ iowrite32(cq_head, &cq->ctrl->cq_head);
+ iowrite32(cq_tail, &cq->ctrl->cq_tail);
+ iowrite32(cq_tail_color, &cq->ctrl->cq_tail_color);
+ iowrite32(interrupt_enable, &cq->ctrl->interrupt_enable);
+ iowrite32(cq_entry_enable, &cq->ctrl->cq_entry_enable);
+ iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable);
+ iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset);
+ writeq(cq_message_addr, &cq->ctrl->cq_message_addr);
+}
+
+void vnic_cq_clean(struct vnic_cq *cq)
+{
+ cq->to_clean = 0;
+ cq->last_color = 0;
+
+ iowrite32(0, &cq->ctrl->cq_head);
+ iowrite32(0, &cq->ctrl->cq_tail);
+ iowrite32(1, &cq->ctrl->cq_tail_color);
+
+ vnic_dev_clear_desc_ring(&cq->ring);
+}
diff --git a/drivers/scsi/fnic/vnic_cq.h b/drivers/scsi/fnic/vnic_cq.h
new file mode 100644
index 000000000000..4ede6809fb1e
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_cq.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_CQ_H_
+#define _VNIC_CQ_H_
+
+#include "cq_desc.h"
+#include "vnic_dev.h"
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_cq_service fnic_cq_service
+#define vnic_cq_free fnic_cq_free
+#define vnic_cq_alloc fnic_cq_alloc
+#define vnic_cq_init fnic_cq_init
+#define vnic_cq_clean fnic_cq_clean
+
+/* Completion queue control */
+struct vnic_cq_ctrl {
+ u64 ring_base; /* 0x00 */
+ u32 ring_size; /* 0x08 */
+ u32 pad0;
+ u32 flow_control_enable; /* 0x10 */
+ u32 pad1;
+ u32 color_enable; /* 0x18 */
+ u32 pad2;
+ u32 cq_head; /* 0x20 */
+ u32 pad3;
+ u32 cq_tail; /* 0x28 */
+ u32 pad4;
+ u32 cq_tail_color; /* 0x30 */
+ u32 pad5;
+ u32 interrupt_enable; /* 0x38 */
+ u32 pad6;
+ u32 cq_entry_enable; /* 0x40 */
+ u32 pad7;
+ u32 cq_message_enable; /* 0x48 */
+ u32 pad8;
+ u32 interrupt_offset; /* 0x50 */
+ u32 pad9;
+ u64 cq_message_addr; /* 0x58 */
+ u32 pad10;
+};
+
+struct vnic_cq {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_cq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ unsigned int to_clean;
+ unsigned int last_color;
+};
+
+static inline unsigned int vnic_cq_service(struct vnic_cq *cq,
+ unsigned int work_to_do,
+ int (*q_service)(struct vnic_dev *vdev, struct cq_desc *cq_desc,
+ u8 type, u16 q_number, u16 completed_index, void *opaque),
+ void *opaque)
+{
+ struct cq_desc *cq_desc;
+ unsigned int work_done = 0;
+ u16 q_number, completed_index;
+ u8 type, color;
+
+ cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ cq_desc_dec(cq_desc, &type, &color,
+ &q_number, &completed_index);
+
+ while (color != cq->last_color) {
+
+ if ((*q_service)(cq->vdev, cq_desc, type,
+ q_number, completed_index, opaque))
+ break;
+
+ cq->to_clean++;
+ if (cq->to_clean == cq->ring.desc_count) {
+ cq->to_clean = 0;
+ cq->last_color = cq->last_color ? 0 : 1;
+ }
+
+ cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ cq_desc_dec(cq_desc, &type, &color,
+ &q_number, &completed_index);
+
+ work_done++;
+ if (work_done >= work_to_do)
+ break;
+ }
+
+ return work_done;
+}
+
+void vnic_cq_free(struct vnic_cq *cq);
+int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+ unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+ unsigned int cq_tail_color, unsigned int interrupt_enable,
+ unsigned int cq_entry_enable, unsigned int message_enable,
+ unsigned int interrupt_offset, u64 message_addr);
+void vnic_cq_clean(struct vnic_cq *cq);
+
+#endif /* _VNIC_CQ_H_ */
diff --git a/drivers/scsi/fnic/vnic_cq_copy.h b/drivers/scsi/fnic/vnic_cq_copy.h
new file mode 100644
index 000000000000..7901ce255a81
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_cq_copy.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_CQ_COPY_H_
+#define _VNIC_CQ_COPY_H_
+
+#include "fcpio.h"
+
+static inline unsigned int vnic_cq_copy_service(
+ struct vnic_cq *cq,
+ int (*q_service)(struct vnic_dev *vdev,
+ unsigned int index,
+ struct fcpio_fw_req *desc),
+ unsigned int work_to_do)
+
+{
+ struct fcpio_fw_req *desc;
+ unsigned int work_done = 0;
+ u8 color;
+
+ desc = (struct fcpio_fw_req *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ fcpio_color_dec(desc, &color);
+
+ while (color != cq->last_color) {
+
+ if ((*q_service)(cq->vdev, cq->index, desc))
+ break;
+
+ cq->to_clean++;
+ if (cq->to_clean == cq->ring.desc_count) {
+ cq->to_clean = 0;
+ cq->last_color = cq->last_color ? 0 : 1;
+ }
+
+ desc = (struct fcpio_fw_req *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ fcpio_color_dec(desc, &color);
+
+ work_done++;
+ if (work_done >= work_to_do)
+ break;
+ }
+
+ return work_done;
+}
+
+#endif /* _VNIC_CQ_COPY_H_ */
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
new file mode 100644
index 000000000000..566770645086
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+#include "vnic_dev.h"
+#include "vnic_stats.h"
+
+struct vnic_res {
+ void __iomem *vaddr;
+ unsigned int count;
+};
+
+struct vnic_dev {
+ void *priv;
+ struct pci_dev *pdev;
+ struct vnic_res res[RES_TYPE_MAX];
+ enum vnic_dev_intr_mode intr_mode;
+ struct vnic_devcmd __iomem *devcmd;
+ struct vnic_devcmd_notify *notify;
+ struct vnic_devcmd_notify notify_copy;
+ dma_addr_t notify_pa;
+ u32 *linkstatus;
+ dma_addr_t linkstatus_pa;
+ struct vnic_stats *stats;
+ dma_addr_t stats_pa;
+ struct vnic_devcmd_fw_info *fw_info;
+ dma_addr_t fw_info_pa;
+};
+
+#define VNIC_MAX_RES_HDR_SIZE \
+ (sizeof(struct vnic_resource_header) + \
+ sizeof(struct vnic_resource) * RES_TYPE_MAX)
+#define VNIC_RES_STRIDE 128
+
+void *vnic_dev_priv(struct vnic_dev *vdev)
+{
+ return vdev->priv;
+}
+
+static int vnic_dev_discover_res(struct vnic_dev *vdev,
+ struct vnic_dev_bar *bar)
+{
+ struct vnic_resource_header __iomem *rh;
+ struct vnic_resource __iomem *r;
+ u8 type;
+
+ if (bar->len < VNIC_MAX_RES_HDR_SIZE) {
+ printk(KERN_ERR "vNIC BAR0 res hdr length error\n");
+ return -EINVAL;
+ }
+
+ rh = bar->vaddr;
+ if (!rh) {
+ printk(KERN_ERR "vNIC BAR0 res hdr not mem-mapped\n");
+ return -EINVAL;
+ }
+
+ if (ioread32(&rh->magic) != VNIC_RES_MAGIC ||
+ ioread32(&rh->version) != VNIC_RES_VERSION) {
+ printk(KERN_ERR "vNIC BAR0 res magic/version error "
+ "exp (%lx/%lx) curr (%x/%x)\n",
+ VNIC_RES_MAGIC, VNIC_RES_VERSION,
+ ioread32(&rh->magic), ioread32(&rh->version));
+ return -EINVAL;
+ }
+
+ r = (struct vnic_resource __iomem *)(rh + 1);
+
+ while ((type = ioread8(&r->type)) != RES_TYPE_EOL) {
+
+ u8 bar_num = ioread8(&r->bar);
+ u32 bar_offset = ioread32(&r->bar_offset);
+ u32 count = ioread32(&r->count);
+ u32 len;
+
+ r++;
+
+ if (bar_num != 0) /* only mapping in BAR0 resources */
+ continue;
+
+ switch (type) {
+ case RES_TYPE_WQ:
+ case RES_TYPE_RQ:
+ case RES_TYPE_CQ:
+ case RES_TYPE_INTR_CTRL:
+ /* each count is stride bytes long */
+ len = count * VNIC_RES_STRIDE;
+ if (len + bar_offset > bar->len) {
+ printk(KERN_ERR "vNIC BAR0 resource %d "
+ "out-of-bounds, offset 0x%x + "
+ "size 0x%x > bar len 0x%lx\n",
+ type, bar_offset,
+ len,
+ bar->len);
+ return -EINVAL;
+ }
+ break;
+ case RES_TYPE_INTR_PBA_LEGACY:
+ case RES_TYPE_DEVCMD:
+ len = count;
+ break;
+ default:
+ continue;
+ }
+
+ vdev->res[type].count = count;
+ vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset;
+ }
+
+ return 0;
+}
+
+unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev,
+ enum vnic_res_type type)
+{
+ return vdev->res[type].count;
+}
+
+void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+ unsigned int index)
+{
+ if (!vdev->res[type].vaddr)
+ return NULL;
+
+ switch (type) {
+ case RES_TYPE_WQ:
+ case RES_TYPE_RQ:
+ case RES_TYPE_CQ:
+ case RES_TYPE_INTR_CTRL:
+ return (char __iomem *)vdev->res[type].vaddr +
+ index * VNIC_RES_STRIDE;
+ default:
+ return (char __iomem *)vdev->res[type].vaddr;
+ }
+}
+
+unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+ unsigned int desc_count,
+ unsigned int desc_size)
+{
+ /* The base address of the desc rings must be 512 byte aligned.
+ * Descriptor count is aligned to groups of 32 descriptors. A
+ * count of 0 means the maximum 4096 descriptors. Descriptor
+ * size is aligned to 16 bytes.
+ */
+
+ unsigned int count_align = 32;
+ unsigned int desc_align = 16;
+
+ ring->base_align = 512;
+
+ if (desc_count == 0)
+ desc_count = 4096;
+
+ ring->desc_count = ALIGN(desc_count, count_align);
+
+ ring->desc_size = ALIGN(desc_size, desc_align);
+
+ ring->size = ring->desc_count * ring->desc_size;
+ ring->size_unaligned = ring->size + ring->base_align;
+
+ return ring->size_unaligned;
+}
+
+void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring)
+{
+ memset(ring->descs, 0, ring->size);
+}
+
+int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ vnic_dev_desc_ring_size(ring, desc_count, desc_size);
+
+ ring->descs_unaligned = pci_alloc_consistent(vdev->pdev,
+ ring->size_unaligned,
+ &ring->base_addr_unaligned);
+
+ if (!ring->descs_unaligned) {
+ printk(KERN_ERR
+ "Failed to allocate ring (size=%d), aborting\n",
+ (int)ring->size);
+ return -ENOMEM;
+ }
+
+ ring->base_addr = ALIGN(ring->base_addr_unaligned,
+ ring->base_align);
+ ring->descs = (u8 *)ring->descs_unaligned +
+ (ring->base_addr - ring->base_addr_unaligned);
+
+ vnic_dev_clear_desc_ring(ring);
+
+ ring->desc_avail = ring->desc_count - 1;
+
+ return 0;
+}
+
+void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
+{
+ if (ring->descs) {
+ pci_free_consistent(vdev->pdev,
+ ring->size_unaligned,
+ ring->descs_unaligned,
+ ring->base_addr_unaligned);
+ ring->descs = NULL;
+ }
+}
+
+int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait)
+{
+ struct vnic_devcmd __iomem *devcmd = vdev->devcmd;
+ int delay;
+ u32 status;
+ int dev_cmd_err[] = {
+ /* convert from fw's version of error.h to host's version */
+ 0, /* ERR_SUCCESS */
+ EINVAL, /* ERR_EINVAL */
+ EFAULT, /* ERR_EFAULT */
+ EPERM, /* ERR_EPERM */
+ EBUSY, /* ERR_EBUSY */
+ };
+ int err;
+
+ status = ioread32(&devcmd->status);
+ if (status & STAT_BUSY) {
+ printk(KERN_ERR "Busy devcmd %d\n", _CMD_N(cmd));
+ return -EBUSY;
+ }
+
+ if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
+ writeq(*a0, &devcmd->args[0]);
+ writeq(*a1, &devcmd->args[1]);
+ wmb();
+ }
+
+ iowrite32(cmd, &devcmd->cmd);
+
+ if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+ return 0;
+
+ for (delay = 0; delay < wait; delay++) {
+
+ udelay(100);
+
+ status = ioread32(&devcmd->status);
+ if (!(status & STAT_BUSY)) {
+
+ if (status & STAT_ERROR) {
+ err = dev_cmd_err[(int)readq(&devcmd->args[0])];
+ printk(KERN_ERR "Error %d devcmd %d\n",
+ err, _CMD_N(cmd));
+ return -err;
+ }
+
+ if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
+ rmb();
+ *a0 = readq(&devcmd->args[0]);
+ *a1 = readq(&devcmd->args[1]);
+ }
+
+ return 0;
+ }
+ }
+
+ printk(KERN_ERR "Timedout devcmd %d\n", _CMD_N(cmd));
+ return -ETIMEDOUT;
+}
+
+int vnic_dev_fw_info(struct vnic_dev *vdev,
+ struct vnic_devcmd_fw_info **fw_info)
+{
+ u64 a0, a1 = 0;
+ int wait = 1000;
+ int err = 0;
+
+ if (!vdev->fw_info) {
+ vdev->fw_info = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_fw_info),
+ &vdev->fw_info_pa);
+ if (!vdev->fw_info)
+ return -ENOMEM;
+
+ a0 = vdev->fw_info_pa;
+
+ /* only get fw_info once and cache it */
+ err = vnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait);
+ }
+
+ *fw_info = vdev->fw_info;
+
+ return err;
+}
+
+int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size,
+ void *value)
+{
+ u64 a0, a1;
+ int wait = 1000;
+ int err;
+
+ a0 = offset;
+ a1 = size;
+
+ err = vnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait);
+
+ switch (size) {
+ case 1:
+ *(u8 *)value = (u8)a0;
+ break;
+ case 2:
+ *(u16 *)value = (u16)a0;
+ break;
+ case 4:
+ *(u32 *)value = (u32)a0;
+ break;
+ case 8:
+ *(u64 *)value = a0;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ return err;
+}
+
+int vnic_dev_stats_clear(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait);
+}
+
+int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
+{
+ u64 a0, a1;
+ int wait = 1000;
+
+ if (!vdev->stats) {
+ vdev->stats = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_stats), &vdev->stats_pa);
+ if (!vdev->stats)
+ return -ENOMEM;
+ }
+
+ *stats = vdev->stats;
+ a0 = vdev->stats_pa;
+ a1 = sizeof(struct vnic_stats);
+
+ return vnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait);
+}
+
+int vnic_dev_close(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait);
+}
+
+int vnic_dev_enable(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait);
+}
+
+int vnic_dev_disable(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait);
+}
+
+int vnic_dev_open(struct vnic_dev *vdev, int arg)
+{
+ u64 a0 = (u32)arg, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait);
+}
+
+int vnic_dev_open_done(struct vnic_dev *vdev, int *done)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ int err;
+
+ *done = 0;
+
+ err = vnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait);
+ if (err)
+ return err;
+
+ *done = (a0 == 0);
+
+ return 0;
+}
+
+int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg)
+{
+ u64 a0 = (u32)arg, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait);
+}
+
+int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ int err;
+
+ *done = 0;
+
+ err = vnic_dev_cmd(vdev, CMD_SOFT_RESET_STATUS, &a0, &a1, wait);
+ if (err)
+ return err;
+
+ *done = (a0 == 0);
+
+ return 0;
+}
+
+int vnic_dev_hang_notify(struct vnic_dev *vdev)
+{
+ u64 a0, a1;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait);
+}
+
+int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
+{
+ u64 a0, a1;
+ int wait = 1000;
+ int err, i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] = 0;
+
+ err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait);
+ if (err)
+ return err;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] = ((u8 *)&a0)[i];
+
+ return 0;
+}
+
+void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
+ int broadcast, int promisc, int allmulti)
+{
+ u64 a0, a1 = 0;
+ int wait = 1000;
+ int err;
+
+ a0 = (directed ? CMD_PFILTER_DIRECTED : 0) |
+ (multicast ? CMD_PFILTER_MULTICAST : 0) |
+ (broadcast ? CMD_PFILTER_BROADCAST : 0) |
+ (promisc ? CMD_PFILTER_PROMISCUOUS : 0) |
+ (allmulti ? CMD_PFILTER_ALL_MULTICAST : 0);
+
+ err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait);
+ if (err)
+ printk(KERN_ERR "Can't set packet filter\n");
+}
+
+void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ int err;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ ((u8 *)&a0)[i] = addr[i];
+
+ err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
+ if (err)
+ printk(KERN_ERR
+ "Can't add addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ err);
+}
+
+void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = 1000;
+ int err;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ ((u8 *)&a0)[i] = addr[i];
+
+ err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait);
+ if (err)
+ printk(KERN_ERR
+ "Can't del addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ err);
+}
+
+int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
+{
+ u64 a0, a1;
+ int wait = 1000;
+
+ if (!vdev->notify) {
+ vdev->notify = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_notify),
+ &vdev->notify_pa);
+ if (!vdev->notify)
+ return -ENOMEM;
+ }
+
+ a0 = vdev->notify_pa;
+ a1 = ((u64)intr << 32) & 0x0000ffff00000000ULL;
+ a1 += sizeof(struct vnic_devcmd_notify);
+
+ return vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+void vnic_dev_notify_unset(struct vnic_dev *vdev)
+{
+ u64 a0, a1;
+ int wait = 1000;
+
+ a0 = 0; /* paddr = 0 to unset notify buffer */
+ a1 = 0x0000ffff00000000ULL; /* intr num = -1 to unreg for intr */
+ a1 += sizeof(struct vnic_devcmd_notify);
+
+ vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+static int vnic_dev_notify_ready(struct vnic_dev *vdev)
+{
+ u32 *words;
+ unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4;
+ unsigned int i;
+ u32 csum;
+
+ if (!vdev->notify)
+ return 0;
+
+ do {
+ csum = 0;
+ memcpy(&vdev->notify_copy, vdev->notify,
+ sizeof(struct vnic_devcmd_notify));
+ words = (u32 *)&vdev->notify_copy;
+ for (i = 1; i < nwords; i++)
+ csum += words[i];
+ } while (csum != words[0]);
+
+ return 1;
+}
+
+int vnic_dev_init(struct vnic_dev *vdev, int arg)
+{
+ u64 a0 = (u32)arg, a1 = 0;
+ int wait = 1000;
+ return vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
+}
+
+int vnic_dev_link_status(struct vnic_dev *vdev)
+{
+ if (vdev->linkstatus)
+ return *vdev->linkstatus;
+
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.link_state;
+}
+
+u32 vnic_dev_port_speed(struct vnic_dev *vdev)
+{
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.port_speed;
+}
+
+u32 vnic_dev_msg_lvl(struct vnic_dev *vdev)
+{
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.msglvl;
+}
+
+u32 vnic_dev_mtu(struct vnic_dev *vdev)
+{
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.mtu;
+}
+
+u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev)
+{
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.link_down_cnt;
+}
+
+void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
+ enum vnic_dev_intr_mode intr_mode)
+{
+ vdev->intr_mode = intr_mode;
+}
+
+enum vnic_dev_intr_mode vnic_dev_get_intr_mode(
+ struct vnic_dev *vdev)
+{
+ return vdev->intr_mode;
+}
+
+void vnic_dev_unregister(struct vnic_dev *vdev)
+{
+ if (vdev) {
+ if (vdev->notify)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_notify),
+ vdev->notify,
+ vdev->notify_pa);
+ if (vdev->linkstatus)
+ pci_free_consistent(vdev->pdev,
+ sizeof(u32),
+ vdev->linkstatus,
+ vdev->linkstatus_pa);
+ if (vdev->stats)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_dev),
+ vdev->stats, vdev->stats_pa);
+ if (vdev->fw_info)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_fw_info),
+ vdev->fw_info, vdev->fw_info_pa);
+ kfree(vdev);
+ }
+}
+
+struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
+ void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar)
+{
+ if (!vdev) {
+ vdev = kzalloc(sizeof(struct vnic_dev), GFP_KERNEL);
+ if (!vdev)
+ return NULL;
+ }
+
+ vdev->priv = priv;
+ vdev->pdev = pdev;
+
+ if (vnic_dev_discover_res(vdev, bar))
+ goto err_out;
+
+ vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
+ if (!vdev->devcmd)
+ goto err_out;
+
+ return vdev;
+
+err_out:
+ vnic_dev_unregister(vdev);
+ return NULL;
+}
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
new file mode 100644
index 000000000000..f9935a8a5a09
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_DEV_H_
+#define _VNIC_DEV_H_
+
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_dev_priv fnic_dev_priv
+#define vnic_dev_get_res_count fnic_dev_get_res_count
+#define vnic_dev_get_res fnic_dev_get_res
+#define vnic_dev_desc_ring_size fnic_dev_desc_ring_siz
+#define vnic_dev_clear_desc_ring fnic_dev_clear_desc_ring
+#define vnic_dev_alloc_desc_ring fnic_dev_alloc_desc_ring
+#define vnic_dev_free_desc_ring fnic_dev_free_desc_ring
+#define vnic_dev_cmd fnic_dev_cmd
+#define vnic_dev_fw_info fnic_dev_fw_info
+#define vnic_dev_spec fnic_dev_spec
+#define vnic_dev_stats_clear fnic_dev_stats_clear
+#define vnic_dev_stats_dump fnic_dev_stats_dump
+#define vnic_dev_hang_notify fnic_dev_hang_notify
+#define vnic_dev_packet_filter fnic_dev_packet_filter
+#define vnic_dev_add_addr fnic_dev_add_addr
+#define vnic_dev_del_addr fnic_dev_del_addr
+#define vnic_dev_mac_addr fnic_dev_mac_addr
+#define vnic_dev_notify_set fnic_dev_notify_set
+#define vnic_dev_notify_unset fnic_dev_notify_unset
+#define vnic_dev_link_status fnic_dev_link_status
+#define vnic_dev_port_speed fnic_dev_port_speed
+#define vnic_dev_msg_lvl fnic_dev_msg_lvl
+#define vnic_dev_mtu fnic_dev_mtu
+#define vnic_dev_link_down_cnt fnic_dev_link_down_cnt
+#define vnic_dev_close fnic_dev_close
+#define vnic_dev_enable fnic_dev_enable
+#define vnic_dev_disable fnic_dev_disable
+#define vnic_dev_open fnic_dev_open
+#define vnic_dev_open_done fnic_dev_open_done
+#define vnic_dev_init fnic_dev_init
+#define vnic_dev_soft_reset fnic_dev_soft_reset
+#define vnic_dev_soft_reset_done fnic_dev_soft_reset_done
+#define vnic_dev_set_intr_mode fnic_dev_set_intr_mode
+#define vnic_dev_get_intr_mode fnic_dev_get_intr_mode
+#define vnic_dev_unregister fnic_dev_unregister
+#define vnic_dev_register fnic_dev_register
+
+#ifndef VNIC_PADDR_TARGET
+#define VNIC_PADDR_TARGET 0x0000000000000000ULL
+#endif
+
+#ifndef readq
+static inline u64 readq(void __iomem *reg)
+{
+ return ((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg);
+}
+
+static inline void writeq(u64 val, void __iomem *reg)
+{
+ writel(val & 0xffffffff, reg);
+ writel(val >> 32, reg + 0x4UL);
+}
+#endif
+
+enum vnic_dev_intr_mode {
+ VNIC_DEV_INTR_MODE_UNKNOWN,
+ VNIC_DEV_INTR_MODE_INTX,
+ VNIC_DEV_INTR_MODE_MSI,
+ VNIC_DEV_INTR_MODE_MSIX,
+};
+
+struct vnic_dev_bar {
+ void __iomem *vaddr;
+ dma_addr_t bus_addr;
+ unsigned long len;
+};
+
+struct vnic_dev_ring {
+ void *descs;
+ size_t size;
+ dma_addr_t base_addr;
+ size_t base_align;
+ void *descs_unaligned;
+ size_t size_unaligned;
+ dma_addr_t base_addr_unaligned;
+ unsigned int desc_size;
+ unsigned int desc_count;
+ unsigned int desc_avail;
+};
+
+struct vnic_dev;
+struct vnic_stats;
+
+void *vnic_dev_priv(struct vnic_dev *vdev);
+unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev,
+ enum vnic_res_type type);
+void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+ unsigned int index);
+unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+ unsigned int desc_count,
+ unsigned int desc_size);
+void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring);
+int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_dev_free_desc_ring(struct vnic_dev *vdev,
+ struct vnic_dev_ring *ring);
+int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait);
+int vnic_dev_fw_info(struct vnic_dev *vdev,
+ struct vnic_devcmd_fw_info **fw_info);
+int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
+ unsigned int size, void *value);
+int vnic_dev_stats_clear(struct vnic_dev *vdev);
+int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
+int vnic_dev_hang_notify(struct vnic_dev *vdev);
+void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
+ int broadcast, int promisc, int allmulti);
+void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr);
+void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr);
+int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
+int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);
+void vnic_dev_notify_unset(struct vnic_dev *vdev);
+int vnic_dev_link_status(struct vnic_dev *vdev);
+u32 vnic_dev_port_speed(struct vnic_dev *vdev);
+u32 vnic_dev_msg_lvl(struct vnic_dev *vdev);
+u32 vnic_dev_mtu(struct vnic_dev *vdev);
+u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev);
+int vnic_dev_close(struct vnic_dev *vdev);
+int vnic_dev_enable(struct vnic_dev *vdev);
+int vnic_dev_disable(struct vnic_dev *vdev);
+int vnic_dev_open(struct vnic_dev *vdev, int arg);
+int vnic_dev_open_done(struct vnic_dev *vdev, int *done);
+int vnic_dev_init(struct vnic_dev *vdev, int arg);
+int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg);
+int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done);
+void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
+ enum vnic_dev_intr_mode intr_mode);
+enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev);
+void vnic_dev_unregister(struct vnic_dev *vdev);
+struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
+ void *priv, struct pci_dev *pdev,
+ struct vnic_dev_bar *bar);
+
+#endif /* _VNIC_DEV_H_ */
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
new file mode 100644
index 000000000000..d62b9061bf12
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_DEVCMD_H_
+#define _VNIC_DEVCMD_H_
+
+#define _CMD_NBITS 14
+#define _CMD_VTYPEBITS 10
+#define _CMD_FLAGSBITS 6
+#define _CMD_DIRBITS 2
+
+#define _CMD_NMASK ((1 << _CMD_NBITS)-1)
+#define _CMD_VTYPEMASK ((1 << _CMD_VTYPEBITS)-1)
+#define _CMD_FLAGSMASK ((1 << _CMD_FLAGSBITS)-1)
+#define _CMD_DIRMASK ((1 << _CMD_DIRBITS)-1)
+
+#define _CMD_NSHIFT 0
+#define _CMD_VTYPESHIFT (_CMD_NSHIFT+_CMD_NBITS)
+#define _CMD_FLAGSSHIFT (_CMD_VTYPESHIFT+_CMD_VTYPEBITS)
+#define _CMD_DIRSHIFT (_CMD_FLAGSSHIFT+_CMD_FLAGSBITS)
+
+/*
+ * Direction bits (from host perspective).
+ */
+#define _CMD_DIR_NONE 0U
+#define _CMD_DIR_WRITE 1U
+#define _CMD_DIR_READ 2U
+#define _CMD_DIR_RW (_CMD_DIR_WRITE | _CMD_DIR_READ)
+
+/*
+ * Flag bits.
+ */
+#define _CMD_FLAGS_NONE 0U
+#define _CMD_FLAGS_NOWAIT 1U
+
+/*
+ * vNIC type bits.
+ */
+#define _CMD_VTYPE_NONE 0U
+#define _CMD_VTYPE_ENET 1U
+#define _CMD_VTYPE_FC 2U
+#define _CMD_VTYPE_SCSI 4U
+#define _CMD_VTYPE_ALL (_CMD_VTYPE_ENET | _CMD_VTYPE_FC | _CMD_VTYPE_SCSI)
+
+/*
+ * Used to create cmds..
+*/
+#define _CMDCF(dir, flags, vtype, nr) \
+ (((dir) << _CMD_DIRSHIFT) | \
+ ((flags) << _CMD_FLAGSSHIFT) | \
+ ((vtype) << _CMD_VTYPESHIFT) | \
+ ((nr) << _CMD_NSHIFT))
+#define _CMDC(dir, vtype, nr) _CMDCF(dir, 0, vtype, nr)
+#define _CMDCNW(dir, vtype, nr) _CMDCF(dir, _CMD_FLAGS_NOWAIT, vtype, nr)
+
+/*
+ * Used to decode cmds..
+*/
+#define _CMD_DIR(cmd) (((cmd) >> _CMD_DIRSHIFT) & _CMD_DIRMASK)
+#define _CMD_FLAGS(cmd) (((cmd) >> _CMD_FLAGSSHIFT) & _CMD_FLAGSMASK)
+#define _CMD_VTYPE(cmd) (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
+#define _CMD_N(cmd) (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
+
+enum vnic_devcmd_cmd {
+ CMD_NONE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
+
+ /* mcpu fw info in mem: (u64)a0=paddr to struct vnic_devcmd_fw_info */
+ CMD_MCPU_FW_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 1),
+
+ /* dev-specific block member:
+ * in: (u16)a0=offset,(u8)a1=size
+ * out: a0=value */
+ CMD_DEV_SPEC = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2),
+
+ /* stats clear */
+ CMD_STATS_CLEAR = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 3),
+
+ /* stats dump in mem: (u64)a0=paddr to stats area,
+ * (u16)a1=sizeof stats area */
+ CMD_STATS_DUMP = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4),
+
+ /* set Rx packet filter: (u32)a0=filters (see CMD_PFILTER_*) */
+ CMD_PACKET_FILTER = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 7),
+
+ /* hang detection notification */
+ CMD_HANG_NOTIFY = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 8),
+
+ /* MAC address in (u48)a0 */
+ CMD_MAC_ADDR = _CMDC(_CMD_DIR_READ,
+ _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 9),
+
+ /* disable/enable promisc mode: (u8)a0=0/1 */
+/***** XXX DEPRECATED *****/
+ CMD_PROMISC_MODE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 10),
+
+ /* disable/enable all-multi mode: (u8)a0=0/1 */
+/***** XXX DEPRECATED *****/
+ CMD_ALLMULTI_MODE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 11),
+
+ /* add addr from (u48)a0 */
+ CMD_ADDR_ADD = _CMDCNW(_CMD_DIR_WRITE,
+ _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 12),
+
+ /* del addr from (u48)a0 */
+ CMD_ADDR_DEL = _CMDCNW(_CMD_DIR_WRITE,
+ _CMD_VTYPE_ENET | _CMD_VTYPE_FC, 13),
+
+ /* add VLAN id in (u16)a0 */
+ CMD_VLAN_ADD = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 14),
+
+ /* del VLAN id in (u16)a0 */
+ CMD_VLAN_DEL = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 15),
+
+ /* nic_cfg in (u32)a0 */
+ CMD_NIC_CFG = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 16),
+
+ /* union vnic_rss_key in mem: (u64)a0=paddr, (u16)a1=len */
+ CMD_RSS_KEY = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 17),
+
+ /* union vnic_rss_cpu in mem: (u64)a0=paddr, (u16)a1=len */
+ CMD_RSS_CPU = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 18),
+
+ /* initiate softreset */
+ CMD_SOFT_RESET = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 19),
+
+ /* softreset status:
+ * out: a0=0 reset complete, a0=1 reset in progress */
+ CMD_SOFT_RESET_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 20),
+
+ /* set struct vnic_devcmd_notify buffer in mem:
+ * in:
+ * (u64)a0=paddr to notify (set paddr=0 to unset)
+ * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify)
+ * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr)
+ * out:
+ * (u32)a1 = effective size
+ */
+ CMD_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 21),
+
+ /* UNDI API: (u64)a0=paddr to s_PXENV_UNDI_ struct,
+ * (u8)a1=PXENV_UNDI_xxx */
+ CMD_UNDI = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 22),
+
+ /* initiate open sequence (u32)a0=flags (see CMD_OPENF_*) */
+ CMD_OPEN = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 23),
+
+ /* open status:
+ * out: a0=0 open complete, a0=1 open in progress */
+ CMD_OPEN_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 24),
+
+ /* close vnic */
+ CMD_CLOSE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25),
+
+ /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */
+ CMD_INIT = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26),
+
+ /* variant of CMD_INIT, with provisioning info
+ * (u64)a0=paddr of vnic_devcmd_provinfo
+ * (u32)a1=sizeof provision info */
+ CMD_INIT_PROV_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 27),
+
+ /* enable virtual link */
+ CMD_ENABLE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28),
+
+ /* disable virtual link */
+ CMD_DISABLE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29),
+
+ /* stats dump all vnics on uplink in mem: (u64)a0=paddr (u32)a1=uif */
+ CMD_STATS_DUMP_ALL = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 30),
+
+ /* init status:
+ * out: a0=0 init complete, a0=1 init in progress
+ * if a0=0, a1=errno */
+ CMD_INIT_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 31),
+
+ /* INT13 API: (u64)a0=paddr to vnic_int13_params struct
+ * (u8)a1=INT13_CMD_xxx */
+ CMD_INT13 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_FC, 32),
+
+ /* logical uplink enable/disable: (u64)a0: 0/1=disable/enable */
+ CMD_LOGICAL_UPLINK = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 33),
+
+ /* undo initialize of virtual link */
+ CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34),
+};
+
+/* flags for CMD_OPEN */
+#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */
+
+/* flags for CMD_INIT */
+#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */
+
+/* flags for CMD_PACKET_FILTER */
+#define CMD_PFILTER_DIRECTED 0x01
+#define CMD_PFILTER_MULTICAST 0x02
+#define CMD_PFILTER_BROADCAST 0x04
+#define CMD_PFILTER_PROMISCUOUS 0x08
+#define CMD_PFILTER_ALL_MULTICAST 0x10
+
+enum vnic_devcmd_status {
+ STAT_NONE = 0,
+ STAT_BUSY = 1 << 0, /* cmd in progress */
+ STAT_ERROR = 1 << 1, /* last cmd caused error (code in a0) */
+};
+
+enum vnic_devcmd_error {
+ ERR_SUCCESS = 0,
+ ERR_EINVAL = 1,
+ ERR_EFAULT = 2,
+ ERR_EPERM = 3,
+ ERR_EBUSY = 4,
+ ERR_ECMDUNKNOWN = 5,
+ ERR_EBADSTATE = 6,
+ ERR_ENOMEM = 7,
+ ERR_ETIMEDOUT = 8,
+ ERR_ELINKDOWN = 9,
+};
+
+struct vnic_devcmd_fw_info {
+ char fw_version[32];
+ char fw_build[32];
+ char hw_version[32];
+ char hw_serial_number[32];
+};
+
+struct vnic_devcmd_notify {
+ u32 csum; /* checksum over following words */
+
+ u32 link_state; /* link up == 1 */
+ u32 port_speed; /* effective port speed (rate limit) */
+ u32 mtu; /* MTU */
+ u32 msglvl; /* requested driver msg lvl */
+ u32 uif; /* uplink interface */
+ u32 status; /* status bits (see VNIC_STF_*) */
+ u32 error; /* error code (see ERR_*) for first ERR */
+ u32 link_down_cnt; /* running count of link down transitions */
+};
+#define VNIC_STF_FATAL_ERR 0x0001 /* fatal fw error */
+
+struct vnic_devcmd_provinfo {
+ u8 oui[3];
+ u8 type;
+ u8 data[0];
+};
+
+/*
+ * Writing cmd register causes STAT_BUSY to get set in status register.
+ * When cmd completes, STAT_BUSY will be cleared.
+ *
+ * If cmd completed successfully STAT_ERROR will be clear
+ * and args registers contain cmd-specific results.
+ *
+ * If cmd error, STAT_ERROR will be set and args[0] contains error code.
+ *
+ * status register is read-only. While STAT_BUSY is set,
+ * all other register contents are read-only.
+ */
+
+/* Make sizeof(vnic_devcmd) a power-of-2 for I/O BAR. */
+#define VNIC_DEVCMD_NARGS 15
+struct vnic_devcmd {
+ u32 status; /* RO */
+ u32 cmd; /* RW */
+ u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */
+};
+
+#endif /* _VNIC_DEVCMD_H_ */
diff --git a/drivers/scsi/fnic/vnic_intr.c b/drivers/scsi/fnic/vnic_intr.c
new file mode 100644
index 000000000000..4f4dc8793d23
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_intr.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+
+void vnic_intr_free(struct vnic_intr *intr)
+{
+ intr->ctrl = NULL;
+}
+
+int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index)
+{
+ intr->index = index;
+ intr->vdev = vdev;
+
+ intr->ctrl = vnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index);
+ if (!intr->ctrl) {
+ printk(KERN_ERR "Failed to hook INTR[%d].ctrl resource\n",
+ index);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+ unsigned int coalescing_type, unsigned int mask_on_assertion)
+{
+ iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer);
+ iowrite32(coalescing_type, &intr->ctrl->coalescing_type);
+ iowrite32(mask_on_assertion, &intr->ctrl->mask_on_assertion);
+ iowrite32(0, &intr->ctrl->int_credits);
+}
+
+void vnic_intr_clean(struct vnic_intr *intr)
+{
+ iowrite32(0, &intr->ctrl->int_credits);
+}
diff --git a/drivers/scsi/fnic/vnic_intr.h b/drivers/scsi/fnic/vnic_intr.h
new file mode 100644
index 000000000000..d5fb40e7c98e
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_intr.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_INTR_H_
+#define _VNIC_INTR_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_intr_unmask fnic_intr_unmask
+#define vnic_intr_mask fnic_intr_mask
+#define vnic_intr_return_credits fnic_intr_return_credits
+#define vnic_intr_credits fnic_intr_credits
+#define vnic_intr_return_all_credits fnic_intr_return_all_credits
+#define vnic_intr_legacy_pba fnic_intr_legacy_pba
+#define vnic_intr_free fnic_intr_free
+#define vnic_intr_alloc fnic_intr_alloc
+#define vnic_intr_init fnic_intr_init
+#define vnic_intr_clean fnic_intr_clean
+
+#define VNIC_INTR_TIMER_MAX 0xffff
+
+#define VNIC_INTR_TIMER_TYPE_ABS 0
+#define VNIC_INTR_TIMER_TYPE_QUIET 1
+
+/* Interrupt control */
+struct vnic_intr_ctrl {
+ u32 coalescing_timer; /* 0x00 */
+ u32 pad0;
+ u32 coalescing_value; /* 0x08 */
+ u32 pad1;
+ u32 coalescing_type; /* 0x10 */
+ u32 pad2;
+ u32 mask_on_assertion; /* 0x18 */
+ u32 pad3;
+ u32 mask; /* 0x20 */
+ u32 pad4;
+ u32 int_credits; /* 0x28 */
+ u32 pad5;
+ u32 int_credit_return; /* 0x30 */
+ u32 pad6;
+};
+
+struct vnic_intr {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_intr_ctrl __iomem *ctrl; /* memory-mapped */
+};
+
+static inline void vnic_intr_unmask(struct vnic_intr *intr)
+{
+ iowrite32(0, &intr->ctrl->mask);
+}
+
+static inline void vnic_intr_mask(struct vnic_intr *intr)
+{
+ iowrite32(1, &intr->ctrl->mask);
+}
+
+static inline void vnic_intr_return_credits(struct vnic_intr *intr,
+ unsigned int credits, int unmask, int reset_timer)
+{
+#define VNIC_INTR_UNMASK_SHIFT 16
+#define VNIC_INTR_RESET_TIMER_SHIFT 17
+
+ u32 int_credit_return = (credits & 0xffff) |
+ (unmask ? (1 << VNIC_INTR_UNMASK_SHIFT) : 0) |
+ (reset_timer ? (1 << VNIC_INTR_RESET_TIMER_SHIFT) : 0);
+
+ iowrite32(int_credit_return, &intr->ctrl->int_credit_return);
+}
+
+static inline unsigned int vnic_intr_credits(struct vnic_intr *intr)
+{
+ return ioread32(&intr->ctrl->int_credits);
+}
+
+static inline void vnic_intr_return_all_credits(struct vnic_intr *intr)
+{
+ unsigned int credits = vnic_intr_credits(intr);
+ int unmask = 1;
+ int reset_timer = 1;
+
+ vnic_intr_return_credits(intr, credits, unmask, reset_timer);
+}
+
+static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
+{
+ /* read PBA without clearing */
+ return ioread32(legacy_pba);
+}
+
+void vnic_intr_free(struct vnic_intr *intr);
+int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index);
+void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+ unsigned int coalescing_type, unsigned int mask_on_assertion);
+void vnic_intr_clean(struct vnic_intr *intr);
+
+#endif /* _VNIC_INTR_H_ */
diff --git a/drivers/scsi/fnic/vnic_nic.h b/drivers/scsi/fnic/vnic_nic.h
new file mode 100644
index 000000000000..f15b83eeaced
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_nic.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_NIC_H_
+#define _VNIC_NIC_H_
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_set_nic_cfg fnic_set_nic_cfg
+
+#define NIC_CFG_RSS_DEFAULT_CPU_MASK_FIELD 0xffUL
+#define NIC_CFG_RSS_DEFAULT_CPU_SHIFT 0
+#define NIC_CFG_RSS_HASH_TYPE (0xffUL << 8)
+#define NIC_CFG_RSS_HASH_TYPE_MASK_FIELD 0xffUL
+#define NIC_CFG_RSS_HASH_TYPE_SHIFT 8
+#define NIC_CFG_RSS_HASH_BITS (7UL << 16)
+#define NIC_CFG_RSS_HASH_BITS_MASK_FIELD 7UL
+#define NIC_CFG_RSS_HASH_BITS_SHIFT 16
+#define NIC_CFG_RSS_BASE_CPU (7UL << 19)
+#define NIC_CFG_RSS_BASE_CPU_MASK_FIELD 7UL
+#define NIC_CFG_RSS_BASE_CPU_SHIFT 19
+#define NIC_CFG_RSS_ENABLE (1UL << 22)
+#define NIC_CFG_RSS_ENABLE_MASK_FIELD 1UL
+#define NIC_CFG_RSS_ENABLE_SHIFT 22
+#define NIC_CFG_TSO_IPID_SPLIT_EN (1UL << 23)
+#define NIC_CFG_TSO_IPID_SPLIT_EN_MASK_FIELD 1UL
+#define NIC_CFG_TSO_IPID_SPLIT_EN_SHIFT 23
+#define NIC_CFG_IG_VLAN_STRIP_EN (1UL << 24)
+#define NIC_CFG_IG_VLAN_STRIP_EN_MASK_FIELD 1UL
+#define NIC_CFG_IG_VLAN_STRIP_EN_SHIFT 24
+
+static inline void vnic_set_nic_cfg(u32 *nic_cfg,
+ u8 rss_default_cpu, u8 rss_hash_type,
+ u8 rss_hash_bits, u8 rss_base_cpu,
+ u8 rss_enable, u8 tso_ipid_split_en,
+ u8 ig_vlan_strip_en)
+{
+ *nic_cfg = (rss_default_cpu & NIC_CFG_RSS_DEFAULT_CPU_MASK_FIELD) |
+ ((rss_hash_type & NIC_CFG_RSS_HASH_TYPE_MASK_FIELD)
+ << NIC_CFG_RSS_HASH_TYPE_SHIFT) |
+ ((rss_hash_bits & NIC_CFG_RSS_HASH_BITS_MASK_FIELD)
+ << NIC_CFG_RSS_HASH_BITS_SHIFT) |
+ ((rss_base_cpu & NIC_CFG_RSS_BASE_CPU_MASK_FIELD)
+ << NIC_CFG_RSS_BASE_CPU_SHIFT) |
+ ((rss_enable & NIC_CFG_RSS_ENABLE_MASK_FIELD)
+ << NIC_CFG_RSS_ENABLE_SHIFT) |
+ ((tso_ipid_split_en & NIC_CFG_TSO_IPID_SPLIT_EN_MASK_FIELD)
+ << NIC_CFG_TSO_IPID_SPLIT_EN_SHIFT) |
+ ((ig_vlan_strip_en & NIC_CFG_IG_VLAN_STRIP_EN_MASK_FIELD)
+ << NIC_CFG_IG_VLAN_STRIP_EN_SHIFT);
+}
+
+#endif /* _VNIC_NIC_H_ */
diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h
new file mode 100644
index 000000000000..2d842f79d41a
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_resource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_RESOURCE_H_
+#define _VNIC_RESOURCE_H_
+
+#define VNIC_RES_MAGIC 0x766E6963L /* 'vnic' */
+#define VNIC_RES_VERSION 0x00000000L
+
+/* vNIC resource types */
+enum vnic_res_type {
+ RES_TYPE_EOL, /* End-of-list */
+ RES_TYPE_WQ, /* Work queues */
+ RES_TYPE_RQ, /* Receive queues */
+ RES_TYPE_CQ, /* Completion queues */
+ RES_TYPE_RSVD1,
+ RES_TYPE_NIC_CFG, /* Enet NIC config registers */
+ RES_TYPE_RSVD2,
+ RES_TYPE_RSVD3,
+ RES_TYPE_RSVD4,
+ RES_TYPE_RSVD5,
+ RES_TYPE_INTR_CTRL, /* Interrupt ctrl table */
+ RES_TYPE_INTR_TABLE, /* MSI/MSI-X Interrupt table */
+ RES_TYPE_INTR_PBA, /* MSI/MSI-X PBA table */
+ RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status */
+ RES_TYPE_RSVD6,
+ RES_TYPE_RSVD7,
+ RES_TYPE_DEVCMD, /* Device command region */
+ RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */
+
+ RES_TYPE_MAX, /* Count of resource types */
+};
+
+struct vnic_resource_header {
+ u32 magic;
+ u32 version;
+};
+
+struct vnic_resource {
+ u8 type;
+ u8 bar;
+ u8 pad[2];
+ u32 bar_offset;
+ u32 count;
+};
+
+#endif /* _VNIC_RESOURCE_H_ */
diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c
new file mode 100644
index 000000000000..bedd0d285630
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_rq.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_dev.h"
+#include "vnic_rq.h"
+
+static int vnic_rq_alloc_bufs(struct vnic_rq *rq)
+{
+ struct vnic_rq_buf *buf;
+ struct vnic_dev *vdev;
+ unsigned int i, j, count = rq->ring.desc_count;
+ unsigned int blks = VNIC_RQ_BUF_BLKS_NEEDED(count);
+
+ vdev = rq->vdev;
+
+ for (i = 0; i < blks; i++) {
+ rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC);
+ if (!rq->bufs[i]) {
+ printk(KERN_ERR "Failed to alloc rq_bufs\n");
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < blks; i++) {
+ buf = rq->bufs[i];
+ for (j = 0; j < VNIC_RQ_BUF_BLK_ENTRIES; j++) {
+ buf->index = i * VNIC_RQ_BUF_BLK_ENTRIES + j;
+ buf->desc = (u8 *)rq->ring.descs +
+ rq->ring.desc_size * buf->index;
+ if (buf->index + 1 == count) {
+ buf->next = rq->bufs[0];
+ break;
+ } else if (j + 1 == VNIC_RQ_BUF_BLK_ENTRIES) {
+ buf->next = rq->bufs[i + 1];
+ } else {
+ buf->next = buf + 1;
+ buf++;
+ }
+ }
+ }
+
+ rq->to_use = rq->to_clean = rq->bufs[0];
+ rq->buf_index = 0;
+
+ return 0;
+}
+
+void vnic_rq_free(struct vnic_rq *rq)
+{
+ struct vnic_dev *vdev;
+ unsigned int i;
+
+ vdev = rq->vdev;
+
+ vnic_dev_free_desc_ring(vdev, &rq->ring);
+
+ for (i = 0; i < VNIC_RQ_BUF_BLKS_MAX; i++) {
+ kfree(rq->bufs[i]);
+ rq->bufs[i] = NULL;
+ }
+
+ rq->ctrl = NULL;
+}
+
+int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ rq->index = index;
+ rq->vdev = vdev;
+
+ rq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_RQ, index);
+ if (!rq->ctrl) {
+ printk(KERN_ERR "Failed to hook RQ[%d] resource\n", index);
+ return -EINVAL;
+ }
+
+ vnic_rq_disable(rq);
+
+ err = vnic_dev_alloc_desc_ring(vdev, &rq->ring, desc_count, desc_size);
+ if (err)
+ return err;
+
+ err = vnic_rq_alloc_bufs(rq);
+ if (err) {
+ vnic_rq_free(rq);
+ return err;
+ }
+
+ return 0;
+}
+
+void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+ u32 fetch_index;
+
+ paddr = (u64)rq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &rq->ctrl->ring_base);
+ iowrite32(rq->ring.desc_count, &rq->ctrl->ring_size);
+ iowrite32(cq_index, &rq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &rq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &rq->ctrl->error_interrupt_offset);
+ iowrite32(0, &rq->ctrl->dropped_packet_count);
+ iowrite32(0, &rq->ctrl->error_status);
+
+ /* Use current fetch_index as the ring starting point */
+ fetch_index = ioread32(&rq->ctrl->fetch_index);
+ rq->to_use = rq->to_clean =
+ &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES]
+ [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES];
+ iowrite32(fetch_index, &rq->ctrl->posted_index);
+
+ rq->buf_index = 0;
+}
+
+unsigned int vnic_rq_error_status(struct vnic_rq *rq)
+{
+ return ioread32(&rq->ctrl->error_status);
+}
+
+void vnic_rq_enable(struct vnic_rq *rq)
+{
+ iowrite32(1, &rq->ctrl->enable);
+}
+
+int vnic_rq_disable(struct vnic_rq *rq)
+{
+ unsigned int wait;
+
+ iowrite32(0, &rq->ctrl->enable);
+
+ /* Wait for HW to ACK disable request */
+ for (wait = 0; wait < 100; wait++) {
+ if (!(ioread32(&rq->ctrl->running)))
+ return 0;
+ udelay(1);
+ }
+
+ printk(KERN_ERR "Failed to disable RQ[%d]\n", rq->index);
+
+ return -ETIMEDOUT;
+}
+
+void vnic_rq_clean(struct vnic_rq *rq,
+ void (*buf_clean)(struct vnic_rq *rq, struct vnic_rq_buf *buf))
+{
+ struct vnic_rq_buf *buf;
+ u32 fetch_index;
+
+ BUG_ON(ioread32(&rq->ctrl->enable));
+
+ buf = rq->to_clean;
+
+ while (vnic_rq_desc_used(rq) > 0) {
+
+ (*buf_clean)(rq, buf);
+
+ buf = rq->to_clean = buf->next;
+ rq->ring.desc_avail++;
+ }
+
+ /* Use current fetch_index as the ring starting point */
+ fetch_index = ioread32(&rq->ctrl->fetch_index);
+ rq->to_use = rq->to_clean =
+ &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES]
+ [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES];
+ iowrite32(fetch_index, &rq->ctrl->posted_index);
+
+ rq->buf_index = 0;
+
+ vnic_dev_clear_desc_ring(&rq->ring);
+}
+
diff --git a/drivers/scsi/fnic/vnic_rq.h b/drivers/scsi/fnic/vnic_rq.h
new file mode 100644
index 000000000000..aebdfbd6ad3c
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_rq.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_RQ_H_
+#define _VNIC_RQ_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_rq_desc_avail fnic_rq_desc_avail
+#define vnic_rq_desc_used fnic_rq_desc_used
+#define vnic_rq_next_desc fnic_rq_next_desc
+#define vnic_rq_next_index fnic_rq_next_index
+#define vnic_rq_next_buf_index fnic_rq_next_buf_index
+#define vnic_rq_post fnic_rq_post
+#define vnic_rq_posting_soon fnic_rq_posting_soon
+#define vnic_rq_return_descs fnic_rq_return_descs
+#define vnic_rq_service fnic_rq_service
+#define vnic_rq_fill fnic_rq_fill
+#define vnic_rq_free fnic_rq_free
+#define vnic_rq_alloc fnic_rq_alloc
+#define vnic_rq_init fnic_rq_init
+#define vnic_rq_error_status fnic_rq_error_status
+#define vnic_rq_enable fnic_rq_enable
+#define vnic_rq_disable fnic_rq_disable
+#define vnic_rq_clean fnic_rq_clean
+
+/* Receive queue control */
+struct vnic_rq_ctrl {
+ u64 ring_base; /* 0x00 */
+ u32 ring_size; /* 0x08 */
+ u32 pad0;
+ u32 posted_index; /* 0x10 */
+ u32 pad1;
+ u32 cq_index; /* 0x18 */
+ u32 pad2;
+ u32 enable; /* 0x20 */
+ u32 pad3;
+ u32 running; /* 0x28 */
+ u32 pad4;
+ u32 fetch_index; /* 0x30 */
+ u32 pad5;
+ u32 error_interrupt_enable; /* 0x38 */
+ u32 pad6;
+ u32 error_interrupt_offset; /* 0x40 */
+ u32 pad7;
+ u32 error_status; /* 0x48 */
+ u32 pad8;
+ u32 dropped_packet_count; /* 0x50 */
+ u32 pad9;
+ u32 dropped_packet_count_rc; /* 0x58 */
+ u32 pad10;
+};
+
+/* Break the vnic_rq_buf allocations into blocks of 64 entries */
+#define VNIC_RQ_BUF_BLK_ENTRIES 64
+#define VNIC_RQ_BUF_BLK_SZ \
+ (VNIC_RQ_BUF_BLK_ENTRIES * sizeof(struct vnic_rq_buf))
+#define VNIC_RQ_BUF_BLKS_NEEDED(entries) \
+ DIV_ROUND_UP(entries, VNIC_RQ_BUF_BLK_ENTRIES)
+#define VNIC_RQ_BUF_BLKS_MAX VNIC_RQ_BUF_BLKS_NEEDED(4096)
+
+struct vnic_rq_buf {
+ struct vnic_rq_buf *next;
+ dma_addr_t dma_addr;
+ void *os_buf;
+ unsigned int os_buf_index;
+ unsigned int len;
+ unsigned int index;
+ void *desc;
+};
+
+struct vnic_rq {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_rq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ struct vnic_rq_buf *bufs[VNIC_RQ_BUF_BLKS_MAX];
+ struct vnic_rq_buf *to_use;
+ struct vnic_rq_buf *to_clean;
+ void *os_buf_head;
+ unsigned int buf_index;
+ unsigned int pkts_outstanding;
+};
+
+static inline unsigned int vnic_rq_desc_avail(struct vnic_rq *rq)
+{
+ /* how many does SW own? */
+ return rq->ring.desc_avail;
+}
+
+static inline unsigned int vnic_rq_desc_used(struct vnic_rq *rq)
+{
+ /* how many does HW own? */
+ return rq->ring.desc_count - rq->ring.desc_avail - 1;
+}
+
+static inline void *vnic_rq_next_desc(struct vnic_rq *rq)
+{
+ return rq->to_use->desc;
+}
+
+static inline unsigned int vnic_rq_next_index(struct vnic_rq *rq)
+{
+ return rq->to_use->index;
+}
+
+static inline unsigned int vnic_rq_next_buf_index(struct vnic_rq *rq)
+{
+ return rq->buf_index++;
+}
+
+static inline void vnic_rq_post(struct vnic_rq *rq,
+ void *os_buf, unsigned int os_buf_index,
+ dma_addr_t dma_addr, unsigned int len)
+{
+ struct vnic_rq_buf *buf = rq->to_use;
+
+ buf->os_buf = os_buf;
+ buf->os_buf_index = os_buf_index;
+ buf->dma_addr = dma_addr;
+ buf->len = len;
+
+ buf = buf->next;
+ rq->to_use = buf;
+ rq->ring.desc_avail--;
+
+ /* Move the posted_index every nth descriptor
+ */
+
+#ifndef VNIC_RQ_RETURN_RATE
+#define VNIC_RQ_RETURN_RATE 0xf /* keep 2^n - 1 */
+#endif
+
+ if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(buf->index, &rq->ctrl->posted_index);
+ }
+}
+
+static inline int vnic_rq_posting_soon(struct vnic_rq *rq)
+{
+ return (rq->to_use->index & VNIC_RQ_RETURN_RATE) == 0;
+}
+
+static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count)
+{
+ rq->ring.desc_avail += count;
+}
+
+enum desc_return_options {
+ VNIC_RQ_RETURN_DESC,
+ VNIC_RQ_DEFER_RETURN_DESC,
+};
+
+static inline void vnic_rq_service(struct vnic_rq *rq,
+ struct cq_desc *cq_desc, u16 completed_index,
+ int desc_return, void (*buf_service)(struct vnic_rq *rq,
+ struct cq_desc *cq_desc, struct vnic_rq_buf *buf,
+ int skipped, void *opaque), void *opaque)
+{
+ struct vnic_rq_buf *buf;
+ int skipped;
+
+ buf = rq->to_clean;
+ while (1) {
+
+ skipped = (buf->index != completed_index);
+
+ (*buf_service)(rq, cq_desc, buf, skipped, opaque);
+
+ if (desc_return == VNIC_RQ_RETURN_DESC)
+ rq->ring.desc_avail++;
+
+ rq->to_clean = buf->next;
+
+ if (!skipped)
+ break;
+
+ buf = rq->to_clean;
+ }
+}
+
+static inline int vnic_rq_fill(struct vnic_rq *rq,
+ int (*buf_fill)(struct vnic_rq *rq))
+{
+ int err;
+
+ while (vnic_rq_desc_avail(rq) > 1) {
+
+ err = (*buf_fill)(rq);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void vnic_rq_free(struct vnic_rq *rq);
+int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
+unsigned int vnic_rq_error_status(struct vnic_rq *rq);
+void vnic_rq_enable(struct vnic_rq *rq);
+int vnic_rq_disable(struct vnic_rq *rq);
+void vnic_rq_clean(struct vnic_rq *rq,
+ void (*buf_clean)(struct vnic_rq *rq, struct vnic_rq_buf *buf));
+
+#endif /* _VNIC_RQ_H_ */
diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h
new file mode 100644
index 000000000000..46baa5254001
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_scsi.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_SCSI_H_
+#define _VNIC_SCSI_H_
+
+#define VNIC_FNIC_WQ_COPY_COUNT_MIN 1
+#define VNIC_FNIC_WQ_COPY_COUNT_MAX 1
+
+#define VNIC_FNIC_WQ_DESCS_MIN 64
+#define VNIC_FNIC_WQ_DESCS_MAX 128
+
+#define VNIC_FNIC_WQ_COPY_DESCS_MIN 64
+#define VNIC_FNIC_WQ_COPY_DESCS_MAX 512
+
+#define VNIC_FNIC_RQ_DESCS_MIN 64
+#define VNIC_FNIC_RQ_DESCS_MAX 128
+
+#define VNIC_FNIC_EDTOV_MIN 1000
+#define VNIC_FNIC_EDTOV_MAX 255000
+#define VNIC_FNIC_EDTOV_DEF 2000
+
+#define VNIC_FNIC_RATOV_MIN 1000
+#define VNIC_FNIC_RATOV_MAX 255000
+
+#define VNIC_FNIC_MAXDATAFIELDSIZE_MIN 256
+#define VNIC_FNIC_MAXDATAFIELDSIZE_MAX 2112
+
+#define VNIC_FNIC_FLOGI_RETRIES_MIN 0
+#define VNIC_FNIC_FLOGI_RETRIES_MAX 0xffffffff
+#define VNIC_FNIC_FLOGI_RETRIES_DEF 0xffffffff
+
+#define VNIC_FNIC_FLOGI_TIMEOUT_MIN 1000
+#define VNIC_FNIC_FLOGI_TIMEOUT_MAX 255000
+
+#define VNIC_FNIC_PLOGI_RETRIES_MIN 0
+#define VNIC_FNIC_PLOGI_RETRIES_MAX 255
+#define VNIC_FNIC_PLOGI_RETRIES_DEF 8
+
+#define VNIC_FNIC_PLOGI_TIMEOUT_MIN 1000
+#define VNIC_FNIC_PLOGI_TIMEOUT_MAX 255000
+
+#define VNIC_FNIC_IO_THROTTLE_COUNT_MIN 256
+#define VNIC_FNIC_IO_THROTTLE_COUNT_MAX 4096
+
+#define VNIC_FNIC_LINK_DOWN_TIMEOUT_MIN 0
+#define VNIC_FNIC_LINK_DOWN_TIMEOUT_MAX 240000
+
+#define VNIC_FNIC_PORT_DOWN_TIMEOUT_MIN 0
+#define VNIC_FNIC_PORT_DOWN_TIMEOUT_MAX 240000
+
+#define VNIC_FNIC_PORT_DOWN_IO_RETRIES_MIN 0
+#define VNIC_FNIC_PORT_DOWN_IO_RETRIES_MAX 255
+
+#define VNIC_FNIC_LUNS_PER_TARGET_MIN 1
+#define VNIC_FNIC_LUNS_PER_TARGET_MAX 1024
+
+/* Device-specific region: scsi configuration */
+struct vnic_fc_config {
+ u64 node_wwn;
+ u64 port_wwn;
+ u32 flags;
+ u32 wq_enet_desc_count;
+ u32 wq_copy_desc_count;
+ u32 rq_desc_count;
+ u32 flogi_retries;
+ u32 flogi_timeout;
+ u32 plogi_retries;
+ u32 plogi_timeout;
+ u32 io_throttle_count;
+ u32 link_down_timeout;
+ u32 port_down_timeout;
+ u32 port_down_io_retries;
+ u32 luns_per_tgt;
+ u16 maxdatafieldsize;
+ u16 ed_tov;
+ u16 ra_tov;
+ u16 intr_timer;
+ u8 intr_timer_type;
+};
+
+#define VFCF_FCP_SEQ_LVL_ERR 0x1 /* Enable FCP-2 Error Recovery */
+#define VFCF_PERBI 0x2 /* persistent binding info available */
+
+#endif /* _VNIC_SCSI_H_ */
diff --git a/drivers/scsi/fnic/vnic_stats.h b/drivers/scsi/fnic/vnic_stats.h
new file mode 100644
index 000000000000..5372e23c1cb3
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_stats.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_STATS_H_
+#define _VNIC_STATS_H_
+
+/* Tx statistics */
+struct vnic_tx_stats {
+ u64 tx_frames_ok;
+ u64 tx_unicast_frames_ok;
+ u64 tx_multicast_frames_ok;
+ u64 tx_broadcast_frames_ok;
+ u64 tx_bytes_ok;
+ u64 tx_unicast_bytes_ok;
+ u64 tx_multicast_bytes_ok;
+ u64 tx_broadcast_bytes_ok;
+ u64 tx_drops;
+ u64 tx_errors;
+ u64 tx_tso;
+ u64 rsvd[16];
+};
+
+/* Rx statistics */
+struct vnic_rx_stats {
+ u64 rx_frames_ok;
+ u64 rx_frames_total;
+ u64 rx_unicast_frames_ok;
+ u64 rx_multicast_frames_ok;
+ u64 rx_broadcast_frames_ok;
+ u64 rx_bytes_ok;
+ u64 rx_unicast_bytes_ok;
+ u64 rx_multicast_bytes_ok;
+ u64 rx_broadcast_bytes_ok;
+ u64 rx_drop;
+ u64 rx_no_bufs;
+ u64 rx_errors;
+ u64 rx_rss;
+ u64 rx_crc_errors;
+ u64 rx_frames_64;
+ u64 rx_frames_127;
+ u64 rx_frames_255;
+ u64 rx_frames_511;
+ u64 rx_frames_1023;
+ u64 rx_frames_1518;
+ u64 rx_frames_to_max;
+ u64 rsvd[16];
+};
+
+struct vnic_stats {
+ struct vnic_tx_stats tx;
+ struct vnic_rx_stats rx;
+};
+
+#endif /* _VNIC_STATS_H_ */
diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c
new file mode 100644
index 000000000000..1f9ea790d130
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_wq.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+
+static int vnic_wq_alloc_bufs(struct vnic_wq *wq)
+{
+ struct vnic_wq_buf *buf;
+ struct vnic_dev *vdev;
+ unsigned int i, j, count = wq->ring.desc_count;
+ unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count);
+
+ vdev = wq->vdev;
+
+ for (i = 0; i < blks; i++) {
+ wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC);
+ if (!wq->bufs[i]) {
+ printk(KERN_ERR "Failed to alloc wq_bufs\n");
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < blks; i++) {
+ buf = wq->bufs[i];
+ for (j = 0; j < VNIC_WQ_BUF_BLK_ENTRIES; j++) {
+ buf->index = i * VNIC_WQ_BUF_BLK_ENTRIES + j;
+ buf->desc = (u8 *)wq->ring.descs +
+ wq->ring.desc_size * buf->index;
+ if (buf->index + 1 == count) {
+ buf->next = wq->bufs[0];
+ break;
+ } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES) {
+ buf->next = wq->bufs[i + 1];
+ } else {
+ buf->next = buf + 1;
+ buf++;
+ }
+ }
+ }
+
+ wq->to_use = wq->to_clean = wq->bufs[0];
+
+ return 0;
+}
+
+void vnic_wq_free(struct vnic_wq *wq)
+{
+ struct vnic_dev *vdev;
+ unsigned int i;
+
+ vdev = wq->vdev;
+
+ vnic_dev_free_desc_ring(vdev, &wq->ring);
+
+ for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) {
+ kfree(wq->bufs[i]);
+ wq->bufs[i] = NULL;
+ }
+
+ wq->ctrl = NULL;
+
+}
+
+int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ wq->index = index;
+ wq->vdev = vdev;
+
+ wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index);
+ if (!wq->ctrl) {
+ printk(KERN_ERR "Failed to hook WQ[%d] resource\n", index);
+ return -EINVAL;
+ }
+
+ vnic_wq_disable(wq);
+
+ err = vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
+ if (err)
+ return err;
+
+ err = vnic_wq_alloc_bufs(wq);
+ if (err) {
+ vnic_wq_free(wq);
+ return err;
+ }
+
+ return 0;
+}
+
+void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+
+ paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &wq->ctrl->ring_base);
+ iowrite32(wq->ring.desc_count, &wq->ctrl->ring_size);
+ iowrite32(0, &wq->ctrl->fetch_index);
+ iowrite32(0, &wq->ctrl->posted_index);
+ iowrite32(cq_index, &wq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+ iowrite32(0, &wq->ctrl->error_status);
+}
+
+unsigned int vnic_wq_error_status(struct vnic_wq *wq)
+{
+ return ioread32(&wq->ctrl->error_status);
+}
+
+void vnic_wq_enable(struct vnic_wq *wq)
+{
+ iowrite32(1, &wq->ctrl->enable);
+}
+
+int vnic_wq_disable(struct vnic_wq *wq)
+{
+ unsigned int wait;
+
+ iowrite32(0, &wq->ctrl->enable);
+
+ /* Wait for HW to ACK disable request */
+ for (wait = 0; wait < 100; wait++) {
+ if (!(ioread32(&wq->ctrl->running)))
+ return 0;
+ udelay(1);
+ }
+
+ printk(KERN_ERR "Failed to disable WQ[%d]\n", wq->index);
+
+ return -ETIMEDOUT;
+}
+
+void vnic_wq_clean(struct vnic_wq *wq,
+ void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf))
+{
+ struct vnic_wq_buf *buf;
+
+ BUG_ON(ioread32(&wq->ctrl->enable));
+
+ buf = wq->to_clean;
+
+ while (vnic_wq_desc_used(wq) > 0) {
+
+ (*buf_clean)(wq, buf);
+
+ buf = wq->to_clean = buf->next;
+ wq->ring.desc_avail++;
+ }
+
+ wq->to_use = wq->to_clean = wq->bufs[0];
+
+ iowrite32(0, &wq->ctrl->fetch_index);
+ iowrite32(0, &wq->ctrl->posted_index);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ vnic_dev_clear_desc_ring(&wq->ring);
+}
diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h
new file mode 100644
index 000000000000..5cd094f79281
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_wq.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_WQ_H_
+#define _VNIC_WQ_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_wq_desc_avail fnic_wq_desc_avail
+#define vnic_wq_desc_used fnic_wq_desc_used
+#define vnic_wq_next_desc fni_cwq_next_desc
+#define vnic_wq_post fnic_wq_post
+#define vnic_wq_service fnic_wq_service
+#define vnic_wq_free fnic_wq_free
+#define vnic_wq_alloc fnic_wq_alloc
+#define vnic_wq_init fnic_wq_init
+#define vnic_wq_error_status fnic_wq_error_status
+#define vnic_wq_enable fnic_wq_enable
+#define vnic_wq_disable fnic_wq_disable
+#define vnic_wq_clean fnic_wq_clean
+
+/* Work queue control */
+struct vnic_wq_ctrl {
+ u64 ring_base; /* 0x00 */
+ u32 ring_size; /* 0x08 */
+ u32 pad0;
+ u32 posted_index; /* 0x10 */
+ u32 pad1;
+ u32 cq_index; /* 0x18 */
+ u32 pad2;
+ u32 enable; /* 0x20 */
+ u32 pad3;
+ u32 running; /* 0x28 */
+ u32 pad4;
+ u32 fetch_index; /* 0x30 */
+ u32 pad5;
+ u32 dca_value; /* 0x38 */
+ u32 pad6;
+ u32 error_interrupt_enable; /* 0x40 */
+ u32 pad7;
+ u32 error_interrupt_offset; /* 0x48 */
+ u32 pad8;
+ u32 error_status; /* 0x50 */
+ u32 pad9;
+};
+
+struct vnic_wq_buf {
+ struct vnic_wq_buf *next;
+ dma_addr_t dma_addr;
+ void *os_buf;
+ unsigned int len;
+ unsigned int index;
+ int sop;
+ void *desc;
+};
+
+/* Break the vnic_wq_buf allocations into blocks of 64 entries */
+#define VNIC_WQ_BUF_BLK_ENTRIES 64
+#define VNIC_WQ_BUF_BLK_SZ \
+ (VNIC_WQ_BUF_BLK_ENTRIES * sizeof(struct vnic_wq_buf))
+#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \
+ DIV_ROUND_UP(entries, VNIC_WQ_BUF_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096)
+
+struct vnic_wq {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_wq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ struct vnic_wq_buf *bufs[VNIC_WQ_BUF_BLKS_MAX];
+ struct vnic_wq_buf *to_use;
+ struct vnic_wq_buf *to_clean;
+ unsigned int pkts_outstanding;
+};
+
+static inline unsigned int vnic_wq_desc_avail(struct vnic_wq *wq)
+{
+ /* how many does SW own? */
+ return wq->ring.desc_avail;
+}
+
+static inline unsigned int vnic_wq_desc_used(struct vnic_wq *wq)
+{
+ /* how many does HW own? */
+ return wq->ring.desc_count - wq->ring.desc_avail - 1;
+}
+
+static inline void *vnic_wq_next_desc(struct vnic_wq *wq)
+{
+ return wq->to_use->desc;
+}
+
+static inline void vnic_wq_post(struct vnic_wq *wq,
+ void *os_buf, dma_addr_t dma_addr,
+ unsigned int len, int sop, int eop)
+{
+ struct vnic_wq_buf *buf = wq->to_use;
+
+ buf->sop = sop;
+ buf->os_buf = eop ? os_buf : NULL;
+ buf->dma_addr = dma_addr;
+ buf->len = len;
+
+ buf = buf->next;
+ if (eop) {
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(buf->index, &wq->ctrl->posted_index);
+ }
+ wq->to_use = buf;
+
+ wq->ring.desc_avail--;
+}
+
+static inline void vnic_wq_service(struct vnic_wq *wq,
+ struct cq_desc *cq_desc, u16 completed_index,
+ void (*buf_service)(struct vnic_wq *wq,
+ struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque),
+ void *opaque)
+{
+ struct vnic_wq_buf *buf;
+
+ buf = wq->to_clean;
+ while (1) {
+
+ (*buf_service)(wq, cq_desc, buf, opaque);
+
+ wq->ring.desc_avail++;
+
+ wq->to_clean = buf->next;
+
+ if (buf->index == completed_index)
+ break;
+
+ buf = wq->to_clean;
+ }
+}
+
+void vnic_wq_free(struct vnic_wq *wq);
+int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
+unsigned int vnic_wq_error_status(struct vnic_wq *wq);
+void vnic_wq_enable(struct vnic_wq *wq);
+int vnic_wq_disable(struct vnic_wq *wq);
+void vnic_wq_clean(struct vnic_wq *wq,
+ void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf));
+
+#endif /* _VNIC_WQ_H_ */
diff --git a/drivers/scsi/fnic/vnic_wq_copy.c b/drivers/scsi/fnic/vnic_wq_copy.c
new file mode 100644
index 000000000000..9eab7e7caf38
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_wq_copy.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_wq_copy.h"
+
+void vnic_wq_copy_enable(struct vnic_wq_copy *wq)
+{
+ iowrite32(1, &wq->ctrl->enable);
+}
+
+int vnic_wq_copy_disable(struct vnic_wq_copy *wq)
+{
+ unsigned int wait;
+
+ iowrite32(0, &wq->ctrl->enable);
+
+ /* Wait for HW to ACK disable request */
+ for (wait = 0; wait < 100; wait++) {
+ if (!(ioread32(&wq->ctrl->running)))
+ return 0;
+ udelay(1);
+ }
+
+ printk(KERN_ERR "Failed to disable Copy WQ[%d],"
+ " fetch index=%d, posted_index=%d\n",
+ wq->index, ioread32(&wq->ctrl->fetch_index),
+ ioread32(&wq->ctrl->posted_index));
+
+ return -ENODEV;
+}
+
+void vnic_wq_copy_clean(struct vnic_wq_copy *wq,
+ void (*q_clean)(struct vnic_wq_copy *wq,
+ struct fcpio_host_req *wq_desc))
+{
+ BUG_ON(ioread32(&wq->ctrl->enable));
+
+ if (vnic_wq_copy_desc_in_use(wq))
+ vnic_wq_copy_service(wq, -1, q_clean);
+
+ wq->to_use_index = wq->to_clean_index = 0;
+
+ iowrite32(0, &wq->ctrl->fetch_index);
+ iowrite32(0, &wq->ctrl->posted_index);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ vnic_dev_clear_desc_ring(&wq->ring);
+}
+
+void vnic_wq_copy_free(struct vnic_wq_copy *wq)
+{
+ struct vnic_dev *vdev;
+
+ vdev = wq->vdev;
+ vnic_dev_free_desc_ring(vdev, &wq->ring);
+ wq->ctrl = NULL;
+}
+
+int vnic_wq_copy_alloc(struct vnic_dev *vdev, struct vnic_wq_copy *wq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size)
+{
+ int err;
+
+ wq->index = index;
+ wq->vdev = vdev;
+ wq->to_use_index = wq->to_clean_index = 0;
+ wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index);
+ if (!wq->ctrl) {
+ printk(KERN_ERR "Failed to hook COPY WQ[%d] resource\n", index);
+ return -EINVAL;
+ }
+
+ vnic_wq_copy_disable(wq);
+
+ err = vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void vnic_wq_copy_init(struct vnic_wq_copy *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+
+ paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &wq->ctrl->ring_base);
+ iowrite32(wq->ring.desc_count, &wq->ctrl->ring_size);
+ iowrite32(0, &wq->ctrl->fetch_index);
+ iowrite32(0, &wq->ctrl->posted_index);
+ iowrite32(cq_index, &wq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+}
+
diff --git a/drivers/scsi/fnic/vnic_wq_copy.h b/drivers/scsi/fnic/vnic_wq_copy.h
new file mode 100644
index 000000000000..6aff9740c3df
--- /dev/null
+++ b/drivers/scsi/fnic/vnic_wq_copy.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _VNIC_WQ_COPY_H_
+#define _VNIC_WQ_COPY_H_
+
+#include <linux/pci.h>
+#include "vnic_wq.h"
+#include "fcpio.h"
+
+#define VNIC_WQ_COPY_MAX 1
+
+struct vnic_wq_copy {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_wq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ unsigned to_use_index;
+ unsigned to_clean_index;
+};
+
+static inline unsigned int vnic_wq_copy_desc_avail(struct vnic_wq_copy *wq)
+{
+ return wq->ring.desc_avail;
+}
+
+static inline unsigned int vnic_wq_copy_desc_in_use(struct vnic_wq_copy *wq)
+{
+ return wq->ring.desc_count - 1 - wq->ring.desc_avail;
+}
+
+static inline void *vnic_wq_copy_next_desc(struct vnic_wq_copy *wq)
+{
+ struct fcpio_host_req *desc = wq->ring.descs;
+ return &desc[wq->to_use_index];
+}
+
+static inline void vnic_wq_copy_post(struct vnic_wq_copy *wq)
+{
+
+ ((wq->to_use_index + 1) == wq->ring.desc_count) ?
+ (wq->to_use_index = 0) : (wq->to_use_index++);
+ wq->ring.desc_avail--;
+
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+
+ iowrite32(wq->to_use_index, &wq->ctrl->posted_index);
+}
+
+static inline void vnic_wq_copy_desc_process(struct vnic_wq_copy *wq, u16 index)
+{
+ unsigned int cnt;
+
+ if (wq->to_clean_index <= index)
+ cnt = (index - wq->to_clean_index) + 1;
+ else
+ cnt = wq->ring.desc_count - wq->to_clean_index + index + 1;
+
+ wq->to_clean_index = ((index + 1) % wq->ring.desc_count);
+ wq->ring.desc_avail += cnt;
+
+}
+
+static inline void vnic_wq_copy_service(struct vnic_wq_copy *wq,
+ u16 completed_index,
+ void (*q_service)(struct vnic_wq_copy *wq,
+ struct fcpio_host_req *wq_desc))
+{
+ struct fcpio_host_req *wq_desc = wq->ring.descs;
+ unsigned int curr_index;
+
+ while (1) {
+
+ if (q_service)
+ (*q_service)(wq, &wq_desc[wq->to_clean_index]);
+
+ wq->ring.desc_avail++;
+
+ curr_index = wq->to_clean_index;
+
+ /* increment the to-clean index so that we start
+ * with an unprocessed index next time we enter the loop
+ */
+ ((wq->to_clean_index + 1) == wq->ring.desc_count) ?
+ (wq->to_clean_index = 0) : (wq->to_clean_index++);
+
+ if (curr_index == completed_index)
+ break;
+
+ /* we have cleaned all the entries */
+ if ((completed_index == (u16)-1) &&
+ (wq->to_clean_index == wq->to_use_index))
+ break;
+ }
+}
+
+void vnic_wq_copy_enable(struct vnic_wq_copy *wq);
+int vnic_wq_copy_disable(struct vnic_wq_copy *wq);
+void vnic_wq_copy_free(struct vnic_wq_copy *wq);
+int vnic_wq_copy_alloc(struct vnic_dev *vdev, struct vnic_wq_copy *wq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_copy_init(struct vnic_wq_copy *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
+void vnic_wq_copy_clean(struct vnic_wq_copy *wq,
+ void (*q_clean)(struct vnic_wq_copy *wq,
+ struct fcpio_host_req *wq_desc));
+
+#endif /* _VNIC_WQ_COPY_H_ */
diff --git a/drivers/scsi/fnic/wq_enet_desc.h b/drivers/scsi/fnic/wq_enet_desc.h
new file mode 100644
index 000000000000..b121cbad18b8
--- /dev/null
+++ b/drivers/scsi/fnic/wq_enet_desc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _WQ_ENET_DESC_H_
+#define _WQ_ENET_DESC_H_
+
+/* Ethernet work queue descriptor: 16B */
+struct wq_enet_desc {
+ __le64 address;
+ __le16 length;
+ __le16 mss_loopback;
+ __le16 header_length_flags;
+ __le16 vlan_tag;
+};
+
+#define WQ_ENET_ADDR_BITS 64
+#define WQ_ENET_LEN_BITS 14
+#define WQ_ENET_LEN_MASK ((1 << WQ_ENET_LEN_BITS) - 1)
+#define WQ_ENET_MSS_BITS 14
+#define WQ_ENET_MSS_MASK ((1 << WQ_ENET_MSS_BITS) - 1)
+#define WQ_ENET_MSS_SHIFT 2
+#define WQ_ENET_LOOPBACK_SHIFT 1
+#define WQ_ENET_HDRLEN_BITS 10
+#define WQ_ENET_HDRLEN_MASK ((1 << WQ_ENET_HDRLEN_BITS) - 1)
+#define WQ_ENET_FLAGS_OM_BITS 2
+#define WQ_ENET_FLAGS_OM_MASK ((1 << WQ_ENET_FLAGS_OM_BITS) - 1)
+#define WQ_ENET_FLAGS_EOP_SHIFT 12
+#define WQ_ENET_FLAGS_CQ_ENTRY_SHIFT 13
+#define WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT 14
+#define WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT 15
+
+#define WQ_ENET_OFFLOAD_MODE_CSUM 0
+#define WQ_ENET_OFFLOAD_MODE_RESERVED 1
+#define WQ_ENET_OFFLOAD_MODE_CSUM_L4 2
+#define WQ_ENET_OFFLOAD_MODE_TSO 3
+
+static inline void wq_enet_desc_enc(struct wq_enet_desc *desc,
+ u64 address, u16 length, u16 mss, u16 header_length,
+ u8 offload_mode, u8 eop, u8 cq_entry, u8 fcoe_encap,
+ u8 vlan_tag_insert, u16 vlan_tag, u8 loopback)
+{
+ desc->address = cpu_to_le64(address);
+ desc->length = cpu_to_le16(length & WQ_ENET_LEN_MASK);
+ desc->mss_loopback = cpu_to_le16((mss & WQ_ENET_MSS_MASK) <<
+ WQ_ENET_MSS_SHIFT | (loopback & 1) << WQ_ENET_LOOPBACK_SHIFT);
+ desc->header_length_flags = cpu_to_le16(
+ (header_length & WQ_ENET_HDRLEN_MASK) |
+ (offload_mode & WQ_ENET_FLAGS_OM_MASK) << WQ_ENET_HDRLEN_BITS |
+ (eop & 1) << WQ_ENET_FLAGS_EOP_SHIFT |
+ (cq_entry & 1) << WQ_ENET_FLAGS_CQ_ENTRY_SHIFT |
+ (fcoe_encap & 1) << WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT |
+ (vlan_tag_insert & 1) << WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT);
+ desc->vlan_tag = cpu_to_le16(vlan_tag);
+}
+
+static inline void wq_enet_desc_dec(struct wq_enet_desc *desc,
+ u64 *address, u16 *length, u16 *mss, u16 *header_length,
+ u8 *offload_mode, u8 *eop, u8 *cq_entry, u8 *fcoe_encap,
+ u8 *vlan_tag_insert, u16 *vlan_tag, u8 *loopback)
+{
+ *address = le64_to_cpu(desc->address);
+ *length = le16_to_cpu(desc->length) & WQ_ENET_LEN_MASK;
+ *mss = (le16_to_cpu(desc->mss_loopback) >> WQ_ENET_MSS_SHIFT) &
+ WQ_ENET_MSS_MASK;
+ *loopback = (u8)((le16_to_cpu(desc->mss_loopback) >>
+ WQ_ENET_LOOPBACK_SHIFT) & 1);
+ *header_length = le16_to_cpu(desc->header_length_flags) &
+ WQ_ENET_HDRLEN_MASK;
+ *offload_mode = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_HDRLEN_BITS) & WQ_ENET_FLAGS_OM_MASK);
+ *eop = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_EOP_SHIFT) & 1);
+ *cq_entry = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_CQ_ENTRY_SHIFT) & 1);
+ *fcoe_encap = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT) & 1);
+ *vlan_tag_insert = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT) & 1);
+ *vlan_tag = le16_to_cpu(desc->vlan_tag);
+}
+
+#endif /* _WQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
index 59349a316e13..1258da34fbc2 100644
--- a/drivers/scsi/gdth_proc.c
+++ b/drivers/scsi/gdth_proc.c
@@ -152,6 +152,7 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
struct Scsi_Host *host, gdth_ha_str *ha)
{
int size = 0,len = 0;
+ int hlen;
off_t begin = 0,pos = 0;
int id, i, j, k, sec, flag;
int no_mdrv = 0, drv_no, is_mirr;
@@ -192,11 +193,11 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
if (reserve_list[0] == 0xff)
strcpy(hrec, "--");
else {
- sprintf(hrec, "%d", reserve_list[0]);
+ hlen = sprintf(hrec, "%d", reserve_list[0]);
for (i = 1; i < MAX_RES_ARGS; i++) {
if (reserve_list[i] == 0xff)
break;
- sprintf(hrec,"%s,%d", hrec, reserve_list[i]);
+ hlen += snprintf(hrec + hlen , 161 - hlen, ",%d", reserve_list[i]);
}
}
size = sprintf(buffer+len,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index ea4abee7a2a9..b4b805e8d7db 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -110,7 +110,7 @@ static const struct {
{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
+ { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" },
{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
@@ -143,6 +143,7 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *);
static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
+static void ibmvfc_npiv_logout(struct ibmvfc_host *);
static const char *unknown_error = "unknown error";
@@ -275,7 +276,7 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
int fc_rsp_len = rsp->fcp_rsp_len;
if ((rsp->flags & FCP_RSP_LEN_VALID) &&
- ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
+ ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
rsp->data.info.rsp_code))
return DID_ERROR << 16;
@@ -431,6 +432,8 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
case IBMVFC_TGT_ACTION_DEL_RPORT:
break;
default:
+ if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
+ tgt->add_rport = 0;
tgt->action = action;
break;
}
@@ -475,6 +478,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
vhost->action = action;
break;
+ case IBMVFC_HOST_ACTION_LOGO_WAIT:
+ if (vhost->action == IBMVFC_HOST_ACTION_LOGO)
+ vhost->action = action;
+ break;
case IBMVFC_HOST_ACTION_INIT_WAIT:
if (vhost->action == IBMVFC_HOST_ACTION_INIT)
vhost->action = action;
@@ -483,7 +490,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
switch (vhost->action) {
case IBMVFC_HOST_ACTION_INIT_WAIT:
case IBMVFC_HOST_ACTION_NONE:
- case IBMVFC_HOST_ACTION_TGT_ADD:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
vhost->action = action;
break;
default:
@@ -494,11 +501,11 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
vhost->action = action;
break;
+ case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_QUERY_TGTS:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
- case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_NONE:
default:
vhost->action = action;
@@ -576,7 +583,7 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
}
list_for_each_entry(tgt, &vhost->targets, queue)
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
vhost->job_step = ibmvfc_npiv_login;
@@ -646,6 +653,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
vhost->state = IBMVFC_NO_CRQ;
+ vhost->logged_in = 0;
dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
free_page((unsigned long)crq->msgs);
}
@@ -692,6 +700,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
vhost->state = IBMVFC_NO_CRQ;
+ vhost->logged_in = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
/* Clean out the queue */
@@ -807,10 +816,10 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
}
/**
- * __ibmvfc_reset_host - Reset the connection to the server (no locking)
+ * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ
* @vhost: struct ibmvfc host to reset
**/
-static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
+static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
{
int rc;
@@ -826,9 +835,25 @@ static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
}
/**
- * ibmvfc_reset_host - Reset the connection to the server
+ * __ibmvfc_reset_host - Reset the connection to the server (no locking)
* @vhost: struct ibmvfc host to reset
**/
+static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
+{
+ if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT &&
+ !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+ scsi_block_requests(vhost->host);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO);
+ vhost->job_step = ibmvfc_npiv_logout;
+ wake_up(&vhost->work_wait_q);
+ } else
+ ibmvfc_hard_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_reset_host - Reset the connection to the server
+ * @vhost: ibmvfc host struct
+ **/
static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
{
unsigned long flags;
@@ -842,9 +867,13 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
* ibmvfc_retry_host_init - Retry host initialization if allowed
* @vhost: ibmvfc host struct
*
+ * Returns: 1 if init will be retried / 0 if not
+ *
**/
-static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
+static int ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
{
+ int retry = 0;
+
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
vhost->delay_init = 1;
if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
@@ -853,11 +882,14 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
} else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
__ibmvfc_reset_host(vhost);
- else
+ else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+ retry = 1;
+ }
}
wake_up(&vhost->work_wait_q);
+ return retry;
}
/**
@@ -1137,8 +1169,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->partition_num = vhost->partition_number;
login_info->vfc_frame_version = 1;
login_info->fcp_version = 3;
+ login_info->flags = IBMVFC_FLUSH_ON_HALT;
if (vhost->client_migrated)
- login_info->flags = IBMVFC_CLIENT_MIGRATED;
+ login_info->flags |= IBMVFC_CLIENT_MIGRATED;
login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
login_info->capabilities = IBMVFC_CAN_MIGRATE;
@@ -1452,6 +1485,27 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
}
/**
+ * ibmvfc_relogin - Log back into the specified device
+ * @sdev: scsi device struct
+ *
+ **/
+static void ibmvfc_relogin(struct scsi_device *sdev)
+{
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct ibmvfc_target *tgt;
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (rport == tgt->rport) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ break;
+ }
+ }
+
+ ibmvfc_reinit_host(vhost);
+}
+
+/**
* ibmvfc_scsi_done - Handle responses from commands
* @evt: ibmvfc event to be handled
*
@@ -1483,7 +1537,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
- ibmvfc_reinit_host(evt->vhost);
+ ibmvfc_relogin(cmnd->device);
if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
cmnd->result = (DID_ERROR << 16);
@@ -2148,13 +2202,31 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
struct ibmvfc_host *vhost)
{
const char *desc = ibmvfc_get_ae_desc(crq->event);
+ struct ibmvfc_target *tgt;
ibmvfc_log(vhost, 3, "%s event received. scsi_id: %llx, wwpn: %llx,"
" node_name: %llx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name);
switch (crq->event) {
- case IBMVFC_AE_LINK_UP:
case IBMVFC_AE_RESUME:
+ switch (crq->link_state) {
+ case IBMVFC_AE_LS_LINK_DOWN:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ break;
+ case IBMVFC_AE_LS_LINK_DEAD:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ case IBMVFC_AE_LS_LINK_UP:
+ case IBMVFC_AE_LS_LINK_BOUNCED:
+ default:
+ vhost->events_to_log |= IBMVFC_AE_LINKUP;
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ break;
+ };
+
+ break;
+ case IBMVFC_AE_LINK_UP:
vhost->events_to_log |= IBMVFC_AE_LINKUP;
vhost->delay_init = 1;
__ibmvfc_reset_host(vhost);
@@ -2168,9 +2240,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_SCN_NPORT:
case IBMVFC_AE_SCN_GROUP:
vhost->events_to_log |= IBMVFC_AE_RSCN;
+ ibmvfc_reinit_host(vhost);
+ break;
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
case IBMVFC_AE_ELS_PLOGI:
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ break;
+ if (crq->scsi_id && tgt->scsi_id != crq->scsi_id)
+ continue;
+ if (crq->wwpn && tgt->ids.port_name != crq->wwpn)
+ continue;
+ if (crq->node_name && tgt->ids.node_name != crq->node_name)
+ continue;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ }
+
ibmvfc_reinit_host(vhost);
break;
case IBMVFC_AE_LINK_DOWN:
@@ -2222,6 +2308,7 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
return;
case IBMVFC_CRQ_XPORT_EVENT:
vhost->state = IBMVFC_NO_CRQ;
+ vhost->logged_in = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
if (crq->format == IBMVFC_PARTITION_MIGRATED) {
/* We need to re-setup the interpartition connection */
@@ -2299,7 +2386,7 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
done = 1;
}
- if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE)
+ if (vhost->scan_complete)
done = 1;
spin_unlock_irqrestore(shost->host_lock, flags);
return done;
@@ -2434,14 +2521,6 @@ static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
vhost->login_buf->resp.partition_name);
}
-static struct device_attribute ibmvfc_host_partition_name = {
- .attr = {
- .name = "partition_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_partition_name,
-};
-
static ssize_t ibmvfc_show_host_device_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2452,14 +2531,6 @@ static ssize_t ibmvfc_show_host_device_name(struct device *dev,
vhost->login_buf->resp.device_name);
}
-static struct device_attribute ibmvfc_host_device_name = {
- .attr = {
- .name = "device_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_device_name,
-};
-
static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2470,14 +2541,6 @@ static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
vhost->login_buf->resp.port_loc_code);
}
-static struct device_attribute ibmvfc_host_loc_code = {
- .attr = {
- .name = "port_loc_code",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_loc_code,
-};
-
static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2488,14 +2551,6 @@ static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
vhost->login_buf->resp.drc_name);
}
-static struct device_attribute ibmvfc_host_drc_name = {
- .attr = {
- .name = "drc_name",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_drc_name,
-};
-
static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2504,13 +2559,13 @@ static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
}
-static struct device_attribute ibmvfc_host_npiv_version = {
- .attr = {
- .name = "npiv_version",
- .mode = S_IRUGO,
- },
- .show = ibmvfc_show_host_npiv_version,
-};
+static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ return snprintf(buf, PAGE_SIZE, "%llx\n", vhost->login_buf->resp.capabilities);
+}
/**
* ibmvfc_show_log_level - Show the adapter's error logging level
@@ -2556,14 +2611,14 @@ static ssize_t ibmvfc_store_log_level(struct device *dev,
return strlen(buf);
}
-static struct device_attribute ibmvfc_log_level_attr = {
- .attr = {
- .name = "log_level",
- .mode = S_IRUGO | S_IWUSR,
- },
- .show = ibmvfc_show_log_level,
- .store = ibmvfc_store_log_level
-};
+static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL);
+static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL);
+static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL);
+static DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL);
+static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL);
+static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL);
+static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
+ ibmvfc_show_log_level, ibmvfc_store_log_level);
#ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
@@ -2612,12 +2667,13 @@ static struct bin_attribute ibmvfc_trace_attr = {
#endif
static struct device_attribute *ibmvfc_attrs[] = {
- &ibmvfc_host_partition_name,
- &ibmvfc_host_device_name,
- &ibmvfc_host_loc_code,
- &ibmvfc_host_drc_name,
- &ibmvfc_host_npiv_version,
- &ibmvfc_log_level_attr,
+ &dev_attr_partition_name,
+ &dev_attr_device_name,
+ &dev_attr_port_loc_code,
+ &dev_attr_drc_name,
+ &dev_attr_npiv_version,
+ &dev_attr_capabilities,
+ &dev_attr_log_level,
NULL
};
@@ -2774,15 +2830,19 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
* @tgt: ibmvfc target struct
* @job_step: initialization job step
*
+ * Returns: 1 if step will be retried / 0 if not
+ *
**/
-static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
+static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
wake_up(&tgt->vhost->work_wait_q);
+ return 0;
} else
ibmvfc_init_tgt(tgt, job_step);
+ return 1;
}
/* Defined in FC-LS */
@@ -2831,7 +2891,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
u32 status = rsp->common.status;
- int index;
+ int index, level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2850,7 +2910,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC)
tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+ tgt->add_rport = 1;
} else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
} else if (prli_rsp[index].retry)
@@ -2867,13 +2927,14 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error),
- rsp->status, rsp->error, status);
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error),
+ rsp->status, rsp->error, status);
break;
};
@@ -2932,6 +2993,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
u32 status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2960,15 +3022,15 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
-
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+ ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+ ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
break;
};
@@ -3129,13 +3191,13 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
tgt_dbg(tgt, "ADISC succeeded\n");
if (ibmvfc_adisc_needs_plogi(mad, tgt))
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
case IBMVFC_MAD_FAILED:
default:
- tgt->need_login = 1;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
@@ -3322,6 +3384,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
u32 status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -3341,19 +3404,19 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
-
if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+ tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+ ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+ ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
break;
};
@@ -3420,7 +3483,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
- tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL);
+ tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
if (!tgt) {
dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n",
scsi_id);
@@ -3472,6 +3535,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
u32 mad_status = rsp->common.status;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
@@ -3480,9 +3544,9 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
break;
case IBMVFC_MAD_FAILED:
- dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
- ibmvfc_retry_host_init(vhost);
+ level += ibmvfc_retry_host_init(vhost);
+ ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -3534,18 +3598,19 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
u32 mad_status = evt->xfer_iu->npiv_login.common.status;
struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
unsigned int npiv_max_sectors;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
ibmvfc_free_event(evt);
break;
case IBMVFC_MAD_FAILED:
- dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
- ibmvfc_retry_host_init(vhost);
+ level += ibmvfc_retry_host_init(vhost);
else
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
ibmvfc_free_event(evt);
return;
case IBMVFC_MAD_CRQ_ERROR:
@@ -3578,6 +3643,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
return;
}
+ vhost->logged_in = 1;
npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
rsp->partition_name, rsp->device_name, rsp->port_loc_code,
@@ -3636,6 +3702,65 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
};
/**
+ * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
+ * @vhost: ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ u32 mad_status = evt->xfer_iu->npiv_logout.common.status;
+
+ ibmvfc_free_event(evt);
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ if (list_empty(&vhost->sent) &&
+ vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+ ibmvfc_init_host(vhost, 0);
+ return;
+ }
+ break;
+ case IBMVFC_MAD_FAILED:
+ case IBMVFC_MAD_NOT_SUPPORTED:
+ case IBMVFC_MAD_CRQ_ERROR:
+ case IBMVFC_MAD_DRIVER_FAILED:
+ default:
+ ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status);
+ break;
+ }
+
+ ibmvfc_hard_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_npiv_logout - Issue an NPIV Logout
+ * @vhost: ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_npiv_logout_mad *mad;
+ struct ibmvfc_event *evt;
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT);
+
+ mad = &evt->iu.npiv_logout;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = 1;
+ mad->common.opcode = IBMVFC_NPIV_LOGOUT;
+ mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent NPIV logout\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+/**
* ibmvfc_dev_init_to_do - Is there target initialization work to do?
* @vhost: ibmvfc host struct
*
@@ -3671,6 +3796,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
switch (vhost->action) {
case IBMVFC_HOST_ACTION_NONE:
case IBMVFC_HOST_ACTION_INIT_WAIT:
+ case IBMVFC_HOST_ACTION_LOGO_WAIT:
return 0;
case IBMVFC_HOST_ACTION_TGT_INIT:
case IBMVFC_HOST_ACTION_QUERY_TGTS:
@@ -3683,9 +3809,9 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
return 1;
+ case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_ALLOC_TGTS:
- case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_QUERY:
@@ -3740,25 +3866,26 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
{
struct ibmvfc_host *vhost = tgt->vhost;
- struct fc_rport *rport = tgt->rport;
+ struct fc_rport *rport;
unsigned long flags;
- if (rport) {
- tgt_dbg(tgt, "Setting rport roles\n");
- fc_remote_port_rolechg(rport, tgt->ids.roles);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ tgt_dbg(tgt, "Adding rport\n");
+ rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+
+ if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt_dbg(tgt, "Deleting rport\n");
+ list_del(&tgt->queue);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ fc_remote_port_delete(rport);
+ del_timer_sync(&tgt->timer);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
return;
}
- tgt_dbg(tgt, "Adding rport\n");
- rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- tgt->rport = rport;
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
if (rport) {
tgt_dbg(tgt, "rport add succeeded\n");
+ tgt->rport = rport;
rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
rport->supported_classes = 0;
tgt->target_id = rport->scsi_target_id;
@@ -3789,8 +3916,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
vhost->events_to_log = 0;
switch (vhost->action) {
case IBMVFC_HOST_ACTION_NONE:
+ case IBMVFC_HOST_ACTION_LOGO_WAIT:
case IBMVFC_HOST_ACTION_INIT_WAIT:
break;
+ case IBMVFC_HOST_ACTION_LOGO:
+ vhost->job_step(vhost);
+ break;
case IBMVFC_HOST_ACTION_INIT:
BUG_ON(vhost->state != IBMVFC_INITIALIZING);
if (vhost->delay_init) {
@@ -3836,11 +3967,21 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
if (vhost->state == IBMVFC_INITIALIZING) {
if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
- ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
- vhost->init_retries = 0;
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- scsi_unblock_requests(vhost->host);
+ if (vhost->reinit) {
+ vhost->reinit = 0;
+ scsi_block_requests(vhost->host);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ } else {
+ ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+ wake_up(&vhost->init_wait_q);
+ schedule_work(&vhost->rport_add_work_q);
+ vhost->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ scsi_unblock_requests(vhost->host);
+ }
+
return;
} else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -3871,24 +4012,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
if (!ibmvfc_dev_init_to_do(vhost))
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
break;
- case IBMVFC_HOST_ACTION_TGT_ADD:
- list_for_each_entry(tgt, &vhost->targets, queue) {
- if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) {
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- ibmvfc_tgt_add_rport(tgt);
- return;
- }
- }
-
- if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- vhost->reinit = 0;
- scsi_block_requests(vhost->host);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- } else {
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
- wake_up(&vhost->init_wait_q);
- }
- break;
default:
break;
};
@@ -4118,6 +4241,56 @@ nomem:
}
/**
+ * ibmvfc_rport_add_thread - Worker thread for rport adds
+ * @work: work struct
+ *
+ **/
+static void ibmvfc_rport_add_thread(struct work_struct *work)
+{
+ struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host,
+ rport_add_work_q);
+ struct ibmvfc_target *tgt;
+ struct fc_rport *rport;
+ unsigned long flags;
+ int did_work;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ do {
+ did_work = 0;
+ if (vhost->state != IBMVFC_ACTIVE)
+ break;
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->add_rport) {
+ did_work = 1;
+ tgt->add_rport = 0;
+ kref_get(&tgt->kref);
+ rport = tgt->rport;
+ if (!rport) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_tgt_add_rport(tgt);
+ } else if (get_device(&rport->dev)) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ tgt_dbg(tgt, "Setting rport roles\n");
+ fc_remote_port_rolechg(rport, tgt->ids.roles);
+ put_device(&rport->dev);
+ }
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ break;
+ }
+ }
+ } while(did_work);
+
+ if (vhost->state == IBMVFC_ACTIVE)
+ vhost->scan_complete = 1;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ LEAVE;
+}
+
+/**
* ibmvfc_probe - Adapter hot plug add entry point
* @vdev: vio device struct
* @id: vio device id struct
@@ -4160,6 +4333,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
strcpy(vhost->partition_name, "UNKNOWN");
init_waitqueue_head(&vhost->work_wait_q);
init_waitqueue_head(&vhost->init_wait_q);
+ INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
if ((rc = ibmvfc_alloc_mem(vhost)))
goto free_scsi_host;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index ca1dcf7a7568..c2668d7d67f5 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,8 +29,8 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.5"
-#define IBMVFC_DRIVER_DATE "(March 19, 2009)"
+#define IBMVFC_DRIVER_VERSION "1.0.6"
+#define IBMVFC_DRIVER_DATE "(May 28, 2009)"
#define IBMVFC_DEFAULT_TIMEOUT 60
#define IBMVFC_ADISC_CANCEL_TIMEOUT 45
@@ -57,9 +57,10 @@
* Ensure we have resources for ERP and initialization:
* 1 for ERP
* 1 for initialization
+ * 1 for NPIV Logout
* 2 for each discovery thread
*/
-#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + (disc_threads * 2))
+#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + (disc_threads * 2))
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
@@ -127,6 +128,7 @@ enum ibmvfc_mad_types {
IBMVFC_IMPLICIT_LOGOUT = 0x0040,
IBMVFC_PASSTHRU = 0x0200,
IBMVFC_TMF_MAD = 0x0100,
+ IBMVFC_NPIV_LOGOUT = 0x0800,
};
struct ibmvfc_mad_common {
@@ -143,6 +145,10 @@ struct ibmvfc_npiv_login_mad {
struct srp_direct_buf buffer;
}__attribute__((packed, aligned (8)));
+struct ibmvfc_npiv_logout_mad {
+ struct ibmvfc_mad_common common;
+}__attribute__((packed, aligned (8)));
+
#define IBMVFC_MAX_NAME 256
struct ibmvfc_npiv_login {
@@ -201,7 +207,8 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_NATIVE_FC 0x01
#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
u32 reserved;
- u64 capabilites;
+ u64 capabilities;
+#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
u32 max_cmds;
u32 scsi_id_sz;
u64 max_dma_len;
@@ -541,9 +548,17 @@ struct ibmvfc_crq_queue {
dma_addr_t msg_token;
};
+enum ibmvfc_ae_link_state {
+ IBMVFC_AE_LS_LINK_UP = 0x01,
+ IBMVFC_AE_LS_LINK_BOUNCED = 0x02,
+ IBMVFC_AE_LS_LINK_DOWN = 0x04,
+ IBMVFC_AE_LS_LINK_DEAD = 0x08,
+};
+
struct ibmvfc_async_crq {
volatile u8 valid;
- u8 pad[3];
+ u8 link_state;
+ u8 pad[2];
u32 pad2;
volatile u64 event;
volatile u64 scsi_id;
@@ -561,6 +576,7 @@ struct ibmvfc_async_crq_queue {
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
+ struct ibmvfc_npiv_logout_mad npiv_logout;
struct ibmvfc_discover_targets discover_targets;
struct ibmvfc_port_login plogi;
struct ibmvfc_process_login prli;
@@ -575,7 +591,6 @@ enum ibmvfc_target_action {
IBMVFC_TGT_ACTION_NONE = 0,
IBMVFC_TGT_ACTION_INIT,
IBMVFC_TGT_ACTION_INIT_WAIT,
- IBMVFC_TGT_ACTION_ADD_RPORT,
IBMVFC_TGT_ACTION_DEL_RPORT,
};
@@ -588,6 +603,7 @@ struct ibmvfc_target {
int target_id;
enum ibmvfc_target_action action;
int need_login;
+ int add_rport;
int init_retries;
u32 cancel_key;
struct ibmvfc_service_parms service_parms;
@@ -627,6 +643,8 @@ struct ibmvfc_event_pool {
enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_NONE = 0,
+ IBMVFC_HOST_ACTION_LOGO,
+ IBMVFC_HOST_ACTION_LOGO_WAIT,
IBMVFC_HOST_ACTION_INIT,
IBMVFC_HOST_ACTION_INIT_WAIT,
IBMVFC_HOST_ACTION_QUERY,
@@ -635,7 +653,6 @@ enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_ALLOC_TGTS,
IBMVFC_HOST_ACTION_TGT_INIT,
IBMVFC_HOST_ACTION_TGT_DEL_FAILED,
- IBMVFC_HOST_ACTION_TGT_ADD,
};
enum ibmvfc_host_state {
@@ -682,6 +699,8 @@ struct ibmvfc_host {
int client_migrated;
int reinit;
int delay_init;
+ int scan_complete;
+ int logged_in;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
@@ -692,6 +711,7 @@ struct ibmvfc_host {
void (*job_step) (struct ibmvfc_host *);
struct task_struct *work_thread;
struct tasklet_struct tasklet;
+ struct work_struct rport_add_work_q;
wait_queue_head_t init_wait_q;
wait_queue_head_t work_wait_q;
};
@@ -707,6 +727,12 @@ struct ibmvfc_host {
#define tgt_err(t, fmt, ...) \
dev_err((t)->vhost->dev, "%llX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
+#define tgt_log(t, level, fmt, ...) \
+ do { \
+ if ((t)->vhost->log_level >= level) \
+ tgt_err(t, fmt, ##__VA_ARGS__); \
+ } while (0)
+
#define ibmvfc_dbg(vhost, ...) \
DBG_CMD(dev_info((vhost)->dev, ##__VA_ARGS__))
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index c9aa7611e408..11d2602ae88e 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -70,6 +70,7 @@
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <asm/firmware.h>
#include <asm/vio.h>
#include <asm/firmware.h>
@@ -87,9 +88,15 @@
*/
static int max_id = 64;
static int max_channel = 3;
-static int init_timeout = 5;
+static int init_timeout = 300;
+static int login_timeout = 60;
+static int info_timeout = 30;
+static int abort_timeout = 60;
+static int reset_timeout = 60;
static int max_requests = IBMVSCSI_MAX_REQUESTS_DEFAULT;
static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2;
+static int fast_fail = 1;
+static int client_reserve = 1;
static struct scsi_transport_template *ibmvscsi_transport_template;
@@ -110,6 +117,10 @@ 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);
MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
+module_param_named(fast_fail, fast_fail, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fast_fail, "Enable fast fail. [Default=1]");
+module_param_named(client_reserve, client_reserve, int, S_IRUGO );
+MODULE_PARM_DESC(client_reserve, "Attempt client managed reserve/release");
/* ------------------------------------------------------------
* Routines for the event pool and event structs
@@ -781,105 +792,53 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
/* ------------------------------------------------------------
* Routines for driver initialization
*/
+
/**
- * adapter_info_rsp: - Handle response to MAD adapter info request
- * @evt_struct: srp_event_struct with the response
+ * map_persist_bufs: - Pre-map persistent data for adapter logins
+ * @hostdata: ibmvscsi_host_data of host
*
- * 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)
+ * Map the capabilities and adapter info DMA buffers to avoid runtime failures.
+ * Return 1 on error, 0 on success.
+ */
+static int map_persist_bufs(struct ibmvscsi_host_data *hostdata)
{
- 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) {
- dev_err(hostdata->dev, "error %d getting adapter info\n",
- evt_struct->xfer_iu->mad.adapter_info.common.status);
- } else {
- dev_info(hostdata->dev, "host srp version: %s, "
- "host partition %s (%d), OS %d, max io %u\n",
- hostdata->madapter_info.srp_version,
- hostdata->madapter_info.partition_name,
- hostdata->madapter_info.partition_number,
- hostdata->madapter_info.os_type,
- hostdata->madapter_info.port_max_txu[0]);
-
- if (hostdata->madapter_info.port_max_txu[0])
- hostdata->host->max_sectors =
- hostdata->madapter_info.port_max_txu[0] >> 9;
-
- if (hostdata->madapter_info.os_type == 3 &&
- strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
- dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n",
- hostdata->madapter_info.srp_version);
- dev_err(hostdata->dev, "limiting scatterlists to %d\n",
- MAX_INDIRECT_BUFS);
- hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
- }
+ hostdata->caps_addr = dma_map_single(hostdata->dev, &hostdata->caps,
+ sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(hostdata->dev, hostdata->caps_addr)) {
+ dev_err(hostdata->dev, "Unable to map capabilities buffer!\n");
+ return 1;
}
+
+ hostdata->adapter_info_addr = dma_map_single(hostdata->dev,
+ &hostdata->madapter_info,
+ sizeof(hostdata->madapter_info),
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(hostdata->dev, hostdata->adapter_info_addr)) {
+ dev_err(hostdata->dev, "Unable to map adapter info buffer!\n");
+ dma_unmap_single(hostdata->dev, hostdata->caps_addr,
+ sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
+ return 1;
+ }
+
+ return 0;
}
/**
- * 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)
+ * unmap_persist_bufs: - Unmap persistent data needed for adapter logins
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Unmap the capabilities and adapter info DMA buffers
+ */
+static void unmap_persist_bufs(struct ibmvscsi_host_data *hostdata)
{
- struct viosrp_adapter_info *req;
- struct srp_event_struct *evt_struct;
- unsigned long flags;
- dma_addr_t addr;
-
- evt_struct = get_event_struct(&hostdata->pool);
- if (!evt_struct) {
- dev_err(hostdata->dev,
- "couldn't allocate an event for ADAPTER_INFO_REQ!\n");
- return;
- }
-
- init_event_struct(evt_struct,
- adapter_info_rsp,
- VIOSRP_MAD_FORMAT,
- init_timeout);
-
- 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 = addr = dma_map_single(hostdata->dev,
- &hostdata->madapter_info,
- sizeof(hostdata->madapter_info),
- DMA_BIDIRECTIONAL);
+ dma_unmap_single(hostdata->dev, hostdata->caps_addr,
+ sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
- if (dma_mapping_error(hostdata->dev, req->buffer)) {
- if (!firmware_has_feature(FW_FEATURE_CMO))
- dev_err(hostdata->dev,
- "Unable to map request_buffer for "
- "adapter_info!\n");
- free_event_struct(&hostdata->pool, evt_struct);
- return;
- }
-
- spin_lock_irqsave(hostdata->host->host_lock, flags);
- if (ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2)) {
- dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n");
- dma_unmap_single(hostdata->dev,
- addr,
- sizeof(hostdata->madapter_info),
- DMA_BIDIRECTIONAL);
- }
- spin_unlock_irqrestore(hostdata->host->host_lock, flags);
-};
+ dma_unmap_single(hostdata->dev, hostdata->adapter_info_addr,
+ sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL);
+}
/**
* login_rsp: - Handle response to SRP login request
@@ -909,9 +868,7 @@ static void login_rsp(struct srp_event_struct *evt_struct)
}
dev_info(hostdata->dev, "SRP_LOGIN succeeded\n");
-
- if (evt_struct->xfer_iu->srp.login_rsp.req_lim_delta < 0)
- dev_err(hostdata->dev, "Invalid request_limit.\n");
+ hostdata->client_migrated = 0;
/* Now we know what the real request-limit is.
* This value is set rather than added to request_limit because
@@ -922,15 +879,12 @@ static void login_rsp(struct srp_event_struct *evt_struct)
/* If we had any pending I/Os, kick them */
scsi_unblock_requests(hostdata->host);
-
- 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)
@@ -939,22 +893,17 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
unsigned long flags;
struct srp_login_req *login;
struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool);
- if (!evt_struct) {
- dev_err(hostdata->dev, "couldn't allocate an event for login req!\n");
- return FAILED;
- }
- init_event_struct(evt_struct,
- login_rsp,
- VIOSRP_SRP_FORMAT,
- init_timeout);
+ BUG_ON(!evt_struct);
+ init_event_struct(evt_struct, login_rsp,
+ VIOSRP_SRP_FORMAT, login_timeout);
login = &evt_struct->iu.srp.login_req;
- memset(login, 0x00, sizeof(struct srp_login_req));
+ memset(login, 0, sizeof(*login));
login->opcode = SRP_LOGIN_REQ;
login->req_it_iu_len = sizeof(union srp_iu);
login->req_buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
-
+
spin_lock_irqsave(hostdata->host->host_lock, flags);
/* Start out with a request limit of 0, since this is negotiated in
* the login request we are just sending and login requests always
@@ -962,13 +911,241 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
*/
atomic_set(&hostdata->request_limit, 0);
- rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2);
+ rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2);
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
dev_info(hostdata->dev, "sent SRP login\n");
return rc;
};
/**
+ * capabilities_rsp: - Handle response to MAD adapter capabilities request
+ * @evt_struct: srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending adapter_info.
+ */
+static void capabilities_rsp(struct srp_event_struct *evt_struct)
+{
+ struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+
+ if (evt_struct->xfer_iu->mad.capabilities.common.status) {
+ dev_err(hostdata->dev, "error 0x%X getting capabilities info\n",
+ evt_struct->xfer_iu->mad.capabilities.common.status);
+ } else {
+ if (hostdata->caps.migration.common.server_support != SERVER_SUPPORTS_CAP)
+ dev_info(hostdata->dev, "Partition migration not supported\n");
+
+ if (client_reserve) {
+ if (hostdata->caps.reserve.common.server_support ==
+ SERVER_SUPPORTS_CAP)
+ dev_info(hostdata->dev, "Client reserve enabled\n");
+ else
+ dev_info(hostdata->dev, "Client reserve not supported\n");
+ }
+ }
+
+ send_srp_login(hostdata);
+}
+
+/**
+ * send_mad_capabilities: - Sends the mad capabilities request
+ * and stores the result so it can be retrieved with
+ * @hostdata: ibmvscsi_host_data of host
+ */
+static void send_mad_capabilities(struct ibmvscsi_host_data *hostdata)
+{
+ struct viosrp_capabilities *req;
+ struct srp_event_struct *evt_struct;
+ unsigned long flags;
+ struct device_node *of_node = hostdata->dev->archdata.of_node;
+ const char *location;
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ BUG_ON(!evt_struct);
+
+ init_event_struct(evt_struct, capabilities_rsp,
+ VIOSRP_MAD_FORMAT, info_timeout);
+
+ req = &evt_struct->iu.mad.capabilities;
+ memset(req, 0, sizeof(*req));
+
+ hostdata->caps.flags = CAP_LIST_SUPPORTED;
+ if (hostdata->client_migrated)
+ hostdata->caps.flags |= CLIENT_MIGRATED;
+
+ strncpy(hostdata->caps.name, dev_name(&hostdata->host->shost_gendev),
+ sizeof(hostdata->caps.name));
+ hostdata->caps.name[sizeof(hostdata->caps.name) - 1] = '\0';
+
+ location = of_get_property(of_node, "ibm,loc-code", NULL);
+ location = location ? location : dev_name(hostdata->dev);
+ strncpy(hostdata->caps.loc, location, sizeof(hostdata->caps.loc));
+ hostdata->caps.loc[sizeof(hostdata->caps.loc) - 1] = '\0';
+
+ req->common.type = VIOSRP_CAPABILITIES_TYPE;
+ req->buffer = hostdata->caps_addr;
+
+ hostdata->caps.migration.common.cap_type = MIGRATION_CAPABILITIES;
+ hostdata->caps.migration.common.length = sizeof(hostdata->caps.migration);
+ hostdata->caps.migration.common.server_support = SERVER_SUPPORTS_CAP;
+ hostdata->caps.migration.ecl = 1;
+
+ if (client_reserve) {
+ hostdata->caps.reserve.common.cap_type = RESERVATION_CAPABILITIES;
+ hostdata->caps.reserve.common.length = sizeof(hostdata->caps.reserve);
+ hostdata->caps.reserve.common.server_support = SERVER_SUPPORTS_CAP;
+ hostdata->caps.reserve.type = CLIENT_RESERVE_SCSI_2;
+ req->common.length = sizeof(hostdata->caps);
+ } else
+ req->common.length = sizeof(hostdata->caps) - sizeof(hostdata->caps.reserve);
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
+ dev_err(hostdata->dev, "couldn't send CAPABILITIES_REQ!\n");
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+};
+
+/**
+ * fast_fail_rsp: - Handle response to MAD enable fast fail
+ * @evt_struct: srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending enable fast fail. Gets called
+ * by ibmvscsi_handle_crq()
+ */
+static void fast_fail_rsp(struct srp_event_struct *evt_struct)
+{
+ struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+ u8 status = evt_struct->xfer_iu->mad.fast_fail.common.status;
+
+ if (status == VIOSRP_MAD_NOT_SUPPORTED)
+ dev_err(hostdata->dev, "fast_fail not supported in server\n");
+ else if (status == VIOSRP_MAD_FAILED)
+ dev_err(hostdata->dev, "fast_fail request failed\n");
+ else if (status != VIOSRP_MAD_SUCCESS)
+ dev_err(hostdata->dev, "error 0x%X enabling fast_fail\n", status);
+
+ send_mad_capabilities(hostdata);
+}
+
+/**
+ * init_host - Start host initialization
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Returns zero if successful.
+ */
+static int enable_fast_fail(struct ibmvscsi_host_data *hostdata)
+{
+ int rc;
+ unsigned long flags;
+ struct viosrp_fast_fail *fast_fail_mad;
+ struct srp_event_struct *evt_struct;
+
+ if (!fast_fail) {
+ send_mad_capabilities(hostdata);
+ return 0;
+ }
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ BUG_ON(!evt_struct);
+
+ init_event_struct(evt_struct, fast_fail_rsp, VIOSRP_MAD_FORMAT, info_timeout);
+
+ fast_fail_mad = &evt_struct->iu.mad.fast_fail;
+ memset(fast_fail_mad, 0, sizeof(*fast_fail_mad));
+ fast_fail_mad->common.type = VIOSRP_ENABLE_FAST_FAIL;
+ fast_fail_mad->common.length = sizeof(*fast_fail_mad);
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ return rc;
+}
+
+/**
+ * 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;
+
+ if (evt_struct->xfer_iu->mad.adapter_info.common.status) {
+ dev_err(hostdata->dev, "error %d getting adapter info\n",
+ evt_struct->xfer_iu->mad.adapter_info.common.status);
+ } else {
+ dev_info(hostdata->dev, "host srp version: %s, "
+ "host partition %s (%d), OS %d, max io %u\n",
+ hostdata->madapter_info.srp_version,
+ hostdata->madapter_info.partition_name,
+ hostdata->madapter_info.partition_number,
+ hostdata->madapter_info.os_type,
+ hostdata->madapter_info.port_max_txu[0]);
+
+ if (hostdata->madapter_info.port_max_txu[0])
+ hostdata->host->max_sectors =
+ hostdata->madapter_info.port_max_txu[0] >> 9;
+
+ if (hostdata->madapter_info.os_type == 3 &&
+ strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
+ dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n",
+ hostdata->madapter_info.srp_version);
+ dev_err(hostdata->dev, "limiting scatterlists to %d\n",
+ MAX_INDIRECT_BUFS);
+ hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
+ }
+ }
+
+ enable_fast_fail(hostdata);
+}
+
+/**
+ * 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;
+ unsigned long flags;
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ BUG_ON(!evt_struct);
+
+ init_event_struct(evt_struct,
+ adapter_info_rsp,
+ VIOSRP_MAD_FORMAT,
+ info_timeout);
+
+ 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 = hostdata->adapter_info_addr;
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
+ dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n");
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+};
+
+/**
+ * init_adapter: Start virtual adapter initialization sequence
+ *
+ */
+static void init_adapter(struct ibmvscsi_host_data *hostdata)
+{
+ send_mad_adapter_info(hostdata);
+}
+
+/**
* 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
@@ -1029,7 +1206,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
init_event_struct(evt,
sync_completion,
VIOSRP_SRP_FORMAT,
- init_timeout);
+ abort_timeout);
tsk_mgmt = &evt->iu.srp.tsk_mgmt;
@@ -1043,7 +1220,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
evt->sync_srp = &srp_rsp;
init_completion(&evt->comp);
- rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2);
+ rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, abort_timeout * 2);
if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY)
break;
@@ -1152,7 +1329,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
init_event_struct(evt,
sync_completion,
VIOSRP_SRP_FORMAT,
- init_timeout);
+ reset_timeout);
tsk_mgmt = &evt->iu.srp.tsk_mgmt;
@@ -1165,7 +1342,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
evt->sync_srp = &srp_rsp;
init_completion(&evt->comp);
- rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2);
+ rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, reset_timeout * 2);
if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY)
break;
@@ -1281,7 +1458,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
if ((rc = ibmvscsi_ops->send_crq(hostdata,
0xC002000000000000LL, 0)) == 0) {
/* Now login */
- send_srp_login(hostdata);
+ init_adapter(hostdata);
} else {
dev_err(hostdata->dev, "Unable to send init rsp. rc=%ld\n", rc);
}
@@ -1291,7 +1468,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
dev_info(hostdata->dev, "partner initialization complete\n");
/* Now login */
- send_srp_login(hostdata);
+ init_adapter(hostdata);
break;
default:
dev_err(hostdata->dev, "unknown crq message type: %d\n", crq->format);
@@ -1303,6 +1480,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
if (crq->format == 0x06) {
/* We need to re-setup the interpartition connection */
dev_info(hostdata->dev, "Re-enabling adapter!\n");
+ hostdata->client_migrated = 1;
purge_requests(hostdata, DID_REQUEUE);
if ((ibmvscsi_ops->reenable_crq_queue(&hostdata->queue,
hostdata)) ||
@@ -1397,7 +1575,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
init_event_struct(evt_struct,
sync_completion,
VIOSRP_MAD_FORMAT,
- init_timeout);
+ info_timeout);
host_config = &evt_struct->iu.mad.host_config;
@@ -1419,7 +1597,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
init_completion(&evt_struct->comp);
spin_lock_irqsave(hostdata->host->host_lock, flags);
- rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2);
+ rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
if (rc == 0)
wait_for_completion(&evt_struct->comp);
@@ -1444,7 +1622,7 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
spin_lock_irqsave(shost->host_lock, lock_flags);
if (sdev->type == TYPE_DISK) {
sdev->allow_restart = 1;
- blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
+ blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
}
scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun);
spin_unlock_irqrestore(shost->host_lock, lock_flags);
@@ -1471,6 +1649,46 @@ static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
/* ------------------------------------------------------------
* sysfs attributes
*/
+static ssize_t show_host_vhost_loc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvscsi_host_data *hostdata = shost_priv(shost);
+ int len;
+
+ len = snprintf(buf, sizeof(hostdata->caps.loc), "%s\n",
+ hostdata->caps.loc);
+ return len;
+}
+
+static struct device_attribute ibmvscsi_host_vhost_loc = {
+ .attr = {
+ .name = "vhost_loc",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_vhost_loc,
+};
+
+static ssize_t show_host_vhost_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvscsi_host_data *hostdata = shost_priv(shost);
+ int len;
+
+ len = snprintf(buf, sizeof(hostdata->caps.name), "%s\n",
+ hostdata->caps.name);
+ return len;
+}
+
+static struct device_attribute ibmvscsi_host_vhost_name = {
+ .attr = {
+ .name = "vhost_name",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_vhost_name,
+};
+
static ssize_t show_host_srp_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1594,6 +1812,8 @@ static struct device_attribute ibmvscsi_host_config = {
};
static struct device_attribute *ibmvscsi_attrs[] = {
+ &ibmvscsi_host_vhost_loc,
+ &ibmvscsi_host_vhost_name,
&ibmvscsi_host_srp_version,
&ibmvscsi_host_partition_name,
&ibmvscsi_host_partition_number,
@@ -1674,6 +1894,11 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
atomic_set(&hostdata->request_limit, -1);
hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT;
+ if (map_persist_bufs(hostdata)) {
+ dev_err(&vdev->dev, "couldn't map persistent buffers\n");
+ goto persist_bufs_failed;
+ }
+
rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events);
if (rc != 0 && rc != H_RESOURCE) {
dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc);
@@ -1687,6 +1912,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
host->max_lun = 8;
host->max_id = max_id;
host->max_channel = max_channel;
+ host->max_cmd_len = 16;
if (scsi_add_host(hostdata->host, hostdata->dev))
goto add_host_failed;
@@ -1733,6 +1959,8 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
init_pool_failed:
ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events);
init_crq_failed:
+ unmap_persist_bufs(hostdata);
+ persist_bufs_failed:
scsi_host_put(host);
scsi_host_alloc_failed:
return -1;
@@ -1741,6 +1969,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
static int ibmvscsi_remove(struct vio_dev *vdev)
{
struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data;
+ unmap_persist_bufs(hostdata);
release_event_pool(&hostdata->pool, hostdata);
ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata,
max_events);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h
index 2d4339d5e16e..76425303def0 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.h
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.h
@@ -90,6 +90,7 @@ struct event_pool {
/* all driver data associated with a host adapter */
struct ibmvscsi_host_data {
atomic_t request_limit;
+ int client_migrated;
struct device *dev;
struct event_pool pool;
struct crq_queue queue;
@@ -97,6 +98,9 @@ struct ibmvscsi_host_data {
struct list_head sent;
struct Scsi_Host *host;
struct mad_adapter_info_data madapter_info;
+ struct capabilities caps;
+ dma_addr_t caps_addr;
+ dma_addr_t adapter_info_addr;
};
/* routines for managing a command/response queue */
diff --git a/drivers/scsi/ibmvscsi/viosrp.h b/drivers/scsi/ibmvscsi/viosrp.h
index 204604501ad8..2cd735d1d196 100644
--- a/drivers/scsi/ibmvscsi/viosrp.h
+++ b/drivers/scsi/ibmvscsi/viosrp.h
@@ -37,6 +37,7 @@
#define SRP_VERSION "16.a"
#define SRP_MAX_IU_LEN 256
+#define SRP_MAX_LOC_LEN 32
union srp_iu {
struct srp_login_req login_req;
@@ -86,7 +87,37 @@ enum viosrp_mad_types {
VIOSRP_EMPTY_IU_TYPE = 0x01,
VIOSRP_ERROR_LOG_TYPE = 0x02,
VIOSRP_ADAPTER_INFO_TYPE = 0x03,
- VIOSRP_HOST_CONFIG_TYPE = 0x04
+ VIOSRP_HOST_CONFIG_TYPE = 0x04,
+ VIOSRP_CAPABILITIES_TYPE = 0x05,
+ VIOSRP_ENABLE_FAST_FAIL = 0x08,
+};
+
+enum viosrp_mad_status {
+ VIOSRP_MAD_SUCCESS = 0x00,
+ VIOSRP_MAD_NOT_SUPPORTED = 0xF1,
+ VIOSRP_MAD_FAILED = 0xF7,
+};
+
+enum viosrp_capability_type {
+ MIGRATION_CAPABILITIES = 0x01,
+ RESERVATION_CAPABILITIES = 0x02,
+};
+
+enum viosrp_capability_support {
+ SERVER_DOES_NOT_SUPPORTS_CAP = 0x0,
+ SERVER_SUPPORTS_CAP = 0x01,
+ SERVER_CAP_DATA = 0x02,
+};
+
+enum viosrp_reserve_type {
+ CLIENT_RESERVE_SCSI_2 = 0x01,
+};
+
+enum viosrp_capability_flag {
+ CLIENT_MIGRATED = 0x01,
+ CLIENT_RECONNECT = 0x02,
+ CAP_LIST_SUPPORTED = 0x04,
+ CAP_LIST_DATA = 0x08,
};
/*
@@ -127,11 +158,46 @@ struct viosrp_host_config {
u64 buffer;
};
+struct viosrp_fast_fail {
+ struct mad_common common;
+};
+
+struct viosrp_capabilities {
+ struct mad_common common;
+ u64 buffer;
+};
+
+struct mad_capability_common {
+ u32 cap_type;
+ u16 length;
+ u16 server_support;
+};
+
+struct mad_reserve_cap {
+ struct mad_capability_common common;
+ u32 type;
+};
+
+struct mad_migration_cap {
+ struct mad_capability_common common;
+ u32 ecl;
+};
+
+struct capabilities{
+ u32 flags;
+ char name[SRP_MAX_LOC_LEN];
+ char loc[SRP_MAX_LOC_LEN];
+ struct mad_migration_cap migration;
+ struct mad_reserve_cap reserve;
+};
+
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;
+ struct viosrp_fast_fail fast_fail;
+ struct viosrp_capabilities capabilities;
};
union viosrp_iu {
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index c09d77591f92..0f8bc772b112 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3654,6 +3654,7 @@ static int ipr_slave_configure(struct scsi_device *sdev)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
struct ipr_resource_entry *res;
+ struct ata_port *ap = NULL;
unsigned long lock_flags = 0;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3672,12 +3673,16 @@ static int ipr_slave_configure(struct scsi_device *sdev)
}
if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res))
sdev->allow_restart = 1;
- if (ipr_is_gata(res) && res->sata_port) {
+ if (ipr_is_gata(res) && res->sata_port)
+ ap = res->sata_port->ap;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ap) {
scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN);
- ata_sas_slave_configure(sdev, res->sata_port->ap);
- } else {
+ ata_sas_slave_configure(sdev, ap);
+ } else
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
- }
+ return 0;
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return 0;
@@ -6998,6 +7003,7 @@ static void ipr_pci_perm_failure(struct pci_dev *pdev)
ioa_cfg->sdt_state = ABORT_DUMP;
ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES;
ioa_cfg->in_ioa_bringdown = 1;
+ ioa_cfg->allow_cmds = 0;
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
}
@@ -7683,7 +7689,7 @@ static void __ipr_remove(struct pci_dev *pdev)
* Return value:
* none
**/
-static void ipr_remove(struct pci_dev *pdev)
+static void __devexit ipr_remove(struct pci_dev *pdev)
{
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
@@ -7859,7 +7865,7 @@ static struct pci_driver ipr_driver = {
.name = IPR_NAME,
.id_table = ipr_pci_table,
.probe = ipr_probe,
- .remove = ipr_remove,
+ .remove = __devexit_p(ipr_remove),
.shutdown = ipr_shutdown,
.err_handler = &ipr_err_handler,
};
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index b3e5e08e44ab..b7c092d63bbe 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -463,7 +463,7 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
}
if (err) {
- iscsi_conn_failure(conn, err);
+ /* got invalid offset/len */
return -EIO;
}
return 0;
@@ -851,6 +851,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = {
.use_clustering = DISABLE_CLUSTERING,
.slave_alloc = iscsi_sw_tcp_slave_alloc,
.slave_configure = iscsi_sw_tcp_slave_configure,
+ .target_alloc = iscsi_target_alloc,
.proc_name = "iscsi_tcp",
.this_id = -1,
};
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index e57556ea5b48..4c880656990b 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -113,6 +113,11 @@ void fc_disc_stop_rports(struct fc_disc *disc)
lport->tt.rport_logoff(rport);
}
+ list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ lport->tt.rport_logoff(rport);
+ }
+
mutex_unlock(&disc->disc_mutex);
}
@@ -131,23 +136,32 @@ static void fc_disc_rport_callback(struct fc_lport *lport,
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_disc *disc = &lport->disc;
- int found = 0;
FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
rport->port_id);
- if (event == RPORT_EV_CREATED) {
+ switch (event) {
+ case RPORT_EV_CREATED:
if (disc) {
- found = 1;
mutex_lock(&disc->disc_mutex);
list_add_tail(&rdata->peers, &disc->rports);
mutex_unlock(&disc->disc_mutex);
}
+ break;
+ case RPORT_EV_LOGO:
+ case RPORT_EV_FAILED:
+ case RPORT_EV_STOP:
+ mutex_lock(&disc->disc_mutex);
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->trans_state == FC_PORTSTATE_ROGUE)
+ list_del(&rdata->peers);
+ mutex_unlock(&rdata->rp_mutex);
+ mutex_unlock(&disc->disc_mutex);
+ break;
+ default:
+ break;
}
- if (!found)
- FC_DEBUG_DISC("The rport (%6x) is not maintained "
- "by the discovery layer\n", rport->port_id);
}
/**
@@ -439,6 +453,7 @@ static int fc_disc_new_target(struct fc_disc *disc,
rdata = rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
rdata->rp_state = RPORT_ST_INIT;
+ list_add_tail(&rdata->peers, &disc->rogue_rports);
lport->tt.rport_login(rport);
}
}
@@ -461,21 +476,29 @@ static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
/**
* fc_disc_done() - Discovery has been completed
* @disc: FC discovery context
+ * Locking Note: This function expects that the disc mutex is locked before
+ * it is called. The discovery callback is then made with the lock released,
+ * and the lock is re-taken before returning from this function
*/
static void fc_disc_done(struct fc_disc *disc)
{
struct fc_lport *lport = disc->lport;
+ enum fc_disc_event event;
FC_DEBUG_DISC("Discovery complete for port (%6x)\n",
fc_host_port_id(lport->host));
- disc->disc_callback(lport, disc->event);
+ event = disc->event;
disc->event = DISC_EV_NONE;
if (disc->requested)
fc_disc_gpn_ft_req(disc);
else
disc->pending = 0;
+
+ mutex_unlock(&disc->disc_mutex);
+ disc->disc_callback(lport, event);
+ mutex_lock(&disc->disc_mutex);
}
/**
@@ -622,6 +645,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
rdata = rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
rdata->local_port = lport;
+ list_add_tail(&rdata->peers,
+ &disc->rogue_rports);
lport->tt.rport_login(rport);
} else
FC_DBG("Failed to allocate memory for "
@@ -681,8 +706,8 @@ static void fc_disc_timeout(struct work_struct *work)
* @fp: response frame
* @lp_arg: Fibre Channel host port instance
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * Locking Note: This function is called without disc mutex held, and
+ * should do all its processing with the mutex held
*/
static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
void *disc_arg)
@@ -695,11 +720,13 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
unsigned int len;
int error;
+ mutex_lock(&disc->disc_mutex);
FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n",
fc_host_port_id(disc->lport->host));
if (IS_ERR(fp)) {
fc_disc_error(disc, fp);
+ mutex_unlock(&disc->disc_mutex);
return;
}
@@ -744,6 +771,8 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
disc->seq_count++;
}
fc_frame_free(fp);
+
+ mutex_unlock(&disc->disc_mutex);
}
/**
@@ -757,7 +786,6 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
{
struct fc_lport *lport;
- struct fc_rport *rport;
struct fc_rport *new_rport;
struct fc_rport_libfc_priv *rdata;
@@ -766,15 +794,12 @@ static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
if (dp->ids.port_id == fc_host_port_id(lport->host))
goto out;
- rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
- if (rport)
- fc_disc_del_target(disc, rport);
-
new_rport = lport->tt.rport_create(dp);
if (new_rport) {
rdata = new_rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
kfree(dp);
+ list_add_tail(&rdata->peers, &disc->rogue_rports);
lport->tt.rport_login(new_rport);
}
return;
@@ -836,6 +861,7 @@ int fc_disc_init(struct fc_lport *lport)
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
+ INIT_LIST_HEAD(&disc->rogue_rports);
disc->lport = lport;
disc->delay = FC_DISC_DELAY;
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index dd47fe619d1e..5878b34bff18 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -41,7 +41,7 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
- u32 did;
+ u32 did = FC_FID_NONE;
enum fc_fh_type fh_type;
int rc;
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 992af05aacf1..7af9bceb8aa9 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1159,6 +1159,10 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
atomic_inc(&mp->stats.xid_not_found);
goto out;
}
+ if (ep->esb_stat & ESB_ST_COMPLETE) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto out;
+ }
if (ep->rxid == FC_XID_UNKNOWN)
ep->rxid = ntohs(fh->fh_rx_id);
if (ep->sid != 0 && ep->sid != ntoh24(fh->fh_d_id)) {
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index f555ae99ad40..ad8b747837b0 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -713,7 +713,7 @@ done:
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
- struct fc_lport *lp;
+ struct fc_lport *lport = fsp->lp;
struct fc_frame_header *fh;
struct fcp_txrdy *dd;
u8 r_ctl;
@@ -724,9 +724,8 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fh = fc_frame_header_get(fp);
r_ctl = fh->fh_r_ctl;
- lp = fsp->lp;
- if (!(lp->state & LPORT_ST_READY))
+ if (!(lport->state & LPORT_ST_READY))
goto out;
if (fc_fcp_lock_pkt(fsp))
goto out;
@@ -779,7 +778,7 @@ errout:
if (IS_ERR(fp))
fc_fcp_error(fsp, fp);
else if (rc == -ENOMEM)
- fc_fcp_reduce_can_queue(lp);
+ fc_fcp_reduce_can_queue(lport);
}
static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
@@ -1897,7 +1896,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
break;
case FC_CMD_ABORTED:
- sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
+ sc_cmd->result = (DID_ERROR << 16) | fsp->io_status;
break;
case FC_CMD_TIME_OUT:
sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index b8178ef398d7..e0c247724d2b 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -618,6 +618,11 @@ int fc_fabric_logoff(struct fc_lport *lport)
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+ mutex_unlock(&lport->lp_mutex);
+ lport->tt.rport_flush_queue();
+ mutex_lock(&lport->lp_mutex);
fc_lport_enter_logo(lport);
mutex_unlock(&lport->lp_mutex);
cancel_delayed_work_sync(&lport->retry_work);
@@ -639,7 +644,12 @@ EXPORT_SYMBOL(fc_fabric_logoff);
*/
int fc_lport_destroy(struct fc_lport *lport)
{
+ mutex_lock(&lport->lp_mutex);
+ lport->state = LPORT_ST_NONE;
+ lport->link_up = 0;
lport->tt.frame_send = fc_frame_drop;
+ mutex_unlock(&lport->lp_mutex);
+
lport->tt.fcp_abort_io(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
return 0;
@@ -1032,17 +1042,19 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a RFT_ID response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_RFT_ID) {
FC_DBG("Received a RFT_ID response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
@@ -1084,17 +1096,19 @@ static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a RPN_ID response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_RPN_ID) {
FC_DBG("Received a RPN_ID response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
if (fh && ct && fh->fh_type == FC_TYPE_CT &&
@@ -1134,17 +1148,19 @@ static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a SCR response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_SCR) {
FC_DBG("Received a SCR response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_ready(lport);
@@ -1360,17 +1376,19 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a LOGO response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_LOGO) {
FC_DBG("Received a LOGO response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_reset(lport);
@@ -1400,10 +1418,6 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
fc_lport_state_enter(lport, LPORT_ST_LOGO);
- /* DNS session should be closed so we can release it here */
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
-
fp = fc_frame_alloc(lport, sizeof(*logo));
if (!fp) {
fc_lport_error(lport, fp);
@@ -1444,17 +1458,19 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a FLOGI response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_FLOGI) {
FC_DBG("Received a FLOGI response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
did = ntoh24(fh->fh_d_id);
if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 0472bb73221e..7bfbff7e0efb 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -267,6 +267,10 @@ static void fc_rport_work(struct work_struct *work)
"(%6x).\n", ids.port_id);
event = RPORT_EV_FAILED;
}
+ if (rport->port_id != FC_FID_DIR_SERV)
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport,
+ RPORT_EV_FAILED);
put_device(&rport->dev);
rport = new_rport;
rdata = new_rport->dd_data;
@@ -325,11 +329,20 @@ int fc_rport_login(struct fc_rport *rport)
int fc_rport_logoff(struct fc_rport *rport)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
mutex_lock(&rdata->rp_mutex);
FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ FC_DEBUG_RPORT("(%6x): Port (%6x) in NONE state,"
+ " not removing", fc_host_port_id(lport->host),
+ rport->port_id);
+ mutex_unlock(&rdata->rp_mutex);
+ goto out;
+ }
+
fc_rport_enter_logo(rport);
/*
@@ -349,6 +362,7 @@ int fc_rport_logoff(struct fc_rport *rport)
mutex_unlock(&rdata->rp_mutex);
+out:
return 0;
}
@@ -430,6 +444,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
case RPORT_ST_PRLI:
case RPORT_ST_LOGO:
rdata->event = RPORT_EV_FAILED;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue,
&rdata->event_work);
break;
@@ -463,7 +478,7 @@ static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
if (PTR_ERR(fp) == -FC_EX_CLOSED)
return fc_rport_error(rport, fp);
- if (rdata->retries < rdata->local_port->max_retry_count) {
+ if (rdata->retries < rdata->local_port->max_rport_retry_count) {
FC_DEBUG_RPORT("Error %ld in state %s, retrying\n",
PTR_ERR(fp), fc_rport_state(rport));
rdata->retries++;
@@ -494,7 +509,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_rport *rport = rp_arg;
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- struct fc_els_flogi *plp;
+ struct fc_els_flogi *plp = NULL;
unsigned int tov;
u16 csp_seq;
u16 cssp_seq;
@@ -505,17 +520,19 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_PLOGI) {
FC_DBG("Received a PLOGI response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
@@ -614,17 +631,19 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_PRLI) {
FC_DBG("Received a PRLI response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -646,6 +665,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
} else {
FC_DBG("Bad ELS response\n");
rdata->event = RPORT_EV_FAILED;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
}
@@ -678,23 +698,26 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_LOGO) {
FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
fc_rport_enter_rtv(rport);
} else {
FC_DBG("Bad ELS response\n");
rdata->event = RPORT_EV_LOGO;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
}
@@ -764,17 +787,19 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_RTV) {
FC_DBG("Received a RTV response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
struct fc_els_rtv_acc *rtv;
@@ -1007,6 +1032,8 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
default:
FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected "
"state %d\n", sid, rdata->rp_state);
+ fc_frame_free(fp);
+ return;
break;
}
@@ -1098,6 +1125,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
reason = ELS_RJT_NONE;
break;
default:
+ fc_frame_free(rx_fp);
+ return;
break;
}
len = fr_len(rx_fp) - sizeof(*fh);
@@ -1227,6 +1256,11 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
"while in state %s\n", ntoh24(fh->fh_s_id),
fc_rport_state(rport));
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ fc_frame_free(fp);
+ return;
+ }
+
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
rjt_data.explan = ELS_EXPL_NONE;
@@ -1256,7 +1290,13 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
"while in state %s\n", ntoh24(fh->fh_s_id),
fc_rport_state(rport));
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ fc_frame_free(fp);
+ return;
+ }
+
rdata->event = RPORT_EV_LOGO;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
@@ -1290,7 +1330,7 @@ int fc_rport_init(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_rport_init);
-int fc_setup_rport()
+int fc_setup_rport(void)
{
rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
if (!rport_event_queue)
@@ -1299,7 +1339,7 @@ int fc_setup_rport()
}
EXPORT_SYMBOL(fc_setup_rport);
-void fc_destroy_rport()
+void fc_destroy_rport(void)
{
destroy_workqueue(rport_event_queue);
}
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 689628359169..59908aead531 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -81,7 +81,8 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
struct Scsi_Host *shost = conn->session->host;
struct iscsi_host *ihost = shost_priv(shost);
- queue_work(ihost->workq, &conn->xmitwork);
+ if (ihost->workq)
+ queue_work(ihost->workq, &conn->xmitwork);
}
EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
@@ -109,11 +110,9 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
* if the window closed with IO queued, then kick the
* xmit thread
*/
- if (!list_empty(&session->leadconn->xmitqueue) ||
- !list_empty(&session->leadconn->mgmtqueue)) {
- if (!(session->tt->caps & CAP_DATA_PATH_OFFLOAD))
- iscsi_conn_queue_work(session->leadconn);
- }
+ if (!list_empty(&session->leadconn->cmdqueue) ||
+ !list_empty(&session->leadconn->mgmtqueue))
+ iscsi_conn_queue_work(session->leadconn);
}
}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
@@ -257,9 +256,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
itt_t itt;
int rc;
- rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
- if (rc)
- return rc;
+ if (conn->session->tt->alloc_pdu) {
+ rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
+ if (rc)
+ return rc;
+ }
hdr = (struct iscsi_cmd *) task->hdr;
itt = hdr->itt;
memset(hdr, 0, sizeof(*hdr));
@@ -364,7 +365,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
return -EIO;
task->state = ISCSI_TASK_RUNNING;
- list_move_tail(&task->running, &conn->run_list);
conn->scsicmd_pdus_cnt++;
ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
@@ -380,26 +380,25 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
}
/**
- * iscsi_complete_command - finish a task
+ * iscsi_free_task - free a task
* @task: iscsi cmd task
*
* Must be called with session lock.
* This function returns the scsi command to scsi-ml or cleans
* up mgmt tasks then returns the task to the pool.
*/
-static void iscsi_complete_command(struct iscsi_task *task)
+static void iscsi_free_task(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;
+ ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n",
+ task->itt, task->state, task->sc);
+
session->tt->cleanup_task(task);
- list_del_init(&task->running);
- task->state = ISCSI_TASK_COMPLETED;
+ task->state = ISCSI_TASK_FREE;
task->sc = NULL;
-
- if (conn->task == task)
- conn->task = NULL;
/*
* login task is preallocated so do not free
*/
@@ -408,9 +407,6 @@ static void iscsi_complete_command(struct iscsi_task *task)
__kfifo_put(session->cmdpool.queue, (void*)&task, sizeof(void*));
- if (conn->ping_task == task)
- conn->ping_task = NULL;
-
if (sc) {
task->sc = NULL;
/* SCSI eh reuses commands to verify us */
@@ -433,7 +429,7 @@ EXPORT_SYMBOL_GPL(__iscsi_get_task);
static void __iscsi_put_task(struct iscsi_task *task)
{
if (atomic_dec_and_test(&task->refcount))
- iscsi_complete_command(task);
+ iscsi_free_task(task);
}
void iscsi_put_task(struct iscsi_task *task)
@@ -446,26 +442,74 @@ void iscsi_put_task(struct iscsi_task *task)
}
EXPORT_SYMBOL_GPL(iscsi_put_task);
+/**
+ * iscsi_complete_task - finish a task
+ * @task: iscsi cmd task
+ * @state: state to complete task with
+ *
+ * Must be called with session lock.
+ */
+static void iscsi_complete_task(struct iscsi_task *task, int state)
+{
+ struct iscsi_conn *conn = task->conn;
+
+ ISCSI_DBG_SESSION(conn->session,
+ "complete task itt 0x%x state %d sc %p\n",
+ task->itt, task->state, task->sc);
+ if (task->state == ISCSI_TASK_COMPLETED ||
+ task->state == ISCSI_TASK_ABRT_TMF ||
+ task->state == ISCSI_TASK_ABRT_SESS_RECOV)
+ return;
+ WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
+ task->state = state;
+
+ if (!list_empty(&task->running))
+ list_del_init(&task->running);
+
+ if (conn->task == task)
+ conn->task = NULL;
+
+ if (conn->ping_task == task)
+ conn->ping_task = NULL;
+
+ /* release get from queueing */
+ __iscsi_put_task(task);
+}
+
/*
- * session lock must be held
+ * session lock must be held and if not called for a task that is
+ * still pending or from the xmit thread, then xmit thread must
+ * be suspended.
*/
-static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
- int err)
+static void fail_scsi_task(struct iscsi_task *task, int err)
{
+ struct iscsi_conn *conn = task->conn;
struct scsi_cmnd *sc;
+ int state;
+ /*
+ * if a command completes and we get a successful tmf response
+ * we will hit this because the scsi eh abort code does not take
+ * a ref to the task.
+ */
sc = task->sc;
if (!sc)
return;
- if (task->state == ISCSI_TASK_PENDING)
+ if (task->state == ISCSI_TASK_PENDING) {
/*
* cmd never made it to the xmit thread, so we should not count
* the cmd in the sequencing
*/
conn->session->queued_cmdsn--;
+ /* it was never sent so just complete like normal */
+ state = ISCSI_TASK_COMPLETED;
+ } else if (err == DID_TRANSPORT_DISRUPTED)
+ state = ISCSI_TASK_ABRT_SESS_RECOV;
+ else
+ state = ISCSI_TASK_ABRT_TMF;
- sc->result = err;
+ sc->result = err << 16;
if (!scsi_bidi_cmnd(sc))
scsi_set_resid(sc, scsi_bufflen(sc));
else {
@@ -473,10 +517,7 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
scsi_in(sc)->resid = scsi_in(sc)->length;
}
- if (conn->task == task)
- conn->task = NULL;
- /* release ref from queuecommand */
- __iscsi_put_task(task);
+ iscsi_complete_task(task, state);
}
static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
@@ -516,7 +557,6 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
session->state = ISCSI_STATE_LOGGING_OUT;
task->state = ISCSI_TASK_RUNNING;
- list_move_tail(&task->running, &conn->mgmt_run_list);
ISCSI_DBG_SESSION(session, "mgmtpdu [op 0x%x hdr->itt 0x%x "
"datalen %d]\n", hdr->opcode & ISCSI_OPCODE_MASK,
hdr->itt, task->data_count);
@@ -528,6 +568,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size)
{
struct iscsi_session *session = conn->session;
+ struct iscsi_host *ihost = shost_priv(session->host);
struct iscsi_task *task;
itt_t itt;
@@ -544,6 +585,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
*/
task = conn->login_task;
else {
+ if (session->state != ISCSI_STATE_LOGGED_IN)
+ return NULL;
+
BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
@@ -559,6 +603,8 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
atomic_set(&task->refcount, 1);
task->conn = conn;
task->sc = NULL;
+ INIT_LIST_HEAD(&task->running);
+ task->state = ISCSI_TASK_PENDING;
if (data_size) {
memcpy(task->data, data, data_size);
@@ -566,11 +612,14 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
} else
task->data_count = 0;
- if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
- iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
- "pdu for mgmt task.\n");
- goto requeue_task;
+ if (conn->session->tt->alloc_pdu) {
+ if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
+ "pdu for mgmt task.\n");
+ goto free_task;
+ }
}
+
itt = task->hdr->itt;
task->hdr_len = sizeof(struct iscsi_hdr);
memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
@@ -583,30 +632,22 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
task->conn->session->age);
}
- INIT_LIST_HEAD(&task->running);
- list_add_tail(&task->running, &conn->mgmtqueue);
-
- if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+ if (!ihost->workq) {
if (iscsi_prep_mgmt_task(conn, task))
goto free_task;
if (session->tt->xmit_task(task))
goto free_task;
-
- } else
+ } else {
+ list_add_tail(&task->running, &conn->mgmtqueue);
iscsi_conn_queue_work(conn);
+ }
return task;
free_task:
__iscsi_put_task(task);
return NULL;
-
-requeue_task:
- if (task != conn->login_task)
- __kfifo_put(session->cmdpool.queue, (void*)&task,
- sizeof(void*));
- return NULL;
}
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -701,11 +742,10 @@ invalid_datalen:
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
out:
- ISCSI_DBG_SESSION(session, "done [sc %p res %d itt 0x%x]\n",
+ ISCSI_DBG_SESSION(session, "cmd rsp done [sc %p res %d itt 0x%x]\n",
sc, sc->result, task->itt);
conn->scsirsp_pdus_cnt++;
-
- __iscsi_put_task(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
}
/**
@@ -724,6 +764,7 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
return;
+ iscsi_update_cmdsn(conn->session, (struct iscsi_nopin *)hdr);
sc->result = (DID_OK << 16) | rhdr->cmd_status;
conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
@@ -738,8 +779,11 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
+ ISCSI_DBG_SESSION(conn->session, "data in with status done "
+ "[sc %p res %d itt 0x%x]\n",
+ sc, sc->result, task->itt);
conn->scsirsp_pdus_cnt++;
- __iscsi_put_task(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
}
static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
@@ -823,7 +867,7 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
*
* The session lock must be held.
*/
-static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
+struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
int i;
@@ -840,6 +884,7 @@ static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
return session->cmds[i];
}
+EXPORT_SYMBOL_GPL(iscsi_itt_to_task);
/**
* __iscsi_complete_pdu - complete pdu
@@ -959,7 +1004,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
}
iscsi_tmf_rsp(conn, hdr);
- __iscsi_put_task(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
break;
case ISCSI_OP_NOOP_IN:
iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
@@ -977,7 +1022,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
goto recv_pdu;
mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
- __iscsi_put_task(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
break;
default:
rc = ISCSI_ERR_BAD_OPCODE;
@@ -989,7 +1034,7 @@ out:
recv_pdu:
if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
rc = ISCSI_ERR_CONN_FAILED;
- __iscsi_put_task(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
return rc;
}
EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
@@ -1166,7 +1211,12 @@ void iscsi_requeue_task(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
- list_move_tail(&task->running, &conn->requeue);
+ /*
+ * this may be on the requeue list already if the xmit_task callout
+ * is handling the r2ts while we are adding new ones
+ */
+ if (list_empty(&task->running))
+ list_add_tail(&task->running, &conn->requeue);
iscsi_conn_queue_work(conn);
}
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1206,6 +1256,7 @@ check_mgmt:
while (!list_empty(&conn->mgmtqueue)) {
conn->task = list_entry(conn->mgmtqueue.next,
struct iscsi_task, running);
+ list_del_init(&conn->task->running);
if (iscsi_prep_mgmt_task(conn, conn->task)) {
__iscsi_put_task(conn->task);
conn->task = NULL;
@@ -1217,23 +1268,26 @@ check_mgmt:
}
/* process pending command queue */
- while (!list_empty(&conn->xmitqueue)) {
+ while (!list_empty(&conn->cmdqueue)) {
if (conn->tmf_state == TMF_QUEUED)
break;
- conn->task = list_entry(conn->xmitqueue.next,
+ conn->task = list_entry(conn->cmdqueue.next,
struct iscsi_task, running);
+ list_del_init(&conn->task->running);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
- fail_command(conn, conn->task, DID_IMM_RETRY << 16);
+ fail_scsi_task(conn->task, DID_IMM_RETRY);
continue;
}
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
if (rc) {
if (rc == -ENOMEM) {
+ list_add_tail(&conn->task->running,
+ &conn->cmdqueue);
conn->task = NULL;
goto again;
} else
- fail_command(conn, conn->task, DID_ABORT << 16);
+ fail_scsi_task(conn->task, DID_ABORT);
continue;
}
rc = iscsi_xmit_task(conn);
@@ -1260,8 +1314,8 @@ check_mgmt:
conn->task = list_entry(conn->requeue.next,
struct iscsi_task, running);
+ list_del_init(&conn->task->running);
conn->task->state = ISCSI_TASK_RUNNING;
- list_move_tail(conn->requeue.next, &conn->run_list);
rc = iscsi_xmit_task(conn);
if (rc)
goto again;
@@ -1328,6 +1382,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
{
struct iscsi_cls_session *cls_session;
struct Scsi_Host *host;
+ struct iscsi_host *ihost;
int reason = 0;
struct iscsi_session *session;
struct iscsi_conn *conn;
@@ -1338,6 +1393,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
sc->SCp.ptr = NULL;
host = sc->device->host;
+ ihost = shost_priv(host);
spin_unlock(host->host_lock);
cls_session = starget_to_session(scsi_target(sc->device));
@@ -1350,13 +1406,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto fault;
}
- /*
- * ISCSI_STATE_FAILED is a temp. state. The recovery
- * code will decide what is best to do with command queued
- * during this time
- */
- if (session->state != ISCSI_STATE_LOGGED_IN &&
- session->state != ISCSI_STATE_FAILED) {
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
/*
* to handle the race between when we set the recovery state
* and block the session we requeue here (commands could
@@ -1364,12 +1414,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
* up because the block code is not locked)
*/
switch (session->state) {
+ case ISCSI_STATE_FAILED:
case ISCSI_STATE_IN_RECOVERY:
reason = FAILURE_SESSION_IN_RECOVERY;
- goto reject;
+ sc->result = DID_IMM_RETRY << 16;
+ break;
case ISCSI_STATE_LOGGING_OUT:
reason = FAILURE_SESSION_LOGGING_OUT;
- goto reject;
+ sc->result = DID_IMM_RETRY << 16;
+ break;
case ISCSI_STATE_RECOVERY_FAILED:
reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
sc->result = DID_TRANSPORT_FAILFAST << 16;
@@ -1402,9 +1455,8 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
reason = FAILURE_OOM;
goto reject;
}
- list_add_tail(&task->running, &conn->xmitqueue);
- if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+ if (!ihost->workq) {
reason = iscsi_prep_scsi_cmd_pdu(task);
if (reason) {
if (reason == -ENOMEM) {
@@ -1419,8 +1471,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
reason = FAILURE_SESSION_NOT_READY;
goto prepd_reject;
}
- } else
+ } else {
+ list_add_tail(&task->running, &conn->cmdqueue);
iscsi_conn_queue_work(conn);
+ }
session->queued_cmdsn++;
spin_unlock(&session->lock);
@@ -1429,7 +1483,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
prepd_reject:
sc->scsi_done = NULL;
- iscsi_complete_command(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
reject:
spin_unlock(&session->lock);
ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
@@ -1439,7 +1493,7 @@ reject:
prepd_fault:
sc->scsi_done = NULL;
- iscsi_complete_command(task);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
fault:
spin_unlock(&session->lock);
ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
@@ -1463,6 +1517,16 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
}
EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
+int iscsi_target_alloc(struct scsi_target *starget)
+{
+ struct iscsi_cls_session *cls_session = starget_to_session(starget);
+ struct iscsi_session *session = cls_session->dd_data;
+
+ starget->can_queue = session->scsi_cmds_max;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_target_alloc);
+
void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = cls_session->dd_data;
@@ -1598,44 +1662,24 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
* Fail commands. session lock held and recv side suspended and xmit
* thread flushed
*/
-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
- int error)
+static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun,
+ int error)
{
- struct iscsi_task *task, *tmp;
-
- if (conn->task) {
- if (lun == -1 ||
- (conn->task->sc && conn->task->sc->device->lun == lun))
- conn->task = NULL;
- }
+ struct iscsi_task *task;
+ int i;
- /* flush pending */
- list_for_each_entry_safe(task, tmp, &conn->xmitqueue, running) {
- if (lun == task->sc->device->lun || lun == -1) {
- ISCSI_DBG_SESSION(conn->session,
- "failing pending sc %p itt 0x%x\n",
- task->sc, task->itt);
- fail_command(conn, task, error << 16);
- }
- }
+ for (i = 0; i < conn->session->cmds_max; i++) {
+ task = conn->session->cmds[i];
+ if (!task->sc || task->state == ISCSI_TASK_FREE)
+ continue;
- list_for_each_entry_safe(task, tmp, &conn->requeue, running) {
- if (lun == task->sc->device->lun || lun == -1) {
- ISCSI_DBG_SESSION(conn->session,
- "failing requeued sc %p itt 0x%x\n",
- task->sc, task->itt);
- fail_command(conn, task, error << 16);
- }
- }
+ if (lun != -1 && lun != task->sc->device->lun)
+ continue;
- /* fail all other running */
- list_for_each_entry_safe(task, tmp, &conn->run_list, running) {
- if (lun == task->sc->device->lun || lun == -1) {
- ISCSI_DBG_SESSION(conn->session,
- "failing in progress sc %p itt 0x%x\n",
- task->sc, task->itt);
- fail_command(conn, task, error << 16);
- }
+ ISCSI_DBG_SESSION(conn->session,
+ "failing sc %p itt 0x%x state %d\n",
+ task->sc, task->itt, task->state);
+ fail_scsi_task(task, error);
}
}
@@ -1645,7 +1689,7 @@ void iscsi_suspend_tx(struct iscsi_conn *conn)
struct iscsi_host *ihost = shost_priv(shost);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+ if (ihost->workq)
flush_workqueue(ihost->workq);
}
EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
@@ -1653,8 +1697,23 @@ EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
static void iscsi_start_tx(struct iscsi_conn *conn)
{
clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
- iscsi_conn_queue_work(conn);
+ iscsi_conn_queue_work(conn);
+}
+
+/*
+ * We want to make sure a ping is in flight. It has timed out.
+ * And we are not busy processing a pdu that is making
+ * progress but got started before the ping and is taking a while
+ * to complete so the ping is just stuck behind it in a queue.
+ */
+static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
+{
+ if (conn->ping_task &&
+ time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+ (conn->ping_timeout * HZ), jiffies))
+ return 1;
+ else
+ return 0;
}
static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
@@ -1692,16 +1751,20 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
* if the ping timedout then we are in the middle of cleaning up
* and can let the iscsi eh handle it
*/
- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
- (conn->ping_timeout * HZ), jiffies))
+ if (iscsi_has_ping_timed_out(conn)) {
rc = BLK_EH_RESET_TIMER;
+ goto done;
+ }
/*
* if we are about to check the transport then give the command
* more time
*/
if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
- jiffies))
+ jiffies)) {
rc = BLK_EH_RESET_TIMER;
+ goto done;
+ }
+
/* if in the middle of checking the transport then give us more time */
if (conn->ping_task)
rc = BLK_EH_RESET_TIMER;
@@ -1728,13 +1791,13 @@ static void iscsi_check_transport_timeouts(unsigned long data)
recv_timeout *= HZ;
last_recv = conn->last_recv;
- if (conn->ping_task &&
- time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
- jiffies)) {
+
+ if (iscsi_has_ping_timed_out(conn)) {
iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
- "expired, last rx %lu, last ping %lu, "
- "now %lu\n", conn->ping_timeout, last_recv,
- conn->last_ping, jiffies);
+ "expired, recv timeout %d, last rx %lu, "
+ "last ping %lu, now %lu\n",
+ conn->ping_timeout, conn->recv_timeout,
+ last_recv, conn->last_ping, jiffies);
spin_unlock(&session->lock);
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return;
@@ -1778,6 +1841,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
+ ISCSI_DBG_SESSION(session, "aborting sc %p\n", sc);
+
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
/*
@@ -1800,6 +1865,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
sc->SCp.phase != session->age) {
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
+ ISCSI_DBG_SESSION(session, "failing abort due to dropped "
+ "session.\n");
return FAILED;
}
@@ -1819,7 +1886,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
}
if (task->state == ISCSI_TASK_PENDING) {
- fail_command(conn, task, DID_ABORT << 16);
+ fail_scsi_task(task, DID_ABORT);
goto success;
}
@@ -1850,7 +1917,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
* then sent more data for the cmd.
*/
spin_lock(&session->lock);
- fail_command(conn, task, DID_ABORT << 16);
+ fail_scsi_task(task, DID_ABORT);
conn->tmf_state = TMF_INITIAL;
spin_unlock(&session->lock);
iscsi_start_tx(conn);
@@ -1957,7 +2024,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
iscsi_suspend_tx(conn);
spin_lock_bh(&session->lock);
- fail_all_commands(conn, sc->device->lun, DID_ERROR);
+ fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
conn->tmf_state = TMF_INITIAL;
spin_unlock_bh(&session->lock);
@@ -2264,6 +2331,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
if (cmd_task_size)
task->dd_data = &task[1];
task->itt = cmd_i;
+ task->state = ISCSI_TASK_FREE;
INIT_LIST_HEAD(&task->running);
}
@@ -2350,10 +2418,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
conn->transport_timer.data = (unsigned long)conn;
conn->transport_timer.function = iscsi_check_transport_timeouts;
- INIT_LIST_HEAD(&conn->run_list);
- INIT_LIST_HEAD(&conn->mgmt_run_list);
INIT_LIST_HEAD(&conn->mgmtqueue);
- INIT_LIST_HEAD(&conn->xmitqueue);
+ INIT_LIST_HEAD(&conn->cmdqueue);
INIT_LIST_HEAD(&conn->requeue);
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
@@ -2521,27 +2587,28 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
EXPORT_SYMBOL_GPL(iscsi_conn_start);
static void
-flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn)
{
- struct iscsi_task *task, *tmp;
+ struct iscsi_task *task;
+ int i, state;
- /* handle pending */
- list_for_each_entry_safe(task, tmp, &conn->mgmtqueue, running) {
- ISCSI_DBG_SESSION(session, "flushing pending mgmt task "
- "itt 0x%x\n", task->itt);
- /* release ref from prep task */
- __iscsi_put_task(task);
- }
+ for (i = 0; i < conn->session->cmds_max; i++) {
+ task = conn->session->cmds[i];
+ if (task->sc)
+ continue;
- /* handle running */
- list_for_each_entry_safe(task, tmp, &conn->mgmt_run_list, running) {
- ISCSI_DBG_SESSION(session, "flushing running mgmt task "
- "itt 0x%x\n", task->itt);
- /* release ref from prep task */
- __iscsi_put_task(task);
- }
+ if (task->state == ISCSI_TASK_FREE)
+ continue;
- conn->task = NULL;
+ ISCSI_DBG_SESSION(conn->session,
+ "failing mgmt itt 0x%x state %d\n",
+ task->itt, task->state);
+ state = ISCSI_TASK_ABRT_SESS_RECOV;
+ if (task->state == ISCSI_TASK_PENDING)
+ state = ISCSI_TASK_COMPLETED;
+ iscsi_complete_task(task, state);
+
+ }
}
static void iscsi_start_session_recovery(struct iscsi_session *session,
@@ -2549,8 +2616,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
{
int old_stop_stage;
- del_timer_sync(&conn->transport_timer);
-
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
if (conn->stop_stage == STOP_CONN_TERM) {
@@ -2568,13 +2633,17 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
session->state = ISCSI_STATE_TERMINATE;
else if (conn->stop_stage != STOP_CONN_RECOVER)
session->state = ISCSI_STATE_IN_RECOVERY;
+ spin_unlock_bh(&session->lock);
+
+ del_timer_sync(&conn->transport_timer);
+ iscsi_suspend_tx(conn);
+ spin_lock_bh(&session->lock);
old_stop_stage = conn->stop_stage;
conn->stop_stage = flag;
conn->c_stage = ISCSI_CONN_STOPPED;
spin_unlock_bh(&session->lock);
- iscsi_suspend_tx(conn);
/*
* for connection level recovery we should not calculate
* header digest. conn->hdr_size used for optimization
@@ -2595,11 +2664,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
* flush queues.
*/
spin_lock_bh(&session->lock);
- if (flag == STOP_CONN_RECOVER)
- fail_all_commands(conn, -1, DID_TRANSPORT_DISRUPTED);
- else
- fail_all_commands(conn, -1, DID_ERROR);
- flush_control_queues(session, conn);
+ fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
+ fail_mgmt_tasks(session, conn);
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
}
@@ -2641,6 +2707,23 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
}
EXPORT_SYMBOL_GPL(iscsi_conn_bind);
+static int iscsi_switch_str_param(char **param, char *new_val_buf)
+{
+ char *new_val;
+
+ if (*param) {
+ if (!strcmp(*param, new_val_buf))
+ return 0;
+ }
+
+ new_val = kstrdup(new_val_buf, GFP_NOIO);
+ if (!new_val)
+ return -ENOMEM;
+
+ kfree(*param);
+ *param = new_val;
+ return 0;
+}
int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen)
@@ -2713,38 +2796,15 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
sscanf(buf, "%u", &conn->exp_statsn);
break;
case ISCSI_PARAM_USERNAME:
- kfree(session->username);
- session->username = kstrdup(buf, GFP_KERNEL);
- if (!session->username)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&session->username, buf);
case ISCSI_PARAM_USERNAME_IN:
- kfree(session->username_in);
- session->username_in = kstrdup(buf, GFP_KERNEL);
- if (!session->username_in)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&session->username_in, buf);
case ISCSI_PARAM_PASSWORD:
- kfree(session->password);
- session->password = kstrdup(buf, GFP_KERNEL);
- if (!session->password)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&session->password, buf);
case ISCSI_PARAM_PASSWORD_IN:
- kfree(session->password_in);
- session->password_in = kstrdup(buf, GFP_KERNEL);
- if (!session->password_in)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&session->password_in, buf);
case ISCSI_PARAM_TARGET_NAME:
- /* this should not change between logins */
- if (session->targetname)
- break;
-
- session->targetname = kstrdup(buf, GFP_KERNEL);
- if (!session->targetname)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&session->targetname, buf);
case ISCSI_PARAM_TPGT:
sscanf(buf, "%d", &session->tpgt);
break;
@@ -2752,25 +2812,11 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
sscanf(buf, "%d", &conn->persistent_port);
break;
case ISCSI_PARAM_PERSISTENT_ADDRESS:
- /*
- * this is the address returned in discovery so it should
- * not change between logins.
- */
- if (conn->persistent_address)
- break;
-
- conn->persistent_address = kstrdup(buf, GFP_KERNEL);
- if (!conn->persistent_address)
- return -ENOMEM;
- break;
+ return iscsi_switch_str_param(&conn->persistent_address, buf);
case ISCSI_PARAM_IFACE_NAME:
- if (!session->ifacename)
- session->ifacename = kstrdup(buf, GFP_KERNEL);
- break;
+ return iscsi_switch_str_param(&session->ifacename, buf);
case ISCSI_PARAM_INITIATOR_NAME:
- if (!session->initiatorname)
- session->initiatorname = kstrdup(buf, GFP_KERNEL);
- break;
+ return iscsi_switch_str_param(&session->initiatorname, buf);
default:
return -ENOSYS;
}
@@ -2841,10 +2887,7 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
len = sprintf(buf, "%s\n", session->ifacename);
break;
case ISCSI_PARAM_INITIATOR_NAME:
- if (!session->initiatorname)
- len = sprintf(buf, "%s\n", "unknown");
- else
- len = sprintf(buf, "%s\n", session->initiatorname);
+ len = sprintf(buf, "%s\n", session->initiatorname);
break;
default:
return -ENOSYS;
@@ -2910,29 +2953,16 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
switch (param) {
case ISCSI_HOST_PARAM_NETDEV_NAME:
- if (!ihost->netdev)
- len = sprintf(buf, "%s\n", "default");
- else
- len = sprintf(buf, "%s\n", ihost->netdev);
+ len = sprintf(buf, "%s\n", ihost->netdev);
break;
case ISCSI_HOST_PARAM_HWADDRESS:
- if (!ihost->hwaddress)
- len = sprintf(buf, "%s\n", "default");
- else
- len = sprintf(buf, "%s\n", ihost->hwaddress);
+ len = sprintf(buf, "%s\n", ihost->hwaddress);
break;
case ISCSI_HOST_PARAM_INITIATOR_NAME:
- if (!ihost->initiatorname)
- len = sprintf(buf, "%s\n", "unknown");
- else
- len = sprintf(buf, "%s\n", ihost->initiatorname);
+ len = sprintf(buf, "%s\n", ihost->initiatorname);
break;
case ISCSI_HOST_PARAM_IPADDRESS:
- if (!strlen(ihost->local_address))
- len = sprintf(buf, "%s\n", "unknown");
- else
- len = sprintf(buf, "%s\n",
- ihost->local_address);
+ len = sprintf(buf, "%s\n", ihost->local_address);
break;
default:
return -ENOSYS;
@@ -2949,17 +2979,11 @@ int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
switch (param) {
case ISCSI_HOST_PARAM_NETDEV_NAME:
- if (!ihost->netdev)
- ihost->netdev = kstrdup(buf, GFP_KERNEL);
- break;
+ return iscsi_switch_str_param(&ihost->netdev, buf);
case ISCSI_HOST_PARAM_HWADDRESS:
- if (!ihost->hwaddress)
- ihost->hwaddress = kstrdup(buf, GFP_KERNEL);
- break;
+ return iscsi_switch_str_param(&ihost->hwaddress, buf);
case ISCSI_HOST_PARAM_INITIATOR_NAME:
- if (!ihost->initiatorname)
- ihost->initiatorname = kstrdup(buf, GFP_KERNEL);
- break;
+ return iscsi_switch_str_param(&ihost->initiatorname, buf);
default:
return -ENOSYS;
}
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 91f8ce4d8d08..2bc07090321d 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -440,8 +440,8 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_r2t_info *r2t;
- /* nothing to do for mgmt or pending tasks */
- if (!task->sc || task->state == ISCSI_TASK_PENDING)
+ /* nothing to do for mgmt */
+ if (!task->sc)
return;
/* flush task's r2t queues */
@@ -473,7 +473,13 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
int datasn = be32_to_cpu(rhdr->datasn);
unsigned total_in_length = scsi_in(task->sc)->length;
- iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+ /*
+ * lib iscsi will update this in the completion handling if there
+ * is status.
+ */
+ if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
+ iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+
if (tcp_conn->in.datalen == 0)
return 0;
@@ -857,6 +863,12 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
int rc = 0;
ISCSI_DBG_TCP(conn, "in %d bytes\n", skb->len - offset);
+ /*
+ * Update for each skb instead of pdu, because over slow networks a
+ * data_in's data could take a while to read in. We also want to
+ * account for r2ts.
+ */
+ conn->last_recv = jiffies;
if (unlikely(conn->suspend_rx)) {
ISCSI_DBG_TCP(conn, "Rx suspended!\n");
@@ -1036,8 +1048,11 @@ flush:
rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent,
r2t->data_count);
- if (rc)
+ if (rc) {
+ iscsi_conn_failure(conn, ISCSI_ERR_XMIT_FAILED);
return rc;
+ }
+
r2t->sent += r2t->data_count;
goto flush;
}
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 531af9ed7199..54fa1e42dc4d 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1937,7 +1937,11 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
if (ret > 0) {
/* positive number is the untransferred residual */
rsp->resid_len = ret;
+ req->resid_len = 0;
ret = 0;
+ } else if (ret == 0) {
+ rsp->resid_len = 0;
+ req->resid_len = 0;
}
return ret;
diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c
index be9a951b977d..1bc3b7567994 100644
--- a/drivers/scsi/libsas/sas_host_smp.c
+++ b/drivers/scsi/libsas/sas_host_smp.c
@@ -176,9 +176,6 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
resp_data[1] = req_data[1];
resp_data[2] = SMP_RESP_FUNC_UNK;
- req->resid_len = blk_rq_bytes(req);
- rsp->resid_len = blk_rq_bytes(rsp);
-
switch (req_data[1]) {
case SMP_REPORT_GENERAL:
req->resid_len -= 8;
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index dcba267db711..540569849099 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -23,6 +23,13 @@
struct lpfc_sli2_slim;
+#define LPFC_PCI_DEV_LP 0x1
+#define LPFC_PCI_DEV_OC 0x2
+
+#define LPFC_SLI_REV2 2
+#define LPFC_SLI_REV3 3
+#define LPFC_SLI_REV4 4
+
#define LPFC_MAX_TARGET 4096 /* max number of targets supported */
#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els
requests */
@@ -98,9 +105,11 @@ struct lpfc_dma_pool {
};
struct hbq_dmabuf {
+ struct lpfc_dmabuf hbuf;
struct lpfc_dmabuf dbuf;
uint32_t size;
uint32_t tag;
+ struct lpfc_rcqe rcqe;
};
/* Priority bit. Set value to exceed low water mark in lpfc_mem. */
@@ -134,7 +143,10 @@ typedef struct lpfc_vpd {
} rev;
struct {
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd2 :24; /* Reserved */
+ uint32_t rsvd3 :19; /* Reserved */
+ uint32_t cdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd2 : 3; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
@@ -152,7 +164,10 @@ typedef struct lpfc_vpd {
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
- uint32_t rsvd2 :24; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t rsvd2 : 3; /* Reserved */
+ uint32_t cdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd3 :19; /* Reserved */
#endif
} sli3Feat;
} lpfc_vpd_t;
@@ -264,8 +279,8 @@ enum hba_state {
};
struct lpfc_vport {
- struct list_head listentry;
struct lpfc_hba *phba;
+ struct list_head listentry;
uint8_t port_type;
#define LPFC_PHYSICAL_PORT 1
#define LPFC_NPIV_PORT 2
@@ -273,6 +288,9 @@ struct lpfc_vport {
enum discovery_state port_state;
uint16_t vpi;
+ uint16_t vfi;
+ uint8_t vfi_state;
+#define LPFC_VFI_REGISTERED 0x1
uint32_t fc_flag; /* FC flags */
/* Several of these flags are HBA centric and should be moved to
@@ -385,6 +403,9 @@ struct lpfc_vport {
#endif
uint8_t stat_data_enabled;
uint8_t stat_data_blocked;
+ struct list_head rcv_buffer_list;
+ uint32_t vport_flag;
+#define STATIC_VPORT 1
};
struct hbq_s {
@@ -420,8 +441,66 @@ enum intr_type_t {
};
struct lpfc_hba {
+ /* SCSI interface function jump table entries */
+ int (*lpfc_new_scsi_buf)
+ (struct lpfc_vport *, int);
+ struct lpfc_scsi_buf * (*lpfc_get_scsi_buf)
+ (struct lpfc_hba *);
+ int (*lpfc_scsi_prep_dma_buf)
+ (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ void (*lpfc_scsi_unprep_dma_buf)
+ (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ void (*lpfc_release_scsi_buf)
+ (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ void (*lpfc_rampdown_queue_depth)
+ (struct lpfc_hba *);
+ void (*lpfc_scsi_prep_cmnd)
+ (struct lpfc_vport *, struct lpfc_scsi_buf *,
+ struct lpfc_nodelist *);
+ int (*lpfc_scsi_prep_task_mgmt_cmd)
+ (struct lpfc_vport *, struct lpfc_scsi_buf *,
+ unsigned int, uint8_t);
+
+ /* IOCB interface function jump table entries */
+ int (*__lpfc_sli_issue_iocb)
+ (struct lpfc_hba *, uint32_t,
+ struct lpfc_iocbq *, uint32_t);
+ void (*__lpfc_sli_release_iocbq)(struct lpfc_hba *,
+ struct lpfc_iocbq *);
+ int (*lpfc_hba_down_post)(struct lpfc_hba *phba);
+
+
+ IOCB_t * (*lpfc_get_iocb_from_iocbq)
+ (struct lpfc_iocbq *);
+ void (*lpfc_scsi_cmd_iocb_cmpl)
+ (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *);
+
+ /* MBOX interface function jump table entries */
+ int (*lpfc_sli_issue_mbox)
+ (struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+ /* Slow-path IOCB process function jump table entries */
+ void (*lpfc_sli_handle_slow_ring_event)
+ (struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ uint32_t mask);
+ /* INIT device interface function jump table entries */
+ int (*lpfc_sli_hbq_to_firmware)
+ (struct lpfc_hba *, uint32_t, struct hbq_dmabuf *);
+ int (*lpfc_sli_brdrestart)
+ (struct lpfc_hba *);
+ int (*lpfc_sli_brdready)
+ (struct lpfc_hba *, uint32_t);
+ void (*lpfc_handle_eratt)
+ (struct lpfc_hba *);
+ void (*lpfc_stop_port)
+ (struct lpfc_hba *);
+
+
+ /* SLI4 specific HBA data structure */
+ struct lpfc_sli4_hba sli4_hba;
+
struct lpfc_sli sli;
- uint32_t sli_rev; /* SLI2 or SLI3 */
+ uint8_t pci_dev_grp; /* lpfc PCI dev group: 0x0, 0x1, 0x2,... */
+ uint32_t sli_rev; /* SLI2, SLI3, or SLI4 */
uint32_t sli3_options; /* Mask of enabled SLI3 options */
#define LPFC_SLI3_HBQ_ENABLED 0x01
#define LPFC_SLI3_NPIV_ENABLED 0x02
@@ -429,6 +508,7 @@ struct lpfc_hba {
#define LPFC_SLI3_CRP_ENABLED 0x08
#define LPFC_SLI3_INB_ENABLED 0x10
#define LPFC_SLI3_BG_ENABLED 0x20
+#define LPFC_SLI3_DSS_ENABLED 0x40
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
@@ -442,7 +522,13 @@ struct lpfc_hba {
uint32_t hba_flag; /* hba generic flags */
#define HBA_ERATT_HANDLED 0x1 /* This flag is set when eratt handled */
-
+#define DEFER_ERATT 0x2 /* Deferred error attention in progress */
+#define HBA_FCOE_SUPPORT 0x4 /* HBA function supports FCOE */
+#define HBA_RECEIVE_BUFFER 0x8 /* Rcv buffer posted to worker thread */
+#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
+#define FCP_XRI_ABORT_EVENT 0x20
+#define ELS_XRI_ABORT_EVENT 0x40
+#define ASYNC_EVENT 0x80
struct lpfc_dmabuf slim2p;
MAILBOX_t *mbox;
@@ -501,6 +587,9 @@ struct lpfc_hba {
uint32_t cfg_poll;
uint32_t cfg_poll_tmo;
uint32_t cfg_use_msi;
+ uint32_t cfg_fcp_imax;
+ uint32_t cfg_fcp_wq_count;
+ uint32_t cfg_fcp_eq_count;
uint32_t cfg_sg_seg_cnt;
uint32_t cfg_prot_sg_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
@@ -510,6 +599,8 @@ struct lpfc_hba {
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
uint32_t cfg_enable_bg;
+ uint32_t cfg_enable_fip;
+ uint32_t cfg_log_verbose;
lpfc_vpd_t vpd; /* vital product data */
@@ -525,11 +616,12 @@ struct lpfc_hba {
unsigned long data_flags;
uint32_t hbq_in_use; /* HBQs in use flag */
- struct list_head hbqbuf_in_list; /* in-fly hbq buffer list */
+ struct list_head rb_pend_list; /* Received buffers to be processed */
uint32_t hbq_count; /* Count of configured HBQs */
struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies */
unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */
+ unsigned long pci_bar1_map; /* Physical address for PCI BAR1 */
unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */
void __iomem *slim_memmap_p; /* Kernel memory mapped address for
PCI BAR0 */
@@ -592,7 +684,8 @@ struct lpfc_hba {
/* pci_mem_pools */
struct pci_pool *lpfc_scsi_dma_buf_pool;
struct pci_pool *lpfc_mbuf_pool;
- struct pci_pool *lpfc_hbq_pool;
+ struct pci_pool *lpfc_hrb_pool; /* header receive buffer pool */
+ struct pci_pool *lpfc_drb_pool; /* data receive buffer pool */
struct lpfc_dma_pool lpfc_mbuf_safety_pool;
mempool_t *mbox_mem_pool;
@@ -608,6 +701,14 @@ struct lpfc_hba {
struct lpfc_vport *pport; /* physical lpfc_vport pointer */
uint16_t max_vpi; /* Maximum virtual nports */
#define LPFC_MAX_VPI 0xFFFF /* Max number of VPI supported */
+ uint16_t max_vports; /*
+ * For IOV HBAs max_vpi can change
+ * after a reset. max_vports is max
+ * number of vports present. This can
+ * be greater than max_vpi.
+ */
+ uint16_t vpi_base;
+ uint16_t vfi_base;
unsigned long *vpi_bmask; /* vpi allocation table */
/* Data structure used by fabric iocb scheduler */
@@ -666,6 +767,11 @@ struct lpfc_hba {
/* Maximum number of events that can be outstanding at any time*/
#define LPFC_MAX_EVT_COUNT 512
atomic_t fast_event_count;
+ struct lpfc_fcf fcf;
+ uint8_t fc_map[3];
+ uint8_t valid_vlan;
+ uint16_t vlan_id;
+ struct list_head fcf_conn_rec_list;
};
static inline struct Scsi_Host *
@@ -723,4 +829,3 @@ lpfc_sli_read_hs(struct lpfc_hba *phba)
return;
}
-
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 40cf0f4f327f..d73e677201f8 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -30,8 +30,10 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -51,7 +53,7 @@
#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8"
/**
- * lpfc_jedec_to_ascii: Hex to ascii convertor according to JEDEC rules.
+ * lpfc_jedec_to_ascii - Hex to ascii convertor according to JEDEC rules
* @incr: integer to convert.
* @hdw: ascii string holding converted integer plus a string terminator.
*
@@ -82,7 +84,7 @@ lpfc_jedec_to_ascii(int incr, char hdw[])
}
/**
- * lpfc_drvr_version_show: Return the Emulex driver string with version number.
+ * lpfc_drvr_version_show - Return the Emulex driver string with version number
* @dev: class unused variable.
* @attr: device attribute, not used.
* @buf: on return contains the module description text.
@@ -152,7 +154,7 @@ lpfc_bg_reftag_err_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_info_show: Return some pci info about the host in ascii.
+ * lpfc_info_show - Return some pci info about the host in ascii
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the formatted text from lpfc_info().
@@ -169,7 +171,7 @@ lpfc_info_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_serialnum_show: Return the hba serial number in ascii.
+ * lpfc_serialnum_show - Return the hba serial number in ascii
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the formatted text serial number.
@@ -188,7 +190,7 @@ lpfc_serialnum_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_temp_sensor_show: Return the temperature sensor level.
+ * lpfc_temp_sensor_show - Return the temperature sensor level
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the formatted support level.
@@ -210,7 +212,7 @@ lpfc_temp_sensor_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_modeldesc_show: Return the model description of the hba.
+ * lpfc_modeldesc_show - Return the model description of the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the scsi vpd model description.
@@ -229,7 +231,7 @@ lpfc_modeldesc_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_modelname_show: Return the model name of the hba.
+ * lpfc_modelname_show - Return the model name of the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the scsi vpd model name.
@@ -248,7 +250,7 @@ lpfc_modelname_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_programtype_show: Return the program type of the hba.
+ * lpfc_programtype_show - Return the program type of the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the scsi vpd program type.
@@ -267,7 +269,7 @@ lpfc_programtype_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_mlomgmt_show: Return the Menlo Maintenance sli flag.
+ * lpfc_mlomgmt_show - Return the Menlo Maintenance sli flag
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the Menlo Maintenance sli flag.
@@ -286,7 +288,7 @@ lpfc_mlomgmt_show(struct device *dev, struct device_attribute *attr, char *buf)
}
/**
- * lpfc_vportnum_show: Return the port number in ascii of the hba.
+ * lpfc_vportnum_show - Return the port number in ascii of the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains scsi vpd program type.
@@ -305,7 +307,7 @@ lpfc_vportnum_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_fwrev_show: Return the firmware rev running in the hba.
+ * lpfc_fwrev_show - Return the firmware rev running in the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the scsi vpd program type.
@@ -326,7 +328,7 @@ lpfc_fwrev_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_hdw_show: Return the jedec information about the hba.
+ * lpfc_hdw_show - Return the jedec information about the hba
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the scsi vpd program type.
@@ -347,7 +349,7 @@ lpfc_hdw_show(struct device *dev, struct device_attribute *attr, char *buf)
}
/**
- * lpfc_option_rom_version_show: Return the adapter ROM FCode version.
+ * lpfc_option_rom_version_show - Return the adapter ROM FCode version
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the ROM and FCode ascii strings.
@@ -366,7 +368,7 @@ lpfc_option_rom_version_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_state_show: Return the link state of the port.
+ * lpfc_state_show - Return the link state of the port
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains text describing the state of the link.
@@ -451,7 +453,7 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_num_discovered_ports_show: Return sum of mapped and unmapped vports.
+ * lpfc_num_discovered_ports_show - Return sum of mapped and unmapped vports
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the sum of fc mapped and unmapped.
@@ -474,7 +476,7 @@ lpfc_num_discovered_ports_show(struct device *dev,
}
/**
- * lpfc_issue_lip: Misnomer, name carried over from long ago.
+ * lpfc_issue_lip - Misnomer, name carried over from long ago
* @shost: Scsi_Host pointer.
*
* Description:
@@ -505,12 +507,14 @@ lpfc_issue_lip(struct Scsi_Host *shost)
return -ENOMEM;
memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
- pmboxq->mb.mbxCommand = MBX_DOWN_LINK;
- pmboxq->mb.mbxOwner = OWN_HOST;
+ pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
+ pmboxq->u.mb.mbxOwner = OWN_HOST;
mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO * 2);
- if ((mbxstatus == MBX_SUCCESS) && (pmboxq->mb.mbxStatus == 0)) {
+ if ((mbxstatus == MBX_SUCCESS) &&
+ (pmboxq->u.mb.mbxStatus == 0 ||
+ pmboxq->u.mb.mbxStatus == MBXERR_LINK_DOWN)) {
memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
lpfc_init_link(phba, pmboxq, phba->cfg_topology,
phba->cfg_link_speed);
@@ -529,7 +533,7 @@ lpfc_issue_lip(struct Scsi_Host *shost)
}
/**
- * lpfc_do_offline: Issues a mailbox command to bring the link down.
+ * lpfc_do_offline - Issues a mailbox command to bring the link down
* @phba: lpfc_hba pointer.
* @type: LPFC_EVT_OFFLINE, LPFC_EVT_WARM_START, LPFC_EVT_KILL.
*
@@ -537,7 +541,7 @@ lpfc_issue_lip(struct Scsi_Host *shost)
* Assumes any error from lpfc_do_offline() will be negative.
* Can wait up to 5 seconds for the port ring buffers count
* to reach zero, prints a warning if it is not zero and continues.
- * lpfc_workq_post_event() returns a non-zero return coce if call fails.
+ * lpfc_workq_post_event() returns a non-zero return code if call fails.
*
* Returns:
* -EIO error posting the event
@@ -591,7 +595,7 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
}
/**
- * lpfc_selective_reset: Offline then onlines the port.
+ * lpfc_selective_reset - Offline then onlines the port
* @phba: lpfc_hba pointer.
*
* Description:
@@ -632,7 +636,7 @@ lpfc_selective_reset(struct lpfc_hba *phba)
}
/**
- * lpfc_issue_reset: Selectively resets an adapter.
+ * lpfc_issue_reset - Selectively resets an adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the string "selective".
@@ -672,7 +676,7 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_nport_evt_cnt_show: Return the number of nport events.
+ * lpfc_nport_evt_cnt_show - Return the number of nport events
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the ascii number of nport events.
@@ -691,7 +695,7 @@ lpfc_nport_evt_cnt_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_board_mode_show: Return the state of the board.
+ * lpfc_board_mode_show - Return the state of the board
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the state of the adapter.
@@ -720,7 +724,7 @@ lpfc_board_mode_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_board_mode_store: Puts the hba in online, offline, warm or error state.
+ * lpfc_board_mode_store - Puts the hba in online, offline, warm or error state
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing one of the strings "online", "offline", "warm" or "error".
@@ -766,14 +770,14 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_get_hba_info: Return various bits of informaton about the adapter.
+ * lpfc_get_hba_info - Return various bits of informaton about the adapter
* @phba: pointer to the adapter structure.
- * @mxri max xri count.
- * @axri available xri count.
- * @mrpi max rpi count.
- * @arpi available rpi count.
- * @mvpi max vpi count.
- * @avpi available vpi count.
+ * @mxri: max xri count.
+ * @axri: available xri count.
+ * @mrpi: max rpi count.
+ * @arpi: available rpi count.
+ * @mvpi: max vpi count.
+ * @avpi: available vpi count.
*
* Description:
* If an integer pointer for an count is not null then the value for the
@@ -789,7 +793,8 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
uint32_t *mrpi, uint32_t *arpi,
uint32_t *mvpi, uint32_t *avpi)
{
- struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_mbx_read_config *rd_config;
LPFC_MBOXQ_t *pmboxq;
MAILBOX_t *pmb;
int rc = 0;
@@ -800,7 +805,7 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
*/
if (phba->link_state < LPFC_LINK_DOWN ||
!phba->mbox_mem_pool ||
- (phba->sli.sli_flag & LPFC_SLI2_ACTIVE) == 0)
+ (phba->sli.sli_flag & LPFC_SLI_ACTIVE) == 0)
return 0;
if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)
@@ -811,13 +816,13 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
return 0;
memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
- pmb = &pmboxq->mb;
+ pmb = &pmboxq->u.mb;
pmb->mbxCommand = MBX_READ_CONFIG;
pmb->mbxOwner = OWN_HOST;
pmboxq->context1 = NULL;
if ((phba->pport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
rc = MBX_NOT_FINISHED;
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -828,25 +833,44 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
return 0;
}
- if (mrpi)
- *mrpi = pmb->un.varRdConfig.max_rpi;
- if (arpi)
- *arpi = pmb->un.varRdConfig.avail_rpi;
- if (mxri)
- *mxri = pmb->un.varRdConfig.max_xri;
- if (axri)
- *axri = pmb->un.varRdConfig.avail_xri;
- if (mvpi)
- *mvpi = pmb->un.varRdConfig.max_vpi;
- if (avpi)
- *avpi = pmb->un.varRdConfig.avail_vpi;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ rd_config = &pmboxq->u.mqe.un.rd_config;
+ if (mrpi)
+ *mrpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config);
+ if (arpi)
+ *arpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config) -
+ phba->sli4_hba.max_cfg_param.rpi_used;
+ if (mxri)
+ *mxri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
+ if (axri)
+ *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) -
+ phba->sli4_hba.max_cfg_param.xri_used;
+ if (mvpi)
+ *mvpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+ if (avpi)
+ *avpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) -
+ phba->sli4_hba.max_cfg_param.vpi_used;
+ } else {
+ if (mrpi)
+ *mrpi = pmb->un.varRdConfig.max_rpi;
+ if (arpi)
+ *arpi = pmb->un.varRdConfig.avail_rpi;
+ if (mxri)
+ *mxri = pmb->un.varRdConfig.max_xri;
+ if (axri)
+ *axri = pmb->un.varRdConfig.avail_xri;
+ if (mvpi)
+ *mvpi = pmb->un.varRdConfig.max_vpi;
+ if (avpi)
+ *avpi = pmb->un.varRdConfig.avail_vpi;
+ }
mempool_free(pmboxq, phba->mbox_mem_pool);
return 1;
}
/**
- * lpfc_max_rpi_show: Return maximum rpi.
+ * lpfc_max_rpi_show - Return maximum rpi
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the maximum rpi count in decimal or "Unknown".
@@ -874,7 +898,7 @@ lpfc_max_rpi_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_used_rpi_show: Return maximum rpi minus available rpi.
+ * lpfc_used_rpi_show - Return maximum rpi minus available rpi
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the used rpi count in decimal or "Unknown".
@@ -902,7 +926,7 @@ lpfc_used_rpi_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_max_xri_show: Return maximum xri.
+ * lpfc_max_xri_show - Return maximum xri
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the maximum xri count in decimal or "Unknown".
@@ -930,7 +954,7 @@ lpfc_max_xri_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_used_xri_show: Return maximum xpi minus the available xpi.
+ * lpfc_used_xri_show - Return maximum xpi minus the available xpi
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the used xri count in decimal or "Unknown".
@@ -958,7 +982,7 @@ lpfc_used_xri_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_max_vpi_show: Return maximum vpi.
+ * lpfc_max_vpi_show - Return maximum vpi
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the maximum vpi count in decimal or "Unknown".
@@ -986,7 +1010,7 @@ lpfc_max_vpi_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_used_vpi_show: Return maximum vpi minus the available vpi.
+ * lpfc_used_vpi_show - Return maximum vpi minus the available vpi
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the used vpi count in decimal or "Unknown".
@@ -1014,7 +1038,7 @@ lpfc_used_vpi_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_npiv_info_show: Return text about NPIV support for the adapter.
+ * lpfc_npiv_info_show - Return text about NPIV support for the adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: text that must be interpreted to determine if npiv is supported.
@@ -1042,7 +1066,7 @@ lpfc_npiv_info_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_poll_show: Return text about poll support for the adapter.
+ * lpfc_poll_show - Return text about poll support for the adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: on return contains the cfg_poll in hex.
@@ -1064,7 +1088,7 @@ lpfc_poll_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_poll_store: Set the value of cfg_poll for the adapter.
+ * lpfc_poll_store - Set the value of cfg_poll for the adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: one or more lpfc_polling_flags values.
@@ -1136,7 +1160,7 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_param_show: Return a cfg attribute value in decimal.
+ * lpfc_param_show - Return a cfg attribute value in decimal
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1164,7 +1188,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
}
/**
- * lpfc_param_hex_show: Return a cfg attribute value in hex.
+ * lpfc_param_hex_show - Return a cfg attribute value in hex
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1173,7 +1197,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
* lpfc_##attr##_show: Return the hex value of an adapters cfg_xxx field.
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the attribute value in hexidecimal.
+ * @buf: on return contains the attribute value in hexadecimal.
*
* Returns: size of formatted string.
**/
@@ -1192,7 +1216,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
}
/**
- * lpfc_param_init: Intializes a cfg attribute.
+ * lpfc_param_init - Intializes a cfg attribute
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1226,7 +1250,7 @@ lpfc_##attr##_init(struct lpfc_hba *phba, int val) \
}
/**
- * lpfc_param_set: Set a cfg attribute value.
+ * lpfc_param_set - Set a cfg attribute value
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1260,7 +1284,7 @@ lpfc_##attr##_set(struct lpfc_hba *phba, int val) \
}
/**
- * lpfc_param_store: Set a vport attribute value.
+ * lpfc_param_store - Set a vport attribute value
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1300,7 +1324,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
}
/**
- * lpfc_vport_param_show: Return decimal formatted cfg attribute value.
+ * lpfc_vport_param_show - Return decimal formatted cfg attribute value
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1326,17 +1350,17 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
}
/**
- * lpfc_vport_param_hex_show: Return hex formatted attribute value.
+ * lpfc_vport_param_hex_show - Return hex formatted attribute value
*
* Description:
* Macro that given an attr e.g.
* hba_queue_depth expands into a function with the name
* lpfc_hba_queue_depth_show
*
- * lpfc_##attr##_show: prints the attribute value in hexidecimal.
+ * lpfc_##attr##_show: prints the attribute value in hexadecimal.
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the attribute value in hexidecimal.
+ * @buf: on return contains the attribute value in hexadecimal.
*
* Returns: length of formatted string.
**/
@@ -1353,7 +1377,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
}
/**
- * lpfc_vport_param_init: Initialize a vport cfg attribute.
+ * lpfc_vport_param_init - Initialize a vport cfg attribute
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1386,7 +1410,7 @@ lpfc_##attr##_init(struct lpfc_vport *vport, int val) \
}
/**
- * lpfc_vport_param_set: Set a vport cfg attribute.
+ * lpfc_vport_param_set - Set a vport cfg attribute
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
@@ -1417,7 +1441,7 @@ lpfc_##attr##_set(struct lpfc_vport *vport, int val) \
}
/**
- * lpfc_vport_param_store: Set a vport attribute.
+ * lpfc_vport_param_store - Set a vport attribute
*
* Description:
* Macro that given an attr e.g. hba_queue_depth
@@ -1576,7 +1600,7 @@ static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL);
static char *lpfc_soft_wwn_key = "C99G71SL8032A";
/**
- * lpfc_soft_wwn_enable_store: Allows setting of the wwn if the key is valid.
+ * lpfc_soft_wwn_enable_store - Allows setting of the wwn if the key is valid
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
* @buf: containing the string lpfc_soft_wwn_key.
@@ -1623,10 +1647,10 @@ static DEVICE_ATTR(lpfc_soft_wwn_enable, S_IWUSR, NULL,
lpfc_soft_wwn_enable_store);
/**
- * lpfc_soft_wwpn_show: Return the cfg soft ww port name of the adapter.
+ * lpfc_soft_wwpn_show - Return the cfg soft ww port name of the adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the wwpn in hexidecimal.
+ * @buf: on return contains the wwpn in hexadecimal.
*
* Returns: size of formatted string.
**/
@@ -1643,10 +1667,10 @@ lpfc_soft_wwpn_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_soft_wwpn_store: Set the ww port name of the adapter.
+ * lpfc_soft_wwpn_store - Set the ww port name of the adapter
* @dev class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: contains the wwpn in hexidecimal.
+ * @buf: contains the wwpn in hexadecimal.
* @count: number of wwpn bytes in buf
*
* Returns:
@@ -1729,10 +1753,10 @@ static DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\
lpfc_soft_wwpn_show, lpfc_soft_wwpn_store);
/**
- * lpfc_soft_wwnn_show: Return the cfg soft ww node name for the adapter.
+ * lpfc_soft_wwnn_show - Return the cfg soft ww node name for the adapter
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the wwnn in hexidecimal.
+ * @buf: on return contains the wwnn in hexadecimal.
*
* Returns: size of formatted string.
**/
@@ -1747,9 +1771,9 @@ lpfc_soft_wwnn_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_soft_wwnn_store: sets the ww node name of the adapter.
+ * lpfc_soft_wwnn_store - sets the ww node name of the adapter
* @cdev: class device that is converted into a Scsi_host.
- * @buf: contains the ww node name in hexidecimal.
+ * @buf: contains the ww node name in hexadecimal.
* @count: number of wwnn bytes in buf.
*
* Returns:
@@ -1845,7 +1869,7 @@ MODULE_PARM_DESC(lpfc_nodev_tmo,
"for a device to come back");
/**
- * lpfc_nodev_tmo_show: Return the hba dev loss timeout value.
+ * lpfc_nodev_tmo_show - Return the hba dev loss timeout value
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the dev loss timeout in decimal.
@@ -1864,7 +1888,7 @@ lpfc_nodev_tmo_show(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_nodev_tmo_init: Set the hba nodev timeout value.
+ * lpfc_nodev_tmo_init - Set the hba nodev timeout value
* @vport: lpfc vport structure pointer.
* @val: contains the nodev timeout value.
*
@@ -1905,7 +1929,7 @@ lpfc_nodev_tmo_init(struct lpfc_vport *vport, int val)
}
/**
- * lpfc_update_rport_devloss_tmo: Update dev loss tmo value.
+ * lpfc_update_rport_devloss_tmo - Update dev loss tmo value
* @vport: lpfc vport structure pointer.
*
* Description:
@@ -1926,7 +1950,7 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport)
}
/**
- * lpfc_nodev_tmo_set: Set the vport nodev tmo and devloss tmo values.
+ * lpfc_nodev_tmo_set - Set the vport nodev tmo and devloss tmo values
* @vport: lpfc vport structure pointer.
* @val: contains the tmo value.
*
@@ -1982,7 +2006,7 @@ lpfc_vport_param_init(devloss_tmo, LPFC_DEF_DEVLOSS_TMO,
lpfc_vport_param_show(devloss_tmo)
/**
- * lpfc_devloss_tmo_set: Sets vport nodev tmo, devloss tmo values, changed bit.
+ * lpfc_devloss_tmo_set - Sets vport nodev tmo, devloss tmo values, changed bit
* @vport: lpfc vport structure pointer.
* @val: contains the tmo value.
*
@@ -2021,22 +2045,9 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
# 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_FCP 0x40 FCP traffic history
-# LOG_NODE 0x80 Node table events
-# LOG_BG 0x200 BlockBuard events
-# LOG_MISC 0x400 Miscellaneous events
-# LOG_SLI 0x800 SLI events
-# LOG_FCP_ERROR 0x1000 Only log FCP errors
-# LOG_LIBDFC 0x2000 LIBDFC events
-# LOG_ALL_MSG 0xffff LOG all messages
+# See lpfc_logmsh.h for definitions.
*/
-LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffff,
+LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffffffff,
"Verbose logging bit-mask");
/*
@@ -2094,7 +2105,7 @@ MODULE_PARM_DESC(lpfc_restrict_login,
lpfc_vport_param_show(restrict_login);
/**
- * lpfc_restrict_login_init: Set the vport restrict login flag.
+ * lpfc_restrict_login_init - Set the vport restrict login flag
* @vport: lpfc vport structure pointer.
* @val: contains the restrict login value.
*
@@ -2128,7 +2139,7 @@ lpfc_restrict_login_init(struct lpfc_vport *vport, int val)
}
/**
- * lpfc_restrict_login_set: Set the vport restrict login flag.
+ * lpfc_restrict_login_set - Set the vport restrict login flag
* @vport: lpfc vport structure pointer.
* @val: contains the restrict login value.
*
@@ -2201,7 +2212,7 @@ LPFC_VPORT_ATTR_R(scan_down, 1, 0, 1,
*/
/**
- * lpfc_topology_set: Set the adapters topology field.
+ * lpfc_topology_set - Set the adapters topology field
* @phba: lpfc_hba pointer.
* @val: topology value.
*
@@ -2216,18 +2227,41 @@ LPFC_VPORT_ATTR_R(scan_down, 1, 0, 1,
* non-zero return value from lpfc_issue_lip()
* -EINVAL val out of range
**/
-static int
-lpfc_topology_set(struct lpfc_hba *phba, int val)
+static ssize_t
+lpfc_topology_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val = 0;
+ int nolip = 0;
+ const char *val_buf = buf;
int err;
uint32_t prev_val;
+
+ if (!strncmp(buf, "nolip ", strlen("nolip "))) {
+ nolip = 1;
+ val_buf = &buf[strlen("nolip ")];
+ }
+
+ if (!isdigit(val_buf[0]))
+ return -EINVAL;
+ if (sscanf(val_buf, "%i", &val) != 1)
+ return -EINVAL;
+
if (val >= 0 && val <= 6) {
prev_val = phba->cfg_topology;
phba->cfg_topology = val;
+ if (nolip)
+ return strlen(buf);
+
err = lpfc_issue_lip(lpfc_shost_from_vport(phba->pport));
- if (err)
+ if (err) {
phba->cfg_topology = prev_val;
- return err;
+ return -EINVAL;
+ } else
+ return strlen(buf);
}
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"%d:0467 lpfc_topology attribute cannot be set to %d, "
@@ -2240,14 +2274,42 @@ module_param(lpfc_topology, int, 0);
MODULE_PARM_DESC(lpfc_topology, "Select Fibre Channel topology");
lpfc_param_show(topology)
lpfc_param_init(topology, 0, 0, 6)
-lpfc_param_store(topology)
static DEVICE_ATTR(lpfc_topology, S_IRUGO | S_IWUSR,
lpfc_topology_show, lpfc_topology_store);
+/**
+ * lpfc_static_vport_show: Read callback function for
+ * lpfc_static_vport sysfs file.
+ * @dev: Pointer to class device object.
+ * @attr: device attribute structure.
+ * @buf: Data buffer.
+ *
+ * This function is the read call back function for
+ * lpfc_static_vport sysfs file. The lpfc_static_vport
+ * sysfs file report the mageability of the vport.
+ **/
+static ssize_t
+lpfc_static_vport_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ if (vport->vport_flag & STATIC_VPORT)
+ sprintf(buf, "1\n");
+ else
+ sprintf(buf, "0\n");
+
+ return strlen(buf);
+}
+
+/*
+ * Sysfs attribute to control the statistical data collection.
+ */
+static DEVICE_ATTR(lpfc_static_vport, S_IRUGO,
+ lpfc_static_vport_show, NULL);
/**
- * lpfc_stat_data_ctrl_store: write call back for lpfc_stat_data_ctrl
- * sysfs file.
+ * lpfc_stat_data_ctrl_store - write call back for lpfc_stat_data_ctrl sysfs file
* @dev: Pointer to class device.
* @buf: Data buffer.
* @count: Size of the data buffer.
@@ -2282,7 +2344,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
unsigned long base, step, bucket_type;
if (!strncmp(buf, "setbucket", strlen("setbucket"))) {
- if (strlen(buf) > LPFC_MAX_DATA_CTRL_LEN)
+ if (strlen(buf) > (LPFC_MAX_DATA_CTRL_LEN - 1))
return -EINVAL;
strcpy(bucket_data, buf);
@@ -2320,7 +2382,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
if (vports == NULL)
return -ENOMEM;
- for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
v_shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(v_shost->host_lock);
/* Block and reset data collection */
@@ -2335,7 +2397,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
phba->bucket_base = base;
phba->bucket_step = step;
- for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
v_shost = lpfc_shost_from_vport(vports[i]);
/* Unblock data collection */
@@ -2352,7 +2414,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
if (vports == NULL)
return -ENOMEM;
- for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
v_shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
vports[i]->stat_data_blocked = 1;
@@ -2411,8 +2473,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
/**
- * lpfc_stat_data_ctrl_show: Read callback function for
- * lpfc_stat_data_ctrl sysfs file.
+ * lpfc_stat_data_ctrl_show - Read function for lpfc_stat_data_ctrl sysfs file
* @dev: Pointer to class device object.
* @buf: Data buffer.
*
@@ -2489,8 +2550,7 @@ static DEVICE_ATTR(lpfc_stat_data_ctrl, S_IRUGO | S_IWUSR,
/**
- * sysfs_drvr_stat_data_read: Read callback function for lpfc_drvr_stat_data
- * sysfs attribute.
+ * sysfs_drvr_stat_data_read - Read function for lpfc_drvr_stat_data attribute
* @kobj: Pointer to the kernel object
* @bin_attr: Attribute object
* @buff: Buffer pointer
@@ -2585,7 +2645,7 @@ static struct bin_attribute sysfs_drvr_stat_data_attr = {
*/
/**
- * lpfc_link_speed_set: Set the adapters link speed.
+ * lpfc_link_speed_set - Set the adapters link speed
* @phba: lpfc_hba pointer.
* @val: link speed value.
*
@@ -2601,12 +2661,29 @@ static struct bin_attribute sysfs_drvr_stat_data_attr = {
* non-zero return value from lpfc_issue_lip()
* -EINVAL val out of range
**/
-static int
-lpfc_link_speed_set(struct lpfc_hba *phba, int val)
+static ssize_t
+lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val = 0;
+ int nolip = 0;
+ const char *val_buf = buf;
int err;
uint32_t prev_val;
+ if (!strncmp(buf, "nolip ", strlen("nolip "))) {
+ nolip = 1;
+ val_buf = &buf[strlen("nolip ")];
+ }
+
+ if (!isdigit(val_buf[0]))
+ return -EINVAL;
+ if (sscanf(val_buf, "%i", &val) != 1)
+ return -EINVAL;
+
if (((val == LINK_SPEED_1G) && !(phba->lmt & LMT_1Gb)) ||
((val == LINK_SPEED_2G) && !(phba->lmt & LMT_2Gb)) ||
((val == LINK_SPEED_4G) && !(phba->lmt & LMT_4Gb)) ||
@@ -2614,14 +2691,19 @@ lpfc_link_speed_set(struct lpfc_hba *phba, int val)
((val == LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)))
return -EINVAL;
- if ((val >= 0 && val <= LPFC_MAX_LINK_SPEED)
+ if ((val >= 0 && val <= 8)
&& (LPFC_LINK_SPEED_BITMAP & (1 << val))) {
prev_val = phba->cfg_link_speed;
phba->cfg_link_speed = val;
+ if (nolip)
+ return strlen(buf);
+
err = lpfc_issue_lip(lpfc_shost_from_vport(phba->pport));
- if (err)
+ if (err) {
phba->cfg_link_speed = prev_val;
- return err;
+ return -EINVAL;
+ } else
+ return strlen(buf);
}
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -2637,7 +2719,7 @@ MODULE_PARM_DESC(lpfc_link_speed, "Select link speed");
lpfc_param_show(link_speed)
/**
- * lpfc_link_speed_init: Set the adapters link speed.
+ * lpfc_link_speed_init - Set the adapters link speed
* @phba: lpfc_hba pointer.
* @val: link speed value.
*
@@ -2668,7 +2750,6 @@ lpfc_link_speed_init(struct lpfc_hba *phba, int val)
return -EINVAL;
}
-lpfc_param_store(link_speed)
static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
lpfc_link_speed_show, lpfc_link_speed_store);
@@ -2804,15 +2885,39 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255,
/*
# lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that
# support this feature
-# 0 = MSI disabled
+# 0 = MSI disabled (default)
# 1 = MSI enabled
-# 2 = MSI-X enabled (default)
-# Value range is [0,2]. Default value is 2.
+# 2 = MSI-X enabled
+# Value range is [0,2]. Default value is 0.
*/
-LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or "
+LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or "
"MSI-X (2), if possible");
/*
+# lpfc_fcp_imax: Set the maximum number of fast-path FCP interrupts per second
+#
+# Value range is [636,651042]. Default value is 10000.
+*/
+LPFC_ATTR_R(fcp_imax, LPFC_FP_DEF_IMAX, LPFC_MIM_IMAX, LPFC_DMULT_CONST,
+ "Set the maximum number of fast-path FCP interrupts per second");
+
+/*
+# lpfc_fcp_wq_count: Set the number of fast-path FCP work queues
+#
+# Value range is [1,31]. Default value is 4.
+*/
+LPFC_ATTR_R(fcp_wq_count, LPFC_FP_WQN_DEF, LPFC_FP_WQN_MIN, LPFC_FP_WQN_MAX,
+ "Set the number of fast-path FCP work queues, if possible");
+
+/*
+# lpfc_fcp_eq_count: Set the number of fast-path FCP event queues
+#
+# Value range is [1,7]. Default value is 1.
+*/
+LPFC_ATTR_R(fcp_eq_count, LPFC_FP_EQN_DEF, LPFC_FP_EQN_MIN, LPFC_FP_EQN_MAX,
+ "Set the number of fast-path FCP event queues, if possible");
+
+/*
# lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware.
# 0 = HBA resets disabled
# 1 = HBA resets enabled (default)
@@ -2836,6 +2941,14 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
*/
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
+/*
+# lpfc_enable_fip: When set, FIP is required to start discovery. If not
+# set, the driver will add an FCF record manually if the port has no
+# FCF records available and start discovery.
+# Value range is [0,1]. Default value is 1 (enabled)
+*/
+LPFC_ATTR_RW(enable_fip, 0, 0, 1, "Enable FIP Discovery");
+
/*
# lpfc_prot_mask: i
@@ -2865,7 +2978,7 @@ MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
/*
- * lpfc_sg_seg_cnt: Initial Maximum DMA Segment Count
+ * lpfc_sg_seg_cnt - Initial Maximum DMA Segment Count
* This value can be set to values between 64 and 256. The default value is
* 64, but may be increased to allow for larger Max I/O sizes. The scsi layer
* will be allowed to request I/Os of sizes up to (MAX_SEG_COUNT * SEG_SIZE).
@@ -2902,6 +3015,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
+ &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_fcp_class,
&dev_attr_lpfc_use_adisc,
&dev_attr_lpfc_ack0,
@@ -2929,6 +3043,9 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_poll,
&dev_attr_lpfc_poll_tmo,
&dev_attr_lpfc_use_msi,
+ &dev_attr_lpfc_fcp_imax,
+ &dev_attr_lpfc_fcp_wq_count,
+ &dev_attr_lpfc_fcp_eq_count,
&dev_attr_lpfc_enable_bg,
&dev_attr_lpfc_soft_wwnn,
&dev_attr_lpfc_soft_wwpn,
@@ -2951,6 +3068,7 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_lun_queue_depth,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
+ &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_hba_queue_depth,
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_restrict_login,
@@ -2963,11 +3081,12 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_enable_da_id,
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
+ &dev_attr_lpfc_static_vport,
NULL,
};
/**
- * sysfs_ctlreg_write: Write method for writing to ctlreg.
+ * sysfs_ctlreg_write - Write method for writing to ctlreg
* @kobj: kernel kobject that contains the kernel class device.
* @bin_attr: kernel attributes passed to us.
* @buf: contains the data to be written to the adapter IOREG space.
@@ -3017,7 +3136,7 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr,
}
/**
- * sysfs_ctlreg_read: Read method for reading from ctlreg.
+ * sysfs_ctlreg_read - Read method for reading from ctlreg
* @kobj: kernel kobject that contains the kernel class device.
* @bin_attr: kernel attributes passed to us.
* @buf: if succesful contains the data from the adapter IOREG space.
@@ -3078,7 +3197,7 @@ static struct bin_attribute sysfs_ctlreg_attr = {
};
/**
- * sysfs_mbox_idle: frees the sysfs mailbox.
+ * sysfs_mbox_idle - frees the sysfs mailbox
* @phba: lpfc_hba pointer
**/
static void
@@ -3095,7 +3214,7 @@ sysfs_mbox_idle(struct lpfc_hba *phba)
}
/**
- * sysfs_mbox_write: Write method for writing information via mbox.
+ * sysfs_mbox_write - Write method for writing information via mbox
* @kobj: kernel kobject that contains the kernel class device.
* @bin_attr: kernel attributes passed to us.
* @buf: contains the data to be written to sysfs mbox.
@@ -3159,7 +3278,7 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr,
}
}
- memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off,
+ memcpy((uint8_t *) &phba->sysfs_mbox.mbox->u.mb + off,
buf, count);
phba->sysfs_mbox.offset = off + count;
@@ -3170,7 +3289,7 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr,
}
/**
- * sysfs_mbox_read: Read method for reading information via mbox.
+ * sysfs_mbox_read - Read method for reading information via mbox
* @kobj: kernel kobject that contains the kernel class device.
* @bin_attr: kernel attributes passed to us.
* @buf: contains the data to be read from sysfs mbox.
@@ -3201,6 +3320,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
int rc;
+ MAILBOX_t *pmb;
if (off > MAILBOX_CMD_SIZE)
return -ERANGE;
@@ -3225,8 +3345,8 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
if (off == 0 &&
phba->sysfs_mbox.state == SMBOX_WRITING &&
phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) {
-
- switch (phba->sysfs_mbox.mbox->mb.mbxCommand) {
+ pmb = &phba->sysfs_mbox.mbox->u.mb;
+ switch (pmb->mbxCommand) {
/* Offline only */
case MBX_INIT_LINK:
case MBX_DOWN_LINK:
@@ -3243,7 +3363,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
if (!(vport->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);
+ pmb->mbxCommand);
sysfs_mbox_idle(phba);
spin_unlock_irq(&phba->hbalock);
return -EPERM;
@@ -3279,13 +3399,13 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
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);
+ pmb->mbxCommand);
sysfs_mbox_idle(phba);
spin_unlock_irq(&phba->hbalock);
return -EPERM;
default:
printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n",
- phba->sysfs_mbox.mbox->mb.mbxCommand);
+ pmb->mbxCommand);
sysfs_mbox_idle(phba);
spin_unlock_irq(&phba->hbalock);
return -EPERM;
@@ -3295,14 +3415,14 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
* or RESTART mailbox commands until the HBA is restarted.
*/
if (phba->pport->stopped &&
- phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_DUMP_MEMORY &&
- phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_RESTART &&
- phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_WRITE_VPARMS &&
- phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_WRITE_WWN)
+ pmb->mbxCommand != MBX_DUMP_MEMORY &&
+ pmb->mbxCommand != MBX_RESTART &&
+ pmb->mbxCommand != MBX_WRITE_VPARMS &&
+ pmb->mbxCommand != MBX_WRITE_WWN)
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"1259 mbox: Issued mailbox cmd "
"0x%x while in stopped state.\n",
- phba->sysfs_mbox.mbox->mb.mbxCommand);
+ pmb->mbxCommand);
phba->sysfs_mbox.mbox->vport = vport;
@@ -3316,7 +3436,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
}
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){
+ (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
spin_unlock_irq(&phba->hbalock);
rc = lpfc_sli_issue_mbox (phba,
@@ -3328,8 +3448,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
spin_unlock_irq(&phba->hbalock);
rc = lpfc_sli_issue_mbox_wait (phba,
phba->sysfs_mbox.mbox,
- lpfc_mbox_tmo_val(phba,
- phba->sysfs_mbox.mbox->mb.mbxCommand) * HZ);
+ lpfc_mbox_tmo_val(phba, pmb->mbxCommand) * HZ);
spin_lock_irq(&phba->hbalock);
}
@@ -3351,7 +3470,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
return -EAGAIN;
}
- memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count);
+ memcpy(buf, (uint8_t *) &pmb + off, count);
phba->sysfs_mbox.offset = off + count;
@@ -3374,7 +3493,7 @@ static struct bin_attribute sysfs_mbox_attr = {
};
/**
- * lpfc_alloc_sysfs_attr: Creates the ctlreg and mbox entries.
+ * lpfc_alloc_sysfs_attr - Creates the ctlreg and mbox entries
* @vport: address of lpfc vport structure.
*
* Return codes:
@@ -3415,7 +3534,7 @@ out:
}
/**
- * lpfc_free_sysfs_attr: Removes the ctlreg and mbox entries.
+ * lpfc_free_sysfs_attr - Removes the ctlreg and mbox entries
* @vport: address of lpfc vport structure.
**/
void
@@ -3437,7 +3556,7 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport)
*/
/**
- * lpfc_get_host_port_id: Copy the vport DID into the scsi host port id.
+ * lpfc_get_host_port_id - Copy the vport DID into the scsi host port id
* @shost: kernel scsi host pointer.
**/
static void
@@ -3450,7 +3569,7 @@ lpfc_get_host_port_id(struct Scsi_Host *shost)
}
/**
- * lpfc_get_host_port_type: Set the value of the scsi host port type.
+ * lpfc_get_host_port_type - Set the value of the scsi host port type
* @shost: kernel scsi host pointer.
**/
static void
@@ -3482,7 +3601,7 @@ lpfc_get_host_port_type(struct Scsi_Host *shost)
}
/**
- * lpfc_get_host_port_state: Set the value of the scsi host port state.
+ * lpfc_get_host_port_state - Set the value of the scsi host port state
* @shost: kernel scsi host pointer.
**/
static void
@@ -3520,7 +3639,7 @@ lpfc_get_host_port_state(struct Scsi_Host *shost)
}
/**
- * lpfc_get_host_speed: Set the value of the scsi host speed.
+ * lpfc_get_host_speed - Set the value of the scsi host speed
* @shost: kernel scsi host pointer.
**/
static void
@@ -3545,6 +3664,9 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
case LA_8GHZ_LINK:
fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
break;
+ case LA_10GHZ_LINK:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
default:
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
@@ -3556,7 +3678,7 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
}
/**
- * lpfc_get_host_fabric_name: Set the value of the scsi host fabric name.
+ * lpfc_get_host_fabric_name - Set the value of the scsi host fabric name
* @shost: kernel scsi host pointer.
**/
static void
@@ -3582,7 +3704,7 @@ lpfc_get_host_fabric_name (struct Scsi_Host *shost)
}
/**
- * lpfc_get_stats: Return statistical information about the adapter.
+ * lpfc_get_stats - Return statistical information about the adapter
* @shost: kernel scsi host pointer.
*
* Notes:
@@ -3612,7 +3734,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
*/
if (phba->link_state < LPFC_LINK_DOWN ||
!phba->mbox_mem_pool ||
- (phba->sli.sli_flag & LPFC_SLI2_ACTIVE) == 0)
+ (phba->sli.sli_flag & LPFC_SLI_ACTIVE) == 0)
return NULL;
if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)
@@ -3623,14 +3745,14 @@ lpfc_get_stats(struct Scsi_Host *shost)
return NULL;
memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
- pmb = &pmboxq->mb;
+ pmb = &pmboxq->u.mb;
pmb->mbxCommand = MBX_READ_STATUS;
pmb->mbxOwner = OWN_HOST;
pmboxq->context1 = NULL;
pmboxq->vport = vport;
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3655,7 +3777,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
pmboxq->vport = vport;
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3707,7 +3829,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
}
/**
- * lpfc_reset_stats: Copy the adapter link stats information.
+ * lpfc_reset_stats - Copy the adapter link stats information
* @shost: kernel scsi host pointer.
**/
static void
@@ -3729,7 +3851,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
return;
memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
- pmb = &pmboxq->mb;
+ pmb = &pmboxq->u.mb;
pmb->mbxCommand = MBX_READ_STATUS;
pmb->mbxOwner = OWN_HOST;
pmb->un.varWords[0] = 0x1; /* reset request */
@@ -3737,7 +3859,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
pmboxq->vport = vport;
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3755,7 +3877,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
pmboxq->vport = vport;
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3788,7 +3910,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
*/
/**
- * lpfc_get_node_by_target: Return the nodelist for a target.
+ * lpfc_get_node_by_target - Return the nodelist for a target
* @starget: kernel scsi target pointer.
*
* Returns:
@@ -3817,7 +3939,7 @@ lpfc_get_node_by_target(struct scsi_target *starget)
}
/**
- * lpfc_get_starget_port_id: Set the target port id to the ndlp DID or -1.
+ * lpfc_get_starget_port_id - Set the target port id to the ndlp DID or -1
* @starget: kernel scsi target pointer.
**/
static void
@@ -3829,7 +3951,7 @@ lpfc_get_starget_port_id(struct scsi_target *starget)
}
/**
- * lpfc_get_starget_node_name: Set the target node name.
+ * lpfc_get_starget_node_name - Set the target node name
* @starget: kernel scsi target pointer.
*
* Description: Set the target node name to the ndlp node name wwn or zero.
@@ -3844,7 +3966,7 @@ lpfc_get_starget_node_name(struct scsi_target *starget)
}
/**
- * lpfc_get_starget_port_name: Set the target port name.
+ * lpfc_get_starget_port_name - Set the target port name
* @starget: kernel scsi target pointer.
*
* Description: set the target port name to the ndlp port name wwn or zero.
@@ -3859,7 +3981,7 @@ lpfc_get_starget_port_name(struct scsi_target *starget)
}
/**
- * lpfc_set_rport_loss_tmo: Set the rport dev loss tmo.
+ * lpfc_set_rport_loss_tmo - Set the rport dev loss tmo
* @rport: fc rport address.
* @timeout: new value for dev loss tmo.
*
@@ -3877,7 +3999,7 @@ lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
}
/**
- * lpfc_rport_show_function: Return rport target information.
+ * lpfc_rport_show_function - Return rport target information
*
* Description:
* Macro that uses field to generate a function with the name lpfc_show_rport_
@@ -3905,7 +4027,7 @@ lpfc_show_rport_##field (struct device *dev, \
static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
/**
- * lpfc_set_vport_symbolic_name: Set the vport's symbolic name.
+ * lpfc_set_vport_symbolic_name - Set the vport's symbolic name
* @fc_vport: The fc_vport who's symbolic name has been changed.
*
* Description:
@@ -3922,6 +4044,21 @@ lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
}
+/**
+ * lpfc_hba_log_verbose_init - Set hba's log verbose level
+ * @phba: Pointer to lpfc_hba struct.
+ *
+ * This function is called by the lpfc_get_cfgparam() routine to set the
+ * module lpfc_log_verbose into the @phba cfg_log_verbose for use with
+ * log messsage according to the module's lpfc_log_verbose parameter setting
+ * before hba port or vport created.
+ **/
+static void
+lpfc_hba_log_verbose_init(struct lpfc_hba *phba, uint32_t verbose)
+{
+ phba->cfg_log_verbose = verbose;
+}
+
struct fc_function_template lpfc_transport_functions = {
/* fixed attributes the driver supports */
.show_host_node_name = 1,
@@ -4048,7 +4185,7 @@ struct fc_function_template lpfc_vport_transport_functions = {
};
/**
- * lpfc_get_cfgparam: Used during probe_one to init the adapter structure.
+ * lpfc_get_cfgparam - Used during probe_one to init the adapter structure
* @phba: lpfc_hba pointer.
**/
void
@@ -4065,6 +4202,9 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
lpfc_use_msi_init(phba, lpfc_use_msi);
+ lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+ lpfc_fcp_wq_count_init(phba, lpfc_fcp_wq_count);
+ lpfc_fcp_eq_count_init(phba, lpfc_fcp_eq_count);
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
lpfc_enable_bg_init(phba, lpfc_enable_bg);
@@ -4073,31 +4213,15 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
- /*
- * Since the sg_tablesize is module parameter, the sg_dma_buf_size
- * used to create the sg_dma_buf_pool must be dynamically calculated.
- * 2 segments are added since the IOCB needs a command and response bde.
- */
- phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
- sizeof(struct fcp_rsp) +
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
-
- if (phba->cfg_enable_bg) {
- phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
- phba->cfg_sg_dma_buf_size +=
- phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
- }
-
- /* Also reinitialize the host templates with new values. */
- lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
- lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
-
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
+ lpfc_enable_fip_init(phba, lpfc_enable_fip);
+ lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
+
return;
}
/**
- * lpfc_get_vport_cfgparam: Used during port create, init the vport structure.
+ * lpfc_get_vport_cfgparam - Used during port create, init the vport structure
* @vport: lpfc_vport pointer.
**/
void
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 07f4976319a5..d2a922997c0f 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -23,6 +23,8 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_dump_static_vport(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+int lpfc_dump_fcoe_param(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -35,17 +37,19 @@ int lpfc_config_msi(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *, int);
void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_read_lnk_stat(struct lpfc_hba *, LPFC_MBOXQ_t *);
-int lpfc_reg_login(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
- LPFC_MBOXQ_t *, uint32_t);
+int lpfc_reg_rpi(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
+ LPFC_MBOXQ_t *, uint32_t);
void lpfc_unreg_login(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
void lpfc_unreg_did(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
-void lpfc_reg_vpi(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_reg_vpi(struct lpfc_vport *, LPFC_MBOXQ_t *);
void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *);
void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
void lpfc_cleanup_rpis(struct lpfc_vport *, int);
int lpfc_linkdown(struct lpfc_hba *);
+void lpfc_linkdown_port(struct lpfc_vport *);
void lpfc_port_link_failure(struct lpfc_vport *);
void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
@@ -54,6 +58,7 @@ void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
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 *);
+void lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_enqueue_node(struct lpfc_vport *, struct lpfc_nodelist *);
void lpfc_dequeue_node(struct lpfc_vport *, struct lpfc_nodelist *);
struct lpfc_nodelist *lpfc_enable_node(struct lpfc_vport *,
@@ -105,6 +110,7 @@ int lpfc_issue_els_adisc(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
int lpfc_issue_els_logo(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
int lpfc_issue_els_npiv_logo(struct lpfc_vport *, struct lpfc_nodelist *);
int lpfc_issue_els_scr(struct lpfc_vport *, uint32_t, uint8_t);
+int lpfc_issue_fabric_reglogin(struct lpfc_vport *);
int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
int lpfc_ct_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
int lpfc_els_rsp_acc(struct lpfc_vport *, uint32_t, struct lpfc_iocbq *,
@@ -149,15 +155,19 @@ int lpfc_online(struct lpfc_hba *);
void lpfc_unblock_mgmt_io(struct lpfc_hba *);
void lpfc_offline_prep(struct lpfc_hba *);
void lpfc_offline(struct lpfc_hba *);
+void lpfc_reset_hba(struct lpfc_hba *);
int lpfc_sli_setup(struct lpfc_hba *);
int lpfc_sli_queue_setup(struct lpfc_hba *);
void lpfc_handle_eratt(struct lpfc_hba *);
void lpfc_handle_latt(struct lpfc_hba *);
-irqreturn_t lpfc_intr_handler(int, void *);
-irqreturn_t lpfc_sp_intr_handler(int, void *);
-irqreturn_t lpfc_fp_intr_handler(int, void *);
+irqreturn_t lpfc_sli_intr_handler(int, void *);
+irqreturn_t lpfc_sli_sp_intr_handler(int, void *);
+irqreturn_t lpfc_sli_fp_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_sp_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_fp_intr_handler(int, void *);
void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *);
@@ -165,16 +175,32 @@ void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_kill_board(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 *);
+void __lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_mbox_cmd_check(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_mbox_dev_check(struct lpfc_hba *);
int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
+void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
+void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
+void lpfc_init_vpi(struct lpfcMboxq *, uint16_t);
+void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
+void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
+void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
void lpfc_config_hbq(struct lpfc_hba *, uint32_t, struct lpfc_hbq_init *,
uint32_t , LPFC_MBOXQ_t *);
struct hbq_dmabuf *lpfc_els_hbq_alloc(struct lpfc_hba *);
void lpfc_els_hbq_free(struct lpfc_hba *, struct hbq_dmabuf *);
+struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *);
+void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *);
+void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
+ uint16_t);
+void lpfc_unregister_unused_fcf(struct lpfc_hba *);
-int lpfc_mem_alloc(struct lpfc_hba *);
+int lpfc_mem_alloc(struct lpfc_hba *, int align);
void lpfc_mem_free(struct lpfc_hba *);
+void lpfc_mem_free_all(struct lpfc_hba *);
void lpfc_stop_vport_timers(struct lpfc_vport *);
void lpfc_poll_timeout(unsigned long ptr);
@@ -184,6 +210,9 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *);
struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *);
void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
+void lpfc_sli_cancel_iocbs(struct lpfc_hba *, struct list_head *, uint32_t,
+ uint32_t);
+void lpfc_sli_wake_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_reset_barrier(struct lpfc_hba * phba);
int lpfc_sli_brdready(struct lpfc_hba *, uint32_t);
@@ -196,12 +225,13 @@ int lpfc_sli_host_down(struct lpfc_vport *);
int lpfc_sli_hba_down(struct lpfc_hba *);
int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
int lpfc_sli_handle_mb_event(struct lpfc_hba *);
-int lpfc_sli_flush_mbox_queue(struct lpfc_hba *);
+void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
int lpfc_sli_check_eratt(struct lpfc_hba *);
-int lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
+void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
struct lpfc_sli_ring *, uint32_t);
+int lpfc_sli4_handle_received_buffer(struct lpfc_hba *);
void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
-int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *,
+int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
@@ -235,7 +265,7 @@ struct lpfc_nodelist *lpfc_findnode_wwpn(struct lpfc_vport *,
int lpfc_sli_issue_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
-int lpfc_sli_issue_iocb_wait(struct lpfc_hba *, struct lpfc_sli_ring *,
+int lpfc_sli_issue_iocb_wait(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, struct lpfc_iocbq *,
uint32_t);
void lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *, struct lpfc_iocbq *,
@@ -252,6 +282,12 @@ void lpfc_in_buf_free(struct lpfc_hba *, struct lpfc_dmabuf *);
const char* lpfc_info(struct Scsi_Host *);
int lpfc_scan_finished(struct Scsi_Host *, unsigned long);
+int lpfc_init_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_sli_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_scsi_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_mbox_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_api_table_setup(struct lpfc_hba *, uint8_t);
+
void lpfc_get_cfgparam(struct lpfc_hba *);
void lpfc_get_vport_cfgparam(struct lpfc_vport *);
int lpfc_alloc_sysfs_attr(struct lpfc_vport *);
@@ -312,8 +348,15 @@ lpfc_send_els_failure_event(struct lpfc_hba *, struct lpfc_iocbq *,
struct lpfc_iocbq *);
struct lpfc_fast_path_event *lpfc_alloc_fast_evt(struct lpfc_hba *);
void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *);
+void lpfc_create_static_vport(struct lpfc_hba *);
+void lpfc_stop_hba_timers(struct lpfc_hba *);
+void lpfc_stop_port(struct lpfc_hba *);
+void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t);
+int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
+void lpfc_start_fdiscs(struct lpfc_hba *phba);
#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
+
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 896c7b0351e5..1dbccfd3d022 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -32,8 +32,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -267,8 +269,6 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
uint32_t tmo, uint8_t retry)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
IOCB_t *icmd;
struct lpfc_iocbq *geniocb;
int rc;
@@ -331,7 +331,7 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;
geniocb->vport = vport;
geniocb->retry = retry;
- rc = lpfc_sli_issue_iocb(phba, pring, geniocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, geniocb, 0);
if (rc == IOCB_ERROR) {
lpfc_sli_release_iocbq(phba, geniocb);
@@ -1578,6 +1578,9 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode)
case LA_8GHZ_LINK:
ae->un.PortSpeed = HBA_PORTSPEED_8GBIT;
break;
+ case LA_10GHZ_LINK:
+ ae->un.PortSpeed = HBA_PORTSPEED_10GBIT;
+ break;
default:
ae->un.PortSpeed =
HBA_PORTSPEED_UNKNOWN;
@@ -1730,7 +1733,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
uint8_t *fwname;
if (vp->rev.rBit) {
- if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+ if (psli->sli_flag & LPFC_SLI_ACTIVE)
rev = vp->rev.sli2FwRev;
else
rev = vp->rev.sli1FwRev;
@@ -1756,7 +1759,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
}
b4 = (rev & 0x0000000f);
- if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+ if (psli->sli_flag & LPFC_SLI_ACTIVE)
fwname = vp->rev.sli2FwName;
else
fwname = vp->rev.sli1FwName;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 81cdcf46c471..2b02b1fb39a0 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2007-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2007-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -33,8 +33,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -47,7 +49,7 @@
#include "lpfc_debugfs.h"
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-/**
+/*
* debugfs interface
*
* To access this interface the user should:
@@ -95,7 +97,7 @@ module_param(lpfc_debugfs_max_slow_ring_trc, int, 0);
MODULE_PARM_DESC(lpfc_debugfs_max_slow_ring_trc,
"Set debugfs slow ring trace depth");
-int lpfc_debugfs_mask_disc_trc;
+static int lpfc_debugfs_mask_disc_trc;
module_param(lpfc_debugfs_mask_disc_trc, int, 0);
MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc,
"Set debugfs discovery trace mask");
@@ -127,7 +129,7 @@ static atomic_t lpfc_debugfs_seq_trc_cnt = ATOMIC_INIT(0);
static unsigned long lpfc_debugfs_start_time = 0L;
/**
- * lpfc_debugfs_disc_trc_data - Dump discovery logging to a buffer.
+ * lpfc_debugfs_disc_trc_data - Dump discovery logging to a buffer
* @vport: The vport to gather the log info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -187,7 +189,7 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
}
/**
- * lpfc_debugfs_slow_ring_trc_data - Dump slow ring logging to a buffer.
+ * lpfc_debugfs_slow_ring_trc_data - Dump slow ring logging to a buffer
* @phba: The HBA to gather the log info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -250,7 +252,7 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
static int lpfc_debugfs_last_hbq = -1;
/**
- * lpfc_debugfs_hbqinfo_data - Dump host buffer queue info to a buffer.
+ * lpfc_debugfs_hbqinfo_data - Dump host buffer queue info to a buffer
* @phba: The HBA to gather host buffer info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -280,6 +282,8 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
struct lpfc_dmabuf *d_buf;
struct hbq_dmabuf *hbq_buf;
+ if (phba->sli_rev != 3)
+ return 0;
cnt = LPFC_HBQINFO_SIZE;
spin_lock_irq(&phba->hbalock);
@@ -369,7 +373,7 @@ skipit:
static int lpfc_debugfs_last_hba_slim_off;
/**
- * lpfc_debugfs_dumpHBASlim_data - Dump HBA SLIM info to a buffer.
+ * lpfc_debugfs_dumpHBASlim_data - Dump HBA SLIM info to a buffer
* @phba: The HBA to gather SLIM info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -399,8 +403,7 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
len += snprintf(buf+len, size-len, "HBA SLIM\n");
lpfc_memcpy_from_slim(buffer,
- ((uint8_t *)phba->MBslimaddr) + lpfc_debugfs_last_hba_slim_off,
- 1024);
+ phba->MBslimaddr + lpfc_debugfs_last_hba_slim_off, 1024);
ptr = (uint32_t *)&buffer[0];
off = lpfc_debugfs_last_hba_slim_off;
@@ -426,7 +429,7 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
}
/**
- * lpfc_debugfs_dumpHostSlim_data - Dump host SLIM info to a buffer.
+ * lpfc_debugfs_dumpHostSlim_data - Dump host SLIM info to a buffer
* @phba: The HBA to gather Host SLIM info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -490,18 +493,21 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
pring->next_cmdidx, pring->local_getidx,
pring->flag, pgpp->rspPutInx, pring->numRiocb);
}
- word0 = readl(phba->HAregaddr);
- word1 = readl(phba->CAregaddr);
- word2 = readl(phba->HSregaddr);
- word3 = readl(phba->HCregaddr);
- len += snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x HC:%08x\n",
- word0, word1, word2, word3);
+
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ word0 = readl(phba->HAregaddr);
+ word1 = readl(phba->CAregaddr);
+ word2 = readl(phba->HSregaddr);
+ word3 = readl(phba->HCregaddr);
+ len += snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
+ "HC:%08x\n", word0, word1, word2, word3);
+ }
spin_unlock_irq(&phba->hbalock);
return len;
}
/**
- * lpfc_debugfs_nodelist_data - Dump target node list to a buffer.
+ * lpfc_debugfs_nodelist_data - Dump target node list to a buffer
* @vport: The vport to gather target node info from.
* @buf: The buffer to dump log into.
* @size: The maximum amount of data to process.
@@ -599,7 +605,7 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
#endif
/**
- * lpfc_debugfs_disc_trc - Store discovery trace log.
+ * lpfc_debugfs_disc_trc - Store discovery trace log
* @vport: The vport to associate this trace string with for retrieval.
* @mask: Log entry classification.
* @fmt: Format string to be displayed when dumping the log.
@@ -643,7 +649,7 @@ lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
}
/**
- * lpfc_debugfs_slow_ring_trc - Store slow ring trace log.
+ * lpfc_debugfs_slow_ring_trc - Store slow ring trace log
* @phba: The phba to associate this trace string with for retrieval.
* @fmt: Format string to be displayed when dumping the log.
* @data1: 1st data parameter to be applied to @fmt.
@@ -682,7 +688,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
- * lpfc_debugfs_disc_trc_open - Open the discovery trace log.
+ * lpfc_debugfs_disc_trc_open - Open the discovery trace log
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -732,7 +738,7 @@ out:
}
/**
- * lpfc_debugfs_slow_ring_trc_open - Open the Slow Ring trace log.
+ * lpfc_debugfs_slow_ring_trc_open - Open the Slow Ring trace log
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -782,7 +788,7 @@ out:
}
/**
- * lpfc_debugfs_hbqinfo_open - Open the hbqinfo debugfs buffer.
+ * lpfc_debugfs_hbqinfo_open - Open the hbqinfo debugfs buffer
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -824,7 +830,7 @@ out:
}
/**
- * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer.
+ * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -866,7 +872,7 @@ out:
}
/**
- * lpfc_debugfs_dumpHostSlim_open - Open the Dump Host SLIM debugfs buffer.
+ * lpfc_debugfs_dumpHostSlim_open - Open the Dump Host SLIM debugfs buffer
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -993,7 +999,7 @@ lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
/**
- * lpfc_debugfs_nodelist_open - Open the nodelist debugfs file.
+ * lpfc_debugfs_nodelist_open - Open the nodelist debugfs file
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -1035,7 +1041,7 @@ out:
}
/**
- * lpfc_debugfs_lseek - Seek through a debugfs file.
+ * lpfc_debugfs_lseek - Seek through a debugfs file
* @file: The file pointer to seek through.
* @off: The offset to seek to or the amount to seek by.
* @whence: Indicates how to seek.
@@ -1073,7 +1079,7 @@ lpfc_debugfs_lseek(struct file *file, loff_t off, int whence)
}
/**
- * lpfc_debugfs_read - Read a debugfs file.
+ * lpfc_debugfs_read - Read a debugfs file
* @file: The file pointer to read from.
* @buf: The buffer to copy the data to.
* @nbytes: The number of bytes to read.
@@ -1098,7 +1104,7 @@ lpfc_debugfs_read(struct file *file, char __user *buf,
}
/**
- * lpfc_debugfs_release - Release the buffer used to store debugfs file data.
+ * lpfc_debugfs_release - Release the buffer used to store debugfs file data
* @inode: The inode pointer that contains a vport pointer. (unused)
* @file: The file pointer that contains the buffer to release.
*
@@ -1210,7 +1216,7 @@ static atomic_t lpfc_debugfs_hba_count;
#endif
/**
- * lpfc_debugfs_initialize - Initialize debugfs for a vport.
+ * lpfc_debugfs_initialize - Initialize debugfs for a vport
* @vport: The vport pointer to initialize.
*
* Description:
@@ -1434,7 +1440,7 @@ debug_failed:
}
/**
- * lpfc_debugfs_terminate - Tear down debugfs infrastructure for this vport.
+ * lpfc_debugfs_terminate - Tear down debugfs infrastructure for this vport
* @vport: The vport pointer to remove from debugfs.
*
* Description:
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index f29e548a90d1..1142070e9484 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -99,6 +99,7 @@ struct lpfc_nodelist {
#define NLP_USG_FREE_ACK_BIT 0x8 /* Indicate ndlp memory free invoked */
struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */
+ struct lpfc_hba *phba;
struct fc_rport *rport; /* Corresponding FC transport
port structure */
struct lpfc_vport *vport;
@@ -134,6 +135,7 @@ struct lpfc_nodelist {
#define NLP_NODEV_REMOVE 0x08000000 /* Defer removal till discovery ends */
#define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */
#define NLP_SC_REQ 0x20000000 /* Target requires authentication */
+#define NLP_RPI_VALID 0x80000000 /* nlp_rpi is valid */
/* ndlp usage management macros */
#define NLP_CHK_NODE_ACT(ndlp) (((ndlp)->nlp_usg_map \
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index a7302480bc4a..6bdeb14878a2 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -28,8 +28,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -55,7 +57,7 @@ static void lpfc_register_new_vport(struct lpfc_hba *phba,
static int lpfc_max_els_tries = 3;
/**
- * lpfc_els_chk_latt: Check host link attention event for a vport.
+ * lpfc_els_chk_latt - Check host link attention event for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine checks whether there is an outstanding host link
@@ -84,7 +86,8 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
uint32_t ha_copy;
if (vport->port_state >= LPFC_VPORT_READY ||
- phba->link_state == LPFC_LINK_DOWN)
+ phba->link_state == LPFC_LINK_DOWN ||
+ phba->sli_rev > LPFC_SLI_REV3)
return 0;
/* Read the HBA Host Attention Register */
@@ -116,7 +119,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
}
/**
- * lpfc_prep_els_iocb: Allocate and prepare a lpfc iocb data structure.
+ * lpfc_prep_els_iocb - Allocate and prepare a lpfc iocb data structure
* @vport: pointer to a host virtual N_Port data structure.
* @expectRsp: flag indicating whether response is expected.
* @cmdSize: size of the ELS command.
@@ -219,7 +222,7 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
icmd->un.elsreq64.myID = vport->fc_myDID;
/* For ELS_REQUEST64_CR, use the VPI by default */
- icmd->ulpContext = vport->vpi;
+ icmd->ulpContext = vport->vpi + phba->vpi_base;
icmd->ulpCt_h = 0;
/* The CT field must be 0=INVALID_RPI for the ECHO cmd */
if (elscmd == ELS_CMD_ECHO)
@@ -290,7 +293,7 @@ els_iocb_free_pcmb_exit:
}
/**
- * lpfc_issue_fabric_reglogin: Issue fabric registration login for a vport.
+ * lpfc_issue_fabric_reglogin - Issue fabric registration login for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues a fabric registration login for a @vport. An
@@ -305,7 +308,7 @@ els_iocb_free_pcmb_exit:
* 0 - successfully issued fabric registration login for @vport
* -ENXIO -- failed to issue fabric registration login for @vport
**/
-static int
+int
lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
@@ -345,8 +348,7 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
err = 4;
goto fail;
}
- rc = lpfc_reg_login(phba, vport->vpi, Fabric_DID, (uint8_t *)sp, mbox,
- 0);
+ rc = lpfc_reg_rpi(phba, vport->vpi, Fabric_DID, (uint8_t *)sp, mbox, 0);
if (rc) {
err = 5;
goto fail_free_mbox;
@@ -386,7 +388,76 @@ fail:
}
/**
- * lpfc_cmpl_els_flogi_fabric: Completion function for flogi to a fabric port.
+ * lpfc_issue_reg_vfi - Register VFI for this vport's fabric login
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * This routine issues a REG_VFI mailbox for the vfi, vpi, fcfi triplet for
+ * the @vport. This mailbox command is necessary for FCoE only.
+ *
+ * Return code
+ * 0 - successfully issued REG_VFI for @vport
+ * A failure code otherwise.
+ **/
+static int
+lpfc_issue_reg_vfi(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_nodelist *ndlp;
+ struct serv_parm *sp;
+ struct lpfc_dmabuf *dmabuf;
+ int rc = 0;
+
+ sp = &phba->fc_fabparam;
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+ dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
+ if (!dmabuf->virt) {
+ rc = -ENOMEM;
+ goto fail_free_dmabuf;
+ }
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ rc = -ENOMEM;
+ goto fail_free_coherent;
+ }
+ vport->port_state = LPFC_FABRIC_CFG_LINK;
+ memcpy(dmabuf->virt, &phba->fc_fabparam, sizeof(vport->fc_sparam));
+ lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_reg_vfi;
+ mboxq->vport = vport;
+ mboxq->context1 = dmabuf;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ rc = -ENXIO;
+ goto fail_free_mbox;
+ }
+ return 0;
+
+fail_free_mbox:
+ mempool_free(mboxq, phba->mbox_mem_pool);
+fail_free_coherent:
+ lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+fail_free_dmabuf:
+ kfree(dmabuf);
+fail:
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0289 Issue Register VFI failed: Err %d\n", rc);
+ return rc;
+}
+
+/**
+ * lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @sp: pointer to service parameter data structure.
@@ -497,19 +568,26 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
}
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
-
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
- vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) {
- lpfc_register_new_vport(phba, vport, ndlp);
- return 0;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
+ if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
+ vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+ lpfc_register_new_vport(phba, vport, ndlp);
+ else
+ lpfc_issue_fabric_reglogin(vport);
+ } else {
+ ndlp->nlp_type |= NLP_FABRIC;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+ lpfc_start_fdiscs(phba);
+ lpfc_do_scr_ns_plogi(phba, vport);
+ } else
+ lpfc_issue_reg_vfi(vport);
}
- lpfc_issue_fabric_reglogin(vport);
return 0;
}
-
/**
- * lpfc_cmpl_els_flogi_nport: Completion function for flogi to an N_Port.
+ * lpfc_cmpl_els_flogi_nport - Completion function for flogi to an N_Port
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @sp: pointer to service parameter data structure.
@@ -626,7 +704,7 @@ fail:
}
/**
- * lpfc_cmpl_els_flogi: Completion callback function for flogi.
+ * lpfc_cmpl_els_flogi - Completion callback function for flogi
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -751,7 +829,7 @@ out:
}
/**
- * lpfc_issue_els_flogi: Issue an flogi iocb command for a vport.
+ * lpfc_issue_els_flogi - Issue an flogi iocb command for a vport
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @retry: number of retries to the command IOCB.
@@ -815,9 +893,14 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (sp->cmn.fcphHigh < FC_PH3)
sp->cmn.fcphHigh = FC_PH3;
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
+ elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
+ /* FLOGI needs to be 3 for WQE FCFI */
+ /* Set the fcfi to the fcfi we registered with */
+ elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+ } else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
sp->cmn.request_multiple_Nport = 1;
-
/* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
icmd->ulpCt_h = 1;
icmd->ulpCt_l = 0;
@@ -849,7 +932,7 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/**
- * lpfc_els_abort_flogi: Abort all outstanding flogi iocbs.
+ * lpfc_els_abort_flogi - Abort all outstanding flogi iocbs
* @phba: pointer to lpfc hba data structure.
*
* This routine aborts all the outstanding Fabric Login (FLOGI) IOCBs
@@ -898,7 +981,7 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
}
/**
- * lpfc_initial_flogi: Issue an initial fabric login for a vport.
+ * lpfc_initial_flogi - Issue an initial fabric login for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues an initial Fabric Login (FLOGI) for the @vport
@@ -930,6 +1013,8 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
if (!ndlp)
return 0;
lpfc_nlp_init(vport, ndlp, Fabric_DID);
+ /* Set the node type */
+ ndlp->nlp_type |= NLP_FABRIC;
/* Put ndlp onto node list */
lpfc_enqueue_node(vport, ndlp);
} else if (!NLP_CHK_NODE_ACT(ndlp)) {
@@ -949,7 +1034,7 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
}
/**
- * lpfc_initial_fdisc: Issue an initial fabric discovery for a vport.
+ * lpfc_initial_fdisc - Issue an initial fabric discovery for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues an initial Fabric Discover (FDISC) for the @vport
@@ -998,7 +1083,7 @@ lpfc_initial_fdisc(struct lpfc_vport *vport)
}
/**
- * lpfc_more_plogi: Check and issue remaining plogis for a vport.
+ * lpfc_more_plogi - Check and issue remaining plogis for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine checks whether there are more remaining Port Logins
@@ -1031,7 +1116,7 @@ lpfc_more_plogi(struct lpfc_vport *vport)
}
/**
- * lpfc_plogi_confirm_nport: Confirm pologi wwpn matches stored ndlp.
+ * lpfc_plogi_confirm_nport - Confirm pologi wwpn matches stored ndlp
* @phba: pointer to lpfc hba data structure.
* @prsp: pointer to response IOCB payload.
* @ndlp: pointer to a node-list data structure.
@@ -1165,7 +1250,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
}
/**
- * lpfc_end_rscn: Check and handle more rscn for a vport.
+ * lpfc_end_rscn - Check and handle more rscn for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine checks whether more Registration State Change
@@ -1197,7 +1282,7 @@ lpfc_end_rscn(struct lpfc_vport *vport)
}
/**
- * lpfc_cmpl_els_plogi: Completion callback function for plogi.
+ * lpfc_cmpl_els_plogi - Completion callback function for plogi
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -1322,7 +1407,7 @@ out:
}
/**
- * lpfc_issue_els_plogi: Issue an plogi iocb command for a vport.
+ * lpfc_issue_els_plogi - Issue an plogi iocb command for a vport
* @vport: pointer to a host virtual N_Port data structure.
* @did: destination port identifier.
* @retry: number of retries to the command IOCB.
@@ -1350,14 +1435,12 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
IOCB_t *icmd;
struct lpfc_nodelist *ndlp;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int ret;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
ndlp = lpfc_findnode_did(vport, did);
if (ndlp && !NLP_CHK_NODE_ACT(ndlp))
@@ -1391,7 +1474,7 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
phba->fc_stat.elsXmitPLOGI++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi;
- ret = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (ret == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
@@ -1401,7 +1484,7 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
}
/**
- * lpfc_cmpl_els_prli: Completion callback function for prli.
+ * lpfc_cmpl_els_prli - Completion callback function for prli
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -1472,7 +1555,7 @@ out:
}
/**
- * lpfc_issue_els_prli: Issue a prli iocb command for a vport.
+ * lpfc_issue_els_prli - Issue a prli iocb command for a vport
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @retry: number of retries to the command IOCB.
@@ -1501,14 +1584,9 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
PRLI *npr;
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
- psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
-
cmdsize = (sizeof(uint32_t) + sizeof(PRLI));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
ndlp->nlp_DID, ELS_CMD_PRLI);
@@ -1550,7 +1628,8 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_PRLI_SND;
spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_PRLI_SND;
spin_unlock_irq(shost->host_lock);
@@ -1562,7 +1641,7 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/**
- * lpfc_rscn_disc: Perform rscn discovery for a vport.
+ * lpfc_rscn_disc - Perform rscn discovery for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine performs Registration State Change Notification (RSCN)
@@ -1588,7 +1667,7 @@ lpfc_rscn_disc(struct lpfc_vport *vport)
}
/**
- * lpfc_adisc_done: Complete the adisc phase of discovery.
+ * lpfc_adisc_done - Complete the adisc phase of discovery
* @vport: pointer to lpfc_vport hba data structure that finished all ADISCs.
*
* This function is called when the final ADISC is completed during discovery.
@@ -1608,7 +1687,8 @@ lpfc_adisc_done(struct lpfc_vport *vport)
* and continue discovery.
*/
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
- !(vport->fc_flag & FC_RSCN_MODE)) {
+ !(vport->fc_flag & FC_RSCN_MODE) &&
+ (phba->sli_rev < LPFC_SLI_REV4)) {
lpfc_issue_reg_vpi(phba, vport);
return;
}
@@ -1639,7 +1719,7 @@ lpfc_adisc_done(struct lpfc_vport *vport)
}
/**
- * lpfc_more_adisc: Issue more adisc as needed.
+ * lpfc_more_adisc - Issue more adisc as needed
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine determines whether there are more ndlps on a @vport
@@ -1672,7 +1752,7 @@ lpfc_more_adisc(struct lpfc_vport *vport)
}
/**
- * lpfc_cmpl_els_adisc: Completion callback function for adisc.
+ * lpfc_cmpl_els_adisc - Completion callback function for adisc
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -1760,7 +1840,7 @@ out:
}
/**
- * lpfc_issue_els_adisc: Issue an address discover iocb to an node on a vport.
+ * lpfc_issue_els_adisc - Issue an address discover iocb to an node on a vport
* @vport: pointer to a virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @retry: number of retries to the command IOCB.
@@ -1788,8 +1868,6 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ADISC *ap;
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
uint8_t *pcmd;
uint16_t cmdsize;
@@ -1822,7 +1900,8 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_ADISC_SND;
spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_ADISC_SND;
spin_unlock_irq(shost->host_lock);
@@ -1833,7 +1912,7 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/**
- * lpfc_cmpl_els_logo: Completion callback function for logo.
+ * lpfc_cmpl_els_logo - Completion callback function for logo
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -1910,7 +1989,7 @@ out:
}
/**
- * lpfc_issue_els_logo: Issue a logo to an node on a vport.
+ * lpfc_issue_els_logo - Issue a logo to an node on a vport
* @vport: pointer to a virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @retry: number of retries to the command IOCB.
@@ -1937,15 +2016,10 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
- psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING];
-
spin_lock_irq(shost->host_lock);
if (ndlp->nlp_flag & NLP_LOGO_SND) {
spin_unlock_irq(shost->host_lock);
@@ -1978,7 +2052,7 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_LOGO_SND;
spin_unlock_irq(shost->host_lock);
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
spin_lock_irq(shost->host_lock);
@@ -1991,7 +2065,7 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/**
- * lpfc_cmpl_els_cmd: Completion callback function for generic els command.
+ * lpfc_cmpl_els_cmd - Completion callback function for generic els command
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -2031,7 +2105,7 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_issue_els_scr: Issue a scr to an node on a vport.
+ * lpfc_issue_els_scr - Issue a scr to an node on a vport
* @vport: pointer to a host virtual N_Port data structure.
* @nportid: N_Port identifier to the remote node.
* @retry: number of retries to the command IOCB.
@@ -2058,14 +2132,12 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
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));
ndlp = lpfc_findnode_did(vport, nportid);
@@ -2108,7 +2180,8 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
phba->fc_stat.elsXmitSCR++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
/* The additional lpfc_nlp_put will cause the following
* lpfc_els_free_iocb routine to trigger the rlease of
* the node.
@@ -2125,7 +2198,7 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
}
/**
- * lpfc_issue_els_farpr: Issue a farp to an node on a vport.
+ * lpfc_issue_els_farpr - Issue a farp to an node on a vport
* @vport: pointer to a host virtual N_Port data structure.
* @nportid: N_Port identifier to the remote node.
* @retry: number of retries to the command IOCB.
@@ -2152,7 +2225,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
FARP *fp;
uint8_t *pcmd;
@@ -2162,7 +2234,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
struct lpfc_nodelist *ndlp;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
cmdsize = (sizeof(uint32_t) + sizeof(FARP));
ndlp = lpfc_findnode_did(vport, nportid);
@@ -2219,7 +2290,8 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
phba->fc_stat.elsXmitFARPR++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
/* The additional lpfc_nlp_put will cause the following
* lpfc_els_free_iocb routine to trigger the release of
* the node.
@@ -2236,7 +2308,7 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
}
/**
- * lpfc_cancel_retry_delay_tmo: Cancel the timer with delayed iocb-cmd retry.
+ * lpfc_cancel_retry_delay_tmo - Cancel the timer with delayed iocb-cmd retry
* @vport: pointer to a host virtual N_Port data structure.
* @nlp: pointer to a node-list data structure.
*
@@ -2291,7 +2363,7 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
}
/**
- * lpfc_els_retry_delay: Timer function with a ndlp delayed function timer.
+ * lpfc_els_retry_delay - Timer function with a ndlp delayed function timer
* @ptr: holder for the pointer to the timer function associated data (ndlp).
*
* This routine is invoked by the ndlp delayed-function timer to check
@@ -2333,7 +2405,7 @@ lpfc_els_retry_delay(unsigned long ptr)
}
/**
- * lpfc_els_retry_delay_handler: Work thread handler for ndlp delayed function.
+ * lpfc_els_retry_delay_handler - Work thread handler for ndlp delayed function
* @ndlp: pointer to a node-list data structure.
*
* This routine is the worker-thread handler for processing the @ndlp delayed
@@ -2404,7 +2476,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
}
/**
- * lpfc_els_retry: Make retry decision on an els command iocb.
+ * lpfc_els_retry - Make retry decision on an els command iocb
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -2732,7 +2804,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_free_data: Free lpfc dma buffer and data structure with an iocb.
+ * lpfc_els_free_data - Free lpfc dma buffer and data structure with an iocb
* @phba: pointer to lpfc hba data structure.
* @buf_ptr1: pointer to the lpfc DMA buffer data structure.
*
@@ -2764,7 +2836,7 @@ lpfc_els_free_data(struct lpfc_hba *phba, struct lpfc_dmabuf *buf_ptr1)
}
/**
- * lpfc_els_free_bpl: Free lpfc dma buffer and data structure with bpl.
+ * lpfc_els_free_bpl - Free lpfc dma buffer and data structure with bpl
* @phba: pointer to lpfc hba data structure.
* @buf_ptr: pointer to the lpfc dma buffer data structure.
*
@@ -2784,7 +2856,7 @@ lpfc_els_free_bpl(struct lpfc_hba *phba, struct lpfc_dmabuf *buf_ptr)
}
/**
- * lpfc_els_free_iocb: Free a command iocb and its associated resources.
+ * lpfc_els_free_iocb - Free a command iocb and its associated resources
* @phba: pointer to lpfc hba data structure.
* @elsiocb: pointer to lpfc els command iocb data structure.
*
@@ -2877,7 +2949,7 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
}
/**
- * lpfc_cmpl_els_logo_acc: Completion callback function to logo acc response.
+ * lpfc_cmpl_els_logo_acc - Completion callback function to logo acc response
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -2931,7 +3003,7 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_mbx_cmpl_dflt_rpi: Completion callbk func for unreg dflt rpi mbox cmd.
+ * lpfc_mbx_cmpl_dflt_rpi - Completion callbk func for unreg dflt rpi mbox cmd
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -2949,6 +3021,14 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
+ /*
+ * This routine is used to register and unregister in previous SLI
+ * modes.
+ */
+ if ((pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) &&
+ (phba->sli_rev == LPFC_SLI_REV4))
+ lpfc_sli4_free_rpi(phba, pmb->u.mb.un.varUnregLogin.rpi);
+
pmb->context1 = NULL;
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
@@ -2961,11 +3041,12 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
*/
lpfc_nlp_not_used(ndlp);
}
+
return;
}
/**
- * lpfc_cmpl_els_rsp: Completion callback function for els response iocb cmd.
+ * lpfc_cmpl_els_rsp - Completion callback function for els response iocb cmd
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -3136,7 +3217,7 @@ out:
}
/**
- * lpfc_els_rsp_acc: Prepare and issue an acc response iocb command.
+ * lpfc_els_rsp_acc - Prepare and issue an acc response iocb command
* @vport: pointer to a host virtual N_Port data structure.
* @flag: the els command code to be accepted.
* @oldiocb: pointer to the original lpfc command iocb data structure.
@@ -3170,7 +3251,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
IOCB_t *icmd;
IOCB_t *oldcmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
@@ -3178,7 +3258,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
ELS_PKT *els_pkt_ptr;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
oldcmd = &oldiocb->iocb;
switch (flag) {
@@ -3266,7 +3345,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
}
phba->fc_stat.elsXmitACC++;
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
@@ -3275,7 +3354,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
}
/**
- * lpfc_els_rsp_reject: Propare and issue a rjt response iocb command.
+ * lpfc_els_rsp_reject - Propare and issue a rjt response iocb command
* @vport: pointer to a virtual N_Port data structure.
* @rejectError:
* @oldiocb: pointer to the original lpfc command iocb data structure.
@@ -3305,15 +3384,12 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
IOCB_t *icmd;
IOCB_t *oldcmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
-
cmdsize = 2 * sizeof(uint32_t);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
ndlp->nlp_DID, ELS_CMD_LS_RJT);
@@ -3346,7 +3422,7 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
phba->fc_stat.elsXmitLSRJT++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
@@ -3356,7 +3432,7 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
}
/**
- * lpfc_els_rsp_adisc_acc: Prepare and issue acc response to adisc iocb cmd.
+ * lpfc_els_rsp_adisc_acc - Prepare and issue acc response to adisc iocb cmd
* @vport: pointer to a virtual N_Port data structure.
* @oldiocb: pointer to the original lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -3379,8 +3455,6 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
ADISC *ap;
IOCB_t *icmd, *oldcmd;
struct lpfc_iocbq *elsiocb;
@@ -3422,7 +3496,7 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
phba->fc_stat.elsXmitACC++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
@@ -3431,7 +3505,7 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
}
/**
- * lpfc_els_rsp_prli_acc: Prepare and issue acc response to prli iocb cmd.
+ * lpfc_els_rsp_prli_acc - Prepare and issue acc response to prli iocb cmd
* @vport: pointer to a virtual N_Port data structure.
* @oldiocb: pointer to the original lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -3459,14 +3533,12 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
IOCB_t *icmd;
IOCB_t *oldcmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */
cmdsize = sizeof(uint32_t) + sizeof(PRLI);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
@@ -3520,7 +3592,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
phba->fc_stat.elsXmitACC++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
@@ -3529,7 +3601,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
}
/**
- * lpfc_els_rsp_rnid_acc: Issue rnid acc response iocb command.
+ * lpfc_els_rsp_rnid_acc - Issue rnid acc response iocb command
* @vport: pointer to a virtual N_Port data structure.
* @format: rnid command format.
* @oldiocb: pointer to the original lpfc command iocb data structure.
@@ -3562,15 +3634,12 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
RNID *rn;
IOCB_t *icmd, *oldcmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING];
-
cmdsize = sizeof(uint32_t) + sizeof(uint32_t)
+ (2 * sizeof(struct lpfc_name));
if (format)
@@ -3626,7 +3695,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
elsiocb->context1 = NULL; /* Don't need ndlp for cmpl,
* it could be freed */
- rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
@@ -3635,7 +3704,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
}
/**
- * lpfc_els_disc_adisc: Issue remaining adisc iocbs to npr nodes of a vport.
+ * lpfc_els_disc_adisc - Issue remaining adisc iocbs to npr nodes of a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues Address Discover (ADISC) ELS commands to those
@@ -3693,7 +3762,7 @@ lpfc_els_disc_adisc(struct lpfc_vport *vport)
}
/**
- * lpfc_els_disc_plogi: Issue plogi for all npr nodes of a vport before adisc.
+ * lpfc_els_disc_plogi - Issue plogi for all npr nodes of a vport before adisc
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues Port Login (PLOGI) ELS commands to all the N_Ports
@@ -3752,7 +3821,7 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
}
/**
- * lpfc_els_flush_rscn: Clean up any rscn activities with a vport.
+ * lpfc_els_flush_rscn - Clean up any rscn activities with a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine cleans up any Registration State Change Notification
@@ -3791,7 +3860,7 @@ lpfc_els_flush_rscn(struct lpfc_vport *vport)
}
/**
- * lpfc_rscn_payload_check: Check whether there is a pending rscn to a did.
+ * lpfc_rscn_payload_check - Check whether there is a pending rscn to a did
* @vport: pointer to a host virtual N_Port data structure.
* @did: remote destination port identifier.
*
@@ -3839,7 +3908,9 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
payload_len -= sizeof(uint32_t);
switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
case RSCN_ADDRESS_FORMAT_PORT:
- if (ns_did.un.word == rscn_did.un.word)
+ if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+ && (ns_did.un.b.area == rscn_did.un.b.area)
+ && (ns_did.un.b.id == rscn_did.un.b.id))
goto return_did_out;
break;
case RSCN_ADDRESS_FORMAT_AREA:
@@ -3866,7 +3937,7 @@ return_did_out:
}
/**
- * lpfc_rscn_recovery_check: Send recovery event to vport nodes matching rscn
+ * lpfc_rscn_recovery_check - Send recovery event to vport nodes matching rscn
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine sends recovery (NLP_EVT_DEVICE_RECOVERY) event to the
@@ -3895,7 +3966,7 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
}
/**
- * lpfc_send_rscn_event: Send an RSCN event to management application.
+ * lpfc_send_rscn_event - Send an RSCN event to management application
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
*
@@ -3938,7 +4009,7 @@ lpfc_send_rscn_event(struct lpfc_vport *vport,
}
/**
- * lpfc_els_rcv_rscn: Process an unsolicited rscn iocb.
+ * lpfc_els_rcv_rscn - Process an unsolicited rscn iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4134,7 +4205,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_handle_rscn: Handle rscn for a vport.
+ * lpfc_els_handle_rscn - Handle rscn for a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine handles the Registration State Configuration Notification
@@ -4222,7 +4293,7 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport)
}
/**
- * lpfc_els_rcv_flogi: Process an unsolicited flogi iocb.
+ * lpfc_els_rcv_flogi - Process an unsolicited flogi iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4300,7 +4371,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
lpfc_init_link(phba, mbox,
phba->cfg_topology,
phba->cfg_link_speed);
- mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+ mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->vport = vport;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
@@ -4336,7 +4407,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rcv_rnid: Process an unsolicited rnid iocb.
+ * lpfc_els_rcv_rnid - Process an unsolicited rnid iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4391,7 +4462,7 @@ lpfc_els_rcv_rnid(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rcv_lirr: Process an unsolicited lirr iocb.
+ * lpfc_els_rcv_lirr - Process an unsolicited lirr iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4419,7 +4490,7 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rsp_rps_acc: Completion callbk func for MBX_READ_LNK_STAT mbox cmd.
+ * lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -4440,8 +4511,6 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
static void
lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
MAILBOX_t *mb;
IOCB_t *icmd;
RPS_RSP *rps_rsp;
@@ -4451,7 +4520,7 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
uint16_t xri, status;
uint32_t cmdsize;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
ndlp = (struct lpfc_nodelist *) pmb->context2;
xri = (uint16_t) ((unsigned long)(pmb->context1));
@@ -4507,13 +4576,13 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
ndlp->nlp_rpi);
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR)
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
lpfc_els_free_iocb(phba, elsiocb);
return;
}
/**
- * lpfc_els_rcv_rps: Process an unsolicited rps iocb.
+ * lpfc_els_rcv_rps - Process an unsolicited rps iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4590,7 +4659,7 @@ reject_out:
}
/**
- * lpfc_els_rsp_rpl_acc: Issue an accept rpl els command.
+ * lpfc_els_rsp_rpl_acc - Issue an accept rpl els command
* @vport: pointer to a host virtual N_Port data structure.
* @cmdsize: size of the ELS command.
* @oldiocb: pointer to the original lpfc command iocb data structure.
@@ -4616,8 +4685,6 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
IOCB_t *icmd, *oldcmd;
RPL_RSP rpl_rsp;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
uint8_t *pcmd;
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
@@ -4654,7 +4721,8 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
ndlp->nlp_rpi);
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
}
@@ -4662,7 +4730,7 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
}
/**
- * lpfc_els_rcv_rpl: Process an unsolicited rpl iocb.
+ * lpfc_els_rcv_rpl - Process an unsolicited rpl iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4721,7 +4789,7 @@ lpfc_els_rcv_rpl(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rcv_farp: Process an unsolicited farp request els command.
+ * lpfc_els_rcv_farp - Process an unsolicited farp request els command
* @vport: pointer to a virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4804,7 +4872,7 @@ lpfc_els_rcv_farp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rcv_farpr: Process an unsolicited farp response iocb.
+ * lpfc_els_rcv_farpr - Process an unsolicited farp response iocb
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
@@ -4842,7 +4910,7 @@ lpfc_els_rcv_farpr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_els_rcv_fan: Process an unsolicited fan iocb command.
+ * lpfc_els_rcv_fan - Process an unsolicited fan iocb command
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @fan_ndlp: pointer to a node-list data structure.
@@ -4883,14 +4951,17 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
} else {
/* FAN verified - skip FLOGI */
vport->fc_myDID = vport->fc_prevDID;
- lpfc_issue_fabric_reglogin(vport);
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ lpfc_issue_fabric_reglogin(vport);
+ else
+ lpfc_issue_reg_vfi(vport);
}
}
return 0;
}
/**
- * lpfc_els_timeout: Handler funciton to the els timer.
+ * lpfc_els_timeout - Handler funciton to the els timer
* @ptr: holder for the timer function associated data.
*
* This routine is invoked by the ELS timer after timeout. It posts the ELS
@@ -4919,7 +4990,7 @@ lpfc_els_timeout(unsigned long ptr)
}
/**
- * lpfc_els_timeout_handler: Process an els timeout event.
+ * lpfc_els_timeout_handler - Process an els timeout event
* @vport: pointer to a virtual N_Port data structure.
*
* This routine is the actual handler function that processes an ELS timeout
@@ -4994,7 +5065,7 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
}
/**
- * lpfc_els_flush_cmd: Clean up the outstanding els commands to a vport.
+ * lpfc_els_flush_cmd - Clean up the outstanding els commands to a vport
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine is used to clean up all the outstanding ELS commands on a
@@ -5058,25 +5129,15 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport)
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- piocb = list_get_first(&completions, struct lpfc_iocbq, list);
- cmd = &piocb->iocb;
- list_del_init(&piocb->list);
-
- if (!piocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, piocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
- }
- }
+ /* Cancell all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
return;
}
/**
- * lpfc_els_flush_all_cmd: Clean up all the outstanding els commands to a HBA.
+ * lpfc_els_flush_all_cmd - Clean up all the outstanding els commands to a HBA
* @phba: pointer to lpfc hba data structure.
*
* This routine is used to clean up all the outstanding ELS commands on a
@@ -5121,23 +5182,16 @@ lpfc_els_flush_all_cmd(struct lpfc_hba *phba)
lpfc_sli_issue_abort_iotag(phba, pring, piocb);
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- piocb = list_get_first(&completions, struct lpfc_iocbq, list);
- cmd = &piocb->iocb;
- list_del_init(&piocb->list);
- if (!piocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, piocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
- }
- }
+
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
+
return;
}
/**
- * lpfc_send_els_failure_event: Posts an ELS command failure event.
+ * lpfc_send_els_failure_event - Posts an ELS command failure event
* @phba: Pointer to hba context object.
* @cmdiocbp: Pointer to command iocb which reported error.
* @rspiocbp: Pointer to response iocb which reported error.
@@ -5204,7 +5258,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
}
/**
- * lpfc_send_els_event: Posts unsolicited els event.
+ * lpfc_send_els_event - Posts unsolicited els event
* @vport: Pointer to vport object.
* @ndlp: Pointer FC node object.
* @cmd: ELS command code.
@@ -5284,7 +5338,7 @@ lpfc_send_els_event(struct lpfc_vport *vport,
/**
- * lpfc_els_unsol_buffer: Process an unsolicited event data buffer.
+ * lpfc_els_unsol_buffer - Process an unsolicited event data buffer
* @phba: pointer to lpfc hba data structure.
* @pring: pointer to a SLI ring.
* @vport: pointer to a host virtual N_Port data structure.
@@ -5583,16 +5637,15 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
dropit:
if (vport && !(vport->load_flag & FC_UNLOADING))
- lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
- "(%d):0111 Dropping received ELS cmd "
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0111 Dropping received ELS cmd "
"Data: x%x x%x x%x\n",
- vport->vpi, icmd->ulpStatus,
- icmd->un.ulpWord[4], icmd->ulpTimeout);
+ icmd->ulpStatus, icmd->un.ulpWord[4], icmd->ulpTimeout);
phba->fc_stat.elsRcvDrop++;
}
/**
- * lpfc_find_vport_by_vpid: Find a vport on a HBA through vport identifier.
+ * lpfc_find_vport_by_vpid - Find a vport on a HBA through vport identifier
* @phba: pointer to lpfc hba data structure.
* @vpi: host virtual N_Port identifier.
*
@@ -5622,7 +5675,7 @@ lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
}
/**
- * lpfc_els_unsol_event: Process an unsolicited event from an els sli ring.
+ * lpfc_els_unsol_event - Process an unsolicited event from an els sli ring
* @phba: pointer to lpfc hba data structure.
* @pring: pointer to a SLI ring.
* @elsiocb: pointer to lpfc els iocb data structure.
@@ -5663,10 +5716,9 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
icmd->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
if (icmd->unsli3.rcvsli3.vpi == 0xffff)
vport = phba->pport;
- else {
- uint16_t vpi = icmd->unsli3.rcvsli3.vpi;
- vport = lpfc_find_vport_by_vpid(phba, vpi);
- }
+ else
+ vport = lpfc_find_vport_by_vpid(phba,
+ icmd->unsli3.rcvsli3.vpi - phba->vpi_base);
}
/* If there are no BDEs associated
* with this IOCB, there is nothing to do.
@@ -5710,7 +5762,7 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_do_scr_ns_plogi: Issue a plogi to the name server for scr.
+ * lpfc_do_scr_ns_plogi - Issue a plogi to the name server for scr
* @phba: pointer to lpfc hba data structure.
* @vport: pointer to a virtual N_Port data structure.
*
@@ -5781,7 +5833,7 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
}
/**
- * lpfc_cmpl_reg_new_vport: Completion callback function to register new vport.
+ * lpfc_cmpl_reg_new_vport - Completion callback function to register new vport
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -5798,7 +5850,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct lpfc_vport *vport = pmb->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
@@ -5835,7 +5887,10 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
} else {
if (vport == phba->pport)
- lpfc_issue_fabric_reglogin(vport);
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ lpfc_issue_fabric_reglogin(vport);
+ else
+ lpfc_issue_reg_vfi(vport);
else
lpfc_do_scr_ns_plogi(phba, vport);
}
@@ -5850,7 +5905,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
/**
- * lpfc_register_new_vport: Register a new vport with a HBA.
+ * lpfc_register_new_vport - Register a new vport with a HBA
* @phba: pointer to lpfc hba data structure.
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
@@ -5867,7 +5922,7 @@ lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
- lpfc_reg_vpi(phba, vport->vpi, vport->fc_myDID, mbox);
+ lpfc_reg_vpi(vport, mbox);
mbox->vport = vport;
mbox->context2 = lpfc_nlp_get(ndlp);
mbox->mbox_cmpl = lpfc_cmpl_reg_new_vport;
@@ -5899,7 +5954,7 @@ mbox_err_exit:
}
/**
- * lpfc_cmpl_els_fdisc: Completion function for fdisc iocb command.
+ * lpfc_cmpl_els_fdisc - Completion function for fdisc iocb command
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -6007,7 +6062,7 @@ out:
}
/**
- * lpfc_issue_els_fdisc: Issue a fdisc iocb command.
+ * lpfc_issue_els_fdisc - Issue a fdisc iocb command
* @vport: pointer to a virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @retry: number of retries to the command IOCB.
@@ -6101,7 +6156,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/**
- * lpfc_cmpl_els_npiv_logo: Completion function with vport logo.
+ * lpfc_cmpl_els_npiv_logo - Completion function with vport logo
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -6136,7 +6191,7 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_issue_els_npiv_logo: Issue a logo off a vport.
+ * lpfc_issue_els_npiv_logo - Issue a logo off a vport
* @vport: pointer to a virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
*
@@ -6156,7 +6211,6 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
@@ -6186,7 +6240,8 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_LOGO_SND;
spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+ IOCB_ERROR) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_LOGO_SND;
spin_unlock_irq(shost->host_lock);
@@ -6197,7 +6252,7 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
}
/**
- * lpfc_fabric_block_timeout: Handler function to the fabric block timer.
+ * lpfc_fabric_block_timeout - Handler function to the fabric block timer
* @ptr: holder for the timer function associated data.
*
* This routine is invoked by the fabric iocb block timer after
@@ -6226,7 +6281,7 @@ lpfc_fabric_block_timeout(unsigned long ptr)
}
/**
- * lpfc_resume_fabric_iocbs: Issue a fabric iocb from driver internal list.
+ * lpfc_resume_fabric_iocbs - Issue a fabric iocb from driver internal list
* @phba: pointer to lpfc hba data structure.
*
* This routine issues one fabric iocb from the driver internal list to
@@ -6241,7 +6296,6 @@ lpfc_resume_fabric_iocbs(struct lpfc_hba *phba)
struct lpfc_iocbq *iocb;
unsigned long iflags;
int ret;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
IOCB_t *cmd;
repeat:
@@ -6265,7 +6319,7 @@ repeat:
"Fabric sched1: ste:x%x",
iocb->vport->port_state, 0, 0);
- ret = lpfc_sli_issue_iocb(phba, pring, iocb, 0);
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
if (ret == IOCB_ERROR) {
iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
@@ -6285,7 +6339,7 @@ repeat:
}
/**
- * lpfc_unblock_fabric_iocbs: Unblock issuing fabric iocb command.
+ * lpfc_unblock_fabric_iocbs - Unblock issuing fabric iocb command
* @phba: pointer to lpfc hba data structure.
*
* This routine unblocks the issuing fabric iocb command. The function
@@ -6303,7 +6357,7 @@ lpfc_unblock_fabric_iocbs(struct lpfc_hba *phba)
}
/**
- * lpfc_block_fabric_iocbs: Block issuing fabric iocb command.
+ * lpfc_block_fabric_iocbs - Block issuing fabric iocb command
* @phba: pointer to lpfc hba data structure.
*
* This routine blocks the issuing fabric iocb for a specified amount of
@@ -6325,7 +6379,7 @@ lpfc_block_fabric_iocbs(struct lpfc_hba *phba)
}
/**
- * lpfc_cmpl_fabric_iocb: Completion callback function for fabric iocb.
+ * lpfc_cmpl_fabric_iocb - Completion callback function for fabric iocb
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
* @rspiocb: pointer to lpfc response iocb data structure.
@@ -6384,7 +6438,7 @@ lpfc_cmpl_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_issue_fabric_iocb: Issue a fabric iocb command.
+ * lpfc_issue_fabric_iocb - Issue a fabric iocb command
* @phba: pointer to lpfc hba data structure.
* @iocb: pointer to lpfc command iocb data structure.
*
@@ -6411,7 +6465,6 @@ static int
lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
{
unsigned long iflags;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
int ready;
int ret;
@@ -6435,7 +6488,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
"Fabric sched2: ste:x%x",
iocb->vport->port_state, 0, 0);
- ret = lpfc_sli_issue_iocb(phba, pring, iocb, 0);
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
if (ret == IOCB_ERROR) {
iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
@@ -6453,7 +6506,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
}
/**
- * lpfc_fabric_abort_vport: Abort a vport's iocbs from driver fabric iocb list.
+ * lpfc_fabric_abort_vport - Abort a vport's iocbs from driver fabric iocb list
* @vport: pointer to a virtual N_Port data structure.
*
* This routine aborts all the IOCBs associated with a @vport from the
@@ -6468,7 +6521,6 @@ static void lpfc_fabric_abort_vport(struct lpfc_vport *vport)
LIST_HEAD(completions);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *tmp_iocb, *piocb;
- IOCB_t *cmd;
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(piocb, tmp_iocb, &phba->fabric_iocb_list,
@@ -6481,19 +6533,13 @@ static void lpfc_fabric_abort_vport(struct lpfc_vport *vport)
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- piocb = list_get_first(&completions, struct lpfc_iocbq, list);
- list_del_init(&piocb->list);
-
- cmd = &piocb->iocb;
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
}
/**
- * lpfc_fabric_abort_nport: Abort a ndlp's iocbs from driver fabric iocb list.
+ * lpfc_fabric_abort_nport - Abort a ndlp's iocbs from driver fabric iocb list
* @ndlp: pointer to a node-list data structure.
*
* This routine aborts all the IOCBs associated with an @ndlp from the
@@ -6506,10 +6552,9 @@ static void lpfc_fabric_abort_vport(struct lpfc_vport *vport)
void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
{
LIST_HEAD(completions);
- struct lpfc_hba *phba = ndlp->vport->phba;
+ struct lpfc_hba *phba = ndlp->phba;
struct lpfc_iocbq *tmp_iocb, *piocb;
struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- IOCB_t *cmd;
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(piocb, tmp_iocb, &phba->fabric_iocb_list,
@@ -6521,19 +6566,13 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- piocb = list_get_first(&completions, struct lpfc_iocbq, list);
- list_del_init(&piocb->list);
-
- cmd = &piocb->iocb;
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
}
/**
- * lpfc_fabric_abort_hba: Abort all iocbs on driver fabric iocb list.
+ * lpfc_fabric_abort_hba - Abort all iocbs on driver fabric iocb list
* @phba: pointer to lpfc hba data structure.
*
* This routine aborts all the IOCBs currently on the driver internal
@@ -6546,20 +6585,47 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
{
LIST_HEAD(completions);
- struct lpfc_iocbq *piocb;
- IOCB_t *cmd;
spin_lock_irq(&phba->hbalock);
list_splice_init(&phba->fabric_iocb_list, &completions);
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- piocb = list_get_first(&completions, struct lpfc_iocbq, list);
- list_del_init(&piocb->list);
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
+}
- cmd = &piocb->iocb;
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
+/**
+ * lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the els xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 slow-path
+ * ELS aborted xri.
+ **/
+void
+lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri)
+{
+ uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+ unsigned long iflag = 0;
+
+ spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, iflag);
+ list_for_each_entry_safe(sglq_entry, sglq_next,
+ &phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
+ if (sglq_entry->sli4_xritag == xri) {
+ list_del(&sglq_entry->list);
+ spin_unlock_irqrestore(
+ &phba->sli4_hba.abts_sgl_list_lock,
+ iflag);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+
+ list_add_tail(&sglq_entry->list,
+ &phba->sli4_hba.lpfc_sgl_list);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return;
+ }
}
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_sgl_list_lock, iflag);
}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 311ed6dea726..35c41ae75be2 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -29,10 +29,12 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_scsi.h"
#include "lpfc.h"
#include "lpfc_logmsg.h"
@@ -78,7 +80,7 @@ lpfc_terminate_rport_io(struct fc_rport *rport)
return;
}
- phba = ndlp->vport->phba;
+ phba = ndlp->phba;
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,
"rport terminate: sid:x%x did:x%x flg:x%x",
@@ -273,10 +275,12 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
!(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
(ndlp->nlp_state != NLP_STE_UNMAPPED_NODE))
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
+
+ lpfc_unregister_unused_fcf(phba);
}
/**
- * lpfc_alloc_fast_evt: Allocates data structure for posting event.
+ * lpfc_alloc_fast_evt - Allocates data structure for posting event
* @phba: Pointer to hba context object.
*
* This function is called from the functions which need to post
@@ -295,15 +299,16 @@ lpfc_alloc_fast_evt(struct lpfc_hba *phba) {
ret = kzalloc(sizeof(struct lpfc_fast_path_event),
GFP_ATOMIC);
- if (ret)
+ if (ret) {
atomic_inc(&phba->fast_event_count);
- INIT_LIST_HEAD(&ret->work_evt.evt_listp);
- ret->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
+ INIT_LIST_HEAD(&ret->work_evt.evt_listp);
+ ret->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
+ }
return ret;
}
/**
- * lpfc_free_fast_evt: Frees event data structure.
+ * lpfc_free_fast_evt - Frees event data structure
* @phba: Pointer to hba context object.
* @evt: Event object which need to be freed.
*
@@ -319,7 +324,7 @@ lpfc_free_fast_evt(struct lpfc_hba *phba,
}
/**
- * lpfc_send_fastpath_evt: Posts events generated from fast path.
+ * lpfc_send_fastpath_evt - Posts events generated from fast path
* @phba: Pointer to hba context object.
* @evtp: Event data structure.
*
@@ -491,6 +496,10 @@ lpfc_work_done(struct lpfc_hba *phba)
phba->work_ha = 0;
spin_unlock_irq(&phba->hbalock);
+ /* First, try to post the next mailbox command to SLI4 device */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ lpfc_sli4_post_async_mbox(phba);
+
if (ha_copy & HA_ERATT)
/* Handle the error attention event */
lpfc_handle_eratt(phba);
@@ -501,9 +510,27 @@ lpfc_work_done(struct lpfc_hba *phba)
if (ha_copy & HA_LATT)
lpfc_handle_latt(phba);
+ /* Process SLI4 events */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
+ if (phba->hba_flag & FCP_XRI_ABORT_EVENT)
+ lpfc_sli4_fcp_xri_abort_event_proc(phba);
+ if (phba->hba_flag & ELS_XRI_ABORT_EVENT)
+ lpfc_sli4_els_xri_abort_event_proc(phba);
+ if (phba->hba_flag & ASYNC_EVENT)
+ lpfc_sli4_async_event_proc(phba);
+ if (phba->hba_flag & HBA_POST_RECEIVE_BUFFER) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~HBA_POST_RECEIVE_BUFFER;
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
+ }
+ if (phba->hba_flag & HBA_RECEIVE_BUFFER)
+ lpfc_sli4_handle_received_buffer(phba);
+ }
+
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi; i++) {
+ for (i = 0; i <= phba->max_vports; i++) {
/*
* We could have no vports in array if unloading, so if
* this happens then just use the pport
@@ -555,23 +582,24 @@ lpfc_work_done(struct lpfc_hba *phba)
/*
* Turn on Ring interrupts
*/
- spin_lock_irq(&phba->hbalock);
- control = readl(phba->HCregaddr);
- if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {
- lpfc_debugfs_slow_ring_trc(phba,
- "WRK Enable ring: cntl:x%x hacopy:x%x",
- control, ha_copy, 0);
-
- control |= (HC_R0INT_ENA << LPFC_ELS_RING);
- writel(control, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
- }
- else {
- lpfc_debugfs_slow_ring_trc(phba,
- "WRK Ring ok: cntl:x%x hacopy:x%x",
- control, ha_copy, 0);
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ spin_lock_irq(&phba->hbalock);
+ control = readl(phba->HCregaddr);
+ if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {
+ lpfc_debugfs_slow_ring_trc(phba,
+ "WRK Enable ring: cntl:x%x hacopy:x%x",
+ control, ha_copy, 0);
+
+ control |= (HC_R0INT_ENA << LPFC_ELS_RING);
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ } else {
+ lpfc_debugfs_slow_ring_trc(phba,
+ "WRK Ring ok: cntl:x%x hacopy:x%x",
+ control, ha_copy, 0);
+ }
+ spin_unlock_irq(&phba->hbalock);
}
- spin_unlock_irq(&phba->hbalock);
}
lpfc_work_list_done(phba);
}
@@ -689,7 +717,7 @@ lpfc_port_link_failure(struct lpfc_vport *vport)
lpfc_can_disctmo(vport);
}
-static void
+void
lpfc_linkdown_port(struct lpfc_vport *vport)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
@@ -716,6 +744,7 @@ lpfc_linkdown(struct lpfc_hba *phba)
if (phba->link_state == LPFC_LINK_DOWN)
return 0;
spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_DISCOVERED);
if (phba->link_state > LPFC_LINK_DOWN) {
phba->link_state = LPFC_LINK_DOWN;
phba->pport->fc_flag &= ~FC_LBIT;
@@ -723,7 +752,7 @@ lpfc_linkdown(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
/* Issue a LINK DOWN event to all nodes */
lpfc_linkdown_port(vports[i]);
}
@@ -833,10 +862,11 @@ lpfc_linkup(struct lpfc_hba *phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
lpfc_linkup_port(vports[i]);
lpfc_destroy_vport_work_array(phba, vports);
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
+ (phba->sli_rev < LPFC_SLI_REV4))
lpfc_issue_clear_la(phba, phba->pport);
return 0;
@@ -854,7 +884,7 @@ lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct lpfc_vport *vport = pmb->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_sli *psli = &phba->sli;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
uint32_t control;
/* Since we don't do discovery right now, turn these off here */
@@ -917,7 +947,7 @@ lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- if (pmb->mb.mbxStatus)
+ if (pmb->u.mb.mbxStatus)
goto out;
mempool_free(pmb, phba->mbox_mem_pool);
@@ -945,7 +975,7 @@ out:
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
"0306 CONFIG_LINK mbxStatus error x%x "
"HBA state x%x\n",
- pmb->mb.mbxStatus, vport->port_state);
+ pmb->u.mb.mbxStatus, vport->port_state);
mempool_free(pmb, phba->mbox_mem_pool);
lpfc_linkdown(phba);
@@ -959,9 +989,592 @@ out:
}
static void
+lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport = mboxq->vport;
+ unsigned long flags;
+
+ if (mboxq->u.mb.mbxStatus) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ "2017 REG_FCFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+ }
+
+ /* Start FCoE discovery by sending a FLOGI. */
+ phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi, &mboxq->u.mqe.un.reg_fcfi);
+ /* Set the FCFI registered flag */
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->fcf.fcf_flag |= FCF_REGISTERED;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (vport->port_state != LPFC_FLOGI) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ lpfc_initial_flogi(vport);
+ }
+
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
+ * lpfc_fab_name_match - Check if the fcf fabric name match.
+ * @fab_name: pointer to fabric name.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine compare the fcf record's fabric name with provided
+ * fabric name. If the fabric name are identical this function
+ * returns 1 else return 0.
+ **/
+static uint32_t
+lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
+{
+ if ((fab_name[0] ==
+ bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record)) &&
+ (fab_name[1] ==
+ bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record)) &&
+ (fab_name[2] ==
+ bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record)) &&
+ (fab_name[3] ==
+ bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record)) &&
+ (fab_name[4] ==
+ bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record)) &&
+ (fab_name[5] ==
+ bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record)) &&
+ (fab_name[6] ==
+ bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record)) &&
+ (fab_name[7] ==
+ bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * lpfc_mac_addr_match - Check if the fcf mac address match.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine compare the fcf record's mac address with HBA's
+ * FCF mac address. If the mac addresses are identical this function
+ * returns 1 else return 0.
+ **/
+static uint32_t
+lpfc_mac_addr_match(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+{
+ if ((phba->fcf.mac_addr[0] ==
+ bf_get(lpfc_fcf_record_mac_0, new_fcf_record)) &&
+ (phba->fcf.mac_addr[1] ==
+ bf_get(lpfc_fcf_record_mac_1, new_fcf_record)) &&
+ (phba->fcf.mac_addr[2] ==
+ bf_get(lpfc_fcf_record_mac_2, new_fcf_record)) &&
+ (phba->fcf.mac_addr[3] ==
+ bf_get(lpfc_fcf_record_mac_3, new_fcf_record)) &&
+ (phba->fcf.mac_addr[4] ==
+ bf_get(lpfc_fcf_record_mac_4, new_fcf_record)) &&
+ (phba->fcf.mac_addr[5] ==
+ bf_get(lpfc_fcf_record_mac_5, new_fcf_record)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * lpfc_copy_fcf_record - Copy fcf information to lpfc_hba.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine copies the FCF information from the FCF
+ * record to lpfc_hba data structure.
+ **/
+static void
+lpfc_copy_fcf_record(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+{
+ phba->fcf.fabric_name[0] =
+ bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record);
+ phba->fcf.fabric_name[1] =
+ bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record);
+ phba->fcf.fabric_name[2] =
+ bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record);
+ phba->fcf.fabric_name[3] =
+ bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record);
+ phba->fcf.fabric_name[4] =
+ bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record);
+ phba->fcf.fabric_name[5] =
+ bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record);
+ phba->fcf.fabric_name[6] =
+ bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record);
+ phba->fcf.fabric_name[7] =
+ bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record);
+ phba->fcf.mac_addr[0] =
+ bf_get(lpfc_fcf_record_mac_0, new_fcf_record);
+ phba->fcf.mac_addr[1] =
+ bf_get(lpfc_fcf_record_mac_1, new_fcf_record);
+ phba->fcf.mac_addr[2] =
+ bf_get(lpfc_fcf_record_mac_2, new_fcf_record);
+ phba->fcf.mac_addr[3] =
+ bf_get(lpfc_fcf_record_mac_3, new_fcf_record);
+ phba->fcf.mac_addr[4] =
+ bf_get(lpfc_fcf_record_mac_4, new_fcf_record);
+ phba->fcf.mac_addr[5] =
+ bf_get(lpfc_fcf_record_mac_5, new_fcf_record);
+ phba->fcf.fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
+ phba->fcf.priority = new_fcf_record->fip_priority;
+}
+
+/**
+ * lpfc_register_fcf - Register the FCF with hba.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine issues a register fcfi mailbox command to register
+ * the fcf with HBA.
+ **/
+static void
+lpfc_register_fcf(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *fcf_mbxq;
+ int rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+
+ /* If the FCF is not availabe do nothing. */
+ if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return;
+ }
+
+ /* The FCF is already registered, start discovery */
+ if (phba->fcf.fcf_flag & FCF_REGISTERED) {
+ phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (phba->pport->port_state != LPFC_FLOGI)
+ lpfc_initial_flogi(phba->pport);
+ return;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+
+ fcf_mbxq = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!fcf_mbxq)
+ return;
+
+ lpfc_reg_fcfi(phba, fcf_mbxq);
+ fcf_mbxq->vport = phba->pport;
+ fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi;
+ rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ mempool_free(fcf_mbxq, phba->mbox_mem_pool);
+
+ return;
+}
+
+/**
+ * lpfc_match_fcf_conn_list - Check if the FCF record can be used for discovery.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ * @boot_flag: Indicates if this record used by boot bios.
+ * @addr_mode: The address mode to be used by this FCF
+ *
+ * This routine compare the fcf record with connect list obtained from the
+ * config region to decide if this FCF can be used for SAN discovery. It returns
+ * 1 if this record can be used for SAN discovery else return zero. If this FCF
+ * record can be used for SAN discovery, the boot_flag will indicate if this FCF
+ * is used by boot bios and addr_mode will indicate the addressing mode to be
+ * used for this FCF when the function returns.
+ * If the FCF record need to be used with a particular vlan id, the vlan is
+ * set in the vlan_id on return of the function. If not VLAN tagging need to
+ * be used with the FCF vlan_id will be set to 0xFFFF;
+ **/
+static int
+lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
+ struct fcf_record *new_fcf_record,
+ uint32_t *boot_flag, uint32_t *addr_mode,
+ uint16_t *vlan_id)
+{
+ struct lpfc_fcf_conn_entry *conn_entry;
+
+ if (!phba->cfg_enable_fip) {
+ *boot_flag = 0;
+ *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+ new_fcf_record);
+ if (phba->valid_vlan)
+ *vlan_id = phba->vlan_id;
+ else
+ *vlan_id = 0xFFFF;
+ return 1;
+ }
+
+ /*
+ * If there are no FCF connection table entry, driver connect to all
+ * FCFs.
+ */
+ if (list_empty(&phba->fcf_conn_rec_list)) {
+ *boot_flag = 0;
+ *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+ new_fcf_record);
+ *vlan_id = 0xFFFF;
+ return 1;
+ }
+
+ list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list, list) {
+ if (!(conn_entry->conn_rec.flags & FCFCNCT_VALID))
+ continue;
+
+ if ((conn_entry->conn_rec.flags & FCFCNCT_FBNM_VALID) &&
+ !lpfc_fab_name_match(conn_entry->conn_rec.fabric_name,
+ new_fcf_record))
+ continue;
+
+ if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID) {
+ /*
+ * If the vlan bit map does not have the bit set for the
+ * vlan id to be used, then it is not a match.
+ */
+ if (!(new_fcf_record->vlan_bitmap
+ [conn_entry->conn_rec.vlan_tag / 8] &
+ (1 << (conn_entry->conn_rec.vlan_tag % 8))))
+ continue;
+ }
+
+ /*
+ * Check if the connection record specifies a required
+ * addressing mode.
+ */
+ if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+ !(conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED)) {
+
+ /*
+ * If SPMA required but FCF not support this continue.
+ */
+ if ((conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+ !(bf_get(lpfc_fcf_record_mac_addr_prov,
+ new_fcf_record) & LPFC_FCF_SPMA))
+ continue;
+
+ /*
+ * If FPMA required but FCF not support this continue.
+ */
+ if (!(conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+ !(bf_get(lpfc_fcf_record_mac_addr_prov,
+ new_fcf_record) & LPFC_FCF_FPMA))
+ continue;
+ }
+
+ /*
+ * This fcf record matches filtering criteria.
+ */
+ if (conn_entry->conn_rec.flags & FCFCNCT_BOOT)
+ *boot_flag = 1;
+ else
+ *boot_flag = 0;
+
+ *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+ new_fcf_record);
+ /*
+ * If the user specified a required address mode, assign that
+ * address mode
+ */
+ if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+ (!(conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED)))
+ *addr_mode = (conn_entry->conn_rec.flags &
+ FCFCNCT_AM_SPMA) ?
+ LPFC_FCF_SPMA : LPFC_FCF_FPMA;
+ /*
+ * If the user specified a prefered address mode, use the
+ * addr mode only if FCF support the addr_mode.
+ */
+ else if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+ (conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED) &&
+ (conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+ (*addr_mode & LPFC_FCF_SPMA))
+ *addr_mode = LPFC_FCF_SPMA;
+ else if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+ (conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED) &&
+ !(conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+ (*addr_mode & LPFC_FCF_FPMA))
+ *addr_mode = LPFC_FCF_FPMA;
+ /*
+ * If user did not specify any addressing mode, use FPMA if
+ * possible else use SPMA.
+ */
+ else if (*addr_mode & LPFC_FCF_FPMA)
+ *addr_mode = LPFC_FCF_FPMA;
+
+ if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID)
+ *vlan_id = conn_entry->conn_rec.vlan_tag;
+ else
+ *vlan_id = 0xFFFF;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_mbx_cmpl_read_fcf_record - Completion handler for read_fcf mbox.
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to mailbox object.
+ *
+ * This function iterate through all the fcf records available in
+ * HBA and choose the optimal FCF record for discovery. After finding
+ * the FCF for discovery it register the FCF record and kick start
+ * discovery.
+ * If FCF_IN_USE flag is set in currently used FCF, the routine try to
+ * use a FCF record which match fabric name and mac address of the
+ * currently used FCF record.
+ * If the driver support only one FCF, it will try to use the FCF record
+ * used by BOOT_BIOS.
+ */
+void
+lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ void *virt_addr;
+ dma_addr_t phys_addr;
+ uint8_t *bytep;
+ struct lpfc_mbx_sge sge;
+ struct lpfc_mbx_read_fcf_tbl *read_fcf;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ struct fcf_record *new_fcf_record;
+ int rc;
+ uint32_t boot_flag, addr_mode;
+ uint32_t next_fcf_index;
+ unsigned long flags;
+ uint16_t vlan_id;
+
+ /* Get the first SGE entry from the non-embedded DMA memory. This
+ * routine only uses a single SGE.
+ */
+ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+ phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+ if (unlikely(!mboxq->sge_array)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2524 Failed to get the non-embedded SGE "
+ "virtual address\n");
+ goto out;
+ }
+ virt_addr = mboxq->sge_array->addr[0];
+
+ shdr = (union lpfc_sli4_cfg_shdr *)virt_addr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &shdr->response);
+ /*
+ * The FCF Record was read and there is no reason for the driver
+ * to maintain the FCF record data or memory. Instead, just need
+ * to book keeping the FCFIs can be used.
+ */
+ if (shdr_status || shdr_add_status) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2521 READ_FCF_RECORD mailbox failed "
+ "with status x%x add_status x%x, mbx\n",
+ shdr_status, shdr_add_status);
+ goto out;
+ }
+ /* Interpreting the returned information of FCF records */
+ read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
+ lpfc_sli_pcimem_bcopy(read_fcf, read_fcf,
+ sizeof(struct lpfc_mbx_read_fcf_tbl));
+ next_fcf_index = bf_get(lpfc_mbx_read_fcf_tbl_nxt_vindx, read_fcf);
+
+ new_fcf_record = (struct fcf_record *)(virt_addr +
+ sizeof(struct lpfc_mbx_read_fcf_tbl));
+ lpfc_sli_pcimem_bcopy(new_fcf_record, new_fcf_record,
+ sizeof(struct fcf_record));
+ bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+
+ rc = lpfc_match_fcf_conn_list(phba, new_fcf_record,
+ &boot_flag, &addr_mode,
+ &vlan_id);
+ /*
+ * If the fcf record does not match with connect list entries
+ * read the next entry.
+ */
+ if (!rc)
+ goto read_next_fcf;
+ /*
+ * If this is not the first FCF discovery of the HBA, use last
+ * FCF record for the discovery.
+ */
+ spin_lock_irqsave(&phba->hbalock, flags);
+ if (phba->fcf.fcf_flag & FCF_IN_USE) {
+ if (lpfc_fab_name_match(phba->fcf.fabric_name,
+ new_fcf_record) &&
+ lpfc_mac_addr_match(phba, new_fcf_record)) {
+ phba->fcf.fcf_flag |= FCF_AVAILABLE;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto out;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+ }
+ if (phba->fcf.fcf_flag & FCF_AVAILABLE) {
+ /*
+ * If the current FCF record does not have boot flag
+ * set and new fcf record has boot flag set, use the
+ * new fcf record.
+ */
+ if (boot_flag && !(phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
+ /* Use this FCF record */
+ lpfc_copy_fcf_record(phba, new_fcf_record);
+ phba->fcf.addr_mode = addr_mode;
+ phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
+ if (vlan_id != 0xFFFF) {
+ phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+ phba->fcf.vlan_id = vlan_id;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+ }
+ /*
+ * If the current FCF record has boot flag set and the
+ * new FCF record does not have boot flag, read the next
+ * FCF record.
+ */
+ if (!boot_flag && (phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+ }
+ /*
+ * If there is a record with lower priority value for
+ * the current FCF, use that record.
+ */
+ if (lpfc_fab_name_match(phba->fcf.fabric_name, new_fcf_record)
+ && (new_fcf_record->fip_priority <
+ phba->fcf.priority)) {
+ /* Use this FCF record */
+ lpfc_copy_fcf_record(phba, new_fcf_record);
+ phba->fcf.addr_mode = addr_mode;
+ if (vlan_id != 0xFFFF) {
+ phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+ phba->fcf.vlan_id = vlan_id;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+ }
+ /*
+ * This is the first available FCF record, use this
+ * record.
+ */
+ lpfc_copy_fcf_record(phba, new_fcf_record);
+ phba->fcf.addr_mode = addr_mode;
+ if (boot_flag)
+ phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
+ phba->fcf.fcf_flag |= FCF_AVAILABLE;
+ if (vlan_id != 0xFFFF) {
+ phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+ phba->fcf.vlan_id = vlan_id;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ goto read_next_fcf;
+
+read_next_fcf:
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0)
+ lpfc_register_fcf(phba);
+ else
+ lpfc_sli4_read_fcf_record(phba, next_fcf_index);
+ return;
+
+out:
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ lpfc_register_fcf(phba);
+
+ return;
+}
+
+/**
+ * lpfc_start_fdiscs - send fdiscs for each vports on this port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function loops through the list of vports on the @phba and issues an
+ * FDISC if possible.
+ */
+void
+lpfc_start_fdiscs(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ int i;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
+ continue;
+ /* There are no vpi for this vport */
+ if (vports[i]->vpi > phba->max_vpi) {
+ lpfc_vport_set_state(vports[i],
+ FC_VPORT_FAILED);
+ continue;
+ }
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ lpfc_vport_set_state(vports[i],
+ FC_VPORT_LINKDOWN);
+ continue;
+ }
+ if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
+ lpfc_initial_fdisc(vports[i]);
+ else {
+ lpfc_vport_set_state(vports[i],
+ FC_VPORT_NO_FABRIC_SUPP);
+ lpfc_printf_vlog(vports[i], KERN_ERR,
+ LOG_ELS,
+ "0259 No NPIV "
+ "Fabric support\n");
+ }
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+void
+lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_dmabuf *dmabuf = mboxq->context1;
+ struct lpfc_vport *vport = mboxq->vport;
+
+ if (mboxq->u.mb.mbxStatus) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ "2018 REG_VFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
+ if (phba->fc_topology == TOPOLOGY_LOOP) {
+ /* FLOGI failed, use loop map to make discovery list */
+ lpfc_disc_list_loopmap(vport);
+ /* Start discovery */
+ lpfc_disc_start(vport);
+ goto fail_free_mem;
+ }
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ goto fail_free_mem;
+ }
+ /* Mark the vport has registered with its VFI */
+ vport->vfi_state |= LPFC_VFI_REGISTERED;
+
+ if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
+ lpfc_start_fdiscs(phba);
+ lpfc_do_scr_ns_plogi(phba, vport);
+ }
+
+fail_free_mem:
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ return;
+}
+
+static void
lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1;
struct lpfc_vport *vport = pmb->vport;
@@ -1012,13 +1625,13 @@ static void
lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
{
struct lpfc_vport *vport = phba->pport;
- LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox;
+ LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox = NULL;
int i;
struct lpfc_dmabuf *mp;
int rc;
+ struct fcf_record *fcf_record;
sparam_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
spin_lock_irq(&phba->hbalock);
switch (la->UlnkSpeed) {
@@ -1034,6 +1647,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
case LA_8GHZ_LINK:
phba->fc_linkspeed = LA_8GHZ_LINK;
break;
+ case LA_10GHZ_LINK:
+ phba->fc_linkspeed = LA_10GHZ_LINK;
+ break;
default:
phba->fc_linkspeed = LA_UNKNW_LINK;
break;
@@ -1115,22 +1731,66 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
mempool_free(sparam_mbox, phba->mbox_mem_pool);
- if (cfglink_mbox)
- mempool_free(cfglink_mbox, phba->mbox_mem_pool);
goto out;
}
}
- if (cfglink_mbox) {
+ if (!(phba->hba_flag & HBA_FCOE_SUPPORT)) {
+ cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!cfglink_mbox)
+ goto out;
vport->port_state = LPFC_LOCAL_CFG_LINK;
lpfc_config_link(phba, cfglink_mbox);
cfglink_mbox->vport = vport;
cfglink_mbox->mbox_cmpl = lpfc_mbx_cmpl_local_config_link;
rc = lpfc_sli_issue_mbox(phba, cfglink_mbox, MBX_NOWAIT);
- if (rc != MBX_NOT_FINISHED)
- return;
- mempool_free(cfglink_mbox, phba->mbox_mem_pool);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(cfglink_mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+ } else {
+ /*
+ * Add the driver's default FCF record at FCF index 0 now. This
+ * is phase 1 implementation that support FCF index 0 and driver
+ * defaults.
+ */
+ if (phba->cfg_enable_fip == 0) {
+ fcf_record = kzalloc(sizeof(struct fcf_record),
+ GFP_KERNEL);
+ if (unlikely(!fcf_record)) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_MBOX | LOG_SLI,
+ "2554 Could not allocate memmory for "
+ "fcf record\n");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ lpfc_sli4_build_dflt_fcf_record(phba, fcf_record,
+ LPFC_FCOE_FCF_DEF_INDEX);
+ rc = lpfc_sli4_add_fcf_record(phba, fcf_record);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_MBOX | LOG_SLI,
+ "2013 Could not manually add FCF "
+ "record 0, status %d\n", rc);
+ rc = -ENODEV;
+ kfree(fcf_record);
+ goto out;
+ }
+ kfree(fcf_record);
+ }
+ /*
+ * The driver is expected to do FIP/FCF. Call the port
+ * and get the FCF Table.
+ */
+ rc = lpfc_sli4_read_fcf_record(phba,
+ LPFC_FCOE_FCF_GET_FIRST);
+ if (rc)
+ goto out;
}
+
+ return;
out:
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
@@ -1147,10 +1807,12 @@ lpfc_enable_la(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
spin_lock_irq(&phba->hbalock);
psli->sli_flag |= LPFC_PROCESS_LA;
- control = readl(phba->HCregaddr);
- control |= HC_LAINT_ENA;
- writel(control, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ control = readl(phba->HCregaddr);
+ control |= HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ }
spin_unlock_irq(&phba->hbalock);
}
@@ -1159,6 +1821,7 @@ lpfc_mbx_issue_link_down(struct lpfc_hba *phba)
{
lpfc_linkdown(phba);
lpfc_enable_la(phba);
+ lpfc_unregister_unused_fcf(phba);
/* turn on Link Attention interrupts - no CLEAR_LA needed */
}
@@ -1175,7 +1838,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct lpfc_vport *vport = pmb->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
READ_LA_VAR *la;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
/* Unblock ELS traffic */
@@ -1190,7 +1853,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
goto lpfc_mbx_cmpl_read_la_free_mbuf;
}
- la = (READ_LA_VAR *) & pmb->mb.un.varReadLA;
+ la = (READ_LA_VAR *) &pmb->u.mb.un.varReadLA;
memcpy(&phba->alpa_map[0], mp->virt, 128);
@@ -1328,7 +1991,7 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
static void
lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_vport *vport = pmb->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
@@ -1381,7 +2044,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
switch (mb->mbxStatus) {
case 0x0011:
@@ -1416,6 +2079,128 @@ out:
return;
}
+/**
+ * lpfc_create_static_vport - Read HBA config region to create static vports.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine issue a DUMP mailbox command for config region 22 to get
+ * the list of static vports to be created. The function create vports
+ * based on the information returned from the HBA.
+ **/
+void
+lpfc_create_static_vport(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmb = NULL;
+ MAILBOX_t *mb;
+ struct static_vport_info *vport_info;
+ int rc, i;
+ struct fc_vport_identifiers vport_id;
+ struct fc_vport *new_fc_vport;
+ struct Scsi_Host *shost;
+ struct lpfc_vport *vport;
+ uint16_t offset = 0;
+ uint8_t *vport_buff;
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0542 lpfc_create_static_vport failed to"
+ " allocate mailbox memory\n");
+ return;
+ }
+
+ mb = &pmb->u.mb;
+
+ vport_info = kzalloc(sizeof(struct static_vport_info), GFP_KERNEL);
+ if (!vport_info) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0543 lpfc_create_static_vport failed to"
+ " allocate vport_info\n");
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return;
+ }
+
+ vport_buff = (uint8_t *) vport_info;
+ do {
+ lpfc_dump_static_vport(phba, pmb, offset);
+ pmb->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox_wait(phba, pmb, LPFC_MBOX_TMO);
+
+ if ((rc != MBX_SUCCESS) || mb->mbxStatus) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0544 lpfc_create_static_vport failed to"
+ " issue dump mailbox command ret 0x%x "
+ "status 0x%x\n",
+ rc, mb->mbxStatus);
+ goto out;
+ }
+
+ if (mb->un.varDmp.word_cnt >
+ sizeof(struct static_vport_info) - offset)
+ mb->un.varDmp.word_cnt =
+ sizeof(struct static_vport_info) - offset;
+
+ lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
+ vport_buff + offset,
+ mb->un.varDmp.word_cnt);
+ offset += mb->un.varDmp.word_cnt;
+
+ } while (mb->un.varDmp.word_cnt &&
+ offset < sizeof(struct static_vport_info));
+
+
+ if ((le32_to_cpu(vport_info->signature) != VPORT_INFO_SIG) ||
+ ((le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK)
+ != VPORT_INFO_REV)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0545 lpfc_create_static_vport bad"
+ " information header 0x%x 0x%x\n",
+ le32_to_cpu(vport_info->signature),
+ le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK);
+
+ goto out;
+ }
+
+ shost = lpfc_shost_from_vport(phba->pport);
+
+ for (i = 0; i < MAX_STATIC_VPORT_COUNT; i++) {
+ memset(&vport_id, 0, sizeof(vport_id));
+ vport_id.port_name = wwn_to_u64(vport_info->vport_list[i].wwpn);
+ vport_id.node_name = wwn_to_u64(vport_info->vport_list[i].wwnn);
+ if (!vport_id.port_name || !vport_id.node_name)
+ continue;
+
+ vport_id.roles = FC_PORT_ROLE_FCP_INITIATOR;
+ vport_id.vport_type = FC_PORTTYPE_NPIV;
+ vport_id.disable = false;
+ new_fc_vport = fc_vport_create(shost, 0, &vport_id);
+
+ if (!new_fc_vport) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0546 lpfc_create_static_vport failed to"
+ " create vport \n");
+ continue;
+ }
+
+ vport = *(struct lpfc_vport **)new_fc_vport->dd_data;
+ vport->vport_flag |= STATIC_VPORT;
+ }
+
+out:
+ /*
+ * If this is timed out command, setting NULL to context2 tell SLI
+ * layer not to use this buffer.
+ */
+ spin_lock_irq(&phba->hbalock);
+ pmb->context2 = NULL;
+ spin_unlock_irq(&phba->hbalock);
+ kfree(vport_info);
+ if (rc != MBX_TIMEOUT)
+ 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
@@ -1426,16 +2211,17 @@ void
lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
struct lpfc_nodelist *ndlp;
- struct lpfc_vport **vports;
- int i;
ndlp = (struct lpfc_nodelist *) pmb->context2;
pmb->context1 = NULL;
pmb->context2 = NULL;
if (mb->mbxStatus) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ "0258 Register Fabric login error: 0x%x\n",
+ mb->mbxStatus);
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
mempool_free(pmb, phba->mbox_mem_pool);
@@ -1454,9 +2240,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "0258 Register Fabric login error: 0x%x\n",
- mb->mbxStatus);
/* Decrement the reference count to ndlp after the reference
* to the ndlp are done.
*/
@@ -1465,34 +2248,12 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
ndlp->nlp_rpi = mb->un.varWords[0];
+ ndlp->nlp_flag |= NLP_RPI_VALID;
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
- vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
- for(i = 0;
- i <= phba->max_vpi && vports[i] != NULL;
- i++) {
- if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
- continue;
- if (phba->fc_topology == TOPOLOGY_LOOP) {
- lpfc_vport_set_state(vports[i],
- FC_VPORT_LINKDOWN);
- continue;
- }
- if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
- lpfc_initial_fdisc(vports[i]);
- else {
- lpfc_vport_set_state(vports[i],
- FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_ELS,
- "0259 No NPIV "
- "Fabric support\n");
- }
- }
- lpfc_destroy_vport_work_array(phba, vports);
+ lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
}
@@ -1516,13 +2277,16 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
void
lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
struct lpfc_vport *vport = pmb->vport;
if (mb->mbxStatus) {
out:
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0260 Register NameServer error: 0x%x\n",
+ mb->mbxStatus);
/* decrement the node reference count held for this
* callback function.
*/
@@ -1546,15 +2310,13 @@ out:
return;
}
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0260 Register NameServer error: 0x%x\n",
- mb->mbxStatus);
return;
}
pmb->context1 = NULL;
ndlp->nlp_rpi = mb->un.varWords[0];
+ ndlp->nlp_flag |= NLP_RPI_VALID;
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
@@ -1858,13 +2620,18 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
NLP_STE_UNUSED_NODE);
}
/**
- * lpfc_initialize_node: Initialize all fields of node object.
+ * lpfc_initialize_node - Initialize all fields of node object
* @vport: Pointer to Virtual Port object.
* @ndlp: Pointer to FC node object.
* @did: FC_ID of the node.
- * This function is always called when node object need to
- * be initialized. It initializes all the fields of the node
- * object.
+ *
+ * This function is always called when node object need to be initialized.
+ * It initializes all the fields of the node object. Although the reference
+ * to phba from @ndlp can be obtained indirectly through it's reference to
+ * @vport, a direct reference to phba is taken here by @ndlp. This is due
+ * to the life-span of the @ndlp might go beyond the existence of @vport as
+ * the final release of ndlp is determined by its reference count. And, the
+ * operation on @ndlp needs the reference to phba.
**/
static inline void
lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
@@ -1877,6 +2644,7 @@ lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
ndlp->nlp_DID = did;
ndlp->vport = vport;
+ ndlp->phba = vport->phba;
ndlp->nlp_sid = NLP_NO_SID;
kref_init(&ndlp->kref);
NLP_INT_NODE_ACT(ndlp);
@@ -2049,7 +2817,7 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba,
if (pring->ringno == LPFC_ELS_RING) {
switch (icmd->ulpCommand) {
case CMD_GEN_REQUEST64_CR:
- if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi)
+ if (iocb->context_un.ndlp == ndlp)
return 1;
case CMD_ELS_REQUEST64_CR:
if (icmd->un.elsreq64.remoteID == ndlp->nlp_DID)
@@ -2086,7 +2854,6 @@ 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;
lpfc_fabric_abort_nport(ndlp);
@@ -2097,7 +2864,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
*/
psli = &phba->sli;
rpi = ndlp->nlp_rpi;
- if (rpi) {
+ if (ndlp->nlp_flag & NLP_RPI_VALID) {
/* Now process each ring */
for (i = 0; i < psli->num_rings; i++) {
pring = &psli->ring[i];
@@ -2122,19 +2889,9 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
}
}
- while (!list_empty(&completions)) {
- iocb = list_get_first(&completions, struct lpfc_iocbq, list);
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- icmd = &iocb->iocb;
- icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (iocb->iocb_cmpl)(phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
return 0;
}
@@ -2155,7 +2912,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
LPFC_MBOXQ_t *mbox;
int rc;
- if (ndlp->nlp_rpi) {
+ if (ndlp->nlp_flag & NLP_RPI_VALID) {
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
lpfc_unreg_login(phba, vport->vpi, ndlp->nlp_rpi, mbox);
@@ -2167,6 +2924,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
}
lpfc_no_rpi(phba, ndlp);
ndlp->nlp_rpi = 0;
+ ndlp->nlp_flag &= ~NLP_RPI_VALID;
return 1;
}
return 0;
@@ -2186,9 +2944,13 @@ lpfc_unreg_all_rpis(struct lpfc_vport *vport)
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->context1 = NULL;
rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool);
- }
+
+ if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
+ "1836 Could not issue "
+ "unreg_login(all_rpis) status %d\n", rc);
}
}
@@ -2206,12 +2968,14 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport)
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->context1 = NULL;
rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+
+ if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
"1815 Could not issue "
- "unreg_did (default rpis)\n");
- mempool_free(mbox, phba->mbox_mem_pool);
- }
+ "unreg_did (default rpis) status %d\n",
+ rc);
}
}
@@ -2251,7 +3015,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
/* cleanup any ndlp on mbox q waiting for reglogin cmpl */
if ((mb = phba->sli.mbox_active)) {
- if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
mb->context2 = NULL;
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
@@ -2260,7 +3024,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
- if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
mp = (struct lpfc_dmabuf *) (mb->context1);
if (mp) {
@@ -2308,13 +3072,14 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
int rc;
lpfc_cancel_retry_delay_tmo(vport, ndlp);
- if (ndlp->nlp_flag & NLP_DEFER_RM && !ndlp->nlp_rpi) {
+ if ((ndlp->nlp_flag & NLP_DEFER_RM) &&
+ !(ndlp->nlp_flag & NLP_RPI_VALID)) {
/* For this case we need to cleanup the default rpi
* allocated by the firmware.
*/
if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))
!= NULL) {
- rc = lpfc_reg_login(phba, vport->vpi, ndlp->nlp_DID,
+ rc = lpfc_reg_rpi(phba, vport->vpi, ndlp->nlp_DID,
(uint8_t *) &vport->fc_sparam, mbox, 0);
if (rc) {
mempool_free(mbox, phba->mbox_mem_pool);
@@ -2470,14 +3235,13 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
if (ndlp->nlp_flag & NLP_RCV_PLOGI)
return NULL;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
-
/* Since this node is marked for discovery,
* delay timeout is not needed.
*/
lpfc_cancel_retry_delay_tmo(vport, ndlp);
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irq(shost->host_lock);
} else
ndlp = NULL;
} else {
@@ -2553,7 +3317,8 @@ lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport)
* clear_la then don't send it.
*/
if ((phba->link_state >= LPFC_CLEAR_LA) ||
- (vport->port_type != LPFC_PHYSICAL_PORT))
+ (vport->port_type != LPFC_PHYSICAL_PORT) ||
+ (phba->sli_rev == LPFC_SLI_REV4))
return;
/* Link up discovery */
@@ -2582,7 +3347,7 @@ lpfc_issue_reg_vpi(struct lpfc_hba *phba, struct lpfc_vport *vport)
regvpimbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (regvpimbox) {
- lpfc_reg_vpi(phba, vport->vpi, vport->fc_myDID, regvpimbox);
+ lpfc_reg_vpi(vport, regvpimbox);
regvpimbox->mbox_cmpl = lpfc_mbx_cmpl_reg_vpi;
regvpimbox->vport = vport;
if (lpfc_sli_issue_mbox(phba, regvpimbox, MBX_NOWAIT)
@@ -2642,7 +3407,8 @@ lpfc_disc_start(struct lpfc_vport *vport)
*/
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
!(vport->fc_flag & FC_PT2PT) &&
- !(vport->fc_flag & FC_RSCN_MODE)) {
+ !(vport->fc_flag & FC_RSCN_MODE) &&
+ (phba->sli_rev < LPFC_SLI_REV4)) {
lpfc_issue_reg_vpi(phba, vport);
return;
}
@@ -2740,19 +3506,9 @@ lpfc_free_tx(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- iocb = list_get_first(&completions, struct lpfc_iocbq, list);
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- icmd = &iocb->iocb;
- icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
}
static void
@@ -2929,11 +3685,13 @@ restart_disc:
* set port_state to PORT_READY if SLI2.
* cmpl_reg_vpi will set port_state to READY for SLI3.
*/
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
- lpfc_issue_reg_vpi(phba, vport);
- else { /* NPIV Not enabled */
- lpfc_issue_clear_la(phba, vport);
- vport->port_state = LPFC_VPORT_READY;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+ lpfc_issue_reg_vpi(phba, vport);
+ else { /* NPIV Not enabled */
+ lpfc_issue_clear_la(phba, vport);
+ vport->port_state = LPFC_VPORT_READY;
+ }
}
/* Setup and issue mailbox INITIALIZE LINK command */
@@ -2949,7 +3707,7 @@ restart_disc:
lpfc_linkdown(phba);
lpfc_init_link(phba, initlinkmbox, phba->cfg_topology,
phba->cfg_link_speed);
- initlinkmbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+ initlinkmbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
initlinkmbox->vport = vport;
initlinkmbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
rc = lpfc_sli_issue_mbox(phba, initlinkmbox, MBX_NOWAIT);
@@ -2969,11 +3727,13 @@ restart_disc:
* set port_state to PORT_READY if SLI2.
* cmpl_reg_vpi will set port_state to READY for SLI3.
*/
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
- lpfc_issue_reg_vpi(phba, vport);
- else { /* NPIV Not enabled */
- lpfc_issue_clear_la(phba, vport);
- vport->port_state = LPFC_VPORT_READY;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+ lpfc_issue_reg_vpi(phba, vport);
+ else { /* NPIV Not enabled */
+ lpfc_issue_clear_la(phba, vport);
+ vport->port_state = LPFC_VPORT_READY;
+ }
}
break;
@@ -3046,7 +3806,7 @@ restart_disc:
void
lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
struct lpfc_vport *vport = pmb->vport;
@@ -3054,6 +3814,7 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
pmb->context1 = NULL;
ndlp->nlp_rpi = mb->un.varWords[0];
+ ndlp->nlp_flag |= NLP_RPI_VALID;
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
@@ -3173,7 +3934,7 @@ lpfc_nlp_release(struct kref *kref)
lpfc_nlp_remove(ndlp->vport, ndlp);
/* clear the ndlp active flag for all release cases */
- phba = ndlp->vport->phba;
+ phba = ndlp->phba;
spin_lock_irqsave(&phba->ndlp_lock, flags);
NLP_CLR_NODE_ACT(ndlp);
spin_unlock_irqrestore(&phba->ndlp_lock, flags);
@@ -3181,7 +3942,7 @@ lpfc_nlp_release(struct kref *kref)
/* free ndlp memory for final ndlp release */
if (NLP_CHK_FREE_REQ(ndlp)) {
kfree(ndlp->lat_data);
- mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool);
+ mempool_free(ndlp, ndlp->phba->nlp_mem_pool);
}
}
@@ -3204,7 +3965,7 @@ lpfc_nlp_get(struct lpfc_nodelist *ndlp)
* ndlp reference count that is in the process of being
* released.
*/
- phba = ndlp->vport->phba;
+ phba = ndlp->phba;
spin_lock_irqsave(&phba->ndlp_lock, flags);
if (!NLP_CHK_NODE_ACT(ndlp) || NLP_CHK_FREE_ACK(ndlp)) {
spin_unlock_irqrestore(&phba->ndlp_lock, flags);
@@ -3240,7 +4001,7 @@ lpfc_nlp_put(struct lpfc_nodelist *ndlp)
"node put: did:x%x flg:x%x refcnt:x%x",
ndlp->nlp_DID, ndlp->nlp_flag,
atomic_read(&ndlp->kref.refcount));
- phba = ndlp->vport->phba;
+ phba = ndlp->phba;
spin_lock_irqsave(&phba->ndlp_lock, flags);
/* Check the ndlp memory free acknowledge flag to avoid the
* possible race condition that kref_put got invoked again
@@ -3307,3 +4068,395 @@ lpfc_nlp_not_used(struct lpfc_nodelist *ndlp)
return 1;
return 0;
}
+
+/**
+ * lpfc_fcf_inuse - Check if FCF can be unregistered.
+ * @phba: Pointer to hba context object.
+ *
+ * This function iterate through all FC nodes associated
+ * will all vports to check if there is any node with
+ * fc_rports associated with it. If there is an fc_rport
+ * associated with the node, then the node is either in
+ * discovered state or its devloss_timer is pending.
+ */
+static int
+lpfc_fcf_inuse(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ int i, ret = 0;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+
+ vports = lpfc_create_vport_work_array(phba);
+
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ spin_lock_irq(shost->host_lock);
+ list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) {
+ if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport &&
+ (ndlp->rport->roles & FC_RPORT_ROLE_FCP_TARGET)) {
+ ret = 1;
+ spin_unlock_irq(shost->host_lock);
+ goto out;
+ }
+ }
+ spin_unlock_irq(shost->host_lock);
+ }
+out:
+ lpfc_destroy_vport_work_array(phba, vports);
+ return ret;
+}
+
+/**
+ * lpfc_unregister_vfi_cmpl - Completion handler for unreg vfi.
+ * @phba: Pointer to hba context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This function frees memory associated with the mailbox command.
+ */
+static void
+lpfc_unregister_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport = mboxq->vport;
+
+ if (mboxq->u.mb.mbxStatus) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2555 UNREG_VFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
+ }
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
+ * lpfc_unregister_fcfi_cmpl - Completion handler for unreg fcfi.
+ * @phba: Pointer to hba context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This function frees memory associated with the mailbox command.
+ */
+static void
+lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport = mboxq->vport;
+
+ if (mboxq->u.mb.mbxStatus) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2550 UNREG_FCFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
+ }
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
+ * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected.
+ * @phba: Pointer to hba context object.
+ *
+ * This function check if there are any connected remote port for the FCF and
+ * if all the devices are disconnected, this function unregister FCFI.
+ * This function also tries to use another FCF for discovery.
+ */
+void
+lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+ struct lpfc_vport **vports;
+ int i;
+
+ spin_lock_irq(&phba->hbalock);
+ /*
+ * If HBA is not running in FIP mode or
+ * If HBA does not support FCoE or
+ * If FCF is not registered.
+ * do nothing.
+ */
+ if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
+ !(phba->fcf.fcf_flag & FCF_REGISTERED) ||
+ (phba->cfg_enable_fip == 0)) {
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ if (lpfc_fcf_inuse(phba))
+ return;
+
+
+ /* Unregister VPIs */
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports &&
+ (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ lpfc_mbx_unreg_vpi(vports[i]);
+ vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ /* Unregister VFI */
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2556 UNREG_VFI mbox allocation failed"
+ "HBA state x%x\n",
+ phba->pport->port_state);
+ return;
+ }
+
+ lpfc_unreg_vfi(mbox, phba->pport->vfi);
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2557 UNREG_VFI issue mbox failed rc x%x "
+ "HBA state x%x\n",
+ rc, phba->pport->port_state);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return;
+ }
+
+ /* Unregister FCF */
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2551 UNREG_FCFI mbox allocation failed"
+ "HBA state x%x\n",
+ phba->pport->port_state);
+ return;
+ }
+
+ lpfc_unreg_fcfi(mbox, phba->fcf.fcfi);
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_unregister_fcfi_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2552 UNREG_FCFI issue mbox failed rc x%x "
+ "HBA state x%x\n",
+ rc, phba->pport->port_state);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return;
+ }
+
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_REGISTERED |
+ FCF_DISCOVERED | FCF_BOOT_ENABLE | FCF_IN_USE |
+ FCF_VALID_VLAN);
+ spin_unlock_irq(&phba->hbalock);
+
+ /*
+ * If driver is not unloading, check if there is any other
+ * FCF record that can be used for discovery.
+ */
+ if ((phba->pport->load_flag & FC_UNLOADING) ||
+ (phba->link_state < LPFC_LINK_UP))
+ return;
+
+ rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+
+ if (rc)
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2553 lpfc_unregister_unused_fcf failed to read FCF"
+ " record HBA state x%x\n",
+ phba->pport->port_state);
+}
+
+/**
+ * lpfc_read_fcf_conn_tbl - Create driver FCF connection table.
+ * @phba: Pointer to hba context object.
+ * @buff: Buffer containing the FCF connection table as in the config
+ * region.
+ * This function create driver data structure for the FCF connection
+ * record table read from config region 23.
+ */
+static void
+lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
+ uint8_t *buff)
+{
+ struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
+ struct lpfc_fcf_conn_hdr *conn_hdr;
+ struct lpfc_fcf_conn_rec *conn_rec;
+ uint32_t record_count;
+ int i;
+
+ /* Free the current connect table */
+ list_for_each_entry_safe(conn_entry, next_conn_entry,
+ &phba->fcf_conn_rec_list, list)
+ kfree(conn_entry);
+
+ conn_hdr = (struct lpfc_fcf_conn_hdr *) buff;
+ record_count = conn_hdr->length * sizeof(uint32_t)/
+ sizeof(struct lpfc_fcf_conn_rec);
+
+ conn_rec = (struct lpfc_fcf_conn_rec *)
+ (buff + sizeof(struct lpfc_fcf_conn_hdr));
+
+ for (i = 0; i < record_count; i++) {
+ if (!(conn_rec[i].flags & FCFCNCT_VALID))
+ continue;
+ conn_entry = kzalloc(sizeof(struct lpfc_fcf_conn_entry),
+ GFP_KERNEL);
+ if (!conn_entry) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2566 Failed to allocate connection"
+ " table entry\n");
+ return;
+ }
+
+ memcpy(&conn_entry->conn_rec, &conn_rec[i],
+ sizeof(struct lpfc_fcf_conn_rec));
+ conn_entry->conn_rec.vlan_tag =
+ le16_to_cpu(conn_entry->conn_rec.vlan_tag) & 0xFFF;
+ conn_entry->conn_rec.flags =
+ le16_to_cpu(conn_entry->conn_rec.flags);
+ list_add_tail(&conn_entry->list,
+ &phba->fcf_conn_rec_list);
+ }
+}
+
+/**
+ * lpfc_read_fcoe_param - Read FCoe parameters from conf region..
+ * @phba: Pointer to hba context object.
+ * @buff: Buffer containing the FCoE parameter data structure.
+ *
+ * This function update driver data structure with config
+ * parameters read from config region 23.
+ */
+static void
+lpfc_read_fcoe_param(struct lpfc_hba *phba,
+ uint8_t *buff)
+{
+ struct lpfc_fip_param_hdr *fcoe_param_hdr;
+ struct lpfc_fcoe_params *fcoe_param;
+
+ fcoe_param_hdr = (struct lpfc_fip_param_hdr *)
+ buff;
+ fcoe_param = (struct lpfc_fcoe_params *)
+ buff + sizeof(struct lpfc_fip_param_hdr);
+
+ if ((fcoe_param_hdr->parm_version != FIPP_VERSION) ||
+ (fcoe_param_hdr->length != FCOE_PARAM_LENGTH))
+ return;
+
+ if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
+ FIPP_MODE_ON)
+ phba->cfg_enable_fip = 1;
+
+ if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
+ FIPP_MODE_OFF)
+ phba->cfg_enable_fip = 0;
+
+ if (fcoe_param_hdr->parm_flags & FIPP_VLAN_VALID) {
+ phba->valid_vlan = 1;
+ phba->vlan_id = le16_to_cpu(fcoe_param->vlan_tag) &
+ 0xFFF;
+ }
+
+ phba->fc_map[0] = fcoe_param->fc_map[0];
+ phba->fc_map[1] = fcoe_param->fc_map[1];
+ phba->fc_map[2] = fcoe_param->fc_map[2];
+ return;
+}
+
+/**
+ * lpfc_get_rec_conf23 - Get a record type in config region data.
+ * @buff: Buffer containing config region 23 data.
+ * @size: Size of the data buffer.
+ * @rec_type: Record type to be searched.
+ *
+ * This function searches config region data to find the begining
+ * of the record specified by record_type. If record found, this
+ * function return pointer to the record else return NULL.
+ */
+static uint8_t *
+lpfc_get_rec_conf23(uint8_t *buff, uint32_t size, uint8_t rec_type)
+{
+ uint32_t offset = 0, rec_length;
+
+ if ((buff[0] == LPFC_REGION23_LAST_REC) ||
+ (size < sizeof(uint32_t)))
+ return NULL;
+
+ rec_length = buff[offset + 1];
+
+ /*
+ * One TLV record has one word header and number of data words
+ * specified in the rec_length field of the record header.
+ */
+ while ((offset + rec_length * sizeof(uint32_t) + sizeof(uint32_t))
+ <= size) {
+ if (buff[offset] == rec_type)
+ return &buff[offset];
+
+ if (buff[offset] == LPFC_REGION23_LAST_REC)
+ return NULL;
+
+ offset += rec_length * sizeof(uint32_t) + sizeof(uint32_t);
+ rec_length = buff[offset + 1];
+ }
+ return NULL;
+}
+
+/**
+ * lpfc_parse_fcoe_conf - Parse FCoE config data read from config region 23.
+ * @phba: Pointer to lpfc_hba data structure.
+ * @buff: Buffer containing config region 23 data.
+ * @size: Size of the data buffer.
+ *
+ * This fuction parse the FCoE config parameters in config region 23 and
+ * populate driver data structure with the parameters.
+ */
+void
+lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
+ uint8_t *buff,
+ uint32_t size)
+{
+ uint32_t offset = 0, rec_length;
+ uint8_t *rec_ptr;
+
+ /*
+ * If data size is less than 2 words signature and version cannot be
+ * verified.
+ */
+ if (size < 2*sizeof(uint32_t))
+ return;
+
+ /* Check the region signature first */
+ if (memcmp(buff, LPFC_REGION23_SIGNATURE, 4)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2567 Config region 23 has bad signature\n");
+ return;
+ }
+
+ offset += 4;
+
+ /* Check the data structure version */
+ if (buff[offset] != LPFC_REGION23_VERSION) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2568 Config region 23 has bad version\n");
+ return;
+ }
+ offset += 4;
+
+ rec_length = buff[offset + 1];
+
+ /* Read FCoE param record */
+ rec_ptr = lpfc_get_rec_conf23(&buff[offset],
+ size - offset, FCOE_PARAM_TYPE);
+ if (rec_ptr)
+ lpfc_read_fcoe_param(phba, rec_ptr);
+
+ /* Read FCF connection table */
+ rec_ptr = lpfc_get_rec_conf23(&buff[offset],
+ size - offset, FCOE_CONN_TBL_TYPE);
+ if (rec_ptr)
+ lpfc_read_fcf_conn_tbl(phba, rec_ptr);
+
+}
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 4168c7b498b8..02aa016b93e9 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -471,6 +471,35 @@ struct serv_parm { /* Structure is in Big Endian format */
};
/*
+ * Virtual Fabric Tagging Header
+ */
+struct fc_vft_header {
+ uint32_t word0;
+#define fc_vft_hdr_r_ctl_SHIFT 24
+#define fc_vft_hdr_r_ctl_MASK 0xFF
+#define fc_vft_hdr_r_ctl_WORD word0
+#define fc_vft_hdr_ver_SHIFT 22
+#define fc_vft_hdr_ver_MASK 0x3
+#define fc_vft_hdr_ver_WORD word0
+#define fc_vft_hdr_type_SHIFT 18
+#define fc_vft_hdr_type_MASK 0xF
+#define fc_vft_hdr_type_WORD word0
+#define fc_vft_hdr_e_SHIFT 16
+#define fc_vft_hdr_e_MASK 0x1
+#define fc_vft_hdr_e_WORD word0
+#define fc_vft_hdr_priority_SHIFT 13
+#define fc_vft_hdr_priority_MASK 0x7
+#define fc_vft_hdr_priority_WORD word0
+#define fc_vft_hdr_vf_id_SHIFT 1
+#define fc_vft_hdr_vf_id_MASK 0xFFF
+#define fc_vft_hdr_vf_id_WORD word0
+ uint32_t word1;
+#define fc_vft_hdr_hopct_SHIFT 24
+#define fc_vft_hdr_hopct_MASK 0xFF
+#define fc_vft_hdr_hopct_WORD word1
+};
+
+/*
* Extended Link Service LS_COMMAND codes (Payload Word 0)
*/
#ifdef __BIG_ENDIAN_BITFIELD
@@ -1152,6 +1181,9 @@ typedef struct {
#define PCI_DEVICE_ID_HORNET 0xfe05
#define PCI_DEVICE_ID_ZEPHYR_SCSP 0xfe11
#define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12
+#define PCI_VENDOR_ID_SERVERENGINE 0x19a2
+#define PCI_DEVICE_ID_TIGERSHARK 0x0704
+#define PCI_DEVICE_ID_TIGERSHARK_S 0x0705
#define JEDEC_ID_ADDRESS 0x0080001c
#define FIREFLY_JEDEC_ID 0x1ACC
@@ -1342,15 +1374,21 @@ typedef struct { /* FireFly BIU registers */
#define MBX_READ_LA64 0x95
#define MBX_REG_VPI 0x96
#define MBX_UNREG_VPI 0x97
-#define MBX_REG_VNPID 0x96
-#define MBX_UNREG_VNPID 0x97
#define MBX_WRITE_WWN 0x98
#define MBX_SET_DEBUG 0x99
#define MBX_LOAD_EXP_ROM 0x9C
-
-#define MBX_MAX_CMDS 0x9D
+#define MBX_SLI4_CONFIG 0x9B
+#define MBX_SLI4_REQ_FTRS 0x9D
+#define MBX_MAX_CMDS 0x9E
+#define MBX_RESUME_RPI 0x9E
#define MBX_SLI2_CMD_MASK 0x80
+#define MBX_REG_VFI 0x9F
+#define MBX_REG_FCFI 0xA0
+#define MBX_UNREG_VFI 0xA1
+#define MBX_UNREG_FCFI 0xA2
+#define MBX_INIT_VFI 0xA3
+#define MBX_INIT_VPI 0xA4
/* IOCB Commands */
@@ -1440,6 +1478,16 @@ typedef struct { /* FireFly BIU registers */
#define CMD_IOCB_LOGENTRY_CN 0x94
#define CMD_IOCB_LOGENTRY_ASYNC_CN 0x96
+/* Unhandled Data Security SLI Commands */
+#define DSSCMD_IWRITE64_CR 0xD8
+#define DSSCMD_IWRITE64_CX 0xD9
+#define DSSCMD_IREAD64_CR 0xDA
+#define DSSCMD_IREAD64_CX 0xDB
+#define DSSCMD_INVALIDATE_DEK 0xDC
+#define DSSCMD_SET_KEK 0xDD
+#define DSSCMD_GET_KEK_ID 0xDE
+#define DSSCMD_GEN_XFER 0xDF
+
#define CMD_MAX_IOCB_CMD 0xE6
#define CMD_IOCB_MASK 0xff
@@ -1466,6 +1514,7 @@ typedef struct { /* FireFly BIU registers */
#define MBXERR_BAD_RCV_LENGTH 14
#define MBXERR_DMA_ERROR 15
#define MBXERR_ERROR 16
+#define MBXERR_LINK_DOWN 0x33
#define MBX_NOT_FINISHED 255
#define MBX_BUSY 0xffffff /* Attempted cmd to busy Mailbox */
@@ -1504,32 +1553,6 @@ struct ulp_bde {
#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_TYPE_BDE_64 0x00 /* BDE (Host_resident) */
-#define BUFF_TYPE_BDE_IMMED 0x01 /* Immediate Data BDE */
-#define BUFF_TYPE_BDE_64P 0x02 /* BDE (Port-resident) */
-#define BUFF_TYPE_BDE_64I 0x08 /* Input BDE (Host-resident) */
-#define BUFF_TYPE_BDE_64IP 0x0A /* Input BDE (Port-resident) */
-#define BUFF_TYPE_BLP_64 0x40 /* BLP (Host-resident) */
-#define BUFF_TYPE_BLP_64P 0x42 /* BLP (Port-resident) */
- } f;
- } tus;
- uint32_t addrLow;
- uint32_t addrHigh;
-};
-
typedef struct ULP_BDL { /* SLI-2 */
#ifdef __BIG_ENDIAN_BITFIELD
uint32_t bdeFlags:8; /* BDL Flags */
@@ -2287,7 +2310,7 @@ typedef struct {
uint32_t rsvd3;
uint32_t rsvd4;
uint32_t rsvd5;
- uint16_t rsvd6;
+ uint16_t vfi;
uint16_t vpi;
#else /* __LITTLE_ENDIAN */
uint32_t rsvd1;
@@ -2297,7 +2320,7 @@ typedef struct {
uint32_t rsvd4;
uint32_t rsvd5;
uint16_t vpi;
- uint16_t rsvd6;
+ uint16_t vfi;
#endif
} REG_VPI_VAR;
@@ -2457,7 +2480,7 @@ typedef struct {
uint32_t entry_index:16;
#endif
- uint32_t rsvd1;
+ uint32_t sli4_length;
uint32_t word_cnt;
uint32_t resp_offset;
} DUMP_VAR;
@@ -2470,9 +2493,32 @@ typedef struct {
#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */
#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */
+#define DMP_REGION_VPORT 0x16 /* VPort info region */
+#define DMP_VPORT_REGION_SIZE 0x200
+#define DMP_MBOX_OFFSET_WORD 0x5
+
+#define DMP_REGION_FCOEPARAM 0x17 /* fcoe param region */
+#define DMP_FCOEPARAM_RGN_SIZE 0x400
+
#define WAKE_UP_PARMS_REGION_ID 4
#define WAKE_UP_PARMS_WORD_SIZE 15
+struct vport_rec {
+ uint8_t wwpn[8];
+ uint8_t wwnn[8];
+};
+
+#define VPORT_INFO_SIG 0x32324752
+#define VPORT_INFO_REV_MASK 0xff
+#define VPORT_INFO_REV 0x1
+#define MAX_STATIC_VPORT_COUNT 16
+struct static_vport_info {
+ uint32_t signature;
+ uint32_t rev;
+ struct vport_rec vport_list[MAX_STATIC_VPORT_COUNT];
+ uint32_t resvd[66];
+};
+
/* Option rom version structure */
struct prog_id {
#ifdef __BIG_ENDIAN_BITFIELD
@@ -2697,7 +2743,9 @@ typedef struct {
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd1 : 23; /* Reserved */
+ uint32_t rsvd1 : 19; /* Reserved */
+ uint32_t cdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd2 : 3; /* Reserved */
uint32_t cbg : 1; /* Configure BlockGuard */
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
@@ -2717,10 +2765,14 @@ typedef struct {
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t cbg : 1; /* Configure BlockGuard */
- uint32_t rsvd1 : 23; /* Reserved */
+ uint32_t rsvd2 : 3; /* Reserved */
+ uint32_t cdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd1 : 19; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd2 : 23; /* Reserved */
+ uint32_t rsvd3 : 19; /* Reserved */
+ uint32_t gdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd4 : 3; /* Reserved */
uint32_t gbg : 1; /* Grant BlockGuard */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
@@ -2740,7 +2792,9 @@ typedef struct {
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gbg : 1; /* Grant BlockGuard */
- uint32_t rsvd2 : 23; /* Reserved */
+ uint32_t rsvd4 : 3; /* Reserved */
+ uint32_t gdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd3 : 19; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
@@ -2753,20 +2807,20 @@ typedef struct {
#ifdef __BIG_ENDIAN_BITFIELD
uint32_t max_hbq : 16; /* Max HBQs Host expect to configure */
- uint32_t rsvd3 : 16; /* Max HBQs Host expect to configure */
+ uint32_t rsvd5 : 16; /* Max HBQs Host expect to configure */
#else /* __LITTLE_ENDIAN */
- uint32_t rsvd3 : 16; /* Max HBQs Host expect to configure */
+ uint32_t rsvd5 : 16; /* Max HBQs Host expect to configure */
uint32_t max_hbq : 16; /* Max HBQs Host expect to configure */
#endif
- uint32_t rsvd4; /* Reserved */
+ uint32_t rsvd6; /* Reserved */
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd5 : 16; /* Reserved */
+ uint32_t rsvd7 : 16; /* Reserved */
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
#else /* __LITTLE_ENDIAN */
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
- uint32_t rsvd5 : 16; /* Reserved */
+ uint32_t rsvd7 : 16; /* Reserved */
#endif
} CONFIG_PORT_VAR;
@@ -3666,3 +3720,5 @@ lpfc_error_lost_link(IOCB_t *iocbp)
#define MENLO_TIMEOUT 30
#define SETVAR_MLOMNT 0x103107
#define SETVAR_MLORST 0x103007
+
+#define BPL_ALIGN_SZ 8 /* 8 byte alignment for bpl and mbufs */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
new file mode 100644
index 000000000000..39c34b3ad29d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -0,0 +1,2141 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2009 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+/* Macros to deal with bit fields. Each bit field must have 3 #defines
+ * associated with it (_SHIFT, _MASK, and _WORD).
+ * EG. For a bit field that is in the 7th bit of the "field4" field of a
+ * structure and is 2 bits in size the following #defines must exist:
+ * struct temp {
+ * uint32_t field1;
+ * uint32_t field2;
+ * uint32_t field3;
+ * uint32_t field4;
+ * #define example_bit_field_SHIFT 7
+ * #define example_bit_field_MASK 0x03
+ * #define example_bit_field_WORD field4
+ * uint32_t field5;
+ * };
+ * Then the macros below may be used to get or set the value of that field.
+ * EG. To get the value of the bit field from the above example:
+ * struct temp t1;
+ * value = bf_get(example_bit_field, &t1);
+ * And then to set that bit field:
+ * bf_set(example_bit_field, &t1, 2);
+ * Or clear that bit field:
+ * bf_set(example_bit_field, &t1, 0);
+ */
+#define bf_get(name, ptr) \
+ (((ptr)->name##_WORD >> name##_SHIFT) & name##_MASK)
+#define bf_set(name, ptr, value) \
+ ((ptr)->name##_WORD = ((((value) & name##_MASK) << name##_SHIFT) | \
+ ((ptr)->name##_WORD & ~(name##_MASK << name##_SHIFT))))
+
+struct dma_address {
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+};
+
+#define LPFC_SLI4_BAR0 1
+#define LPFC_SLI4_BAR1 2
+#define LPFC_SLI4_BAR2 4
+
+#define LPFC_SLI4_MBX_EMBED true
+#define LPFC_SLI4_MBX_NEMBED false
+
+#define LPFC_SLI4_MB_WORD_COUNT 64
+#define LPFC_MAX_MQ_PAGE 8
+#define LPFC_MAX_WQ_PAGE 8
+#define LPFC_MAX_CQ_PAGE 4
+#define LPFC_MAX_EQ_PAGE 8
+
+#define LPFC_VIR_FUNC_MAX 32 /* Maximum number of virtual functions */
+#define LPFC_PCI_FUNC_MAX 5 /* Maximum number of PCI functions */
+#define LPFC_VFR_PAGE_SIZE 0x1000 /* 4KB BAR2 per-VF register page size */
+
+/* Define SLI4 Alignment requirements. */
+#define LPFC_ALIGN_16_BYTE 16
+#define LPFC_ALIGN_64_BYTE 64
+
+/* Define SLI4 specific definitions. */
+#define LPFC_MQ_CQE_BYTE_OFFSET 256
+#define LPFC_MBX_CMD_HDR_LENGTH 16
+#define LPFC_MBX_ERROR_RANGE 0x4000
+#define LPFC_BMBX_BIT1_ADDR_HI 0x2
+#define LPFC_BMBX_BIT1_ADDR_LO 0
+#define LPFC_RPI_HDR_COUNT 64
+#define LPFC_HDR_TEMPLATE_SIZE 4096
+#define LPFC_RPI_ALLOC_ERROR 0xFFFF
+#define LPFC_FCF_RECORD_WD_CNT 132
+#define LPFC_ENTIRE_FCF_DATABASE 0
+#define LPFC_DFLT_FCF_INDEX 0
+
+/* Virtual function numbers */
+#define LPFC_VF0 0
+#define LPFC_VF1 1
+#define LPFC_VF2 2
+#define LPFC_VF3 3
+#define LPFC_VF4 4
+#define LPFC_VF5 5
+#define LPFC_VF6 6
+#define LPFC_VF7 7
+#define LPFC_VF8 8
+#define LPFC_VF9 9
+#define LPFC_VF10 10
+#define LPFC_VF11 11
+#define LPFC_VF12 12
+#define LPFC_VF13 13
+#define LPFC_VF14 14
+#define LPFC_VF15 15
+#define LPFC_VF16 16
+#define LPFC_VF17 17
+#define LPFC_VF18 18
+#define LPFC_VF19 19
+#define LPFC_VF20 20
+#define LPFC_VF21 21
+#define LPFC_VF22 22
+#define LPFC_VF23 23
+#define LPFC_VF24 24
+#define LPFC_VF25 25
+#define LPFC_VF26 26
+#define LPFC_VF27 27
+#define LPFC_VF28 28
+#define LPFC_VF29 29
+#define LPFC_VF30 30
+#define LPFC_VF31 31
+
+/* PCI function numbers */
+#define LPFC_PCI_FUNC0 0
+#define LPFC_PCI_FUNC1 1
+#define LPFC_PCI_FUNC2 2
+#define LPFC_PCI_FUNC3 3
+#define LPFC_PCI_FUNC4 4
+
+/* Active interrupt test count */
+#define LPFC_ACT_INTR_CNT 4
+
+/* Delay Multiplier constant */
+#define LPFC_DMULT_CONST 651042
+#define LPFC_MIM_IMAX 636
+#define LPFC_FP_DEF_IMAX 10000
+#define LPFC_SP_DEF_IMAX 10000
+
+struct ulp_bde64 {
+ 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_TYPE_BDE_64 0x00 /* BDE (Host_resident) */
+#define BUFF_TYPE_BDE_IMMED 0x01 /* Immediate Data BDE */
+#define BUFF_TYPE_BDE_64P 0x02 /* BDE (Port-resident) */
+#define BUFF_TYPE_BDE_64I 0x08 /* Input BDE (Host-resident) */
+#define BUFF_TYPE_BDE_64IP 0x0A /* Input BDE (Port-resident) */
+#define BUFF_TYPE_BLP_64 0x40 /* BLP (Host-resident) */
+#define BUFF_TYPE_BLP_64P 0x42 /* BLP (Port-resident) */
+ } f;
+ } tus;
+ uint32_t addrLow;
+ uint32_t addrHigh;
+};
+
+struct lpfc_sli4_flags {
+ uint32_t word0;
+#define lpfc_fip_flag_SHIFT 0
+#define lpfc_fip_flag_MASK 0x00000001
+#define lpfc_fip_flag_WORD word0
+};
+
+/* event queue entry structure */
+struct lpfc_eqe {
+ uint32_t word0;
+#define lpfc_eqe_resource_id_SHIFT 16
+#define lpfc_eqe_resource_id_MASK 0x000000FF
+#define lpfc_eqe_resource_id_WORD word0
+#define lpfc_eqe_minor_code_SHIFT 4
+#define lpfc_eqe_minor_code_MASK 0x00000FFF
+#define lpfc_eqe_minor_code_WORD word0
+#define lpfc_eqe_major_code_SHIFT 1
+#define lpfc_eqe_major_code_MASK 0x00000007
+#define lpfc_eqe_major_code_WORD word0
+#define lpfc_eqe_valid_SHIFT 0
+#define lpfc_eqe_valid_MASK 0x00000001
+#define lpfc_eqe_valid_WORD word0
+};
+
+/* completion queue entry structure (common fields for all cqe types) */
+struct lpfc_cqe {
+ uint32_t reserved0;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t word3;
+#define lpfc_cqe_valid_SHIFT 31
+#define lpfc_cqe_valid_MASK 0x00000001
+#define lpfc_cqe_valid_WORD word3
+#define lpfc_cqe_code_SHIFT 16
+#define lpfc_cqe_code_MASK 0x000000FF
+#define lpfc_cqe_code_WORD word3
+};
+
+/* Completion Queue Entry Status Codes */
+#define CQE_STATUS_SUCCESS 0x0
+#define CQE_STATUS_FCP_RSP_FAILURE 0x1
+#define CQE_STATUS_REMOTE_STOP 0x2
+#define CQE_STATUS_LOCAL_REJECT 0x3
+#define CQE_STATUS_NPORT_RJT 0x4
+#define CQE_STATUS_FABRIC_RJT 0x5
+#define CQE_STATUS_NPORT_BSY 0x6
+#define CQE_STATUS_FABRIC_BSY 0x7
+#define CQE_STATUS_INTERMED_RSP 0x8
+#define CQE_STATUS_LS_RJT 0x9
+#define CQE_STATUS_CMD_REJECT 0xb
+#define CQE_STATUS_FCP_TGT_LENCHECK 0xc
+#define CQE_STATUS_NEED_BUFF_ENTRY 0xf
+
+/* Status returned by hardware (valid only if status = CQE_STATUS_SUCCESS). */
+#define CQE_HW_STATUS_NO_ERR 0x0
+#define CQE_HW_STATUS_UNDERRUN 0x1
+#define CQE_HW_STATUS_OVERRUN 0x2
+
+/* Completion Queue Entry Codes */
+#define CQE_CODE_COMPL_WQE 0x1
+#define CQE_CODE_RELEASE_WQE 0x2
+#define CQE_CODE_RECEIVE 0x4
+#define CQE_CODE_XRI_ABORTED 0x5
+
+/* completion queue entry for wqe completions */
+struct lpfc_wcqe_complete {
+ uint32_t word0;
+#define lpfc_wcqe_c_request_tag_SHIFT 16
+#define lpfc_wcqe_c_request_tag_MASK 0x0000FFFF
+#define lpfc_wcqe_c_request_tag_WORD word0
+#define lpfc_wcqe_c_status_SHIFT 8
+#define lpfc_wcqe_c_status_MASK 0x000000FF
+#define lpfc_wcqe_c_status_WORD word0
+#define lpfc_wcqe_c_hw_status_SHIFT 0
+#define lpfc_wcqe_c_hw_status_MASK 0x000000FF
+#define lpfc_wcqe_c_hw_status_WORD word0
+ uint32_t total_data_placed;
+ uint32_t parameter;
+ uint32_t word3;
+#define lpfc_wcqe_c_valid_SHIFT lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_c_valid_MASK lpfc_cqe_valid_MASK
+#define lpfc_wcqe_c_valid_WORD lpfc_cqe_valid_WORD
+#define lpfc_wcqe_c_xb_SHIFT 28
+#define lpfc_wcqe_c_xb_MASK 0x00000001
+#define lpfc_wcqe_c_xb_WORD word3
+#define lpfc_wcqe_c_pv_SHIFT 27
+#define lpfc_wcqe_c_pv_MASK 0x00000001
+#define lpfc_wcqe_c_pv_WORD word3
+#define lpfc_wcqe_c_priority_SHIFT 24
+#define lpfc_wcqe_c_priority_MASK 0x00000007
+#define lpfc_wcqe_c_priority_WORD word3
+#define lpfc_wcqe_c_code_SHIFT lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_c_code_MASK lpfc_cqe_code_MASK
+#define lpfc_wcqe_c_code_WORD lpfc_cqe_code_WORD
+};
+
+/* completion queue entry for wqe release */
+struct lpfc_wcqe_release {
+ uint32_t reserved0;
+ uint32_t reserved1;
+ uint32_t word2;
+#define lpfc_wcqe_r_wq_id_SHIFT 16
+#define lpfc_wcqe_r_wq_id_MASK 0x0000FFFF
+#define lpfc_wcqe_r_wq_id_WORD word2
+#define lpfc_wcqe_r_wqe_index_SHIFT 0
+#define lpfc_wcqe_r_wqe_index_MASK 0x0000FFFF
+#define lpfc_wcqe_r_wqe_index_WORD word2
+ uint32_t word3;
+#define lpfc_wcqe_r_valid_SHIFT lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_r_valid_MASK lpfc_cqe_valid_MASK
+#define lpfc_wcqe_r_valid_WORD lpfc_cqe_valid_WORD
+#define lpfc_wcqe_r_code_SHIFT lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_r_code_MASK lpfc_cqe_code_MASK
+#define lpfc_wcqe_r_code_WORD lpfc_cqe_code_WORD
+};
+
+struct sli4_wcqe_xri_aborted {
+ uint32_t word0;
+#define lpfc_wcqe_xa_status_SHIFT 8
+#define lpfc_wcqe_xa_status_MASK 0x000000FF
+#define lpfc_wcqe_xa_status_WORD word0
+ uint32_t parameter;
+ uint32_t word2;
+#define lpfc_wcqe_xa_remote_xid_SHIFT 16
+#define lpfc_wcqe_xa_remote_xid_MASK 0x0000FFFF
+#define lpfc_wcqe_xa_remote_xid_WORD word2
+#define lpfc_wcqe_xa_xri_SHIFT 0
+#define lpfc_wcqe_xa_xri_MASK 0x0000FFFF
+#define lpfc_wcqe_xa_xri_WORD word2
+ uint32_t word3;
+#define lpfc_wcqe_xa_valid_SHIFT lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_xa_valid_MASK lpfc_cqe_valid_MASK
+#define lpfc_wcqe_xa_valid_WORD lpfc_cqe_valid_WORD
+#define lpfc_wcqe_xa_ia_SHIFT 30
+#define lpfc_wcqe_xa_ia_MASK 0x00000001
+#define lpfc_wcqe_xa_ia_WORD word3
+#define CQE_XRI_ABORTED_IA_REMOTE 0
+#define CQE_XRI_ABORTED_IA_LOCAL 1
+#define lpfc_wcqe_xa_br_SHIFT 29
+#define lpfc_wcqe_xa_br_MASK 0x00000001
+#define lpfc_wcqe_xa_br_WORD word3
+#define CQE_XRI_ABORTED_BR_BA_ACC 0
+#define CQE_XRI_ABORTED_BR_BA_RJT 1
+#define lpfc_wcqe_xa_eo_SHIFT 28
+#define lpfc_wcqe_xa_eo_MASK 0x00000001
+#define lpfc_wcqe_xa_eo_WORD word3
+#define CQE_XRI_ABORTED_EO_REMOTE 0
+#define CQE_XRI_ABORTED_EO_LOCAL 1
+#define lpfc_wcqe_xa_code_SHIFT lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_xa_code_MASK lpfc_cqe_code_MASK
+#define lpfc_wcqe_xa_code_WORD lpfc_cqe_code_WORD
+};
+
+/* completion queue entry structure for rqe completion */
+struct lpfc_rcqe {
+ uint32_t word0;
+#define lpfc_rcqe_bindex_SHIFT 16
+#define lpfc_rcqe_bindex_MASK 0x0000FFF
+#define lpfc_rcqe_bindex_WORD word0
+#define lpfc_rcqe_status_SHIFT 8
+#define lpfc_rcqe_status_MASK 0x000000FF
+#define lpfc_rcqe_status_WORD word0
+#define FC_STATUS_RQ_SUCCESS 0x10 /* Async receive successful */
+#define FC_STATUS_RQ_BUF_LEN_EXCEEDED 0x11 /* payload truncated */
+#define FC_STATUS_INSUFF_BUF_NEED_BUF 0x12 /* Insufficient buffers */
+#define FC_STATUS_INSUFF_BUF_FRM_DISC 0x13 /* Frame Discard */
+ uint32_t reserved1;
+ uint32_t word2;
+#define lpfc_rcqe_length_SHIFT 16
+#define lpfc_rcqe_length_MASK 0x0000FFFF
+#define lpfc_rcqe_length_WORD word2
+#define lpfc_rcqe_rq_id_SHIFT 6
+#define lpfc_rcqe_rq_id_MASK 0x000003FF
+#define lpfc_rcqe_rq_id_WORD word2
+#define lpfc_rcqe_fcf_id_SHIFT 0
+#define lpfc_rcqe_fcf_id_MASK 0x0000003F
+#define lpfc_rcqe_fcf_id_WORD word2
+ uint32_t word3;
+#define lpfc_rcqe_valid_SHIFT lpfc_cqe_valid_SHIFT
+#define lpfc_rcqe_valid_MASK lpfc_cqe_valid_MASK
+#define lpfc_rcqe_valid_WORD lpfc_cqe_valid_WORD
+#define lpfc_rcqe_port_SHIFT 30
+#define lpfc_rcqe_port_MASK 0x00000001
+#define lpfc_rcqe_port_WORD word3
+#define lpfc_rcqe_hdr_length_SHIFT 24
+#define lpfc_rcqe_hdr_length_MASK 0x0000001F
+#define lpfc_rcqe_hdr_length_WORD word3
+#define lpfc_rcqe_code_SHIFT lpfc_cqe_code_SHIFT
+#define lpfc_rcqe_code_MASK lpfc_cqe_code_MASK
+#define lpfc_rcqe_code_WORD lpfc_cqe_code_WORD
+#define lpfc_rcqe_eof_SHIFT 8
+#define lpfc_rcqe_eof_MASK 0x000000FF
+#define lpfc_rcqe_eof_WORD word3
+#define FCOE_EOFn 0x41
+#define FCOE_EOFt 0x42
+#define FCOE_EOFni 0x49
+#define FCOE_EOFa 0x50
+#define lpfc_rcqe_sof_SHIFT 0
+#define lpfc_rcqe_sof_MASK 0x000000FF
+#define lpfc_rcqe_sof_WORD word3
+#define FCOE_SOFi2 0x2d
+#define FCOE_SOFi3 0x2e
+#define FCOE_SOFn2 0x35
+#define FCOE_SOFn3 0x36
+};
+
+struct lpfc_wqe_generic{
+ struct ulp_bde64 bde;
+ uint32_t word3;
+ uint32_t word4;
+ uint32_t word5;
+ uint32_t word6;
+#define lpfc_wqe_gen_context_SHIFT 16
+#define lpfc_wqe_gen_context_MASK 0x0000FFFF
+#define lpfc_wqe_gen_context_WORD word6
+#define lpfc_wqe_gen_xri_SHIFT 0
+#define lpfc_wqe_gen_xri_MASK 0x0000FFFF
+#define lpfc_wqe_gen_xri_WORD word6
+ uint32_t word7;
+#define lpfc_wqe_gen_lnk_SHIFT 23
+#define lpfc_wqe_gen_lnk_MASK 0x00000001
+#define lpfc_wqe_gen_lnk_WORD word7
+#define lpfc_wqe_gen_erp_SHIFT 22
+#define lpfc_wqe_gen_erp_MASK 0x00000001
+#define lpfc_wqe_gen_erp_WORD word7
+#define lpfc_wqe_gen_pu_SHIFT 20
+#define lpfc_wqe_gen_pu_MASK 0x00000003
+#define lpfc_wqe_gen_pu_WORD word7
+#define lpfc_wqe_gen_class_SHIFT 16
+#define lpfc_wqe_gen_class_MASK 0x00000007
+#define lpfc_wqe_gen_class_WORD word7
+#define lpfc_wqe_gen_command_SHIFT 8
+#define lpfc_wqe_gen_command_MASK 0x000000FF
+#define lpfc_wqe_gen_command_WORD word7
+#define lpfc_wqe_gen_status_SHIFT 4
+#define lpfc_wqe_gen_status_MASK 0x0000000F
+#define lpfc_wqe_gen_status_WORD word7
+#define lpfc_wqe_gen_ct_SHIFT 2
+#define lpfc_wqe_gen_ct_MASK 0x00000007
+#define lpfc_wqe_gen_ct_WORD word7
+ uint32_t abort_tag;
+ uint32_t word9;
+#define lpfc_wqe_gen_request_tag_SHIFT 0
+#define lpfc_wqe_gen_request_tag_MASK 0x0000FFFF
+#define lpfc_wqe_gen_request_tag_WORD word9
+ uint32_t word10;
+#define lpfc_wqe_gen_ccp_SHIFT 24
+#define lpfc_wqe_gen_ccp_MASK 0x000000FF
+#define lpfc_wqe_gen_ccp_WORD word10
+#define lpfc_wqe_gen_ccpe_SHIFT 23
+#define lpfc_wqe_gen_ccpe_MASK 0x00000001
+#define lpfc_wqe_gen_ccpe_WORD word10
+#define lpfc_wqe_gen_pv_SHIFT 19
+#define lpfc_wqe_gen_pv_MASK 0x00000001
+#define lpfc_wqe_gen_pv_WORD word10
+#define lpfc_wqe_gen_pri_SHIFT 16
+#define lpfc_wqe_gen_pri_MASK 0x00000007
+#define lpfc_wqe_gen_pri_WORD word10
+ uint32_t word11;
+#define lpfc_wqe_gen_cq_id_SHIFT 16
+#define lpfc_wqe_gen_cq_id_MASK 0x000003FF
+#define lpfc_wqe_gen_cq_id_WORD word11
+#define LPFC_WQE_CQ_ID_DEFAULT 0x3ff
+#define lpfc_wqe_gen_wqec_SHIFT 7
+#define lpfc_wqe_gen_wqec_MASK 0x00000001
+#define lpfc_wqe_gen_wqec_WORD word11
+#define lpfc_wqe_gen_cmd_type_SHIFT 0
+#define lpfc_wqe_gen_cmd_type_MASK 0x0000000F
+#define lpfc_wqe_gen_cmd_type_WORD word11
+ uint32_t payload[4];
+};
+
+struct lpfc_rqe {
+ uint32_t address_hi;
+ uint32_t address_lo;
+};
+
+/* buffer descriptors */
+struct lpfc_bde4 {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+ uint32_t word2;
+#define lpfc_bde4_last_SHIFT 31
+#define lpfc_bde4_last_MASK 0x00000001
+#define lpfc_bde4_last_WORD word2
+#define lpfc_bde4_sge_offset_SHIFT 0
+#define lpfc_bde4_sge_offset_MASK 0x000003FF
+#define lpfc_bde4_sge_offset_WORD word2
+ uint32_t word3;
+#define lpfc_bde4_length_SHIFT 0
+#define lpfc_bde4_length_MASK 0x000000FF
+#define lpfc_bde4_length_WORD word3
+};
+
+struct lpfc_register {
+ uint32_t word0;
+};
+
+#define LPFC_UERR_STATUS_HI 0x00A4
+#define LPFC_UERR_STATUS_LO 0x00A0
+#define LPFC_ONLINE0 0x00B0
+#define LPFC_ONLINE1 0x00B4
+#define LPFC_SCRATCHPAD 0x0058
+
+/* BAR0 Registers */
+#define LPFC_HST_STATE 0x00AC
+#define lpfc_hst_state_perr_SHIFT 31
+#define lpfc_hst_state_perr_MASK 0x1
+#define lpfc_hst_state_perr_WORD word0
+#define lpfc_hst_state_sfi_SHIFT 30
+#define lpfc_hst_state_sfi_MASK 0x1
+#define lpfc_hst_state_sfi_WORD word0
+#define lpfc_hst_state_nip_SHIFT 29
+#define lpfc_hst_state_nip_MASK 0x1
+#define lpfc_hst_state_nip_WORD word0
+#define lpfc_hst_state_ipc_SHIFT 28
+#define lpfc_hst_state_ipc_MASK 0x1
+#define lpfc_hst_state_ipc_WORD word0
+#define lpfc_hst_state_xrom_SHIFT 27
+#define lpfc_hst_state_xrom_MASK 0x1
+#define lpfc_hst_state_xrom_WORD word0
+#define lpfc_hst_state_dl_SHIFT 26
+#define lpfc_hst_state_dl_MASK 0x1
+#define lpfc_hst_state_dl_WORD word0
+#define lpfc_hst_state_port_status_SHIFT 0
+#define lpfc_hst_state_port_status_MASK 0xFFFF
+#define lpfc_hst_state_port_status_WORD word0
+
+#define LPFC_POST_STAGE_POWER_ON_RESET 0x0000
+#define LPFC_POST_STAGE_AWAITING_HOST_RDY 0x0001
+#define LPFC_POST_STAGE_HOST_RDY 0x0002
+#define LPFC_POST_STAGE_BE_RESET 0x0003
+#define LPFC_POST_STAGE_SEEPROM_CS_START 0x0100
+#define LPFC_POST_STAGE_SEEPROM_CS_DONE 0x0101
+#define LPFC_POST_STAGE_DDR_CONFIG_START 0x0200
+#define LPFC_POST_STAGE_DDR_CONFIG_DONE 0x0201
+#define LPFC_POST_STAGE_DDR_CALIBRATE_START 0x0300
+#define LPFC_POST_STAGE_DDR_CALIBRATE_DONE 0x0301
+#define LPFC_POST_STAGE_DDR_TEST_START 0x0400
+#define LPFC_POST_STAGE_DDR_TEST_DONE 0x0401
+#define LPFC_POST_STAGE_REDBOOT_INIT_START 0x0600
+#define LPFC_POST_STAGE_REDBOOT_INIT_DONE 0x0601
+#define LPFC_POST_STAGE_FW_IMAGE_LOAD_START 0x0700
+#define LPFC_POST_STAGE_FW_IMAGE_LOAD_DONE 0x0701
+#define LPFC_POST_STAGE_ARMFW_START 0x0800
+#define LPFC_POST_STAGE_DHCP_QUERY_START 0x0900
+#define LPFC_POST_STAGE_DHCP_QUERY_DONE 0x0901
+#define LPFC_POST_STAGE_BOOT_TARGET_DISCOVERY_START 0x0A00
+#define LPFC_POST_STAGE_BOOT_TARGET_DISCOVERY_DONE 0x0A01
+#define LPFC_POST_STAGE_RC_OPTION_SET 0x0B00
+#define LPFC_POST_STAGE_SWITCH_LINK 0x0B01
+#define LPFC_POST_STAGE_SEND_ICDS_MESSAGE 0x0B02
+#define LPFC_POST_STAGE_PERFROM_TFTP 0x0B03
+#define LPFC_POST_STAGE_PARSE_XML 0x0B04
+#define LPFC_POST_STAGE_DOWNLOAD_IMAGE 0x0B05
+#define LPFC_POST_STAGE_FLASH_IMAGE 0x0B06
+#define LPFC_POST_STAGE_RC_DONE 0x0B07
+#define LPFC_POST_STAGE_REBOOT_SYSTEM 0x0B08
+#define LPFC_POST_STAGE_MAC_ADDRESS 0x0C00
+#define LPFC_POST_STAGE_ARMFW_READY 0xC000
+#define LPFC_POST_STAGE_ARMFW_UE 0xF000
+
+#define lpfc_scratchpad_slirev_SHIFT 4
+#define lpfc_scratchpad_slirev_MASK 0xF
+#define lpfc_scratchpad_slirev_WORD word0
+#define lpfc_scratchpad_chiptype_SHIFT 8
+#define lpfc_scratchpad_chiptype_MASK 0xFF
+#define lpfc_scratchpad_chiptype_WORD word0
+#define lpfc_scratchpad_featurelevel1_SHIFT 16
+#define lpfc_scratchpad_featurelevel1_MASK 0xFF
+#define lpfc_scratchpad_featurelevel1_WORD word0
+#define lpfc_scratchpad_featurelevel2_SHIFT 24
+#define lpfc_scratchpad_featurelevel2_MASK 0xFF
+#define lpfc_scratchpad_featurelevel2_WORD word0
+
+/* BAR1 Registers */
+#define LPFC_IMR_MASK_ALL 0xFFFFFFFF
+#define LPFC_ISCR_CLEAR_ALL 0xFFFFFFFF
+
+#define LPFC_HST_ISR0 0x0C18
+#define LPFC_HST_ISR1 0x0C1C
+#define LPFC_HST_ISR2 0x0C20
+#define LPFC_HST_ISR3 0x0C24
+#define LPFC_HST_ISR4 0x0C28
+
+#define LPFC_HST_IMR0 0x0C48
+#define LPFC_HST_IMR1 0x0C4C
+#define LPFC_HST_IMR2 0x0C50
+#define LPFC_HST_IMR3 0x0C54
+#define LPFC_HST_IMR4 0x0C58
+
+#define LPFC_HST_ISCR0 0x0C78
+#define LPFC_HST_ISCR1 0x0C7C
+#define LPFC_HST_ISCR2 0x0C80
+#define LPFC_HST_ISCR3 0x0C84
+#define LPFC_HST_ISCR4 0x0C88
+
+#define LPFC_SLI4_INTR0 BIT0
+#define LPFC_SLI4_INTR1 BIT1
+#define LPFC_SLI4_INTR2 BIT2
+#define LPFC_SLI4_INTR3 BIT3
+#define LPFC_SLI4_INTR4 BIT4
+#define LPFC_SLI4_INTR5 BIT5
+#define LPFC_SLI4_INTR6 BIT6
+#define LPFC_SLI4_INTR7 BIT7
+#define LPFC_SLI4_INTR8 BIT8
+#define LPFC_SLI4_INTR9 BIT9
+#define LPFC_SLI4_INTR10 BIT10
+#define LPFC_SLI4_INTR11 BIT11
+#define LPFC_SLI4_INTR12 BIT12
+#define LPFC_SLI4_INTR13 BIT13
+#define LPFC_SLI4_INTR14 BIT14
+#define LPFC_SLI4_INTR15 BIT15
+#define LPFC_SLI4_INTR16 BIT16
+#define LPFC_SLI4_INTR17 BIT17
+#define LPFC_SLI4_INTR18 BIT18
+#define LPFC_SLI4_INTR19 BIT19
+#define LPFC_SLI4_INTR20 BIT20
+#define LPFC_SLI4_INTR21 BIT21
+#define LPFC_SLI4_INTR22 BIT22
+#define LPFC_SLI4_INTR23 BIT23
+#define LPFC_SLI4_INTR24 BIT24
+#define LPFC_SLI4_INTR25 BIT25
+#define LPFC_SLI4_INTR26 BIT26
+#define LPFC_SLI4_INTR27 BIT27
+#define LPFC_SLI4_INTR28 BIT28
+#define LPFC_SLI4_INTR29 BIT29
+#define LPFC_SLI4_INTR30 BIT30
+#define LPFC_SLI4_INTR31 BIT31
+
+/* BAR2 Registers */
+#define LPFC_RQ_DOORBELL 0x00A0
+#define lpfc_rq_doorbell_num_posted_SHIFT 16
+#define lpfc_rq_doorbell_num_posted_MASK 0x3FFF
+#define lpfc_rq_doorbell_num_posted_WORD word0
+#define LPFC_RQ_POST_BATCH 8 /* RQEs to post at one time */
+#define lpfc_rq_doorbell_id_SHIFT 0
+#define lpfc_rq_doorbell_id_MASK 0x03FF
+#define lpfc_rq_doorbell_id_WORD word0
+
+#define LPFC_WQ_DOORBELL 0x0040
+#define lpfc_wq_doorbell_num_posted_SHIFT 24
+#define lpfc_wq_doorbell_num_posted_MASK 0x00FF
+#define lpfc_wq_doorbell_num_posted_WORD word0
+#define lpfc_wq_doorbell_index_SHIFT 16
+#define lpfc_wq_doorbell_index_MASK 0x00FF
+#define lpfc_wq_doorbell_index_WORD word0
+#define lpfc_wq_doorbell_id_SHIFT 0
+#define lpfc_wq_doorbell_id_MASK 0xFFFF
+#define lpfc_wq_doorbell_id_WORD word0
+
+#define LPFC_EQCQ_DOORBELL 0x0120
+#define lpfc_eqcq_doorbell_arm_SHIFT 29
+#define lpfc_eqcq_doorbell_arm_MASK 0x0001
+#define lpfc_eqcq_doorbell_arm_WORD word0
+#define lpfc_eqcq_doorbell_num_released_SHIFT 16
+#define lpfc_eqcq_doorbell_num_released_MASK 0x1FFF
+#define lpfc_eqcq_doorbell_num_released_WORD word0
+#define lpfc_eqcq_doorbell_qt_SHIFT 10
+#define lpfc_eqcq_doorbell_qt_MASK 0x0001
+#define lpfc_eqcq_doorbell_qt_WORD word0
+#define LPFC_QUEUE_TYPE_COMPLETION 0
+#define LPFC_QUEUE_TYPE_EVENT 1
+#define lpfc_eqcq_doorbell_eqci_SHIFT 9
+#define lpfc_eqcq_doorbell_eqci_MASK 0x0001
+#define lpfc_eqcq_doorbell_eqci_WORD word0
+#define lpfc_eqcq_doorbell_cqid_SHIFT 0
+#define lpfc_eqcq_doorbell_cqid_MASK 0x03FF
+#define lpfc_eqcq_doorbell_cqid_WORD word0
+#define lpfc_eqcq_doorbell_eqid_SHIFT 0
+#define lpfc_eqcq_doorbell_eqid_MASK 0x01FF
+#define lpfc_eqcq_doorbell_eqid_WORD word0
+
+#define LPFC_BMBX 0x0160
+#define lpfc_bmbx_addr_SHIFT 2
+#define lpfc_bmbx_addr_MASK 0x3FFFFFFF
+#define lpfc_bmbx_addr_WORD word0
+#define lpfc_bmbx_hi_SHIFT 1
+#define lpfc_bmbx_hi_MASK 0x0001
+#define lpfc_bmbx_hi_WORD word0
+#define lpfc_bmbx_rdy_SHIFT 0
+#define lpfc_bmbx_rdy_MASK 0x0001
+#define lpfc_bmbx_rdy_WORD word0
+
+#define LPFC_MQ_DOORBELL 0x0140
+#define lpfc_mq_doorbell_num_posted_SHIFT 16
+#define lpfc_mq_doorbell_num_posted_MASK 0x3FFF
+#define lpfc_mq_doorbell_num_posted_WORD word0
+#define lpfc_mq_doorbell_id_SHIFT 0
+#define lpfc_mq_doorbell_id_MASK 0x03FF
+#define lpfc_mq_doorbell_id_WORD word0
+
+struct lpfc_sli4_cfg_mhdr {
+ uint32_t word1;
+#define lpfc_mbox_hdr_emb_SHIFT 0
+#define lpfc_mbox_hdr_emb_MASK 0x00000001
+#define lpfc_mbox_hdr_emb_WORD word1
+#define lpfc_mbox_hdr_sge_cnt_SHIFT 3
+#define lpfc_mbox_hdr_sge_cnt_MASK 0x0000001F
+#define lpfc_mbox_hdr_sge_cnt_WORD word1
+ uint32_t payload_length;
+ uint32_t tag_lo;
+ uint32_t tag_hi;
+ uint32_t reserved5;
+};
+
+union lpfc_sli4_cfg_shdr {
+ struct {
+ uint32_t word6;
+#define lpfc_mbox_hdr_opcode_SHIFT 0
+#define lpfc_mbox_hdr_opcode_MASK 0x000000FF
+#define lpfc_mbox_hdr_opcode_WORD word6
+#define lpfc_mbox_hdr_subsystem_SHIFT 8
+#define lpfc_mbox_hdr_subsystem_MASK 0x000000FF
+#define lpfc_mbox_hdr_subsystem_WORD word6
+#define lpfc_mbox_hdr_port_number_SHIFT 16
+#define lpfc_mbox_hdr_port_number_MASK 0x000000FF
+#define lpfc_mbox_hdr_port_number_WORD word6
+#define lpfc_mbox_hdr_domain_SHIFT 24
+#define lpfc_mbox_hdr_domain_MASK 0x000000FF
+#define lpfc_mbox_hdr_domain_WORD word6
+ uint32_t timeout;
+ uint32_t request_length;
+ uint32_t reserved9;
+ } request;
+ struct {
+ uint32_t word6;
+#define lpfc_mbox_hdr_opcode_SHIFT 0
+#define lpfc_mbox_hdr_opcode_MASK 0x000000FF
+#define lpfc_mbox_hdr_opcode_WORD word6
+#define lpfc_mbox_hdr_subsystem_SHIFT 8
+#define lpfc_mbox_hdr_subsystem_MASK 0x000000FF
+#define lpfc_mbox_hdr_subsystem_WORD word6
+#define lpfc_mbox_hdr_domain_SHIFT 24
+#define lpfc_mbox_hdr_domain_MASK 0x000000FF
+#define lpfc_mbox_hdr_domain_WORD word6
+ uint32_t word7;
+#define lpfc_mbox_hdr_status_SHIFT 0
+#define lpfc_mbox_hdr_status_MASK 0x000000FF
+#define lpfc_mbox_hdr_status_WORD word7
+#define lpfc_mbox_hdr_add_status_SHIFT 8
+#define lpfc_mbox_hdr_add_status_MASK 0x000000FF
+#define lpfc_mbox_hdr_add_status_WORD word7
+ uint32_t response_length;
+ uint32_t actual_response_length;
+ } response;
+};
+
+/* Mailbox structures */
+struct mbox_header {
+ struct lpfc_sli4_cfg_mhdr cfg_mhdr;
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+};
+
+/* Subsystem Definitions */
+#define LPFC_MBOX_SUBSYSTEM_COMMON 0x1
+#define LPFC_MBOX_SUBSYSTEM_FCOE 0xC
+
+/* Device Specific Definitions */
+
+/* The HOST ENDIAN defines are in Big Endian format. */
+#define HOST_ENDIAN_LOW_WORD0 0xFF3412FF
+#define HOST_ENDIAN_HIGH_WORD1 0xFF7856FF
+
+/* Common Opcodes */
+#define LPFC_MBOX_OPCODE_CQ_CREATE 0x0C
+#define LPFC_MBOX_OPCODE_EQ_CREATE 0x0D
+#define LPFC_MBOX_OPCODE_MQ_CREATE 0x15
+#define LPFC_MBOX_OPCODE_GET_CNTL_ATTRIBUTES 0x20
+#define LPFC_MBOX_OPCODE_NOP 0x21
+#define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35
+#define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36
+#define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37
+#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
+
+/* FCoE Opcodes */
+#define LPFC_MBOX_OPCODE_FCOE_WQ_CREATE 0x01
+#define LPFC_MBOX_OPCODE_FCOE_WQ_DESTROY 0x02
+#define LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES 0x03
+#define LPFC_MBOX_OPCODE_FCOE_REMOVE_SGL_PAGES 0x04
+#define LPFC_MBOX_OPCODE_FCOE_RQ_CREATE 0x05
+#define LPFC_MBOX_OPCODE_FCOE_RQ_DESTROY 0x06
+#define LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE 0x08
+#define LPFC_MBOX_OPCODE_FCOE_ADD_FCF 0x09
+#define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A
+#define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B
+
+/* Mailbox command structures */
+struct eq_context {
+ uint32_t word0;
+#define lpfc_eq_context_size_SHIFT 31
+#define lpfc_eq_context_size_MASK 0x00000001
+#define lpfc_eq_context_size_WORD word0
+#define LPFC_EQE_SIZE_4 0x0
+#define LPFC_EQE_SIZE_16 0x1
+#define lpfc_eq_context_valid_SHIFT 29
+#define lpfc_eq_context_valid_MASK 0x00000001
+#define lpfc_eq_context_valid_WORD word0
+ uint32_t word1;
+#define lpfc_eq_context_count_SHIFT 26
+#define lpfc_eq_context_count_MASK 0x00000003
+#define lpfc_eq_context_count_WORD word1
+#define LPFC_EQ_CNT_256 0x0
+#define LPFC_EQ_CNT_512 0x1
+#define LPFC_EQ_CNT_1024 0x2
+#define LPFC_EQ_CNT_2048 0x3
+#define LPFC_EQ_CNT_4096 0x4
+ uint32_t word2;
+#define lpfc_eq_context_delay_multi_SHIFT 13
+#define lpfc_eq_context_delay_multi_MASK 0x000003FF
+#define lpfc_eq_context_delay_multi_WORD word2
+ uint32_t reserved3;
+};
+
+struct sgl_page_pairs {
+ uint32_t sgl_pg0_addr_lo;
+ uint32_t sgl_pg0_addr_hi;
+ uint32_t sgl_pg1_addr_lo;
+ uint32_t sgl_pg1_addr_hi;
+};
+
+struct lpfc_mbx_post_sgl_pages {
+ struct mbox_header header;
+ uint32_t word0;
+#define lpfc_post_sgl_pages_xri_SHIFT 0
+#define lpfc_post_sgl_pages_xri_MASK 0x0000FFFF
+#define lpfc_post_sgl_pages_xri_WORD word0
+#define lpfc_post_sgl_pages_xricnt_SHIFT 16
+#define lpfc_post_sgl_pages_xricnt_MASK 0x0000FFFF
+#define lpfc_post_sgl_pages_xricnt_WORD word0
+ struct sgl_page_pairs sgl_pg_pairs[1];
+};
+
+/* word0 of page-1 struct shares the same SHIFT/MASK/WORD defines as above */
+struct lpfc_mbx_post_uembed_sgl_page1 {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ uint32_t word0;
+ struct sgl_page_pairs sgl_pg_pairs;
+};
+
+struct lpfc_mbx_sge {
+ uint32_t pa_lo;
+ uint32_t pa_hi;
+ uint32_t length;
+};
+
+struct lpfc_mbx_nembed_cmd {
+ struct lpfc_sli4_cfg_mhdr cfg_mhdr;
+#define LPFC_SLI4_MBX_SGE_MAX_PAGES 19
+ struct lpfc_mbx_sge sge[LPFC_SLI4_MBX_SGE_MAX_PAGES];
+};
+
+struct lpfc_mbx_nembed_sge_virt {
+ void *addr[LPFC_SLI4_MBX_SGE_MAX_PAGES];
+};
+
+struct lpfc_mbx_eq_create {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_eq_create_num_pages_SHIFT 0
+#define lpfc_mbx_eq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_eq_create_num_pages_WORD word0
+ struct eq_context context;
+ struct dma_address page[LPFC_MAX_EQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_eq_create_q_id_SHIFT 0
+#define lpfc_mbx_eq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_eq_create_q_id_WORD word0
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_eq_destroy {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_eq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_eq_destroy_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_eq_destroy_q_id_WORD word0
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_nop {
+ struct mbox_header header;
+ uint32_t context[2];
+};
+
+struct cq_context {
+ uint32_t word0;
+#define lpfc_cq_context_event_SHIFT 31
+#define lpfc_cq_context_event_MASK 0x00000001
+#define lpfc_cq_context_event_WORD word0
+#define lpfc_cq_context_valid_SHIFT 29
+#define lpfc_cq_context_valid_MASK 0x00000001
+#define lpfc_cq_context_valid_WORD word0
+#define lpfc_cq_context_count_SHIFT 27
+#define lpfc_cq_context_count_MASK 0x00000003
+#define lpfc_cq_context_count_WORD word0
+#define LPFC_CQ_CNT_256 0x0
+#define LPFC_CQ_CNT_512 0x1
+#define LPFC_CQ_CNT_1024 0x2
+ uint32_t word1;
+#define lpfc_cq_eq_id_SHIFT 22
+#define lpfc_cq_eq_id_MASK 0x000000FF
+#define lpfc_cq_eq_id_WORD word1
+ uint32_t reserved0;
+ uint32_t reserved1;
+};
+
+struct lpfc_mbx_cq_create {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_cq_create_num_pages_SHIFT 0
+#define lpfc_mbx_cq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_num_pages_WORD word0
+ struct cq_context context;
+ struct dma_address page[LPFC_MAX_CQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_cq_create_q_id_SHIFT 0
+#define lpfc_mbx_cq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_q_id_WORD word0
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_cq_destroy {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_cq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_cq_destroy_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_cq_destroy_q_id_WORD word0
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+struct wq_context {
+ uint32_t reserved0;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t reserved3;
+};
+
+struct lpfc_mbx_wq_create {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_wq_create_num_pages_SHIFT 0
+#define lpfc_mbx_wq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_num_pages_WORD word0
+#define lpfc_mbx_wq_create_cq_id_SHIFT 16
+#define lpfc_mbx_wq_create_cq_id_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_cq_id_WORD word0
+ struct dma_address page[LPFC_MAX_WQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_wq_create_q_id_SHIFT 0
+#define lpfc_mbx_wq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_q_id_WORD word0
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_wq_destroy {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_wq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_wq_destroy_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_wq_destroy_q_id_WORD word0
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+#define LPFC_HDR_BUF_SIZE 128
+#define LPFC_DATA_BUF_SIZE 4096
+struct rq_context {
+ uint32_t word0;
+#define lpfc_rq_context_rq_size_SHIFT 16
+#define lpfc_rq_context_rq_size_MASK 0x0000000F
+#define lpfc_rq_context_rq_size_WORD word0
+#define LPFC_RQ_RING_SIZE_512 9 /* 512 entries */
+#define LPFC_RQ_RING_SIZE_1024 10 /* 1024 entries */
+#define LPFC_RQ_RING_SIZE_2048 11 /* 2048 entries */
+#define LPFC_RQ_RING_SIZE_4096 12 /* 4096 entries */
+ uint32_t reserved1;
+ uint32_t word2;
+#define lpfc_rq_context_cq_id_SHIFT 16
+#define lpfc_rq_context_cq_id_MASK 0x000003FF
+#define lpfc_rq_context_cq_id_WORD word2
+#define lpfc_rq_context_buf_size_SHIFT 0
+#define lpfc_rq_context_buf_size_MASK 0x0000FFFF
+#define lpfc_rq_context_buf_size_WORD word2
+ uint32_t reserved3;
+};
+
+struct lpfc_mbx_rq_create {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rq_create_num_pages_SHIFT 0
+#define lpfc_mbx_rq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_num_pages_WORD word0
+ struct rq_context context;
+ struct dma_address page[LPFC_MAX_WQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rq_create_q_id_SHIFT 0
+#define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_q_id_WORD word0
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_rq_destroy {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_rq_destroy_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_rq_destroy_q_id_WORD word0
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+struct mq_context {
+ uint32_t word0;
+#define lpfc_mq_context_cq_id_SHIFT 22
+#define lpfc_mq_context_cq_id_MASK 0x000003FF
+#define lpfc_mq_context_cq_id_WORD word0
+#define lpfc_mq_context_count_SHIFT 16
+#define lpfc_mq_context_count_MASK 0x0000000F
+#define lpfc_mq_context_count_WORD word0
+#define LPFC_MQ_CNT_16 0x5
+#define LPFC_MQ_CNT_32 0x6
+#define LPFC_MQ_CNT_64 0x7
+#define LPFC_MQ_CNT_128 0x8
+ uint32_t word1;
+#define lpfc_mq_context_valid_SHIFT 31
+#define lpfc_mq_context_valid_MASK 0x00000001
+#define lpfc_mq_context_valid_WORD word1
+ uint32_t reserved2;
+ uint32_t reserved3;
+};
+
+struct lpfc_mbx_mq_create {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_mq_create_num_pages_SHIFT 0
+#define lpfc_mbx_mq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_mq_create_num_pages_WORD word0
+ struct mq_context context;
+ struct dma_address page[LPFC_MAX_MQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_mq_create_q_id_SHIFT 0
+#define lpfc_mbx_mq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_mq_create_q_id_WORD word0
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_mq_destroy {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_mq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_mq_destroy_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_mq_destroy_q_id_WORD word0
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_post_hdr_tmpl {
+ struct mbox_header header;
+ uint32_t word10;
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_SHIFT 0
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_MASK 0x0000FFFF
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_WORD word10
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_SHIFT 16
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_MASK 0x0000FFFF
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_WORD word10
+ uint32_t rpi_paddr_lo;
+ uint32_t rpi_paddr_hi;
+};
+
+struct sli4_sge { /* SLI-4 */
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+
+ uint32_t word2;
+#define lpfc_sli4_sge_offset_SHIFT 0 /* Offset of buffer - Not used*/
+#define lpfc_sli4_sge_offset_MASK 0x00FFFFFF
+#define lpfc_sli4_sge_offset_WORD word2
+#define lpfc_sli4_sge_last_SHIFT 31 /* Last SEG in the SGL sets
+ this flag !! */
+#define lpfc_sli4_sge_last_MASK 0x00000001
+#define lpfc_sli4_sge_last_WORD word2
+ uint32_t word3;
+#define lpfc_sli4_sge_len_SHIFT 0
+#define lpfc_sli4_sge_len_MASK 0x0001FFFF
+#define lpfc_sli4_sge_len_WORD word3
+};
+
+struct fcf_record {
+ uint32_t max_rcv_size;
+ uint32_t fka_adv_period;
+ uint32_t fip_priority;
+ uint32_t word3;
+#define lpfc_fcf_record_mac_0_SHIFT 0
+#define lpfc_fcf_record_mac_0_MASK 0x000000FF
+#define lpfc_fcf_record_mac_0_WORD word3
+#define lpfc_fcf_record_mac_1_SHIFT 8
+#define lpfc_fcf_record_mac_1_MASK 0x000000FF
+#define lpfc_fcf_record_mac_1_WORD word3
+#define lpfc_fcf_record_mac_2_SHIFT 16
+#define lpfc_fcf_record_mac_2_MASK 0x000000FF
+#define lpfc_fcf_record_mac_2_WORD word3
+#define lpfc_fcf_record_mac_3_SHIFT 24
+#define lpfc_fcf_record_mac_3_MASK 0x000000FF
+#define lpfc_fcf_record_mac_3_WORD word3
+ uint32_t word4;
+#define lpfc_fcf_record_mac_4_SHIFT 0
+#define lpfc_fcf_record_mac_4_MASK 0x000000FF
+#define lpfc_fcf_record_mac_4_WORD word4
+#define lpfc_fcf_record_mac_5_SHIFT 8
+#define lpfc_fcf_record_mac_5_MASK 0x000000FF
+#define lpfc_fcf_record_mac_5_WORD word4
+#define lpfc_fcf_record_fcf_avail_SHIFT 16
+#define lpfc_fcf_record_fcf_avail_MASK 0x000000FF
+#define lpfc_fcf_record_fc_avail_WORD word4
+#define lpfc_fcf_record_mac_addr_prov_SHIFT 24
+#define lpfc_fcf_record_mac_addr_prov_MASK 0x000000FF
+#define lpfc_fcf_record_mac_addr_prov_WORD word4
+#define LPFC_FCF_FPMA 1 /* Fabric Provided MAC Address */
+#define LPFC_FCF_SPMA 2 /* Server Provided MAC Address */
+ uint32_t word5;
+#define lpfc_fcf_record_fab_name_0_SHIFT 0
+#define lpfc_fcf_record_fab_name_0_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_0_WORD word5
+#define lpfc_fcf_record_fab_name_1_SHIFT 8
+#define lpfc_fcf_record_fab_name_1_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_1_WORD word5
+#define lpfc_fcf_record_fab_name_2_SHIFT 16
+#define lpfc_fcf_record_fab_name_2_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_2_WORD word5
+#define lpfc_fcf_record_fab_name_3_SHIFT 24
+#define lpfc_fcf_record_fab_name_3_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_3_WORD word5
+ uint32_t word6;
+#define lpfc_fcf_record_fab_name_4_SHIFT 0
+#define lpfc_fcf_record_fab_name_4_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_4_WORD word6
+#define lpfc_fcf_record_fab_name_5_SHIFT 8
+#define lpfc_fcf_record_fab_name_5_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_5_WORD word6
+#define lpfc_fcf_record_fab_name_6_SHIFT 16
+#define lpfc_fcf_record_fab_name_6_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_6_WORD word6
+#define lpfc_fcf_record_fab_name_7_SHIFT 24
+#define lpfc_fcf_record_fab_name_7_MASK 0x000000FF
+#define lpfc_fcf_record_fab_name_7_WORD word6
+ uint32_t word7;
+#define lpfc_fcf_record_fc_map_0_SHIFT 0
+#define lpfc_fcf_record_fc_map_0_MASK 0x000000FF
+#define lpfc_fcf_record_fc_map_0_WORD word7
+#define lpfc_fcf_record_fc_map_1_SHIFT 8
+#define lpfc_fcf_record_fc_map_1_MASK 0x000000FF
+#define lpfc_fcf_record_fc_map_1_WORD word7
+#define lpfc_fcf_record_fc_map_2_SHIFT 16
+#define lpfc_fcf_record_fc_map_2_MASK 0x000000FF
+#define lpfc_fcf_record_fc_map_2_WORD word7
+#define lpfc_fcf_record_fcf_valid_SHIFT 24
+#define lpfc_fcf_record_fcf_valid_MASK 0x000000FF
+#define lpfc_fcf_record_fcf_valid_WORD word7
+ uint32_t word8;
+#define lpfc_fcf_record_fcf_index_SHIFT 0
+#define lpfc_fcf_record_fcf_index_MASK 0x0000FFFF
+#define lpfc_fcf_record_fcf_index_WORD word8
+#define lpfc_fcf_record_fcf_state_SHIFT 16
+#define lpfc_fcf_record_fcf_state_MASK 0x0000FFFF
+#define lpfc_fcf_record_fcf_state_WORD word8
+ uint8_t vlan_bitmap[512];
+};
+
+struct lpfc_mbx_read_fcf_tbl {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ union {
+ struct {
+ uint32_t word10;
+#define lpfc_mbx_read_fcf_tbl_indx_SHIFT 0
+#define lpfc_mbx_read_fcf_tbl_indx_MASK 0x0000FFFF
+#define lpfc_mbx_read_fcf_tbl_indx_WORD word10
+ } request;
+ struct {
+ uint32_t eventag;
+ } response;
+ } u;
+ uint32_t word11;
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_SHIFT 0
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_MASK 0x0000FFFF
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_WORD word11
+};
+
+struct lpfc_mbx_add_fcf_tbl_entry {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ uint32_t word10;
+#define lpfc_mbx_add_fcf_tbl_fcfi_SHIFT 0
+#define lpfc_mbx_add_fcf_tbl_fcfi_MASK 0x0000FFFF
+#define lpfc_mbx_add_fcf_tbl_fcfi_WORD word10
+ struct lpfc_mbx_sge fcf_sge;
+};
+
+struct lpfc_mbx_del_fcf_tbl_entry {
+ struct mbox_header header;
+ uint32_t word10;
+#define lpfc_mbx_del_fcf_tbl_count_SHIFT 0
+#define lpfc_mbx_del_fcf_tbl_count_MASK 0x0000FFFF
+#define lpfc_mbx_del_fcf_tbl_count_WORD word10
+#define lpfc_mbx_del_fcf_tbl_index_SHIFT 16
+#define lpfc_mbx_del_fcf_tbl_index_MASK 0x0000FFFF
+#define lpfc_mbx_del_fcf_tbl_index_WORD word10
+};
+
+/* Status field for embedded SLI_CONFIG mailbox command */
+#define STATUS_SUCCESS 0x0
+#define STATUS_FAILED 0x1
+#define STATUS_ILLEGAL_REQUEST 0x2
+#define STATUS_ILLEGAL_FIELD 0x3
+#define STATUS_INSUFFICIENT_BUFFER 0x4
+#define STATUS_UNAUTHORIZED_REQUEST 0x5
+#define STATUS_FLASHROM_SAVE_FAILED 0x17
+#define STATUS_FLASHROM_RESTORE_FAILED 0x18
+#define STATUS_ICCBINDEX_ALLOC_FAILED 0x1a
+#define STATUS_IOCTLHANDLE_ALLOC_FAILED 0x1b
+#define STATUS_INVALID_PHY_ADDR_FROM_OSM 0x1c
+#define STATUS_INVALID_PHY_ADDR_LEN_FROM_OSM 0x1d
+#define STATUS_ASSERT_FAILED 0x1e
+#define STATUS_INVALID_SESSION 0x1f
+#define STATUS_INVALID_CONNECTION 0x20
+#define STATUS_BTL_PATH_EXCEEDS_OSM_LIMIT 0x21
+#define STATUS_BTL_NO_FREE_SLOT_PATH 0x24
+#define STATUS_BTL_NO_FREE_SLOT_TGTID 0x25
+#define STATUS_OSM_DEVSLOT_NOT_FOUND 0x26
+#define STATUS_FLASHROM_READ_FAILED 0x27
+#define STATUS_POLL_IOCTL_TIMEOUT 0x28
+#define STATUS_ERROR_ACITMAIN 0x2a
+#define STATUS_REBOOT_REQUIRED 0x2c
+#define STATUS_FCF_IN_USE 0x3a
+
+struct lpfc_mbx_sli4_config {
+ struct mbox_header header;
+};
+
+struct lpfc_mbx_init_vfi {
+ uint32_t word1;
+#define lpfc_init_vfi_vr_SHIFT 31
+#define lpfc_init_vfi_vr_MASK 0x00000001
+#define lpfc_init_vfi_vr_WORD word1
+#define lpfc_init_vfi_vt_SHIFT 30
+#define lpfc_init_vfi_vt_MASK 0x00000001
+#define lpfc_init_vfi_vt_WORD word1
+#define lpfc_init_vfi_vf_SHIFT 29
+#define lpfc_init_vfi_vf_MASK 0x00000001
+#define lpfc_init_vfi_vf_WORD word1
+#define lpfc_init_vfi_vfi_SHIFT 0
+#define lpfc_init_vfi_vfi_MASK 0x0000FFFF
+#define lpfc_init_vfi_vfi_WORD word1
+ uint32_t word2;
+#define lpfc_init_vfi_fcfi_SHIFT 0
+#define lpfc_init_vfi_fcfi_MASK 0x0000FFFF
+#define lpfc_init_vfi_fcfi_WORD word2
+ uint32_t word3;
+#define lpfc_init_vfi_pri_SHIFT 13
+#define lpfc_init_vfi_pri_MASK 0x00000007
+#define lpfc_init_vfi_pri_WORD word3
+#define lpfc_init_vfi_vf_id_SHIFT 1
+#define lpfc_init_vfi_vf_id_MASK 0x00000FFF
+#define lpfc_init_vfi_vf_id_WORD word3
+ uint32_t word4;
+#define lpfc_init_vfi_hop_count_SHIFT 24
+#define lpfc_init_vfi_hop_count_MASK 0x000000FF
+#define lpfc_init_vfi_hop_count_WORD word4
+};
+
+struct lpfc_mbx_reg_vfi {
+ uint32_t word1;
+#define lpfc_reg_vfi_vp_SHIFT 28
+#define lpfc_reg_vfi_vp_MASK 0x00000001
+#define lpfc_reg_vfi_vp_WORD word1
+#define lpfc_reg_vfi_vfi_SHIFT 0
+#define lpfc_reg_vfi_vfi_MASK 0x0000FFFF
+#define lpfc_reg_vfi_vfi_WORD word1
+ uint32_t word2;
+#define lpfc_reg_vfi_vpi_SHIFT 16
+#define lpfc_reg_vfi_vpi_MASK 0x0000FFFF
+#define lpfc_reg_vfi_vpi_WORD word2
+#define lpfc_reg_vfi_fcfi_SHIFT 0
+#define lpfc_reg_vfi_fcfi_MASK 0x0000FFFF
+#define lpfc_reg_vfi_fcfi_WORD word2
+ uint32_t word3_rsvd;
+ uint32_t word4_rsvd;
+ struct ulp_bde64 bde;
+ uint32_t word8_rsvd;
+ uint32_t word9_rsvd;
+ uint32_t word10;
+#define lpfc_reg_vfi_nport_id_SHIFT 0
+#define lpfc_reg_vfi_nport_id_MASK 0x00FFFFFF
+#define lpfc_reg_vfi_nport_id_WORD word10
+};
+
+struct lpfc_mbx_init_vpi {
+ uint32_t word1;
+#define lpfc_init_vpi_vfi_SHIFT 16
+#define lpfc_init_vpi_vfi_MASK 0x0000FFFF
+#define lpfc_init_vpi_vfi_WORD word1
+#define lpfc_init_vpi_vpi_SHIFT 0
+#define lpfc_init_vpi_vpi_MASK 0x0000FFFF
+#define lpfc_init_vpi_vpi_WORD word1
+};
+
+struct lpfc_mbx_read_vpi {
+ uint32_t word1_rsvd;
+ uint32_t word2;
+#define lpfc_mbx_read_vpi_vnportid_SHIFT 0
+#define lpfc_mbx_read_vpi_vnportid_MASK 0x00FFFFFF
+#define lpfc_mbx_read_vpi_vnportid_WORD word2
+ uint32_t word3_rsvd;
+ uint32_t word4;
+#define lpfc_mbx_read_vpi_acq_alpa_SHIFT 0
+#define lpfc_mbx_read_vpi_acq_alpa_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_acq_alpa_WORD word4
+#define lpfc_mbx_read_vpi_pb_SHIFT 15
+#define lpfc_mbx_read_vpi_pb_MASK 0x00000001
+#define lpfc_mbx_read_vpi_pb_WORD word4
+#define lpfc_mbx_read_vpi_spec_alpa_SHIFT 16
+#define lpfc_mbx_read_vpi_spec_alpa_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_spec_alpa_WORD word4
+#define lpfc_mbx_read_vpi_ns_SHIFT 30
+#define lpfc_mbx_read_vpi_ns_MASK 0x00000001
+#define lpfc_mbx_read_vpi_ns_WORD word4
+#define lpfc_mbx_read_vpi_hl_SHIFT 31
+#define lpfc_mbx_read_vpi_hl_MASK 0x00000001
+#define lpfc_mbx_read_vpi_hl_WORD word4
+ uint32_t word5_rsvd;
+ uint32_t word6;
+#define lpfc_mbx_read_vpi_vpi_SHIFT 0
+#define lpfc_mbx_read_vpi_vpi_MASK 0x0000FFFF
+#define lpfc_mbx_read_vpi_vpi_WORD word6
+ uint32_t word7;
+#define lpfc_mbx_read_vpi_mac_0_SHIFT 0
+#define lpfc_mbx_read_vpi_mac_0_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_0_WORD word7
+#define lpfc_mbx_read_vpi_mac_1_SHIFT 8
+#define lpfc_mbx_read_vpi_mac_1_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_1_WORD word7
+#define lpfc_mbx_read_vpi_mac_2_SHIFT 16
+#define lpfc_mbx_read_vpi_mac_2_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_2_WORD word7
+#define lpfc_mbx_read_vpi_mac_3_SHIFT 24
+#define lpfc_mbx_read_vpi_mac_3_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_3_WORD word7
+ uint32_t word8;
+#define lpfc_mbx_read_vpi_mac_4_SHIFT 0
+#define lpfc_mbx_read_vpi_mac_4_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_4_WORD word8
+#define lpfc_mbx_read_vpi_mac_5_SHIFT 8
+#define lpfc_mbx_read_vpi_mac_5_MASK 0x000000FF
+#define lpfc_mbx_read_vpi_mac_5_WORD word8
+#define lpfc_mbx_read_vpi_vlan_tag_SHIFT 16
+#define lpfc_mbx_read_vpi_vlan_tag_MASK 0x00000FFF
+#define lpfc_mbx_read_vpi_vlan_tag_WORD word8
+#define lpfc_mbx_read_vpi_vv_SHIFT 28
+#define lpfc_mbx_read_vpi_vv_MASK 0x0000001
+#define lpfc_mbx_read_vpi_vv_WORD word8
+};
+
+struct lpfc_mbx_unreg_vfi {
+ uint32_t word1_rsvd;
+ uint32_t word2;
+#define lpfc_unreg_vfi_vfi_SHIFT 0
+#define lpfc_unreg_vfi_vfi_MASK 0x0000FFFF
+#define lpfc_unreg_vfi_vfi_WORD word2
+};
+
+struct lpfc_mbx_resume_rpi {
+ uint32_t word1;
+#define lpfc_resume_rpi_rpi_SHIFT 0
+#define lpfc_resume_rpi_rpi_MASK 0x0000FFFF
+#define lpfc_resume_rpi_rpi_WORD word1
+ uint32_t event_tag;
+ uint32_t word3_rsvd;
+ uint32_t word4_rsvd;
+ uint32_t word5_rsvd;
+ uint32_t word6;
+#define lpfc_resume_rpi_vpi_SHIFT 0
+#define lpfc_resume_rpi_vpi_MASK 0x0000FFFF
+#define lpfc_resume_rpi_vpi_WORD word6
+#define lpfc_resume_rpi_vfi_SHIFT 16
+#define lpfc_resume_rpi_vfi_MASK 0x0000FFFF
+#define lpfc_resume_rpi_vfi_WORD word6
+};
+
+#define REG_FCF_INVALID_QID 0xFFFF
+struct lpfc_mbx_reg_fcfi {
+ uint32_t word1;
+#define lpfc_reg_fcfi_info_index_SHIFT 0
+#define lpfc_reg_fcfi_info_index_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_info_index_WORD word1
+#define lpfc_reg_fcfi_fcfi_SHIFT 16
+#define lpfc_reg_fcfi_fcfi_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_fcfi_WORD word1
+ uint32_t word2;
+#define lpfc_reg_fcfi_rq_id1_SHIFT 0
+#define lpfc_reg_fcfi_rq_id1_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_rq_id1_WORD word2
+#define lpfc_reg_fcfi_rq_id0_SHIFT 16
+#define lpfc_reg_fcfi_rq_id0_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_rq_id0_WORD word2
+ uint32_t word3;
+#define lpfc_reg_fcfi_rq_id3_SHIFT 0
+#define lpfc_reg_fcfi_rq_id3_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_rq_id3_WORD word3
+#define lpfc_reg_fcfi_rq_id2_SHIFT 16
+#define lpfc_reg_fcfi_rq_id2_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_rq_id2_WORD word3
+ uint32_t word4;
+#define lpfc_reg_fcfi_type_match0_SHIFT 24
+#define lpfc_reg_fcfi_type_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match0_WORD word4
+#define lpfc_reg_fcfi_type_mask0_SHIFT 16
+#define lpfc_reg_fcfi_type_mask0_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_mask0_WORD word4
+#define lpfc_reg_fcfi_rctl_match0_SHIFT 8
+#define lpfc_reg_fcfi_rctl_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match0_WORD word4
+#define lpfc_reg_fcfi_rctl_mask0_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask0_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_mask0_WORD word4
+ uint32_t word5;
+#define lpfc_reg_fcfi_type_match1_SHIFT 24
+#define lpfc_reg_fcfi_type_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match1_WORD word5
+#define lpfc_reg_fcfi_type_mask1_SHIFT 16
+#define lpfc_reg_fcfi_type_mask1_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_mask1_WORD word5
+#define lpfc_reg_fcfi_rctl_match1_SHIFT 8
+#define lpfc_reg_fcfi_rctl_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match1_WORD word5
+#define lpfc_reg_fcfi_rctl_mask1_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask1_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_mask1_WORD word5
+ uint32_t word6;
+#define lpfc_reg_fcfi_type_match2_SHIFT 24
+#define lpfc_reg_fcfi_type_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match2_WORD word6
+#define lpfc_reg_fcfi_type_mask2_SHIFT 16
+#define lpfc_reg_fcfi_type_mask2_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_mask2_WORD word6
+#define lpfc_reg_fcfi_rctl_match2_SHIFT 8
+#define lpfc_reg_fcfi_rctl_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match2_WORD word6
+#define lpfc_reg_fcfi_rctl_mask2_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask2_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_mask2_WORD word6
+ uint32_t word7;
+#define lpfc_reg_fcfi_type_match3_SHIFT 24
+#define lpfc_reg_fcfi_type_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match3_WORD word7
+#define lpfc_reg_fcfi_type_mask3_SHIFT 16
+#define lpfc_reg_fcfi_type_mask3_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_mask3_WORD word7
+#define lpfc_reg_fcfi_rctl_match3_SHIFT 8
+#define lpfc_reg_fcfi_rctl_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match3_WORD word7
+#define lpfc_reg_fcfi_rctl_mask3_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask3_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_mask3_WORD word7
+ uint32_t word8;
+#define lpfc_reg_fcfi_mam_SHIFT 13
+#define lpfc_reg_fcfi_mam_MASK 0x00000003
+#define lpfc_reg_fcfi_mam_WORD word8
+#define LPFC_MAM_BOTH 0 /* Both SPMA and FPMA */
+#define LPFC_MAM_SPMA 1 /* Server Provided MAC Address */
+#define LPFC_MAM_FPMA 2 /* Fabric Provided MAC Address */
+#define lpfc_reg_fcfi_vv_SHIFT 12
+#define lpfc_reg_fcfi_vv_MASK 0x00000001
+#define lpfc_reg_fcfi_vv_WORD word8
+#define lpfc_reg_fcfi_vlan_tag_SHIFT 0
+#define lpfc_reg_fcfi_vlan_tag_MASK 0x00000FFF
+#define lpfc_reg_fcfi_vlan_tag_WORD word8
+};
+
+struct lpfc_mbx_unreg_fcfi {
+ uint32_t word1_rsv;
+ uint32_t word2;
+#define lpfc_unreg_fcfi_SHIFT 0
+#define lpfc_unreg_fcfi_MASK 0x0000FFFF
+#define lpfc_unreg_fcfi_WORD word2
+};
+
+struct lpfc_mbx_read_rev {
+ uint32_t word1;
+#define lpfc_mbx_rd_rev_sli_lvl_SHIFT 16
+#define lpfc_mbx_rd_rev_sli_lvl_MASK 0x0000000F
+#define lpfc_mbx_rd_rev_sli_lvl_WORD word1
+#define lpfc_mbx_rd_rev_fcoe_SHIFT 20
+#define lpfc_mbx_rd_rev_fcoe_MASK 0x00000001
+#define lpfc_mbx_rd_rev_fcoe_WORD word1
+#define lpfc_mbx_rd_rev_vpd_SHIFT 29
+#define lpfc_mbx_rd_rev_vpd_MASK 0x00000001
+#define lpfc_mbx_rd_rev_vpd_WORD word1
+ uint32_t first_hw_rev;
+ uint32_t second_hw_rev;
+ uint32_t word4_rsvd;
+ uint32_t third_hw_rev;
+ uint32_t word6;
+#define lpfc_mbx_rd_rev_fcph_low_SHIFT 0
+#define lpfc_mbx_rd_rev_fcph_low_MASK 0x000000FF
+#define lpfc_mbx_rd_rev_fcph_low_WORD word6
+#define lpfc_mbx_rd_rev_fcph_high_SHIFT 8
+#define lpfc_mbx_rd_rev_fcph_high_MASK 0x000000FF
+#define lpfc_mbx_rd_rev_fcph_high_WORD word6
+#define lpfc_mbx_rd_rev_ftr_lvl_low_SHIFT 16
+#define lpfc_mbx_rd_rev_ftr_lvl_low_MASK 0x000000FF
+#define lpfc_mbx_rd_rev_ftr_lvl_low_WORD word6
+#define lpfc_mbx_rd_rev_ftr_lvl_high_SHIFT 24
+#define lpfc_mbx_rd_rev_ftr_lvl_high_MASK 0x000000FF
+#define lpfc_mbx_rd_rev_ftr_lvl_high_WORD word6
+ uint32_t word7_rsvd;
+ uint32_t fw_id_rev;
+ uint8_t fw_name[16];
+ uint32_t ulp_fw_id_rev;
+ uint8_t ulp_fw_name[16];
+ uint32_t word18_47_rsvd[30];
+ uint32_t word48;
+#define lpfc_mbx_rd_rev_avail_len_SHIFT 0
+#define lpfc_mbx_rd_rev_avail_len_MASK 0x00FFFFFF
+#define lpfc_mbx_rd_rev_avail_len_WORD word48
+ uint32_t vpd_paddr_low;
+ uint32_t vpd_paddr_high;
+ uint32_t avail_vpd_len;
+ uint32_t rsvd_52_63[12];
+};
+
+struct lpfc_mbx_read_config {
+ uint32_t word1;
+#define lpfc_mbx_rd_conf_max_bbc_SHIFT 0
+#define lpfc_mbx_rd_conf_max_bbc_MASK 0x000000FF
+#define lpfc_mbx_rd_conf_max_bbc_WORD word1
+#define lpfc_mbx_rd_conf_init_bbc_SHIFT 8
+#define lpfc_mbx_rd_conf_init_bbc_MASK 0x000000FF
+#define lpfc_mbx_rd_conf_init_bbc_WORD word1
+ uint32_t word2;
+#define lpfc_mbx_rd_conf_nport_did_SHIFT 0
+#define lpfc_mbx_rd_conf_nport_did_MASK 0x00FFFFFF
+#define lpfc_mbx_rd_conf_nport_did_WORD word2
+#define lpfc_mbx_rd_conf_topology_SHIFT 24
+#define lpfc_mbx_rd_conf_topology_MASK 0x000000FF
+#define lpfc_mbx_rd_conf_topology_WORD word2
+ uint32_t word3;
+#define lpfc_mbx_rd_conf_ao_SHIFT 0
+#define lpfc_mbx_rd_conf_ao_MASK 0x00000001
+#define lpfc_mbx_rd_conf_ao_WORD word3
+#define lpfc_mbx_rd_conf_bb_scn_SHIFT 8
+#define lpfc_mbx_rd_conf_bb_scn_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_bb_scn_WORD word3
+#define lpfc_mbx_rd_conf_cbb_scn_SHIFT 12
+#define lpfc_mbx_rd_conf_cbb_scn_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_cbb_scn_WORD word3
+#define lpfc_mbx_rd_conf_mc_SHIFT 29
+#define lpfc_mbx_rd_conf_mc_MASK 0x00000001
+#define lpfc_mbx_rd_conf_mc_WORD word3
+ uint32_t word4;
+#define lpfc_mbx_rd_conf_e_d_tov_SHIFT 0
+#define lpfc_mbx_rd_conf_e_d_tov_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_e_d_tov_WORD word4
+ uint32_t word5;
+#define lpfc_mbx_rd_conf_lp_tov_SHIFT 0
+#define lpfc_mbx_rd_conf_lp_tov_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_lp_tov_WORD word5
+ uint32_t word6;
+#define lpfc_mbx_rd_conf_r_a_tov_SHIFT 0
+#define lpfc_mbx_rd_conf_r_a_tov_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_r_a_tov_WORD word6
+ uint32_t word7;
+#define lpfc_mbx_rd_conf_r_t_tov_SHIFT 0
+#define lpfc_mbx_rd_conf_r_t_tov_MASK 0x000000FF
+#define lpfc_mbx_rd_conf_r_t_tov_WORD word7
+ uint32_t word8;
+#define lpfc_mbx_rd_conf_al_tov_SHIFT 0
+#define lpfc_mbx_rd_conf_al_tov_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_al_tov_WORD word8
+ uint32_t word9;
+#define lpfc_mbx_rd_conf_lmt_SHIFT 0
+#define lpfc_mbx_rd_conf_lmt_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_lmt_WORD word9
+ uint32_t word10;
+#define lpfc_mbx_rd_conf_max_alpa_SHIFT 0
+#define lpfc_mbx_rd_conf_max_alpa_MASK 0x000000FF
+#define lpfc_mbx_rd_conf_max_alpa_WORD word10
+ uint32_t word11_rsvd;
+ uint32_t word12;
+#define lpfc_mbx_rd_conf_xri_base_SHIFT 0
+#define lpfc_mbx_rd_conf_xri_base_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_xri_base_WORD word12
+#define lpfc_mbx_rd_conf_xri_count_SHIFT 16
+#define lpfc_mbx_rd_conf_xri_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_xri_count_WORD word12
+ uint32_t word13;
+#define lpfc_mbx_rd_conf_rpi_base_SHIFT 0
+#define lpfc_mbx_rd_conf_rpi_base_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_rpi_base_WORD word13
+#define lpfc_mbx_rd_conf_rpi_count_SHIFT 16
+#define lpfc_mbx_rd_conf_rpi_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_rpi_count_WORD word13
+ uint32_t word14;
+#define lpfc_mbx_rd_conf_vpi_base_SHIFT 0
+#define lpfc_mbx_rd_conf_vpi_base_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_vpi_base_WORD word14
+#define lpfc_mbx_rd_conf_vpi_count_SHIFT 16
+#define lpfc_mbx_rd_conf_vpi_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_vpi_count_WORD word14
+ uint32_t word15;
+#define lpfc_mbx_rd_conf_vfi_base_SHIFT 0
+#define lpfc_mbx_rd_conf_vfi_base_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_vfi_base_WORD word15
+#define lpfc_mbx_rd_conf_vfi_count_SHIFT 16
+#define lpfc_mbx_rd_conf_vfi_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_vfi_count_WORD word15
+ uint32_t word16;
+#define lpfc_mbx_rd_conf_fcfi_base_SHIFT 0
+#define lpfc_mbx_rd_conf_fcfi_base_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_fcfi_base_WORD word16
+#define lpfc_mbx_rd_conf_fcfi_count_SHIFT 16
+#define lpfc_mbx_rd_conf_fcfi_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_fcfi_count_WORD word16
+ uint32_t word17;
+#define lpfc_mbx_rd_conf_rq_count_SHIFT 0
+#define lpfc_mbx_rd_conf_rq_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_rq_count_WORD word17
+#define lpfc_mbx_rd_conf_eq_count_SHIFT 16
+#define lpfc_mbx_rd_conf_eq_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_eq_count_WORD word17
+ uint32_t word18;
+#define lpfc_mbx_rd_conf_wq_count_SHIFT 0
+#define lpfc_mbx_rd_conf_wq_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_wq_count_WORD word18
+#define lpfc_mbx_rd_conf_cq_count_SHIFT 16
+#define lpfc_mbx_rd_conf_cq_count_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_cq_count_WORD word18
+};
+
+struct lpfc_mbx_request_features {
+ uint32_t word1;
+#define lpfc_mbx_rq_ftr_qry_SHIFT 0
+#define lpfc_mbx_rq_ftr_qry_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_qry_WORD word1
+ uint32_t word2;
+#define lpfc_mbx_rq_ftr_rq_iaab_SHIFT 0
+#define lpfc_mbx_rq_ftr_rq_iaab_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_iaab_WORD word2
+#define lpfc_mbx_rq_ftr_rq_npiv_SHIFT 1
+#define lpfc_mbx_rq_ftr_rq_npiv_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_npiv_WORD word2
+#define lpfc_mbx_rq_ftr_rq_dif_SHIFT 2
+#define lpfc_mbx_rq_ftr_rq_dif_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_dif_WORD word2
+#define lpfc_mbx_rq_ftr_rq_vf_SHIFT 3
+#define lpfc_mbx_rq_ftr_rq_vf_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_vf_WORD word2
+#define lpfc_mbx_rq_ftr_rq_fcpi_SHIFT 4
+#define lpfc_mbx_rq_ftr_rq_fcpi_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpi_WORD word2
+#define lpfc_mbx_rq_ftr_rq_fcpt_SHIFT 5
+#define lpfc_mbx_rq_ftr_rq_fcpt_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpt_WORD word2
+#define lpfc_mbx_rq_ftr_rq_fcpc_SHIFT 6
+#define lpfc_mbx_rq_ftr_rq_fcpc_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpc_WORD word2
+#define lpfc_mbx_rq_ftr_rq_ifip_SHIFT 7
+#define lpfc_mbx_rq_ftr_rq_ifip_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_ifip_WORD word2
+ uint32_t word3;
+#define lpfc_mbx_rq_ftr_rsp_iaab_SHIFT 0
+#define lpfc_mbx_rq_ftr_rsp_iaab_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_iaab_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_npiv_SHIFT 1
+#define lpfc_mbx_rq_ftr_rsp_npiv_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_npiv_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_dif_SHIFT 2
+#define lpfc_mbx_rq_ftr_rsp_dif_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_dif_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_vf_SHIFT 3
+#define lpfc_mbx_rq_ftr_rsp_vf__MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_vf_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_fcpi_SHIFT 4
+#define lpfc_mbx_rq_ftr_rsp_fcpi_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpi_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_fcpt_SHIFT 5
+#define lpfc_mbx_rq_ftr_rsp_fcpt_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpt_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_fcpc_SHIFT 6
+#define lpfc_mbx_rq_ftr_rsp_fcpc_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpc_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_ifip_SHIFT 7
+#define lpfc_mbx_rq_ftr_rsp_ifip_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_ifip_WORD word3
+};
+
+/* Mailbox Completion Queue Error Messages */
+#define MB_CQE_STATUS_SUCCESS 0x0
+#define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1
+#define MB_CQE_STATUS_INVALID_PARAMETER 0x2
+#define MB_CQE_STATUS_INSUFFICIENT_RESOURCES 0x3
+#define MB_CEQ_STATUS_QUEUE_FLUSHING 0x4
+#define MB_CQE_STATUS_DMA_FAILED 0x5
+
+/* mailbox queue entry structure */
+struct lpfc_mqe {
+ uint32_t word0;
+#define lpfc_mqe_status_SHIFT 16
+#define lpfc_mqe_status_MASK 0x0000FFFF
+#define lpfc_mqe_status_WORD word0
+#define lpfc_mqe_command_SHIFT 8
+#define lpfc_mqe_command_MASK 0x000000FF
+#define lpfc_mqe_command_WORD word0
+ union {
+ uint32_t mb_words[LPFC_SLI4_MB_WORD_COUNT - 1];
+ /* sli4 mailbox commands */
+ struct lpfc_mbx_sli4_config sli4_config;
+ struct lpfc_mbx_init_vfi init_vfi;
+ struct lpfc_mbx_reg_vfi reg_vfi;
+ struct lpfc_mbx_reg_vfi unreg_vfi;
+ struct lpfc_mbx_init_vpi init_vpi;
+ struct lpfc_mbx_resume_rpi resume_rpi;
+ struct lpfc_mbx_read_fcf_tbl read_fcf_tbl;
+ struct lpfc_mbx_add_fcf_tbl_entry add_fcf_entry;
+ struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry;
+ struct lpfc_mbx_reg_fcfi reg_fcfi;
+ struct lpfc_mbx_unreg_fcfi unreg_fcfi;
+ struct lpfc_mbx_mq_create mq_create;
+ struct lpfc_mbx_eq_create eq_create;
+ struct lpfc_mbx_cq_create cq_create;
+ struct lpfc_mbx_wq_create wq_create;
+ struct lpfc_mbx_rq_create rq_create;
+ struct lpfc_mbx_mq_destroy mq_destroy;
+ struct lpfc_mbx_eq_destroy eq_destroy;
+ struct lpfc_mbx_cq_destroy cq_destroy;
+ struct lpfc_mbx_wq_destroy wq_destroy;
+ struct lpfc_mbx_rq_destroy rq_destroy;
+ struct lpfc_mbx_post_sgl_pages post_sgl_pages;
+ struct lpfc_mbx_nembed_cmd nembed_cmd;
+ struct lpfc_mbx_read_rev read_rev;
+ struct lpfc_mbx_read_vpi read_vpi;
+ struct lpfc_mbx_read_config rd_config;
+ struct lpfc_mbx_request_features req_ftrs;
+ struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
+ struct lpfc_mbx_nop nop;
+ } un;
+};
+
+struct lpfc_mcqe {
+ uint32_t word0;
+#define lpfc_mcqe_status_SHIFT 0
+#define lpfc_mcqe_status_MASK 0x0000FFFF
+#define lpfc_mcqe_status_WORD word0
+#define lpfc_mcqe_ext_status_SHIFT 16
+#define lpfc_mcqe_ext_status_MASK 0x0000FFFF
+#define lpfc_mcqe_ext_status_WORD word0
+ uint32_t mcqe_tag0;
+ uint32_t mcqe_tag1;
+ uint32_t trailer;
+#define lpfc_trailer_valid_SHIFT 31
+#define lpfc_trailer_valid_MASK 0x00000001
+#define lpfc_trailer_valid_WORD trailer
+#define lpfc_trailer_async_SHIFT 30
+#define lpfc_trailer_async_MASK 0x00000001
+#define lpfc_trailer_async_WORD trailer
+#define lpfc_trailer_hpi_SHIFT 29
+#define lpfc_trailer_hpi_MASK 0x00000001
+#define lpfc_trailer_hpi_WORD trailer
+#define lpfc_trailer_completed_SHIFT 28
+#define lpfc_trailer_completed_MASK 0x00000001
+#define lpfc_trailer_completed_WORD trailer
+#define lpfc_trailer_consumed_SHIFT 27
+#define lpfc_trailer_consumed_MASK 0x00000001
+#define lpfc_trailer_consumed_WORD trailer
+#define lpfc_trailer_type_SHIFT 16
+#define lpfc_trailer_type_MASK 0x000000FF
+#define lpfc_trailer_type_WORD trailer
+#define lpfc_trailer_code_SHIFT 8
+#define lpfc_trailer_code_MASK 0x000000FF
+#define lpfc_trailer_code_WORD trailer
+#define LPFC_TRAILER_CODE_LINK 0x1
+#define LPFC_TRAILER_CODE_FCOE 0x2
+#define LPFC_TRAILER_CODE_DCBX 0x3
+};
+
+struct lpfc_acqe_link {
+ uint32_t word0;
+#define lpfc_acqe_link_speed_SHIFT 24
+#define lpfc_acqe_link_speed_MASK 0x000000FF
+#define lpfc_acqe_link_speed_WORD word0
+#define LPFC_ASYNC_LINK_SPEED_ZERO 0x0
+#define LPFC_ASYNC_LINK_SPEED_10MBPS 0x1
+#define LPFC_ASYNC_LINK_SPEED_100MBPS 0x2
+#define LPFC_ASYNC_LINK_SPEED_1GBPS 0x3
+#define LPFC_ASYNC_LINK_SPEED_10GBPS 0x4
+#define lpfc_acqe_link_duplex_SHIFT 16
+#define lpfc_acqe_link_duplex_MASK 0x000000FF
+#define lpfc_acqe_link_duplex_WORD word0
+#define LPFC_ASYNC_LINK_DUPLEX_NONE 0x0
+#define LPFC_ASYNC_LINK_DUPLEX_HALF 0x1
+#define LPFC_ASYNC_LINK_DUPLEX_FULL 0x2
+#define lpfc_acqe_link_status_SHIFT 8
+#define lpfc_acqe_link_status_MASK 0x000000FF
+#define lpfc_acqe_link_status_WORD word0
+#define LPFC_ASYNC_LINK_STATUS_DOWN 0x0
+#define LPFC_ASYNC_LINK_STATUS_UP 0x1
+#define LPFC_ASYNC_LINK_STATUS_LOGICAL_DOWN 0x2
+#define LPFC_ASYNC_LINK_STATUS_LOGICAL_UP 0x3
+#define lpfc_acqe_link_physical_SHIFT 0
+#define lpfc_acqe_link_physical_MASK 0x000000FF
+#define lpfc_acqe_link_physical_WORD word0
+#define LPFC_ASYNC_LINK_PORT_A 0x0
+#define LPFC_ASYNC_LINK_PORT_B 0x1
+ uint32_t word1;
+#define lpfc_acqe_link_fault_SHIFT 0
+#define lpfc_acqe_link_fault_MASK 0x000000FF
+#define lpfc_acqe_link_fault_WORD word1
+#define LPFC_ASYNC_LINK_FAULT_NONE 0x0
+#define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1
+#define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2
+ uint32_t event_tag;
+ uint32_t trailer;
+};
+
+struct lpfc_acqe_fcoe {
+ uint32_t fcf_index;
+ uint32_t word1;
+#define lpfc_acqe_fcoe_fcf_count_SHIFT 0
+#define lpfc_acqe_fcoe_fcf_count_MASK 0x0000FFFF
+#define lpfc_acqe_fcoe_fcf_count_WORD word1
+#define lpfc_acqe_fcoe_event_type_SHIFT 16
+#define lpfc_acqe_fcoe_event_type_MASK 0x0000FFFF
+#define lpfc_acqe_fcoe_event_type_WORD word1
+#define LPFC_FCOE_EVENT_TYPE_NEW_FCF 0x1
+#define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2
+#define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3
+ uint32_t event_tag;
+ uint32_t trailer;
+};
+
+struct lpfc_acqe_dcbx {
+ uint32_t tlv_ttl;
+ uint32_t reserved;
+ uint32_t event_tag;
+ uint32_t trailer;
+};
+
+/*
+ * Define the bootstrap mailbox (bmbx) region used to communicate
+ * mailbox command between the host and port. The mailbox consists
+ * of a payload area of 256 bytes and a completion queue of length
+ * 16 bytes.
+ */
+struct lpfc_bmbx_create {
+ struct lpfc_mqe mqe;
+ struct lpfc_mcqe mcqe;
+};
+
+#define SGL_ALIGN_SZ 64
+#define SGL_PAGE_SIZE 4096
+/* align SGL addr on a size boundary - adjust address up */
+#define NO_XRI ((uint16_t)-1)
+struct wqe_common {
+ uint32_t word6;
+#define wqe_xri_SHIFT 0
+#define wqe_xri_MASK 0x0000FFFF
+#define wqe_xri_WORD word6
+#define wqe_ctxt_tag_SHIFT 16
+#define wqe_ctxt_tag_MASK 0x0000FFFF
+#define wqe_ctxt_tag_WORD word6
+ uint32_t word7;
+#define wqe_ct_SHIFT 2
+#define wqe_ct_MASK 0x00000003
+#define wqe_ct_WORD word7
+#define wqe_status_SHIFT 4
+#define wqe_status_MASK 0x0000000f
+#define wqe_status_WORD word7
+#define wqe_cmnd_SHIFT 8
+#define wqe_cmnd_MASK 0x000000ff
+#define wqe_cmnd_WORD word7
+#define wqe_class_SHIFT 16
+#define wqe_class_MASK 0x00000007
+#define wqe_class_WORD word7
+#define wqe_pu_SHIFT 20
+#define wqe_pu_MASK 0x00000003
+#define wqe_pu_WORD word7
+#define wqe_erp_SHIFT 22
+#define wqe_erp_MASK 0x00000001
+#define wqe_erp_WORD word7
+#define wqe_lnk_SHIFT 23
+#define wqe_lnk_MASK 0x00000001
+#define wqe_lnk_WORD word7
+#define wqe_tmo_SHIFT 24
+#define wqe_tmo_MASK 0x000000ff
+#define wqe_tmo_WORD word7
+ uint32_t abort_tag; /* word 8 in WQE */
+ uint32_t word9;
+#define wqe_reqtag_SHIFT 0
+#define wqe_reqtag_MASK 0x0000FFFF
+#define wqe_reqtag_WORD word9
+#define wqe_rcvoxid_SHIFT 16
+#define wqe_rcvoxid_MASK 0x0000FFFF
+#define wqe_rcvoxid_WORD word9
+ uint32_t word10;
+#define wqe_pri_SHIFT 16
+#define wqe_pri_MASK 0x00000007
+#define wqe_pri_WORD word10
+#define wqe_pv_SHIFT 19
+#define wqe_pv_MASK 0x00000001
+#define wqe_pv_WORD word10
+#define wqe_xc_SHIFT 21
+#define wqe_xc_MASK 0x00000001
+#define wqe_xc_WORD word10
+#define wqe_ccpe_SHIFT 23
+#define wqe_ccpe_MASK 0x00000001
+#define wqe_ccpe_WORD word10
+#define wqe_ccp_SHIFT 24
+#define wqe_ccp_MASK 0x000000ff
+#define wqe_ccp_WORD word10
+ uint32_t word11;
+#define wqe_cmd_type_SHIFT 0
+#define wqe_cmd_type_MASK 0x0000000f
+#define wqe_cmd_type_WORD word11
+#define wqe_wqec_SHIFT 7
+#define wqe_wqec_MASK 0x00000001
+#define wqe_wqec_WORD word11
+#define wqe_cqid_SHIFT 16
+#define wqe_cqid_MASK 0x000003ff
+#define wqe_cqid_WORD word11
+};
+
+struct wqe_did {
+ uint32_t word5;
+#define wqe_els_did_SHIFT 0
+#define wqe_els_did_MASK 0x00FFFFFF
+#define wqe_els_did_WORD word5
+#define wqe_xmit_bls_ar_SHIFT 30
+#define wqe_xmit_bls_ar_MASK 0x00000001
+#define wqe_xmit_bls_ar_WORD word5
+#define wqe_xmit_bls_xo_SHIFT 31
+#define wqe_xmit_bls_xo_MASK 0x00000001
+#define wqe_xmit_bls_xo_WORD word5
+};
+
+struct els_request64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t payload_len;
+ uint32_t word4;
+#define els_req64_sid_SHIFT 0
+#define els_req64_sid_MASK 0x00FFFFFF
+#define els_req64_sid_WORD word4
+#define els_req64_sp_SHIFT 24
+#define els_req64_sp_MASK 0x00000001
+#define els_req64_sp_WORD word4
+#define els_req64_vf_SHIFT 25
+#define els_req64_vf_MASK 0x00000001
+#define els_req64_vf_WORD word4
+ struct wqe_did wqe_dest;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t word12;
+#define els_req64_vfid_SHIFT 1
+#define els_req64_vfid_MASK 0x00000FFF
+#define els_req64_vfid_WORD word12
+#define els_req64_pri_SHIFT 13
+#define els_req64_pri_MASK 0x00000007
+#define els_req64_pri_WORD word12
+ uint32_t word13;
+#define els_req64_hopcnt_SHIFT 24
+#define els_req64_hopcnt_MASK 0x000000ff
+#define els_req64_hopcnt_WORD word13
+ uint32_t reserved[2];
+};
+
+struct xmit_els_rsp64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t rsvd3;
+ uint32_t rsvd4;
+ struct wqe_did wqe_dest;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4];
+};
+
+struct xmit_bls_rsp64_wqe {
+ uint32_t payload0;
+ uint32_t word1;
+#define xmit_bls_rsp64_rxid_SHIFT 0
+#define xmit_bls_rsp64_rxid_MASK 0x0000ffff
+#define xmit_bls_rsp64_rxid_WORD word1
+#define xmit_bls_rsp64_oxid_SHIFT 16
+#define xmit_bls_rsp64_oxid_MASK 0x0000ffff
+#define xmit_bls_rsp64_oxid_WORD word1
+ uint32_t word2;
+#define xmit_bls_rsp64_seqcntlo_SHIFT 0
+#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff
+#define xmit_bls_rsp64_seqcntlo_WORD word2
+#define xmit_bls_rsp64_seqcnthi_SHIFT 16
+#define xmit_bls_rsp64_seqcnthi_MASK 0x0000ffff
+#define xmit_bls_rsp64_seqcnthi_WORD word2
+ uint32_t rsrvd3;
+ uint32_t rsrvd4;
+ struct wqe_did wqe_dest;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4];
+};
+struct wqe_rctl_dfctl {
+ uint32_t word5;
+#define wqe_si_SHIFT 2
+#define wqe_si_MASK 0x000000001
+#define wqe_si_WORD word5
+#define wqe_la_SHIFT 3
+#define wqe_la_MASK 0x000000001
+#define wqe_la_WORD word5
+#define wqe_ls_SHIFT 7
+#define wqe_ls_MASK 0x000000001
+#define wqe_ls_WORD word5
+#define wqe_dfctl_SHIFT 8
+#define wqe_dfctl_MASK 0x0000000ff
+#define wqe_dfctl_WORD word5
+#define wqe_type_SHIFT 16
+#define wqe_type_MASK 0x0000000ff
+#define wqe_type_WORD word5
+#define wqe_rctl_SHIFT 24
+#define wqe_rctl_MASK 0x0000000ff
+#define wqe_rctl_WORD word5
+};
+
+struct xmit_seq64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t paylaod_offset;
+ uint32_t relative_offset;
+ struct wqe_rctl_dfctl wge_ctl;
+ struct wqe_common wqe_com; /* words 6-11 */
+ /* Note: word10 different REVISIT */
+ uint32_t xmit_len;
+ uint32_t rsvd_12_15[3];
+};
+struct xmit_bcast64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t paylaod_len;
+ uint32_t rsvd4;
+ struct wqe_rctl_dfctl wge_ctl; /* word 5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4];
+};
+
+struct gen_req64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t command_len;
+ uint32_t payload_len;
+ struct wqe_rctl_dfctl wge_ctl; /* word 5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4];
+};
+
+struct create_xri_wqe {
+ uint32_t rsrvd[5]; /* words 0-4 */
+ struct wqe_did wqe_dest; /* word 5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+#define T_REQUEST_TAG 3
+#define T_XRI_TAG 1
+
+struct abort_cmd_wqe {
+ uint32_t rsrvd[3];
+ uint32_t word3;
+#define abort_cmd_ia_SHIFT 0
+#define abort_cmd_ia_MASK 0x000000001
+#define abort_cmd_ia_WORD word3
+#define abort_cmd_criteria_SHIFT 8
+#define abort_cmd_criteria_MASK 0x0000000ff
+#define abort_cmd_criteria_WORD word3
+ uint32_t rsrvd4;
+ uint32_t rsrvd5;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+struct fcp_iwrite64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t payload_len;
+ uint32_t total_xfer_len;
+ uint32_t initial_xfer_len;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+struct fcp_iread64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t payload_len; /* word 3 */
+ uint32_t total_xfer_len; /* word 4 */
+ uint32_t rsrvd5; /* word 5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+struct fcp_icmnd64_wqe {
+ struct ulp_bde64 bde; /* words 0-2 */
+ uint32_t rsrvd[3]; /* words 3-5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+
+union lpfc_wqe {
+ uint32_t words[16];
+ struct lpfc_wqe_generic generic;
+ struct fcp_icmnd64_wqe fcp_icmd;
+ struct fcp_iread64_wqe fcp_iread;
+ struct fcp_iwrite64_wqe fcp_iwrite;
+ struct abort_cmd_wqe abort_cmd;
+ struct create_xri_wqe create_xri;
+ struct xmit_bcast64_wqe xmit_bcast64;
+ struct xmit_seq64_wqe xmit_sequence;
+ struct xmit_bls_rsp64_wqe xmit_bls_rsp;
+ struct xmit_els_rsp64_wqe xmit_els_rsp;
+ struct els_request64_wqe els_req;
+ struct gen_req64_wqe gen_req;
+};
+
+#define FCP_COMMAND 0x0
+#define FCP_COMMAND_DATA_OUT 0x1
+#define ELS_COMMAND_NON_FIP 0xC
+#define ELS_COMMAND_FIP 0xD
+#define OTHER_COMMAND 0x8
+
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 06874e6009ea..2f5907f92eea 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -34,8 +34,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -51,16 +53,30 @@ char *_dump_buf_dif;
unsigned long _dump_buf_dif_order;
spinlock_t _dump_buf_lock;
-static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
+static int lpfc_sli4_queue_create(struct lpfc_hba *);
+static void lpfc_sli4_queue_destroy(struct lpfc_hba *);
+static int lpfc_create_bootstrap_mbox(struct lpfc_hba *);
+static int lpfc_setup_endian_order(struct lpfc_hba *);
+static int lpfc_sli4_read_config(struct lpfc_hba *);
+static void lpfc_destroy_bootstrap_mbox(struct lpfc_hba *);
+static void lpfc_free_sgl_list(struct lpfc_hba *);
+static int lpfc_init_sgl_list(struct lpfc_hba *);
+static int lpfc_init_active_sgl_array(struct lpfc_hba *);
+static void lpfc_free_active_sgl(struct lpfc_hba *);
+static int lpfc_hba_down_post_s3(struct lpfc_hba *phba);
+static int lpfc_hba_down_post_s4(struct lpfc_hba *phba);
+static int lpfc_sli4_cq_event_pool_create(struct lpfc_hba *);
+static void lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *);
+static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *);
static struct scsi_transport_template *lpfc_transport_template = NULL;
static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
static DEFINE_IDR(lpfc_hba_index);
/**
- * lpfc_config_port_prep: Perform lpfc initialization prior to config port.
+ * lpfc_config_port_prep - Perform lpfc initialization prior to config port
* @phba: pointer to lpfc hba data structure.
*
* This routine will do LPFC initialization prior to issuing the CONFIG_PORT
@@ -92,7 +108,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
return -ENOMEM;
}
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
phba->link_state = LPFC_INIT_MBX_CMDS;
if (lpfc_is_LC_HBA(phba->pcidev->device)) {
@@ -205,6 +221,11 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
mb->mbxCommand, mb->mbxStatus);
mb->un.varDmp.word_cnt = 0;
}
+ /* dump mem may return a zero when finished or we got a
+ * mailbox error, either way we are done.
+ */
+ if (mb->un.varDmp.word_cnt == 0)
+ break;
if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
@@ -221,7 +242,7 @@ out_free_mbox:
}
/**
- * lpfc_config_async_cmpl: Completion handler for config async event mbox cmd.
+ * lpfc_config_async_cmpl - Completion handler for config async event mbox cmd
* @phba: pointer to lpfc hba data structure.
* @pmboxq: pointer to the driver internal queue element for mailbox command.
*
@@ -233,7 +254,7 @@ out_free_mbox:
static void
lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
{
- if (pmboxq->mb.mbxStatus == MBX_SUCCESS)
+ if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS)
phba->temp_sensor_support = 1;
else
phba->temp_sensor_support = 0;
@@ -242,8 +263,7 @@ lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
}
/**
- * lpfc_dump_wakeup_param_cmpl: Completion handler for dump memory mailbox
- * command used for getting wake up parameters.
+ * lpfc_dump_wakeup_param_cmpl - dump memory mailbox command completion handler
* @phba: pointer to lpfc hba data structure.
* @pmboxq: pointer to the driver internal queue element for mailbox command.
*
@@ -261,7 +281,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
/* character array used for decoding dist type. */
char dist_char[] = "nabx";
- if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
+ if (pmboxq->u.mb.mbxStatus != MBX_SUCCESS) {
mempool_free(pmboxq, phba->mbox_mem_pool);
return;
}
@@ -269,7 +289,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
prg = (struct prog_id *) &prog_id_word;
/* word 7 contain option rom version */
- prog_id_word = pmboxq->mb.un.varWords[7];
+ prog_id_word = pmboxq->u.mb.un.varWords[7];
/* Decode the Option rom version word to a readable string */
if (prg->dist < 4)
@@ -287,7 +307,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
}
/**
- * lpfc_config_port_post: Perform lpfc initialization after config port.
+ * lpfc_config_port_post - Perform lpfc initialization after config port
* @phba: pointer to lpfc hba data structure.
*
* This routine will do LPFC initialization after the CONFIG_PORT mailbox
@@ -303,6 +323,7 @@ int
lpfc_config_port_post(struct lpfc_hba *phba)
{
struct lpfc_vport *vport = phba->pport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
LPFC_MBOXQ_t *pmb;
MAILBOX_t *mb;
struct lpfc_dmabuf *mp;
@@ -325,7 +346,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
phba->link_state = LPFC_HBA_ERROR;
return -ENOMEM;
}
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
/* Get login parameters for NID. */
lpfc_read_sparam(phba, pmb, 0);
@@ -360,6 +381,12 @@ lpfc_config_port_post(struct lpfc_hba *phba)
sizeof (struct lpfc_name));
memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
sizeof (struct lpfc_name));
+
+ /* Update the fc_host data structures with new wwn. */
+ fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
+ fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
+ fc_host_max_npiv_vports(shost) = phba->max_vpi;
+
/* 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) {
@@ -455,17 +482,18 @@ lpfc_config_port_post(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
"0352 Config MSI mailbox command "
"failed, mbxCmd x%x, mbxStatus x%x\n",
- pmb->mb.mbxCommand, pmb->mb.mbxStatus);
+ pmb->u.mb.mbxCommand,
+ pmb->u.mb.mbxStatus);
mempool_free(pmb, phba->mbox_mem_pool);
return -EIO;
}
}
+ spin_lock_irq(&phba->hbalock);
/* Initialize ERATT handling flag */
phba->hba_flag &= ~HBA_ERATT_HANDLED;
/* Enable appropriate host interrupts */
- spin_lock_irq(&phba->hbalock);
status = readl(phba->HCregaddr);
status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA;
if (psli->num_rings > 0)
@@ -551,7 +579,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
}
/**
- * lpfc_hba_down_prep: Perform lpfc uninitialization prior to HBA reset.
+ * lpfc_hba_down_prep - Perform lpfc uninitialization prior to HBA reset
* @phba: pointer to lpfc HBA data structure.
*
* This routine will do LPFC uninitialization before the HBA is reset when
@@ -566,16 +594,20 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
int i;
- /* Disable interrupts */
- writel(0, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
+
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ /* Disable interrupts */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ }
if (phba->pport->load_flag & FC_UNLOADING)
lpfc_cleanup_discovery_resources(phba->pport);
else {
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports &&
+ vports[i] != NULL; i++)
lpfc_cleanup_discovery_resources(vports[i]);
lpfc_destroy_vport_work_array(phba, vports);
}
@@ -583,7 +615,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
}
/**
- * lpfc_hba_down_post: Perform lpfc uninitialization after HBA reset.
+ * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
* @phba: pointer to lpfc HBA data structure.
*
* This routine will do uninitialization after the HBA is reset when bring
@@ -593,14 +625,12 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
* 0 - sucess.
* Any other value - error.
**/
-int
-lpfc_hba_down_post(struct lpfc_hba *phba)
+static int
+lpfc_hba_down_post_s3(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
struct lpfc_dmabuf *mp, *next_mp;
- struct lpfc_iocbq *iocb;
- IOCB_t *cmd = NULL;
LIST_HEAD(completions);
int i;
@@ -628,20 +658,9 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
pring->txcmplq_cnt = 0;
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- iocb = list_get_first(&completions, struct lpfc_iocbq,
- list);
- cmd = &iocb->iocb;
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
lpfc_sli_abort_iocb_ring(phba, pring);
spin_lock_irq(&phba->hbalock);
@@ -650,9 +669,80 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
return 0;
}
+/**
+ * lpfc_hba_down_post_s4 - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do uninitialization after the HBA is reset when bring
+ * down the SLI Layer.
+ *
+ * Return codes
+ * 0 - sucess.
+ * Any other value - error.
+ **/
+static int
+lpfc_hba_down_post_s4(struct lpfc_hba *phba)
+{
+ struct lpfc_scsi_buf *psb, *psb_next;
+ LIST_HEAD(aborts);
+ int ret;
+ unsigned long iflag = 0;
+ ret = lpfc_hba_down_post_s3(phba);
+ if (ret)
+ return ret;
+ /* At this point in time the HBA is either reset or DOA. Either
+ * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be
+ * on the lpfc_sgl_list so that it can either be freed if the
+ * driver is unloading or reposted if the driver is restarting
+ * the port.
+ */
+ spin_lock_irq(&phba->hbalock); /* required for lpfc_sgl_list and */
+ /* scsl_buf_list */
+ /* abts_sgl_list_lock required because worker thread uses this
+ * list.
+ */
+ spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_abts_els_sgl_list,
+ &phba->sli4_hba.lpfc_sgl_list);
+ spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
+ /* abts_scsi_buf_list_lock required because worker thread uses this
+ * list.
+ */
+ spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list,
+ &aborts);
+ spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ spin_unlock_irq(&phba->hbalock);
+
+ list_for_each_entry_safe(psb, psb_next, &aborts, list) {
+ psb->pCmd = NULL;
+ psb->status = IOSTAT_SUCCESS;
+ }
+ spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+ list_splice(&aborts, &phba->lpfc_scsi_buf_list);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+ return 0;
+}
/**
- * lpfc_hb_timeout: The HBA-timer timeout handler.
+ * lpfc_hba_down_post - Wrapper func for hba down post routine
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine wraps the actual SLI3 or SLI4 routine for performing
+ * uninitialization after the HBA is reset when bring down the SLI Layer.
+ *
+ * Return codes
+ * 0 - sucess.
+ * Any other value - error.
+ **/
+int
+lpfc_hba_down_post(struct lpfc_hba *phba)
+{
+ return (*phba->lpfc_hba_down_post)(phba);
+}
+
+/**
+ * lpfc_hb_timeout - The HBA-timer timeout handler
* @ptr: unsigned long holds the pointer to lpfc hba data structure.
*
* This is the HBA-timer timeout handler registered to the lpfc driver. When
@@ -686,7 +776,7 @@ lpfc_hb_timeout(unsigned long ptr)
}
/**
- * lpfc_hb_mbox_cmpl: The lpfc heart-beat mailbox command callback function.
+ * lpfc_hb_mbox_cmpl - The lpfc heart-beat mailbox command callback function
* @phba: pointer to lpfc hba data structure.
* @pmboxq: pointer to the driver internal queue element for mailbox command.
*
@@ -721,7 +811,7 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
}
/**
- * lpfc_hb_timeout_handler: The HBA-timer timeout handler.
+ * lpfc_hb_timeout_handler - The HBA-timer timeout handler
* @phba: pointer to lpfc hba data structure.
*
* This is the actual HBA-timer timeout handler to be invoked by the worker
@@ -817,7 +907,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
"taking this port offline.\n");
spin_lock_irq(&phba->hbalock);
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
lpfc_offline_prep(phba);
@@ -830,7 +920,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
}
/**
- * lpfc_offline_eratt: Bring lpfc offline on hardware error attention.
+ * lpfc_offline_eratt - Bring lpfc offline on hardware error attention
* @phba: pointer to lpfc hba data structure.
*
* This routine is called to bring the HBA offline when HBA hardware error
@@ -842,13 +932,15 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
spin_lock_irq(&phba->hbalock);
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
lpfc_offline_prep(phba);
lpfc_offline(phba);
lpfc_reset_barrier(phba);
+ spin_lock_irq(&phba->hbalock);
lpfc_sli_brdreset(phba);
+ spin_unlock_irq(&phba->hbalock);
lpfc_hba_down_post(phba);
lpfc_sli_brdready(phba, HS_MBRDY);
lpfc_unblock_mgmt_io(phba);
@@ -857,7 +949,119 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
}
/**
- * lpfc_handle_eratt: The HBA hardware error handler.
+ * lpfc_sli4_offline_eratt - Bring lpfc offline on SLI4 hardware error attention
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to bring a SLI4 HBA offline when HBA hardware error
+ * other than Port Error 6 has been detected.
+ **/
+static void
+lpfc_sli4_offline_eratt(struct lpfc_hba *phba)
+{
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ lpfc_sli4_brdreset(phba);
+ lpfc_hba_down_post(phba);
+ lpfc_sli4_post_status_check(phba);
+ lpfc_unblock_mgmt_io(phba);
+ phba->link_state = LPFC_HBA_ERROR;
+}
+
+/**
+ * lpfc_handle_deferred_eratt - The HBA hardware deferred error handler
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to handle the deferred HBA hardware error
+ * conditions. This type of error is indicated by HBA by setting ER1
+ * and another ER bit in the host status register. The driver will
+ * wait until the ER1 bit clears before handling the error condition.
+ **/
+static void
+lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
+{
+ uint32_t old_host_status = phba->work_hs;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_sli *psli = &phba->sli;
+
+ /* If the pci channel is offline, ignore possible errors,
+ * since we cannot communicate with the pci card anyway.
+ */
+ if (pci_channel_offline(phba->pcidev)) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~DEFER_ERATT;
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0479 Deferred Adapter Hardware Error "
+ "Data: x%x x%x x%x\n",
+ phba->work_hs,
+ phba->work_status[0], phba->work_status[1]);
+
+ spin_lock_irq(&phba->hbalock);
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
+
+
+ /*
+ * Firmware stops when it triggred erratt. 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];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+
+ /*
+ * There was a firmware error. Take the hba offline and then
+ * attempt to restart it.
+ */
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+
+ /* Wait for the ER1 bit to clear.*/
+ while (phba->work_hs & HS_FFER1) {
+ msleep(100);
+ phba->work_hs = readl(phba->HSregaddr);
+ /* If driver is unloading let the worker thread continue */
+ if (phba->pport->load_flag & FC_UNLOADING) {
+ phba->work_hs = 0;
+ break;
+ }
+ }
+
+ /*
+ * This is to ptrotect against a race condition in which
+ * first write to the host attention register clear the
+ * host status register.
+ */
+ if ((!phba->work_hs) && (!(phba->pport->load_flag & FC_UNLOADING)))
+ phba->work_hs = old_host_status & ~HS_FFER1;
+
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~DEFER_ERATT;
+ spin_unlock_irq(&phba->hbalock);
+ phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
+ phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
+}
+
+static void
+lpfc_board_errevt_to_mgmt(struct lpfc_hba *phba)
+{
+ struct lpfc_board_event_header board_event;
+ struct Scsi_Host *shost;
+
+ board_event.event_type = FC_REG_BOARD_EVENT;
+ board_event.subcategory = LPFC_EVENT_PORTINTERR;
+ shost = lpfc_shost_from_vport(phba->pport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(board_event),
+ (char *) &board_event,
+ LPFC_NL_VENDOR_ID);
+}
+
+/**
+ * lpfc_handle_eratt_s3 - The SLI3 HBA hardware error handler
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to handle the following HBA hardware error
@@ -866,8 +1070,8 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
* 2 - DMA ring index out of range
* 3 - Mailbox command came back as unknown
**/
-void
-lpfc_handle_eratt(struct lpfc_hba *phba)
+static void
+lpfc_handle_eratt_s3(struct lpfc_hba *phba)
{
struct lpfc_vport *vport = phba->pport;
struct lpfc_sli *psli = &phba->sli;
@@ -876,24 +1080,26 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
unsigned long temperature;
struct temp_event temp_event_data;
struct Scsi_Host *shost;
- struct lpfc_board_event_header board_event;
/* If the pci channel is offline, ignore possible errors,
- * since we cannot communicate with the pci card anyway. */
- if (pci_channel_offline(phba->pcidev))
+ * since we cannot communicate with the pci card anyway.
+ */
+ if (pci_channel_offline(phba->pcidev)) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~DEFER_ERATT;
+ spin_unlock_irq(&phba->hbalock);
return;
+ }
+
/* If resets are disabled then leave the HBA alone and return */
if (!phba->cfg_enable_hba_reset)
return;
/* Send an internal error event to mgmt application */
- board_event.event_type = FC_REG_BOARD_EVENT;
- board_event.subcategory = LPFC_EVENT_PORTINTERR;
- shost = lpfc_shost_from_vport(phba->pport);
- fc_host_post_vendor_event(shost, fc_get_event_number(),
- sizeof(board_event),
- (char *) &board_event,
- LPFC_NL_VENDOR_ID);
+ lpfc_board_errevt_to_mgmt(phba);
+
+ if (phba->hba_flag & DEFER_ERATT)
+ lpfc_handle_deferred_eratt(phba);
if (phba->work_hs & HS_FFER6) {
/* Re-establishing Link */
@@ -904,7 +1110,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
phba->work_status[0], phba->work_status[1]);
spin_lock_irq(&phba->hbalock);
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
/*
@@ -976,7 +1182,66 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
}
/**
- * lpfc_handle_latt: The HBA link event handler.
+ * lpfc_handle_eratt_s4 - The SLI4 HBA hardware error handler
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to handle the SLI4 HBA hardware error attention
+ * conditions.
+ **/
+static void
+lpfc_handle_eratt_s4(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+ uint32_t event_data;
+ struct Scsi_Host *shost;
+
+ /* If the pci channel is offline, ignore possible errors, since
+ * we cannot communicate with the pci card anyway.
+ */
+ if (pci_channel_offline(phba->pcidev))
+ return;
+ /* If resets are disabled then leave the HBA alone and return */
+ if (!phba->cfg_enable_hba_reset)
+ return;
+
+ /* Send an internal error event to mgmt application */
+ lpfc_board_errevt_to_mgmt(phba);
+
+ /* For now, the actual action for SLI4 device handling is not
+ * specified yet, just treated it as adaptor hardware failure
+ */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0143 SLI4 Adapter Hardware Error Data: x%x x%x\n",
+ phba->work_status[0], phba->work_status[1]);
+
+ event_data = FC_REG_DUMP_EVENT;
+ shost = lpfc_shost_from_vport(vport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(event_data), (char *) &event_data,
+ SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+
+ lpfc_sli4_offline_eratt(phba);
+}
+
+/**
+ * lpfc_handle_eratt - Wrapper func for handling hba error attention
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba error attention handling
+ * routine from the API jump table function pointer from the lpfc_hba struct.
+ *
+ * Return codes
+ * 0 - sucess.
+ * Any other value - error.
+ **/
+void
+lpfc_handle_eratt(struct lpfc_hba *phba)
+{
+ (*phba->lpfc_handle_eratt)(phba);
+}
+
+/**
+ * lpfc_handle_latt - The HBA link event handler
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked from the worker thread to handle a HBA host
@@ -1063,7 +1328,7 @@ lpfc_handle_latt_err_exit:
}
/**
- * lpfc_parse_vpd: Parse VPD (Vital Product Data).
+ * lpfc_parse_vpd - Parse VPD (Vital Product Data)
* @phba: pointer to lpfc hba data structure.
* @vpd: pointer to the vital product data.
* @len: length of the vital product data in bytes.
@@ -1076,7 +1341,7 @@ lpfc_handle_latt_err_exit:
* 0 - pointer to the VPD passed in is NULL
* 1 - success
**/
-static int
+int
lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
{
uint8_t lenlo, lenhi;
@@ -1213,7 +1478,7 @@ lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
}
/**
- * lpfc_get_hba_model_desc: Retrieve HBA device model name and description.
+ * lpfc_get_hba_model_desc - Retrieve HBA device model name and description
* @phba: pointer to lpfc hba data structure.
* @mdp: pointer to the data structure to hold the derived model name.
* @descp: pointer to the data structure to hold the derived description.
@@ -1231,6 +1496,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
uint16_t dev_id = phba->pcidev->device;
int max_speed;
int GE = 0;
+ int oneConnect = 0; /* default is not a oneConnect */
struct {
char * name;
int max_speed;
@@ -1322,7 +1588,8 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
break;
case PCI_DEVICE_ID_ZEPHYR_DCSP:
- m = (typeof(m)){"LPe11002-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LP2105", max_speed, "PCIe"};
+ GE = 1;
break;
case PCI_DEVICE_ID_ZMID:
m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
@@ -1375,6 +1642,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
case PCI_DEVICE_ID_PROTEUS_S:
m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
break;
+ case PCI_DEVICE_ID_TIGERSHARK:
+ oneConnect = 1;
+ m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"};
+ break;
+ case PCI_DEVICE_ID_TIGERSHARK_S:
+ oneConnect = 1;
+ m = (typeof(m)) {"OCe10100-F-S", max_speed, "PCIe"};
+ break;
default:
m = (typeof(m)){ NULL };
break;
@@ -1382,17 +1657,28 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
if (mdp && mdp[0] == '\0')
snprintf(mdp, 79,"%s", m.name);
- if (descp && descp[0] == '\0')
- snprintf(descp, 255,
- "Emulex %s %d%s %s %s",
- m.name, m.max_speed,
- (GE) ? "GE" : "Gb",
- m.bus,
- (GE) ? "FCoE Adapter" : "Fibre Channel Adapter");
+ /* oneConnect hba requires special processing, they are all initiators
+ * and we put the port number on the end
+ */
+ if (descp && descp[0] == '\0') {
+ if (oneConnect)
+ snprintf(descp, 255,
+ "Emulex OneConnect %s, FCoE Initiator, Port %s",
+ m.name,
+ phba->Port);
+ else
+ snprintf(descp, 255,
+ "Emulex %s %d%s %s %s",
+ m.name, m.max_speed,
+ (GE) ? "GE" : "Gb",
+ m.bus,
+ (GE) ? "FCoE Adapter" :
+ "Fibre Channel Adapter");
+ }
}
/**
- * lpfc_post_buffer: Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring.
+ * lpfc_post_buffer - Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring
* @phba: pointer to lpfc hba data structure.
* @pring: pointer to a IOCB ring.
* @cnt: the number of IOCBs to be posted to the IOCB ring.
@@ -1471,7 +1757,8 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
icmd->ulpCommand = CMD_QUE_RING_BUF64_CN;
icmd->ulpLe = 1;
- if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, pring->ringno, iocb, 0) ==
+ IOCB_ERROR) {
lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
kfree(mp1);
cnt++;
@@ -1493,7 +1780,7 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
}
/**
- * lpfc_post_rcv_buf: Post the initial receive IOCB buffers to ELS ring.
+ * lpfc_post_rcv_buf - Post the initial receive IOCB buffers to ELS ring
* @phba: pointer to lpfc hba data structure.
*
* This routine posts initial receive IOCB buffers to the ELS ring. The
@@ -1518,7 +1805,7 @@ lpfc_post_rcv_buf(struct lpfc_hba *phba)
#define S(N,V) (((V)<<(N))|((V)>>(32-(N))))
/**
- * lpfc_sha_init: Set up initial array of hash table entries.
+ * lpfc_sha_init - Set up initial array of hash table entries
* @HashResultPointer: pointer to an array as hash table.
*
* This routine sets up the initial values to the array of hash table entries
@@ -1535,7 +1822,7 @@ lpfc_sha_init(uint32_t * HashResultPointer)
}
/**
- * lpfc_sha_iterate: Iterate initial hash table with the working hash table.
+ * lpfc_sha_iterate - Iterate initial hash table with the working hash table
* @HashResultPointer: pointer to an initial/result hash table.
* @HashWorkingPointer: pointer to an working hash table.
*
@@ -1592,7 +1879,7 @@ lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
}
/**
- * lpfc_challenge_key: Create challenge key based on WWPN of the HBA.
+ * lpfc_challenge_key - Create challenge key based on WWPN of the HBA
* @RandomChallenge: pointer to the entry of host challenge random number array.
* @HashWorking: pointer to the entry of the working hash array.
*
@@ -1608,7 +1895,7 @@ lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking)
}
/**
- * lpfc_hba_init: Perform special handling for LC HBA initialization.
+ * lpfc_hba_init - Perform special handling for LC HBA initialization
* @phba: pointer to lpfc hba data structure.
* @hbainit: pointer to an array of unsigned 32-bit integers.
*
@@ -1637,7 +1924,7 @@ lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
}
/**
- * lpfc_cleanup: Performs vport cleanups before deleting a vport.
+ * lpfc_cleanup - Performs vport cleanups before deleting a vport
* @vport: pointer to a virtual N_Port data structure.
*
* This routine performs the necessary cleanups before deleting the @vport.
@@ -1699,7 +1986,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
* Lets wait for this to happen, if needed.
*/
while (!list_empty(&vport->fc_nodes)) {
-
if (i++ > 3000) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
"0233 Nodelist not empty\n");
@@ -1720,11 +2006,10 @@ lpfc_cleanup(struct lpfc_vport *vport)
/* Wait for any activity on ndlps to settle */
msleep(10);
}
- return;
}
/**
- * lpfc_stop_vport_timers: Stop all the timers associated with a vport.
+ * lpfc_stop_vport_timers - Stop all the timers associated with a vport
* @vport: pointer to a virtual N_Port data structure.
*
* This routine stops all the timers associated with a @vport. This function
@@ -1741,27 +2026,41 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
}
/**
- * lpfc_stop_phba_timers: Stop all the timers associated with an HBA.
+ * lpfc_stop_hba_timers - Stop all the timers associated with an HBA
* @phba: pointer to lpfc hba data structure.
*
* This routine stops all the timers associated with a HBA. This function is
* invoked before either putting a HBA offline or unloading the driver.
**/
-static void
-lpfc_stop_phba_timers(struct lpfc_hba *phba)
+void
+lpfc_stop_hba_timers(struct lpfc_hba *phba)
{
- del_timer_sync(&phba->fcp_poll_timer);
lpfc_stop_vport_timers(phba->pport);
del_timer_sync(&phba->sli.mbox_tmo);
del_timer_sync(&phba->fabric_block_timer);
- phba->hb_outstanding = 0;
- del_timer_sync(&phba->hb_tmofunc);
del_timer_sync(&phba->eratt_poll);
+ del_timer_sync(&phba->hb_tmofunc);
+ phba->hb_outstanding = 0;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ /* Stop any LightPulse device specific driver timers */
+ del_timer_sync(&phba->fcp_poll_timer);
+ break;
+ case LPFC_PCI_DEV_OC:
+ /* Stop any OneConnect device sepcific driver timers */
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0297 Invalid device group (x%x)\n",
+ phba->pci_dev_grp);
+ break;
+ }
return;
}
/**
- * lpfc_block_mgmt_io: Mark a HBA's management interface as blocked.
+ * lpfc_block_mgmt_io - Mark a HBA's management interface as blocked
* @phba: pointer to lpfc hba data structure.
*
* This routine marks a HBA's management interface as blocked. Once the HBA's
@@ -1781,7 +2080,7 @@ lpfc_block_mgmt_io(struct lpfc_hba * phba)
}
/**
- * lpfc_online: Initialize and bring a HBA online.
+ * lpfc_online - Initialize and bring a HBA online
* @phba: pointer to lpfc hba data structure.
*
* This routine initializes the HBA and brings a HBA online. During this
@@ -1816,14 +2115,21 @@ lpfc_online(struct lpfc_hba *phba)
return 1;
}
- if (lpfc_sli_hba_setup(phba)) { /* Initialize the HBA */
- lpfc_unblock_mgmt_io(phba);
- return 1;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (lpfc_sli4_hba_setup(phba)) { /* Initialize SLI4 HBA */
+ lpfc_unblock_mgmt_io(phba);
+ return 1;
+ }
+ } else {
+ if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */
+ lpfc_unblock_mgmt_io(phba);
+ return 1;
+ }
}
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
struct Scsi_Host *shost;
shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
@@ -1839,7 +2145,7 @@ lpfc_online(struct lpfc_hba *phba)
}
/**
- * lpfc_unblock_mgmt_io: Mark a HBA's management interface to be not blocked.
+ * lpfc_unblock_mgmt_io - Mark a HBA's management interface to be not blocked
* @phba: pointer to lpfc hba data structure.
*
* This routine marks a HBA's management interface as not blocked. Once the
@@ -1860,7 +2166,7 @@ lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
}
/**
- * lpfc_offline_prep: Prepare a HBA to be brought offline.
+ * lpfc_offline_prep - Prepare a HBA to be brought offline
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to prepare a HBA to be brought offline. It performs
@@ -1885,11 +2191,12 @@ lpfc_offline_prep(struct lpfc_hba * phba)
/* Issue an unreg_login to all nodes on all vports */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
struct Scsi_Host *shost;
if (vports[i]->load_flag & FC_UNLOADING)
continue;
+ vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
@@ -1913,11 +2220,11 @@ lpfc_offline_prep(struct lpfc_hba * phba)
}
lpfc_destroy_vport_work_array(phba, vports);
- lpfc_sli_flush_mbox_queue(phba);
+ lpfc_sli_mbox_sys_shutdown(phba);
}
/**
- * lpfc_offline: Bring a HBA offline.
+ * lpfc_offline - Bring a HBA offline
* @phba: pointer to lpfc hba data structure.
*
* This routine actually brings a HBA offline. It stops all the timers
@@ -1934,11 +2241,11 @@ lpfc_offline(struct lpfc_hba *phba)
if (phba->pport->fc_flag & FC_OFFLINE_MODE)
return;
- /* stop all timers associated with this hba */
- lpfc_stop_phba_timers(phba);
+ /* stop port and all timers associated with this hba */
+ lpfc_stop_port(phba);
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
lpfc_stop_vport_timers(vports[i]);
lpfc_destroy_vport_work_array(phba, vports);
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -1951,7 +2258,7 @@ lpfc_offline(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
vports[i]->work_port_events = 0;
@@ -1962,7 +2269,7 @@ lpfc_offline(struct lpfc_hba *phba)
}
/**
- * lpfc_scsi_free: Free all the SCSI buffers and IOCBs from driver lists.
+ * lpfc_scsi_free - Free all the SCSI buffers and IOCBs from driver lists
* @phba: pointer to lpfc hba data structure.
*
* This routine is to free all the SCSI buffers and IOCBs from the driver
@@ -2001,7 +2308,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
}
/**
- * lpfc_create_port: Create an FC port.
+ * lpfc_create_port - Create an FC port
* @phba: pointer to lpfc hba data structure.
* @instance: a unique integer ID to this FC port.
* @dev: pointer to the device data structure.
@@ -2044,6 +2351,10 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ shost->dma_boundary = LPFC_SLI4_MAX_SEGMENT_SIZE;
+ shost->sg_tablesize = phba->cfg_sg_seg_cnt;
+ }
/*
* Set initial can_queue value since 0 is no longer supported and
@@ -2061,6 +2372,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
/* Initialize all internally managed lists. */
INIT_LIST_HEAD(&vport->fc_nodes);
+ INIT_LIST_HEAD(&vport->rcv_buffer_list);
spin_lock_init(&vport->work_port_lock);
init_timer(&vport->fc_disctmo);
@@ -2091,7 +2403,7 @@ out:
}
/**
- * destroy_port: Destroy an FC port.
+ * destroy_port - destroy an FC port
* @vport: pointer to an lpfc virtual N_Port data structure.
*
* This routine destroys a FC port from the upper layer protocol. All the
@@ -2116,7 +2428,7 @@ destroy_port(struct lpfc_vport *vport)
}
/**
- * lpfc_get_instance: Get a unique integer ID.
+ * lpfc_get_instance - Get a unique integer ID
*
* This routine allocates a unique integer ID from lpfc_hba_index pool. It
* uses the kernel idr facility to perform the task.
@@ -2139,7 +2451,7 @@ lpfc_get_instance(void)
}
/**
- * lpfc_scan_finished: method for SCSI layer to detect whether scan is done.
+ * lpfc_scan_finished - method for SCSI layer to detect whether scan is done
* @shost: pointer to SCSI host data structure.
* @time: elapsed time of the scan in jiffies.
*
@@ -2197,7 +2509,7 @@ finished:
}
/**
- * lpfc_host_attrib_init: Initialize SCSI host attributes on a FC port.
+ * lpfc_host_attrib_init - Initialize SCSI host attributes on a FC port
* @shost: pointer to SCSI host data structure.
*
* This routine initializes a given SCSI host attributes on a FC port. The
@@ -2252,15 +2564,3461 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
}
/**
- * lpfc_enable_msix: Enable MSI-X interrupt mode.
+ * lpfc_stop_port_s3 - Stop SLI3 device port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to stop an SLI3 device port, it stops the device
+ * from generating interrupts and stops the device driver's timers for the
+ * device.
+ **/
+static void
+lpfc_stop_port_s3(struct lpfc_hba *phba)
+{
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ /* Reset some HBA SLI setup states */
+ lpfc_stop_hba_timers(phba);
+ phba->pport->work_port_events = 0;
+}
+
+/**
+ * lpfc_stop_port_s4 - Stop SLI4 device port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to stop an SLI4 device port, it stops the device
+ * from generating interrupts and stops the device driver's timers for the
+ * device.
+ **/
+static void
+lpfc_stop_port_s4(struct lpfc_hba *phba)
+{
+ /* Reset some HBA SLI4 setup states */
+ lpfc_stop_hba_timers(phba);
+ phba->pport->work_port_events = 0;
+ phba->sli4_hba.intr_enable = 0;
+ /* Hard clear it for now, shall have more graceful way to wait later */
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+}
+
+/**
+ * lpfc_stop_port - Wrapper function for stopping hba port
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba stop port routine from
+ * the API jump table function pointer from the lpfc_hba struct.
+ **/
+void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+ phba->lpfc_stop_port(phba);
+}
+
+/**
+ * lpfc_sli4_remove_dflt_fcf - Remove the driver default fcf record from the port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the driver default fcf record from
+ * the port. This routine currently acts on FCF Index 0.
+ *
+ **/
+void
+lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
+{
+ int rc = 0;
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_del_fcf_tbl_entry *del_fcf_record;
+ uint32_t mbox_tmo, req_len;
+ uint32_t shdr_status, shdr_add_status;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2020 Failed to allocate mbox for ADD_FCF cmd\n");
+ return;
+ }
+
+ req_len = sizeof(struct lpfc_mbx_del_fcf_tbl_entry) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ rc = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_DELETE_FCF,
+ req_len, LPFC_SLI4_MBX_EMBED);
+ /*
+ * In phase 1, there is a single FCF index, 0. In phase2, the driver
+ * supports multiple FCF indices.
+ */
+ del_fcf_record = &mboxq->u.mqe.un.del_fcf_entry;
+ bf_set(lpfc_mbx_del_fcf_tbl_count, del_fcf_record, 1);
+ bf_set(lpfc_mbx_del_fcf_tbl_index, del_fcf_record,
+ phba->fcf.fcf_indx);
+
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ }
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &del_fcf_record->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &del_fcf_record->header.cfg_shdr.response);
+ if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2516 DEL FCF of default FCF Index failed "
+ "mbx status x%x, status x%x add_status x%x\n",
+ rc, shdr_status, shdr_add_status);
+ }
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link-attention link fault code and
+ * translate it into the base driver's read link attention mailbox command
+ * status.
+ *
+ * Return: Link-attention status in terms of base driver's coding.
+ **/
+static uint16_t
+lpfc_sli4_parse_latt_fault(struct lpfc_hba *phba,
+ struct lpfc_acqe_link *acqe_link)
+{
+ uint16_t latt_fault;
+
+ switch (bf_get(lpfc_acqe_link_fault, acqe_link)) {
+ case LPFC_ASYNC_LINK_FAULT_NONE:
+ case LPFC_ASYNC_LINK_FAULT_LOCAL:
+ case LPFC_ASYNC_LINK_FAULT_REMOTE:
+ latt_fault = 0;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0398 Invalid link fault code: x%x\n",
+ bf_get(lpfc_acqe_link_fault, acqe_link));
+ latt_fault = MBXERR_ERROR;
+ break;
+ }
+ return latt_fault;
+}
+
+/**
+ * lpfc_sli4_parse_latt_type - Parse sli4 link attention type
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link attention type and translate it
+ * into the base driver's link attention type coding.
+ *
+ * Return: Link attention type in terms of base driver's coding.
+ **/
+static uint8_t
+lpfc_sli4_parse_latt_type(struct lpfc_hba *phba,
+ struct lpfc_acqe_link *acqe_link)
+{
+ uint8_t att_type;
+
+ switch (bf_get(lpfc_acqe_link_status, acqe_link)) {
+ case LPFC_ASYNC_LINK_STATUS_DOWN:
+ case LPFC_ASYNC_LINK_STATUS_LOGICAL_DOWN:
+ att_type = AT_LINK_DOWN;
+ break;
+ case LPFC_ASYNC_LINK_STATUS_UP:
+ /* Ignore physical link up events - wait for logical link up */
+ att_type = AT_RESERVED;
+ break;
+ case LPFC_ASYNC_LINK_STATUS_LOGICAL_UP:
+ att_type = AT_LINK_UP;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0399 Invalid link attention type: x%x\n",
+ bf_get(lpfc_acqe_link_status, acqe_link));
+ att_type = AT_RESERVED;
+ break;
+ }
+ return att_type;
+}
+
+/**
+ * lpfc_sli4_parse_latt_link_speed - Parse sli4 link-attention link speed
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link-attention link speed and translate
+ * it into the base driver's link-attention link speed coding.
+ *
+ * Return: Link-attention link speed in terms of base driver's coding.
+ **/
+static uint8_t
+lpfc_sli4_parse_latt_link_speed(struct lpfc_hba *phba,
+ struct lpfc_acqe_link *acqe_link)
+{
+ uint8_t link_speed;
+
+ switch (bf_get(lpfc_acqe_link_speed, acqe_link)) {
+ case LPFC_ASYNC_LINK_SPEED_ZERO:
+ link_speed = LA_UNKNW_LINK;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_10MBPS:
+ link_speed = LA_UNKNW_LINK;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100MBPS:
+ link_speed = LA_UNKNW_LINK;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_1GBPS:
+ link_speed = LA_1GHZ_LINK;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ link_speed = LA_10GHZ_LINK;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0483 Invalid link-attention link speed: x%x\n",
+ bf_get(lpfc_acqe_link_speed, acqe_link));
+ link_speed = LA_UNKNW_LINK;
+ break;
+ }
+ return link_speed;
+}
+
+/**
+ * lpfc_sli4_async_link_evt - Process the asynchronous link event
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to handle the SLI4 asynchronous link event.
+ **/
+static void
+lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
+ struct lpfc_acqe_link *acqe_link)
+{
+ struct lpfc_dmabuf *mp;
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ READ_LA_VAR *la;
+ uint8_t att_type;
+
+ att_type = lpfc_sli4_parse_latt_type(phba, acqe_link);
+ if (att_type != AT_LINK_DOWN && att_type != AT_LINK_UP)
+ return;
+ pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0395 The mboxq allocation failed\n");
+ return;
+ }
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0396 The lpfc_dmabuf allocation failed\n");
+ goto out_free_pmb;
+ }
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+ if (!mp->virt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0397 The mbuf allocation failed\n");
+ goto out_free_dmabuf;
+ }
+
+ /* Cleanup any outstanding ELS commands */
+ lpfc_els_flush_all_cmd(phba);
+
+ /* Block ELS IOCBs until we have done process link event */
+ phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
+
+ /* Update link event statistics */
+ phba->sli.slistat.link_event++;
+
+ /* Create pseudo lpfc_handle_latt mailbox command from link ACQE */
+ lpfc_read_la(phba, pmb, mp);
+ pmb->vport = phba->pport;
+
+ /* Parse and translate status field */
+ mb = &pmb->u.mb;
+ mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, acqe_link);
+
+ /* Parse and translate link attention fields */
+ la = (READ_LA_VAR *) &pmb->u.mb.un.varReadLA;
+ la->eventTag = acqe_link->event_tag;
+ la->attType = att_type;
+ la->UlnkSpeed = lpfc_sli4_parse_latt_link_speed(phba, acqe_link);
+
+ /* Fake the the following irrelvant fields */
+ la->topology = TOPOLOGY_PT_PT;
+ la->granted_AL_PA = 0;
+ la->il = 0;
+ la->pb = 0;
+ la->fa = 0;
+ la->mm = 0;
+
+ /* Keep the link status for extra SLI4 state machine reference */
+ phba->sli4_hba.link_state.speed =
+ bf_get(lpfc_acqe_link_speed, acqe_link);
+ phba->sli4_hba.link_state.duplex =
+ bf_get(lpfc_acqe_link_duplex, acqe_link);
+ phba->sli4_hba.link_state.status =
+ bf_get(lpfc_acqe_link_status, acqe_link);
+ phba->sli4_hba.link_state.physical =
+ bf_get(lpfc_acqe_link_physical, acqe_link);
+ phba->sli4_hba.link_state.fault =
+ bf_get(lpfc_acqe_link_fault, acqe_link);
+
+ /* Invoke the lpfc_handle_latt mailbox command callback function */
+ lpfc_mbx_cmpl_read_la(phba, pmb);
+
+ return;
+
+out_free_dmabuf:
+ kfree(mp);
+out_free_pmb:
+ mempool_free(pmb, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_async_fcoe_evt - Process the asynchronous fcoe event
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async fcoe completion queue entry.
+ *
+ * This routine is to handle the SLI4 asynchronous fcoe event.
+ **/
+static void
+lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
+ struct lpfc_acqe_fcoe *acqe_fcoe)
+{
+ uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
+ int rc;
+
+ switch (event_type) {
+ case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2546 New FCF found index 0x%x tag 0x%x \n",
+ acqe_fcoe->fcf_index,
+ acqe_fcoe->event_tag);
+ /*
+ * If the current FCF is in discovered state,
+ * do nothing.
+ */
+ spin_lock_irq(&phba->hbalock);
+ if (phba->fcf.fcf_flag & FCF_DISCOVERED) {
+ spin_unlock_irq(&phba->hbalock);
+ break;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Read the FCF table and re-discover SAN. */
+ rc = lpfc_sli4_read_fcf_record(phba,
+ LPFC_FCOE_FCF_GET_FIRST);
+ if (rc)
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2547 Read FCF record failed 0x%x\n",
+ rc);
+ break;
+
+ case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2548 FCF Table full count 0x%x tag 0x%x \n",
+ bf_get(lpfc_acqe_fcoe_fcf_count, acqe_fcoe),
+ acqe_fcoe->event_tag);
+ break;
+
+ case LPFC_FCOE_EVENT_TYPE_FCF_DEAD:
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2549 FCF disconnected fron network index 0x%x"
+ " tag 0x%x \n", acqe_fcoe->fcf_index,
+ acqe_fcoe->event_tag);
+ /* If the event is not for currently used fcf do nothing */
+ if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index)
+ break;
+ /*
+ * Currently, driver support only one FCF - so treat this as
+ * a link down.
+ */
+ lpfc_linkdown(phba);
+ /* Unregister FCF if no devices connected to it */
+ lpfc_unregister_unused_fcf(phba);
+ break;
+
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0288 Unknown FCoE event type 0x%x event tag "
+ "0x%x\n", event_type, acqe_fcoe->event_tag);
+ break;
+ }
+}
+
+/**
+ * lpfc_sli4_async_dcbx_evt - Process the asynchronous dcbx event
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async dcbx completion queue entry.
+ *
+ * This routine is to handle the SLI4 asynchronous dcbx event.
+ **/
+static void
+lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
+ struct lpfc_acqe_dcbx *acqe_dcbx)
+{
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0290 The SLI4 DCBX asynchronous event is not "
+ "handled yet\n");
+}
+
+/**
+ * lpfc_sli4_async_event_proc - Process all the pending asynchronous event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 asynchronous events.
+ **/
+void lpfc_sli4_async_event_proc(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event;
+
+ /* First, declare the async event has been handled */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~ASYNC_EVENT;
+ spin_unlock_irq(&phba->hbalock);
+ /* Now, handle all the async events */
+ while (!list_empty(&phba->sli4_hba.sp_asynce_work_queue)) {
+ /* Get the first event from the head of the event queue */
+ spin_lock_irq(&phba->hbalock);
+ list_remove_head(&phba->sli4_hba.sp_asynce_work_queue,
+ cq_event, struct lpfc_cq_event, list);
+ spin_unlock_irq(&phba->hbalock);
+ /* Process the asynchronous event */
+ switch (bf_get(lpfc_trailer_code, &cq_event->cqe.mcqe_cmpl)) {
+ case LPFC_TRAILER_CODE_LINK:
+ lpfc_sli4_async_link_evt(phba,
+ &cq_event->cqe.acqe_link);
+ break;
+ case LPFC_TRAILER_CODE_FCOE:
+ lpfc_sli4_async_fcoe_evt(phba,
+ &cq_event->cqe.acqe_fcoe);
+ break;
+ case LPFC_TRAILER_CODE_DCBX:
+ lpfc_sli4_async_dcbx_evt(phba,
+ &cq_event->cqe.acqe_dcbx);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "1804 Invalid asynchrous event code: "
+ "x%x\n", bf_get(lpfc_trailer_code,
+ &cq_event->cqe.mcqe_cmpl));
+ break;
+ }
+ /* Free the completion event processed to the free pool */
+ lpfc_sli4_cq_event_release(phba, cq_event);
+ }
+}
+
+/**
+ * lpfc_api_table_setup - Set up per hba pci-device group func api jump table
+ * @phba: pointer to lpfc hba data structure.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine is invoked to set up the per HBA PCI-Device group function
+ * API jump table entries.
+ *
+ * Return: 0 if success, otherwise -ENODEV
+ **/
+int
+lpfc_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+ int rc;
+
+ /* Set up lpfc PCI-device group */
+ phba->pci_dev_grp = dev_grp;
+
+ /* The LPFC_PCI_DEV_OC uses SLI4 */
+ if (dev_grp == LPFC_PCI_DEV_OC)
+ phba->sli_rev = LPFC_SLI_REV4;
+
+ /* Set up device INIT API function jump table */
+ rc = lpfc_init_api_table_setup(phba, dev_grp);
+ if (rc)
+ return -ENODEV;
+ /* Set up SCSI API function jump table */
+ rc = lpfc_scsi_api_table_setup(phba, dev_grp);
+ if (rc)
+ return -ENODEV;
+ /* Set up SLI API function jump table */
+ rc = lpfc_sli_api_table_setup(phba, dev_grp);
+ if (rc)
+ return -ENODEV;
+ /* Set up MBOX API function jump table */
+ rc = lpfc_mbox_api_table_setup(phba, dev_grp);
+ if (rc)
+ return -ENODEV;
+
+ return 0;
+}
+
+/**
+ * lpfc_log_intr_mode - Log the active interrupt mode
+ * @phba: pointer to lpfc hba data structure.
+ * @intr_mode: active interrupt mode adopted.
+ *
+ * This routine it invoked to log the currently used active interrupt mode
+ * to the device.
+ **/
+static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+{
+ switch (intr_mode) {
+ case 0:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0470 Enable INTx interrupt mode.\n");
+ break;
+ case 1:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0481 Enabled MSI interrupt mode.\n");
+ break;
+ case 2:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0480 Enabled MSI-X interrupt mode.\n");
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0482 Illegal interrupt mode.\n");
+ break;
+ }
+ return;
+}
+
+/**
+ * lpfc_enable_pci_dev - Enable a generic PCI device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the PCI device that is common to all
+ * PCI devices.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_enable_pci_dev(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+ int bars;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ goto out_error;
+ else
+ pdev = phba->pcidev;
+ /* Select PCI BARs */
+ bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ /* Enable PCI device */
+ if (pci_enable_device_mem(pdev))
+ goto out_error;
+ /* Request PCI resource for the device */
+ if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
+ goto out_disable_device;
+ /* Set up device as PCI master and save state for EEH */
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+ pci_save_state(pdev);
+
+ return 0;
+
+out_disable_device:
+ pci_disable_device(pdev);
+out_error:
+ return -ENODEV;
+}
+
+/**
+ * lpfc_disable_pci_dev - Disable a generic PCI device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the PCI device that is common to all
+ * PCI devices.
+ **/
+static void
+lpfc_disable_pci_dev(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+ int bars;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ return;
+ else
+ pdev = phba->pcidev;
+ /* Select PCI BARs */
+ bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ /* Release PCI resource and disable PCI device */
+ pci_release_selected_regions(pdev, bars);
+ pci_disable_device(pdev);
+ /* Null out PCI private reference to driver */
+ pci_set_drvdata(pdev, NULL);
+
+ return;
+}
+
+/**
+ * lpfc_reset_hba - Reset a hba
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to reset a hba device. It brings the HBA
+ * offline, performs a board restart, and then brings the board back
+ * online. The lpfc_offline calls lpfc_sli_hba_down which will clean up
+ * on outstanding mailbox commands.
+ **/
+void
+lpfc_reset_hba(struct lpfc_hba *phba)
+{
+ /* If resets are disabled then set error state and return. */
+ if (!phba->cfg_enable_hba_reset) {
+ phba->link_state = LPFC_HBA_ERROR;
+ return;
+ }
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ lpfc_sli_brdrestart(phba);
+ lpfc_online(phba);
+ lpfc_unblock_mgmt_io(phba);
+}
+
+/**
+ * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources specific to
+ * support the SLI-3 HBA device it attached to.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli;
+
+ /*
+ * Initialize timers used by driver
+ */
+
+ /* Heartbeat timer */
+ init_timer(&phba->hb_tmofunc);
+ phba->hb_tmofunc.function = lpfc_hb_timeout;
+ phba->hb_tmofunc.data = (unsigned long)phba;
+
+ psli = &phba->sli;
+ /* MBOX heartbeat timer */
+ init_timer(&psli->mbox_tmo);
+ psli->mbox_tmo.function = lpfc_mbox_timeout;
+ psli->mbox_tmo.data = (unsigned long) phba;
+ /* FCP polling mode timer */
+ init_timer(&phba->fcp_poll_timer);
+ phba->fcp_poll_timer.function = lpfc_poll_timeout;
+ phba->fcp_poll_timer.data = (unsigned long) phba;
+ /* Fabric block timer */
+ init_timer(&phba->fabric_block_timer);
+ phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
+ phba->fabric_block_timer.data = (unsigned long) phba;
+ /* EA polling mode timer */
+ init_timer(&phba->eratt_poll);
+ phba->eratt_poll.function = lpfc_poll_eratt;
+ phba->eratt_poll.data = (unsigned long) phba;
+
+ /* Host attention work mask setup */
+ phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT);
+ phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
+
+ /* Get all the module params for configuring this host */
+ lpfc_get_cfgparam(phba);
+ /*
+ * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+ * used to create the sg_dma_buf_pool must be dynamically calculated.
+ * 2 segments are added since the IOCB needs a command and response bde.
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp) +
+ ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+
+ if (phba->cfg_enable_bg) {
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+ phba->cfg_sg_dma_buf_size +=
+ phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+ }
+
+ /* Also reinitialize the host templates with new values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
+ phba->max_vpi = LPFC_MAX_VPI;
+ /* This will be set to correct value after config_port mbox */
+ phba->max_vports = 0;
+
+ /*
+ * Initialize the SLI Layer to run with lpfc HBAs.
+ */
+ lpfc_sli_setup(phba);
+ lpfc_sli_queue_setup(phba);
+
+ /* Allocate device driver memory */
+ if (lpfc_mem_alloc(phba, BPL_ALIGN_SZ))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * lpfc_sli_driver_resource_unset - Unset drvr internal resources for SLI3 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up
+ * specific for supporting the SLI-3 HBA device it attached to.
+ **/
+static void
+lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
+{
+ /* Free device driver memory allocated */
+ lpfc_mem_free_all(phba);
+
+ return;
+}
+
+/**
+ * lpfc_sli4_driver_resource_setup - Setup drvr internal resources for SLI4 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources specific to
+ * support the SLI-4 HBA device it attached to.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli;
+ int rc;
+ int i, hbq_count;
+
+ /* Before proceed, wait for POST done and device ready */
+ rc = lpfc_sli4_post_status_check(phba);
+ if (rc)
+ return -ENODEV;
+
+ /*
+ * Initialize timers used by driver
+ */
+
+ /* Heartbeat timer */
+ init_timer(&phba->hb_tmofunc);
+ phba->hb_tmofunc.function = lpfc_hb_timeout;
+ phba->hb_tmofunc.data = (unsigned long)phba;
+
+ psli = &phba->sli;
+ /* MBOX heartbeat timer */
+ init_timer(&psli->mbox_tmo);
+ psli->mbox_tmo.function = lpfc_mbox_timeout;
+ psli->mbox_tmo.data = (unsigned long) phba;
+ /* Fabric block timer */
+ init_timer(&phba->fabric_block_timer);
+ phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
+ phba->fabric_block_timer.data = (unsigned long) phba;
+ /* EA polling mode timer */
+ init_timer(&phba->eratt_poll);
+ phba->eratt_poll.function = lpfc_poll_eratt;
+ phba->eratt_poll.data = (unsigned long) phba;
+ /*
+ * We need to do a READ_CONFIG mailbox command here before
+ * calling lpfc_get_cfgparam. For VFs this will report the
+ * MAX_XRI, MAX_VPI, MAX_RPI, MAX_IOCB, and MAX_VFI settings.
+ * All of the resources allocated
+ * for this Port are tied to these values.
+ */
+ /* Get all the module params for configuring this host */
+ lpfc_get_cfgparam(phba);
+ phba->max_vpi = LPFC_MAX_VPI;
+ /* This will be set to correct value after the read_config mbox */
+ phba->max_vports = 0;
+
+ /* Program the default value of vlan_id and fc_map */
+ phba->valid_vlan = 0;
+ phba->fc_map[0] = LPFC_FCOE_FCF_MAP0;
+ phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
+ phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
+
+ /*
+ * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+ * used to create the sg_dma_buf_pool must be dynamically calculated.
+ * 2 segments are added since the IOCB needs a command and response bde.
+ * To insure that the scsi sgl does not cross a 4k page boundary only
+ * sgl sizes of 1k, 2k, 4k, and 8k are supported.
+ * Table of sgl sizes and seg_cnt:
+ * sgl size, sg_seg_cnt total seg
+ * 1k 50 52
+ * 2k 114 116
+ * 4k 242 244
+ * 8k 498 500
+ * cmd(32) + rsp(160) + (52 * sizeof(sli4_sge)) = 1024
+ * cmd(32) + rsp(160) + (116 * sizeof(sli4_sge)) = 2048
+ * cmd(32) + rsp(160) + (244 * sizeof(sli4_sge)) = 4096
+ * cmd(32) + rsp(160) + (500 * sizeof(sli4_sge)) = 8192
+ */
+ if (phba->cfg_sg_seg_cnt <= LPFC_DEFAULT_SG_SEG_CNT)
+ phba->cfg_sg_seg_cnt = 50;
+ else if (phba->cfg_sg_seg_cnt <= 114)
+ phba->cfg_sg_seg_cnt = 114;
+ else if (phba->cfg_sg_seg_cnt <= 242)
+ phba->cfg_sg_seg_cnt = 242;
+ else
+ phba->cfg_sg_seg_cnt = 498;
+
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd)
+ + sizeof(struct fcp_rsp);
+ phba->cfg_sg_dma_buf_size +=
+ ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge));
+
+ /* Initialize buffer queue management fields */
+ hbq_count = lpfc_sli_hbq_count();
+ for (i = 0; i < hbq_count; ++i)
+ INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
+ INIT_LIST_HEAD(&phba->rb_pend_list);
+ phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_sli4_rb_alloc;
+ phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_sli4_rb_free;
+
+ /*
+ * Initialize the SLI Layer to run with lpfc SLI4 HBAs.
+ */
+ /* Initialize the Abort scsi buffer list used by driver */
+ spin_lock_init(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+ /* This abort list used by worker thread */
+ spin_lock_init(&phba->sli4_hba.abts_sgl_list_lock);
+
+ /*
+ * Initialize dirver internal slow-path work queues
+ */
+
+ /* Driver internel slow-path CQ Event pool */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool);
+ /* Response IOCB work queue list */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_rspiocb_work_queue);
+ /* Asynchronous event CQ Event work queue list */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue);
+ /* Fast-path XRI aborted CQ Event work queue list */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
+ /* Slow-path XRI aborted CQ Event work queue list */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_els_xri_aborted_work_queue);
+ /* Receive queue CQ Event work queue list */
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_unsol_work_queue);
+
+ /* Initialize the driver internal SLI layer lists. */
+ lpfc_sli_setup(phba);
+ lpfc_sli_queue_setup(phba);
+
+ /* Allocate device driver memory */
+ rc = lpfc_mem_alloc(phba, SGL_ALIGN_SZ);
+ if (rc)
+ return -ENOMEM;
+
+ /* Create the bootstrap mailbox command */
+ rc = lpfc_create_bootstrap_mbox(phba);
+ if (unlikely(rc))
+ goto out_free_mem;
+
+ /* Set up the host's endian order with the device. */
+ rc = lpfc_setup_endian_order(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
+ /* Set up the hba's configuration parameters. */
+ rc = lpfc_sli4_read_config(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
+ /* Perform a function reset */
+ rc = lpfc_pci_function_reset(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
+ /* Create all the SLI4 queues */
+ rc = lpfc_sli4_queue_create(phba);
+ if (rc)
+ goto out_free_bsmbx;
+
+ /* Create driver internal CQE event pool */
+ rc = lpfc_sli4_cq_event_pool_create(phba);
+ if (rc)
+ goto out_destroy_queue;
+
+ /* Initialize and populate the iocb list per host */
+ rc = lpfc_init_sgl_list(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1400 Failed to initialize sgl list.\n");
+ goto out_destroy_cq_event_pool;
+ }
+ rc = lpfc_init_active_sgl_array(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1430 Failed to initialize sgl list.\n");
+ goto out_free_sgl_list;
+ }
+
+ rc = lpfc_sli4_init_rpi_hdrs(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1432 Failed to initialize rpi headers.\n");
+ goto out_free_active_sgl;
+ }
+
+ phba->sli4_hba.fcp_eq_hdl = kzalloc((sizeof(struct lpfc_fcp_eq_hdl) *
+ phba->cfg_fcp_eq_count), GFP_KERNEL);
+ if (!phba->sli4_hba.fcp_eq_hdl) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2572 Failed allocate memory for fast-path "
+ "per-EQ handle array\n");
+ goto out_remove_rpi_hdrs;
+ }
+
+ phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) *
+ phba->sli4_hba.cfg_eqn), GFP_KERNEL);
+ if (!phba->sli4_hba.msix_entries) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2573 Failed allocate memory for msi-x "
+ "interrupt vector entries\n");
+ goto out_free_fcp_eq_hdl;
+ }
+
+ return rc;
+
+out_free_fcp_eq_hdl:
+ kfree(phba->sli4_hba.fcp_eq_hdl);
+out_remove_rpi_hdrs:
+ lpfc_sli4_remove_rpi_hdrs(phba);
+out_free_active_sgl:
+ lpfc_free_active_sgl(phba);
+out_free_sgl_list:
+ lpfc_free_sgl_list(phba);
+out_destroy_cq_event_pool:
+ lpfc_sli4_cq_event_pool_destroy(phba);
+out_destroy_queue:
+ lpfc_sli4_queue_destroy(phba);
+out_free_bsmbx:
+ lpfc_destroy_bootstrap_mbox(phba);
+out_free_mem:
+ lpfc_mem_free(phba);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_driver_resource_unset - Unset drvr internal resources for SLI4 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up
+ * specific for supporting the SLI-4 HBA device it attached to.
+ **/
+static void
+lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
+{
+ struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
+
+ /* unregister default FCFI from the HBA */
+ lpfc_sli4_fcfi_unreg(phba, phba->fcf.fcfi);
+
+ /* Free the default FCR table */
+ lpfc_sli_remove_dflt_fcf(phba);
+
+ /* Free memory allocated for msi-x interrupt vector entries */
+ kfree(phba->sli4_hba.msix_entries);
+
+ /* Free memory allocated for fast-path work queue handles */
+ kfree(phba->sli4_hba.fcp_eq_hdl);
+
+ /* Free the allocated rpi headers. */
+ lpfc_sli4_remove_rpi_hdrs(phba);
+
+ /* Free the ELS sgl list */
+ lpfc_free_active_sgl(phba);
+ lpfc_free_sgl_list(phba);
+
+ /* Free the SCSI sgl management array */
+ kfree(phba->sli4_hba.lpfc_scsi_psb_array);
+
+ /* Free the SLI4 queues */
+ lpfc_sli4_queue_destroy(phba);
+
+ /* Free the completion queue EQ event pool */
+ lpfc_sli4_cq_event_release_all(phba);
+ lpfc_sli4_cq_event_pool_destroy(phba);
+
+ /* Reset SLI4 HBA FCoE function */
+ lpfc_pci_function_reset(phba);
+
+ /* Free the bsmbx region. */
+ lpfc_destroy_bootstrap_mbox(phba);
+
+ /* Free the SLI Layer memory with SLI4 HBAs */
+ lpfc_mem_free_all(phba);
+
+ /* Free the current connect table */
+ list_for_each_entry_safe(conn_entry, next_conn_entry,
+ &phba->fcf_conn_rec_list, list)
+ kfree(conn_entry);
+
+ return;
+}
+
+/**
+ * lpfc_init_api_table_setup - Set up init api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the device INIT interface API function jump table
+ * in @phba struct.
+ *
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+ switch (dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ phba->lpfc_hba_down_post = lpfc_hba_down_post_s3;
+ phba->lpfc_handle_eratt = lpfc_handle_eratt_s3;
+ phba->lpfc_stop_port = lpfc_stop_port_s3;
+ break;
+ case LPFC_PCI_DEV_OC:
+ phba->lpfc_hba_down_post = lpfc_hba_down_post_s4;
+ phba->lpfc_handle_eratt = lpfc_handle_eratt_s4;
+ phba->lpfc_stop_port = lpfc_stop_port_s4;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1431 Invalid HBA PCI-device group: 0x%x\n",
+ dev_grp);
+ return -ENODEV;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources before the
+ * device specific resource setup to support the HBA device it attached to.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
+{
+ /*
+ * Driver resources common to all SLI revisions
+ */
+ atomic_set(&phba->fast_event_count, 0);
+ spin_lock_init(&phba->hbalock);
+
+ /* Initialize ndlp management spinlock */
+ spin_lock_init(&phba->ndlp_lock);
+
+ INIT_LIST_HEAD(&phba->port_list);
+ INIT_LIST_HEAD(&phba->work_list);
+ init_waitqueue_head(&phba->wait_4_mlo_m_q);
+
+ /* Initialize the wait queue head for the kernel thread */
+ init_waitqueue_head(&phba->work_waitq);
+
+ /* Initialize the scsi buffer list used by driver for scsi IO */
+ spin_lock_init(&phba->scsi_buf_list_lock);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
+
+ /* Initialize the fabric iocb list */
+ INIT_LIST_HEAD(&phba->fabric_iocb_list);
+
+ /* Initialize list to save ELS buffers */
+ INIT_LIST_HEAD(&phba->elsbuf);
+
+ /* Initialize FCF connection rec list */
+ INIT_LIST_HEAD(&phba->fcf_conn_rec_list);
+
+ return 0;
+}
+
+/**
+ * lpfc_setup_driver_resource_phase2 - Phase2 setup driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources after the
+ * device specific resource setup to support the HBA device it attached to.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_setup_driver_resource_phase2(struct lpfc_hba *phba)
+{
+ int error;
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_unset_driver_resource_phase2 - Phase2 unset driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up after
+ * the device specific resource setup for supporting the HBA device it
+ * attached to.
+ **/
+static void
+lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba)
+{
+ /* Stop kernel worker thread */
+ kthread_stop(phba->worker_thread);
+}
+
+/**
+ * lpfc_free_iocb_list - Free iocb list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver's IOCB list and memory.
+ **/
+static void
+lpfc_free_iocb_list(struct lpfc_hba *phba)
+{
+ struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
+
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry_safe(iocbq_entry, iocbq_next,
+ &phba->lpfc_iocb_list, list) {
+ list_del(&iocbq_entry->list);
+ kfree(iocbq_entry);
+ phba->total_iocbq_bufs--;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ return;
+}
+
+/**
+ * lpfc_init_iocb_list - Allocate and initialize iocb list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and initizlize the driver's IOCB
+ * list and set up the IOCB tag array accordingly.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_init_iocb_list(struct lpfc_hba *phba, int iocb_count)
+{
+ struct lpfc_iocbq *iocbq_entry = NULL;
+ uint16_t iotag;
+ int i;
+
+ /* Initialize and populate the iocb list per host. */
+ INIT_LIST_HEAD(&phba->lpfc_iocb_list);
+ for (i = 0; i < iocb_count; i++) {
+ iocbq_entry = kzalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
+ if (iocbq_entry == NULL) {
+ printk(KERN_ERR "%s: only allocated %d iocbs of "
+ "expected %d count. Unloading driver.\n",
+ __func__, i, LPFC_IOCB_LIST_CNT);
+ goto out_free_iocbq;
+ }
+
+ iotag = lpfc_sli_next_iotag(phba, iocbq_entry);
+ if (iotag == 0) {
+ kfree(iocbq_entry);
+ printk(KERN_ERR "%s: failed to allocate IOTAG. "
+ "Unloading driver.\n", __func__);
+ goto out_free_iocbq;
+ }
+ iocbq_entry->sli4_xritag = NO_XRI;
+
+ spin_lock_irq(&phba->hbalock);
+ list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
+ phba->total_iocbq_bufs++;
+ spin_unlock_irq(&phba->hbalock);
+ }
+
+ return 0;
+
+out_free_iocbq:
+ lpfc_free_iocb_list(phba);
+
+ return -ENOMEM;
+}
+
+/**
+ * lpfc_free_sgl_list - Free sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver's sgl list and memory.
+ **/
+static void
+lpfc_free_sgl_list(struct lpfc_hba *phba)
+{
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+ LIST_HEAD(sglq_list);
+ int rc = 0;
+
+ spin_lock_irq(&phba->hbalock);
+ list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &sglq_list);
+ spin_unlock_irq(&phba->hbalock);
+
+ list_for_each_entry_safe(sglq_entry, sglq_next,
+ &sglq_list, list) {
+ list_del(&sglq_entry->list);
+ lpfc_mbuf_free(phba, sglq_entry->virt, sglq_entry->phys);
+ kfree(sglq_entry);
+ phba->sli4_hba.total_sglq_bufs--;
+ }
+ rc = lpfc_sli4_remove_all_sgl_pages(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2005 Unable to deregister pages from HBA: %x", rc);
+ }
+ kfree(phba->sli4_hba.lpfc_els_sgl_array);
+}
+
+/**
+ * lpfc_init_active_sgl_array - Allocate the buf to track active ELS XRIs.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate the driver's active sgl memory.
+ * This array will hold the sglq_entry's for active IOs.
+ **/
+static int
+lpfc_init_active_sgl_array(struct lpfc_hba *phba)
+{
+ int size;
+ size = sizeof(struct lpfc_sglq *);
+ size *= phba->sli4_hba.max_cfg_param.max_xri;
+
+ phba->sli4_hba.lpfc_sglq_active_list =
+ kzalloc(size, GFP_KERNEL);
+ if (!phba->sli4_hba.lpfc_sglq_active_list)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lpfc_free_active_sgl - Free the buf that tracks active ELS XRIs.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to walk through the array of active sglq entries
+ * and free all of the resources.
+ * This is just a place holder for now.
+ **/
+static void
+lpfc_free_active_sgl(struct lpfc_hba *phba)
+{
+ kfree(phba->sli4_hba.lpfc_sglq_active_list);
+}
+
+/**
+ * lpfc_init_sgl_list - Allocate and initialize sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and initizlize the driver's sgl
+ * list and set up the sgl xritag tag array accordingly.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_init_sgl_list(struct lpfc_hba *phba)
+{
+ struct lpfc_sglq *sglq_entry = NULL;
+ int i;
+ int els_xri_cnt;
+
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2400 lpfc_init_sgl_list els %d.\n",
+ els_xri_cnt);
+ /* Initialize and populate the sglq list per host/VF. */
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_sgl_list);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_els_sgl_list);
+
+ /* Sanity check on XRI management */
+ if (phba->sli4_hba.max_cfg_param.max_xri <= els_xri_cnt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2562 No room left for SCSI XRI allocation: "
+ "max_xri=%d, els_xri=%d\n",
+ phba->sli4_hba.max_cfg_param.max_xri,
+ els_xri_cnt);
+ return -ENOMEM;
+ }
+
+ /* Allocate memory for the ELS XRI management array */
+ phba->sli4_hba.lpfc_els_sgl_array =
+ kzalloc((sizeof(struct lpfc_sglq *) * els_xri_cnt),
+ GFP_KERNEL);
+
+ if (!phba->sli4_hba.lpfc_els_sgl_array) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2401 Failed to allocate memory for ELS "
+ "XRI management array of size %d.\n",
+ els_xri_cnt);
+ return -ENOMEM;
+ }
+
+ /* Keep the SCSI XRI into the XRI management array */
+ phba->sli4_hba.scsi_xri_max =
+ phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
+ phba->sli4_hba.scsi_xri_cnt = 0;
+
+ phba->sli4_hba.lpfc_scsi_psb_array =
+ kzalloc((sizeof(struct lpfc_scsi_buf *) *
+ phba->sli4_hba.scsi_xri_max), GFP_KERNEL);
+
+ if (!phba->sli4_hba.lpfc_scsi_psb_array) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2563 Failed to allocate memory for SCSI "
+ "XRI management array of size %d.\n",
+ phba->sli4_hba.scsi_xri_max);
+ kfree(phba->sli4_hba.lpfc_els_sgl_array);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < els_xri_cnt; i++) {
+ sglq_entry = kzalloc(sizeof(struct lpfc_sglq), GFP_KERNEL);
+ if (sglq_entry == NULL) {
+ printk(KERN_ERR "%s: only allocated %d sgls of "
+ "expected %d count. Unloading driver.\n",
+ __func__, i, els_xri_cnt);
+ goto out_free_mem;
+ }
+
+ sglq_entry->sli4_xritag = lpfc_sli4_next_xritag(phba);
+ if (sglq_entry->sli4_xritag == NO_XRI) {
+ kfree(sglq_entry);
+ printk(KERN_ERR "%s: failed to allocate XRI.\n"
+ "Unloading driver.\n", __func__);
+ goto out_free_mem;
+ }
+ sglq_entry->buff_type = GEN_BUFF_TYPE;
+ sglq_entry->virt = lpfc_mbuf_alloc(phba, 0, &sglq_entry->phys);
+ if (sglq_entry->virt == NULL) {
+ kfree(sglq_entry);
+ printk(KERN_ERR "%s: failed to allocate mbuf.\n"
+ "Unloading driver.\n", __func__);
+ goto out_free_mem;
+ }
+ sglq_entry->sgl = sglq_entry->virt;
+ memset(sglq_entry->sgl, 0, LPFC_BPL_SIZE);
+
+ /* The list order is used by later block SGL registraton */
+ spin_lock_irq(&phba->hbalock);
+ list_add_tail(&sglq_entry->list, &phba->sli4_hba.lpfc_sgl_list);
+ phba->sli4_hba.lpfc_els_sgl_array[i] = sglq_entry;
+ phba->sli4_hba.total_sglq_bufs++;
+ spin_unlock_irq(&phba->hbalock);
+ }
+ return 0;
+
+out_free_mem:
+ kfree(phba->sli4_hba.lpfc_scsi_psb_array);
+ lpfc_free_sgl_list(phba);
+ return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec. This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ * No locks are held here because this is an initialization routine
+ * called only from probe or lpfc_online when interrupts are not
+ * enabled and the driver is reinitializing the device.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba)
+{
+ int rc = 0;
+ int longs;
+ uint16_t rpi_count;
+ struct lpfc_rpi_hdr *rpi_hdr;
+
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_rpi_hdr_list);
+
+ /*
+ * Provision an rpi bitmask range for discovery. The total count
+ * is the difference between max and base + 1.
+ */
+ rpi_count = phba->sli4_hba.max_cfg_param.rpi_base +
+ phba->sli4_hba.max_cfg_param.max_rpi - 1;
+
+ longs = ((rpi_count) + BITS_PER_LONG - 1) / BITS_PER_LONG;
+ phba->sli4_hba.rpi_bmask = kzalloc(longs * sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.rpi_bmask)
+ return -ENOMEM;
+
+ rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
+ if (!rpi_hdr) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0391 Error during rpi post operation\n");
+ lpfc_sli4_remove_rpis(phba);
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_create_rpi_hdr - Allocate an rpi header memory region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate a single 4KB memory region to
+ * support rpis and stores them in the phba. This single region
+ * provides support for up to 64 rpis. The region is used globally
+ * by the device.
+ *
+ * Returns:
+ * A valid rpi hdr on success.
+ * A NULL pointer on any failure.
+ **/
+struct lpfc_rpi_hdr *
+lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
+{
+ uint16_t rpi_limit, curr_rpi_range;
+ struct lpfc_dmabuf *dmabuf;
+ struct lpfc_rpi_hdr *rpi_hdr;
+
+ rpi_limit = phba->sli4_hba.max_cfg_param.rpi_base +
+ phba->sli4_hba.max_cfg_param.max_rpi - 1;
+
+ spin_lock_irq(&phba->hbalock);
+ curr_rpi_range = phba->sli4_hba.next_rpi;
+ spin_unlock_irq(&phba->hbalock);
+
+ /*
+ * The port has a limited number of rpis. The increment here
+ * is LPFC_RPI_HDR_COUNT - 1 to account for the starting value
+ * and to allow the full max_rpi range per port.
+ */
+ if ((curr_rpi_range + (LPFC_RPI_HDR_COUNT - 1)) > rpi_limit)
+ return NULL;
+
+ /*
+ * First allocate the protocol header region for the port. The
+ * port expects a 4KB DMA-mapped memory region that is 4K aligned.
+ */
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf)
+ return NULL;
+
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+ LPFC_HDR_TEMPLATE_SIZE,
+ &dmabuf->phys,
+ GFP_KERNEL);
+ if (!dmabuf->virt) {
+ rpi_hdr = NULL;
+ goto err_free_dmabuf;
+ }
+
+ memset(dmabuf->virt, 0, LPFC_HDR_TEMPLATE_SIZE);
+ if (!IS_ALIGNED(dmabuf->phys, LPFC_HDR_TEMPLATE_SIZE)) {
+ rpi_hdr = NULL;
+ goto err_free_coherent;
+ }
+
+ /* Save the rpi header data for cleanup later. */
+ rpi_hdr = kzalloc(sizeof(struct lpfc_rpi_hdr), GFP_KERNEL);
+ if (!rpi_hdr)
+ goto err_free_coherent;
+
+ rpi_hdr->dmabuf = dmabuf;
+ rpi_hdr->len = LPFC_HDR_TEMPLATE_SIZE;
+ rpi_hdr->page_count = 1;
+ spin_lock_irq(&phba->hbalock);
+ rpi_hdr->start_rpi = phba->sli4_hba.next_rpi;
+ list_add_tail(&rpi_hdr->list, &phba->sli4_hba.lpfc_rpi_hdr_list);
+
+ /*
+ * The next_rpi stores the next module-64 rpi value to post
+ * in any subsequent rpi memory region postings.
+ */
+ phba->sli4_hba.next_rpi += LPFC_RPI_HDR_COUNT;
+ spin_unlock_irq(&phba->hbalock);
+ return rpi_hdr;
+
+ err_free_coherent:
+ dma_free_coherent(&phba->pcidev->dev, LPFC_HDR_TEMPLATE_SIZE,
+ dmabuf->virt, dmabuf->phys);
+ err_free_dmabuf:
+ kfree(dmabuf);
+ return NULL;
+}
+
+/**
+ * lpfc_sli4_remove_rpi_hdrs - Remove all rpi header memory regions
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove all memory resources allocated
+ * to support rpis. This routine presumes the caller has released all
+ * rpis consumed by fabric or port logins and is prepared to have
+ * the header pages removed.
+ **/
+void
+lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba)
+{
+ struct lpfc_rpi_hdr *rpi_hdr, *next_rpi_hdr;
+
+ list_for_each_entry_safe(rpi_hdr, next_rpi_hdr,
+ &phba->sli4_hba.lpfc_rpi_hdr_list, list) {
+ list_del(&rpi_hdr->list);
+ dma_free_coherent(&phba->pcidev->dev, rpi_hdr->len,
+ rpi_hdr->dmabuf->virt, rpi_hdr->dmabuf->phys);
+ kfree(rpi_hdr->dmabuf);
+ kfree(rpi_hdr);
+ }
+
+ phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
+ memset(phba->sli4_hba.rpi_bmask, 0, sizeof(*phba->sli4_hba.rpi_bmask));
+}
+
+/**
+ * lpfc_hba_alloc - Allocate driver hba data structure for a device.
+ * @pdev: pointer to pci device data structure.
+ *
+ * This routine is invoked to allocate the driver hba data structure for an
+ * HBA device. If the allocation is successful, the phba reference to the
+ * PCI device data structure is set.
+ *
+ * Return codes
+ * pointer to @phba - sucessful
+ * NULL - error
+ **/
+static struct lpfc_hba *
+lpfc_hba_alloc(struct pci_dev *pdev)
+{
+ struct lpfc_hba *phba;
+
+ /* Allocate memory for HBA structure */
+ phba = kzalloc(sizeof(struct lpfc_hba), GFP_KERNEL);
+ if (!phba) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1417 Failed to allocate hba struct.\n");
+ return NULL;
+ }
+
+ /* Set reference to PCI device in HBA structure */
+ phba->pcidev = pdev;
+
+ /* Assign an unused board number */
+ phba->brd_no = lpfc_get_instance();
+ if (phba->brd_no < 0) {
+ kfree(phba);
+ return NULL;
+ }
+
+ return phba;
+}
+
+/**
+ * lpfc_hba_free - Free driver hba data structure with a device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver hba data structure with an
+ * HBA device.
+ **/
+static void
+lpfc_hba_free(struct lpfc_hba *phba)
+{
+ /* Release the driver assigned board number */
+ idr_remove(&lpfc_hba_index, phba->brd_no);
+
+ kfree(phba);
+ return;
+}
+
+/**
+ * lpfc_create_shost - Create hba physical port with associated scsi host.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to create HBA physical port and associate a SCSI
+ * host with it.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_create_shost(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport;
+ struct Scsi_Host *shost;
+
+ /* Initialize HBA FC 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;
+
+ vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
+ if (!vport)
+ return -ENODEV;
+
+ shost = lpfc_shost_from_vport(vport);
+ phba->pport = vport;
+ lpfc_debugfs_initialize(vport);
+ /* Put reference to SCSI host to driver's device private data */
+ pci_set_drvdata(phba->pcidev, shost);
+
+ return 0;
+}
+
+/**
+ * lpfc_destroy_shost - Destroy hba physical port with associated scsi host.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to destroy HBA physical port and the associated
+ * SCSI host.
+ **/
+static void
+lpfc_destroy_shost(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+
+ /* Destroy physical port that associated with the SCSI host */
+ destroy_port(vport);
+
+ return;
+}
+
+/**
+ * lpfc_setup_bg - Setup Block guard structures and debug areas.
+ * @phba: pointer to lpfc hba data structure.
+ * @shost: the shost to be used to detect Block guard settings.
+ *
+ * This routine sets up the local Block guard protocol settings for @shost.
+ * This routine also allocates memory for debugging bg buffers.
+ **/
+static void
+lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
+{
+ int pagecnt = 10;
+ if (lpfc_prot_mask && lpfc_prot_guard) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1478 Registering BlockGuard with the "
+ "SCSI layer\n");
+ scsi_host_set_prot(shost, lpfc_prot_mask);
+ scsi_host_set_guard(shost, lpfc_prot_guard);
+ }
+ if (!_dump_buf_data) {
+ while (pagecnt) {
+ spin_lock_init(&_dump_buf_lock);
+ _dump_buf_data =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_data at 0x%p\n",
+ (1 << pagecnt), _dump_buf_data);
+ _dump_buf_data_order = pagecnt;
+ memset(_dump_buf_data, 0,
+ ((1 << PAGE_SHIFT) << pagecnt));
+ break;
+ } else
+ --pagecnt;
+ }
+ if (!_dump_buf_data_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+ } else
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ "\n", _dump_buf_data);
+ if (!_dump_buf_dif) {
+ while (pagecnt) {
+ _dump_buf_dif =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_dif at 0x%p\n",
+ (1 << pagecnt), _dump_buf_dif);
+ _dump_buf_dif_order = pagecnt;
+ memset(_dump_buf_dif, 0,
+ ((1 << PAGE_SHIFT) << pagecnt));
+ break;
+ } else
+ --pagecnt;
+ }
+ if (!_dump_buf_dif_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+ } else
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ _dump_buf_dif);
+}
+
+/**
+ * lpfc_post_init_setup - Perform necessary device post initialization setup.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to perform all the necessary post initialization
+ * setup for the device.
+ **/
+static void
+lpfc_post_init_setup(struct lpfc_hba *phba)
+{
+ struct Scsi_Host *shost;
+ struct lpfc_adapter_event_header adapter_event;
+
+ /* Get the default values for Model Name and Description */
+ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
+
+ /*
+ * hba setup may have changed the hba_queue_depth so we need to
+ * adjust the value of can_queue.
+ */
+ shost = pci_get_drvdata(phba->pcidev);
+ shost->can_queue = phba->cfg_hba_queue_depth - 10;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ lpfc_setup_bg(phba, shost);
+
+ lpfc_host_attrib_init(shost);
+
+ if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
+ spin_lock_irq(shost->host_lock);
+ lpfc_poll_start_timer(phba);
+ spin_unlock_irq(shost->host_lock);
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0428 Perform SCSI scan\n");
+ /* Send board arrival event to upper layer */
+ adapter_event.event_type = FC_REG_ADAPTER_EVENT;
+ adapter_event.subcategory = LPFC_EVENT_ARRIVAL;
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(adapter_event),
+ (char *) &adapter_event,
+ LPFC_NL_VENDOR_ID);
+ return;
+}
+
+/**
+ * lpfc_sli_pci_mem_setup - Setup SLI3 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the PCI device memory space for device
+ * with SLI-3 interface spec.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+ unsigned long bar0map_len, bar2map_len;
+ int i, hbq_count;
+ void *ptr;
+ int error = -ENODEV;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ return error;
+ else
+ pdev = phba->pcidev;
+
+ /* Set the device DMA mask size */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
+ return error;
+
+ /* Get the bus address of Bar0 and Bar2 and the number of bytes
+ * required by each mapping.
+ */
+ phba->pci_bar0_map = pci_resource_start(pdev, 0);
+ bar0map_len = pci_resource_len(pdev, 0);
+
+ phba->pci_bar2_map = pci_resource_start(pdev, 2);
+ bar2map_len = pci_resource_len(pdev, 2);
+
+ /* Map HBA SLIM to a kernel virtual address. */
+ phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
+ if (!phba->slim_memmap_p) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "ioremap failed for SLIM memory.\n");
+ goto out;
+ }
+
+ /* Map HBA Control Registers to a kernel virtual address. */
+ phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
+ if (!phba->ctrl_regs_memmap_p) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "ioremap failed for HBA control registers.\n");
+ goto out_iounmap_slim;
+ }
+
+ /* Allocate memory for SLI-2 structures */
+ phba->slim2p.virt = dma_alloc_coherent(&pdev->dev,
+ SLI2_SLIM_SIZE,
+ &phba->slim2p.phys,
+ GFP_KERNEL);
+ if (!phba->slim2p.virt)
+ goto out_iounmap;
+
+ memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
+ phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
+ phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
+ phba->IOCBs = (phba->slim2p.virt +
+ offsetof(struct lpfc_sli2_slim, IOCBs));
+
+ phba->hbqslimp.virt = dma_alloc_coherent(&pdev->dev,
+ lpfc_sli_hbq_size(),
+ &phba->hbqslimp.phys,
+ GFP_KERNEL);
+ if (!phba->hbqslimp.virt)
+ goto out_free_slim;
+
+ hbq_count = lpfc_sli_hbq_count();
+ ptr = phba->hbqslimp.virt;
+ for (i = 0; i < hbq_count; ++i) {
+ phba->hbqs[i].hbq_virt = ptr;
+ INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
+ ptr += (lpfc_hbq_defs[i]->entry_count *
+ sizeof(struct lpfc_hbq_entry));
+ }
+ phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_els_hbq_alloc;
+ phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_els_hbq_free;
+
+ memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
+
+ INIT_LIST_HEAD(&phba->rb_pend_list);
+
+ phba->MBslimaddr = phba->slim_memmap_p;
+ phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
+ phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
+ phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
+ phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+
+ return 0;
+
+out_free_slim:
+ dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+ phba->slim2p.virt, phba->slim2p.phys);
+out_iounmap:
+ iounmap(phba->ctrl_regs_memmap_p);
+out_iounmap_slim:
+ iounmap(phba->slim_memmap_p);
+out:
+ return error;
+}
+
+/**
+ * lpfc_sli_pci_mem_unset - Unset SLI3 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the PCI device memory space for device
+ * with SLI-3 interface spec.
+ **/
+static void
+lpfc_sli_pci_mem_unset(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ return;
+ else
+ pdev = phba->pcidev;
+
+ /* Free coherent DMA memory allocated */
+ dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
+ phba->hbqslimp.virt, phba->hbqslimp.phys);
+ dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+ phba->slim2p.virt, phba->slim2p.phys);
+
+ /* I/O memory unmap */
+ iounmap(phba->ctrl_regs_memmap_p);
+ iounmap(phba->slim_memmap_p);
+
+ return;
+}
+
+/**
+ * lpfc_sli4_post_status_check - Wait for SLI4 POST done and check status
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to wait for SLI4 device Power On Self Test (POST)
+ * done and check status.
+ *
+ * Return 0 if successful, otherwise -ENODEV.
+ **/
+int
+lpfc_sli4_post_status_check(struct lpfc_hba *phba)
+{
+ struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad;
+ uint32_t onlnreg0, onlnreg1;
+ int i, port_error = -ENODEV;
+
+ if (!phba->sli4_hba.STAregaddr)
+ return -ENODEV;
+
+ /* With uncoverable error, log the error message and return error */
+ onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
+ onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
+ if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
+ uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+ uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+ if (uerrlo_reg.word0 || uerrhi_reg.word0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1422 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "online0_reg=0x%x, online1_reg=0x%x\n",
+ uerrlo_reg.word0, uerrhi_reg.word0,
+ onlnreg0, onlnreg1);
+ }
+ return -ENODEV;
+ }
+
+ /* Wait up to 30 seconds for the SLI Port POST done and ready */
+ for (i = 0; i < 3000; i++) {
+ sta_reg.word0 = readl(phba->sli4_hba.STAregaddr);
+ /* Encounter fatal POST error, break out */
+ if (bf_get(lpfc_hst_state_perr, &sta_reg)) {
+ port_error = -ENODEV;
+ break;
+ }
+ if (LPFC_POST_STAGE_ARMFW_READY ==
+ bf_get(lpfc_hst_state_port_status, &sta_reg)) {
+ port_error = 0;
+ break;
+ }
+ msleep(10);
+ }
+
+ if (port_error)
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1408 Failure HBA POST Status: sta_reg=0x%x, "
+ "perr=x%x, sfi=x%x, nip=x%x, ipc=x%x, xrom=x%x, "
+ "dl=x%x, pstatus=x%x\n", sta_reg.word0,
+ bf_get(lpfc_hst_state_perr, &sta_reg),
+ bf_get(lpfc_hst_state_sfi, &sta_reg),
+ bf_get(lpfc_hst_state_nip, &sta_reg),
+ bf_get(lpfc_hst_state_ipc, &sta_reg),
+ bf_get(lpfc_hst_state_xrom, &sta_reg),
+ bf_get(lpfc_hst_state_dl, &sta_reg),
+ bf_get(lpfc_hst_state_port_status, &sta_reg));
+
+ /* Log device information */
+ scratchpad.word0 = readl(phba->sli4_hba.SCRATCHPADregaddr);
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2534 Device Info: ChipType=0x%x, SliRev=0x%x, "
+ "FeatureL1=0x%x, FeatureL2=0x%x\n",
+ bf_get(lpfc_scratchpad_chiptype, &scratchpad),
+ bf_get(lpfc_scratchpad_slirev, &scratchpad),
+ bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
+ bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
+
+ return port_error;
+}
+
+/**
+ * lpfc_sli4_bar0_register_memmap - Set up SLI4 BAR0 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up SLI4 BAR0 PCI config space register
+ * memory map.
+ **/
+static void
+lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
+{
+ phba->sli4_hba.UERRLOregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UERR_STATUS_LO;
+ phba->sli4_hba.UERRHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UERR_STATUS_HI;
+ phba->sli4_hba.ONLINE0regaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_ONLINE0;
+ phba->sli4_hba.ONLINE1regaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_ONLINE1;
+ phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_SCRATCHPAD;
+}
+
+/**
+ * lpfc_sli4_bar1_register_memmap - Set up SLI4 BAR1 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up SLI4 BAR1 control status register (CSR)
+ * memory map.
+ **/
+static void
+lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba)
+{
+
+ phba->sli4_hba.STAregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_STATE;
+ phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISR0;
+ phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_IMR0;
+ phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISCR0;
+ return;
+}
+
+/**
+ * lpfc_sli4_bar2_register_memmap - Set up SLI4 BAR2 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ * @vf: virtual function number
+ *
+ * This routine is invoked to set up SLI4 BAR2 doorbell register memory map
+ * based on the given viftual function number, @vf.
+ *
+ * Return 0 if successful, otherwise -ENODEV.
+ **/
+static int
+lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
+{
+ if (vf > LPFC_VIR_FUNC_MAX)
+ return -ENODEV;
+
+ phba->sli4_hba.RQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE + LPFC_RQ_DOORBELL);
+ phba->sli4_hba.WQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE + LPFC_WQ_DOORBELL);
+ phba->sli4_hba.EQCQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE + LPFC_EQCQ_DOORBELL);
+ phba->sli4_hba.MQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE + LPFC_MQ_DOORBELL);
+ phba->sli4_hba.BMBXregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE + LPFC_BMBX);
+ return 0;
+}
+
+/**
+ * lpfc_create_bootstrap_mbox - Create the bootstrap mailbox
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to create the bootstrap mailbox
+ * region consistent with the SLI-4 interface spec. This
+ * routine allocates all memory necessary to communicate
+ * mailbox commands to the port and sets up all alignment
+ * needs. No locks are expected to be held when calling
+ * this routine.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - could not allocated memory.
+ **/
+static int
+lpfc_create_bootstrap_mbox(struct lpfc_hba *phba)
+{
+ uint32_t bmbx_size;
+ struct lpfc_dmabuf *dmabuf;
+ struct dma_address *dma_address;
+ uint32_t pa_addr;
+ uint64_t phys_addr;
+
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ /*
+ * The bootstrap mailbox region is comprised of 2 parts
+ * plus an alignment restriction of 16 bytes.
+ */
+ bmbx_size = sizeof(struct lpfc_bmbx_create) + (LPFC_ALIGN_16_BYTE - 1);
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+ bmbx_size,
+ &dmabuf->phys,
+ GFP_KERNEL);
+ if (!dmabuf->virt) {
+ kfree(dmabuf);
+ return -ENOMEM;
+ }
+ memset(dmabuf->virt, 0, bmbx_size);
+
+ /*
+ * Initialize the bootstrap mailbox pointers now so that the register
+ * operations are simple later. The mailbox dma address is required
+ * to be 16-byte aligned. Also align the virtual memory as each
+ * maibox is copied into the bmbx mailbox region before issuing the
+ * command to the port.
+ */
+ phba->sli4_hba.bmbx.dmabuf = dmabuf;
+ phba->sli4_hba.bmbx.bmbx_size = bmbx_size;
+
+ phba->sli4_hba.bmbx.avirt = PTR_ALIGN(dmabuf->virt,
+ LPFC_ALIGN_16_BYTE);
+ phba->sli4_hba.bmbx.aphys = ALIGN(dmabuf->phys,
+ LPFC_ALIGN_16_BYTE);
+
+ /*
+ * Set the high and low physical addresses now. The SLI4 alignment
+ * requirement is 16 bytes and the mailbox is posted to the port
+ * as two 30-bit addresses. The other data is a bit marking whether
+ * the 30-bit address is the high or low address.
+ * Upcast bmbx aphys to 64bits so shift instruction compiles
+ * clean on 32 bit machines.
+ */
+ dma_address = &phba->sli4_hba.bmbx.dma_address;
+ phys_addr = (uint64_t)phba->sli4_hba.bmbx.aphys;
+ pa_addr = (uint32_t) ((phys_addr >> 34) & 0x3fffffff);
+ dma_address->addr_hi = (uint32_t) ((pa_addr << 2) |
+ LPFC_BMBX_BIT1_ADDR_HI);
+
+ pa_addr = (uint32_t) ((phba->sli4_hba.bmbx.aphys >> 4) & 0x3fffffff);
+ dma_address->addr_lo = (uint32_t) ((pa_addr << 2) |
+ LPFC_BMBX_BIT1_ADDR_LO);
+ return 0;
+}
+
+/**
+ * lpfc_destroy_bootstrap_mbox - Destroy all bootstrap mailbox resources
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to teardown the bootstrap mailbox
+ * region and release all host resources. This routine requires
+ * the caller to ensure all mailbox commands recovered, no
+ * additional mailbox comands are sent, and interrupts are disabled
+ * before calling this routine.
+ *
+ **/
+static void
+lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
+{
+ dma_free_coherent(&phba->pcidev->dev,
+ phba->sli4_hba.bmbx.bmbx_size,
+ phba->sli4_hba.bmbx.dmabuf->virt,
+ phba->sli4_hba.bmbx.dmabuf->phys);
+
+ kfree(phba->sli4_hba.bmbx.dmabuf);
+ memset(&phba->sli4_hba.bmbx, 0, sizeof(struct lpfc_bmbx));
+}
+
+/**
+ * lpfc_sli4_read_config - Get the config parameters.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to read the configuration parameters from the HBA.
+ * The configuration parameters are used to set the base and maximum values
+ * for RPI's XRI's VPI's VFI's and FCFIs. These values also affect the resource
+ * allocation for the port.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_sli4_read_config(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmb;
+ struct lpfc_mbx_read_config *rd_config;
+ uint32_t rc = 0;
+
+ pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2011 Unable to allocate memory for issuing "
+ "SLI_CONFIG_SPECIAL mailbox command\n");
+ return -ENOMEM;
+ }
+
+ lpfc_read_config(phba, pmb);
+
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2012 Mailbox failed , mbxCmd x%x "
+ "READ_CONFIG, mbxStatus x%x\n",
+ bf_get(lpfc_mqe_command, &pmb->u.mqe),
+ bf_get(lpfc_mqe_status, &pmb->u.mqe));
+ rc = -EIO;
+ } else {
+ rd_config = &pmb->u.mqe.un.rd_config;
+ phba->sli4_hba.max_cfg_param.max_xri =
+ bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
+ phba->sli4_hba.max_cfg_param.xri_base =
+ bf_get(lpfc_mbx_rd_conf_xri_base, rd_config);
+ phba->sli4_hba.max_cfg_param.max_vpi =
+ bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+ phba->sli4_hba.max_cfg_param.vpi_base =
+ bf_get(lpfc_mbx_rd_conf_vpi_base, rd_config);
+ phba->sli4_hba.max_cfg_param.max_rpi =
+ bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config);
+ phba->sli4_hba.max_cfg_param.rpi_base =
+ bf_get(lpfc_mbx_rd_conf_rpi_base, rd_config);
+ phba->sli4_hba.max_cfg_param.max_vfi =
+ bf_get(lpfc_mbx_rd_conf_vfi_count, rd_config);
+ phba->sli4_hba.max_cfg_param.vfi_base =
+ bf_get(lpfc_mbx_rd_conf_vfi_base, rd_config);
+ phba->sli4_hba.max_cfg_param.max_fcfi =
+ bf_get(lpfc_mbx_rd_conf_fcfi_count, rd_config);
+ phba->sli4_hba.max_cfg_param.fcfi_base =
+ bf_get(lpfc_mbx_rd_conf_fcfi_base, rd_config);
+ phba->sli4_hba.max_cfg_param.max_eq =
+ bf_get(lpfc_mbx_rd_conf_eq_count, rd_config);
+ phba->sli4_hba.max_cfg_param.max_rq =
+ bf_get(lpfc_mbx_rd_conf_rq_count, rd_config);
+ phba->sli4_hba.max_cfg_param.max_wq =
+ bf_get(lpfc_mbx_rd_conf_wq_count, rd_config);
+ phba->sli4_hba.max_cfg_param.max_cq =
+ bf_get(lpfc_mbx_rd_conf_cq_count, rd_config);
+ phba->lmt = bf_get(lpfc_mbx_rd_conf_lmt, rd_config);
+ phba->sli4_hba.next_xri = phba->sli4_hba.max_cfg_param.xri_base;
+ phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
+ phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
+ phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
+ phba->max_vpi = phba->sli4_hba.max_cfg_param.max_vpi;
+ phba->max_vports = phba->max_vpi;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2003 cfg params XRI(B:%d M:%d), "
+ "VPI(B:%d M:%d) "
+ "VFI(B:%d M:%d) "
+ "RPI(B:%d M:%d) "
+ "FCFI(B:%d M:%d)\n",
+ phba->sli4_hba.max_cfg_param.xri_base,
+ phba->sli4_hba.max_cfg_param.max_xri,
+ phba->sli4_hba.max_cfg_param.vpi_base,
+ phba->sli4_hba.max_cfg_param.max_vpi,
+ phba->sli4_hba.max_cfg_param.vfi_base,
+ phba->sli4_hba.max_cfg_param.max_vfi,
+ phba->sli4_hba.max_cfg_param.rpi_base,
+ phba->sli4_hba.max_cfg_param.max_rpi,
+ phba->sli4_hba.max_cfg_param.fcfi_base,
+ phba->sli4_hba.max_cfg_param.max_fcfi);
+ }
+ mempool_free(pmb, phba->mbox_mem_pool);
+
+ /* Reset the DFT_HBA_Q_DEPTH to the max xri */
+ if (phba->cfg_hba_queue_depth > (phba->sli4_hba.max_cfg_param.max_xri))
+ phba->cfg_hba_queue_depth =
+ phba->sli4_hba.max_cfg_param.max_xri;
+ return rc;
+}
+
+/**
+ * lpfc_dev_endian_order_setup - Notify the port of the host's endian order.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to setup the host-side endian order to the
+ * HBA consistent with the SLI-4 interface spec.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_setup_endian_order(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ uint32_t rc = 0;
+ uint32_t endian_mb_data[2] = {HOST_ENDIAN_LOW_WORD0,
+ HOST_ENDIAN_HIGH_WORD1};
+
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0492 Unable to allocate memory for issuing "
+ "SLI_CONFIG_SPECIAL mailbox command\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * The SLI4_CONFIG_SPECIAL mailbox command requires the first two
+ * words to contain special data values and no other data.
+ */
+ memset(mboxq, 0, sizeof(LPFC_MBOXQ_t));
+ memcpy(&mboxq->u.mqe, &endian_mb_data, sizeof(endian_mb_data));
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0493 SLI_CONFIG_SPECIAL mailbox failed with "
+ "status x%x\n",
+ rc);
+ rc = -EIO;
+ }
+
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_queue_create - Create all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate all the SLI4 queues for the FCoE HBA
+ * operation. For each SLI4 queue type, the parameters such as queue entry
+ * count (queue depth) shall be taken from the module parameter. For now,
+ * we just use some constant number as place holder.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_sli4_queue_create(struct lpfc_hba *phba)
+{
+ struct lpfc_queue *qdesc;
+ int fcp_eqidx, fcp_cqidx, fcp_wqidx;
+ int cfg_fcp_wq_count;
+ int cfg_fcp_eq_count;
+
+ /*
+ * Sanity check for confiugred queue parameters against the run-time
+ * device parameters
+ */
+
+ /* Sanity check on FCP fast-path WQ parameters */
+ cfg_fcp_wq_count = phba->cfg_fcp_wq_count;
+ if (cfg_fcp_wq_count >
+ (phba->sli4_hba.max_cfg_param.max_wq - LPFC_SP_WQN_DEF)) {
+ cfg_fcp_wq_count = phba->sli4_hba.max_cfg_param.max_wq -
+ LPFC_SP_WQN_DEF;
+ if (cfg_fcp_wq_count < LPFC_FP_WQN_MIN) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2581 Not enough WQs (%d) from "
+ "the pci function for supporting "
+ "FCP WQs (%d)\n",
+ phba->sli4_hba.max_cfg_param.max_wq,
+ phba->cfg_fcp_wq_count);
+ goto out_error;
+ }
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2582 Not enough WQs (%d) from the pci "
+ "function for supporting the requested "
+ "FCP WQs (%d), the actual FCP WQs can "
+ "be supported: %d\n",
+ phba->sli4_hba.max_cfg_param.max_wq,
+ phba->cfg_fcp_wq_count, cfg_fcp_wq_count);
+ }
+ /* The actual number of FCP work queues adopted */
+ phba->cfg_fcp_wq_count = cfg_fcp_wq_count;
+
+ /* Sanity check on FCP fast-path EQ parameters */
+ cfg_fcp_eq_count = phba->cfg_fcp_eq_count;
+ if (cfg_fcp_eq_count >
+ (phba->sli4_hba.max_cfg_param.max_eq - LPFC_SP_EQN_DEF)) {
+ cfg_fcp_eq_count = phba->sli4_hba.max_cfg_param.max_eq -
+ LPFC_SP_EQN_DEF;
+ if (cfg_fcp_eq_count < LPFC_FP_EQN_MIN) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2574 Not enough EQs (%d) from the "
+ "pci function for supporting FCP "
+ "EQs (%d)\n",
+ phba->sli4_hba.max_cfg_param.max_eq,
+ phba->cfg_fcp_eq_count);
+ goto out_error;
+ }
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2575 Not enough EQs (%d) from the pci "
+ "function for supporting the requested "
+ "FCP EQs (%d), the actual FCP EQs can "
+ "be supported: %d\n",
+ phba->sli4_hba.max_cfg_param.max_eq,
+ phba->cfg_fcp_eq_count, cfg_fcp_eq_count);
+ }
+ /* It does not make sense to have more EQs than WQs */
+ if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2593 The number of FCP EQs (%d) is more "
+ "than the number of FCP WQs (%d), take "
+ "the number of FCP EQs same as than of "
+ "WQs (%d)\n", cfg_fcp_eq_count,
+ phba->cfg_fcp_wq_count,
+ phba->cfg_fcp_wq_count);
+ cfg_fcp_eq_count = phba->cfg_fcp_wq_count;
+ }
+ /* The actual number of FCP event queues adopted */
+ phba->cfg_fcp_eq_count = cfg_fcp_eq_count;
+ /* The overall number of event queues used */
+ phba->sli4_hba.cfg_eqn = phba->cfg_fcp_eq_count + LPFC_SP_EQN_DEF;
+
+ /*
+ * Create Event Queues (EQs)
+ */
+
+ /* Get EQ depth from module parameter, fake the default for now */
+ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
+ phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT;
+
+ /* Create slow path event queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
+ phba->sli4_hba.eq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0496 Failed allocate slow-path EQ\n");
+ goto out_error;
+ }
+ phba->sli4_hba.sp_eq = qdesc;
+
+ /* Create fast-path FCP Event Queue(s) */
+ phba->sli4_hba.fp_eq = kzalloc((sizeof(struct lpfc_queue *) *
+ phba->cfg_fcp_eq_count), GFP_KERNEL);
+ if (!phba->sli4_hba.fp_eq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2576 Failed allocate memory for fast-path "
+ "EQ record array\n");
+ goto out_free_sp_eq;
+ }
+ for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
+ phba->sli4_hba.eq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0497 Failed allocate fast-path EQ\n");
+ goto out_free_fp_eq;
+ }
+ phba->sli4_hba.fp_eq[fcp_eqidx] = qdesc;
+ }
+
+ /*
+ * Create Complete Queues (CQs)
+ */
+
+ /* Get CQ depth from module parameter, fake the default for now */
+ phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
+ phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
+
+ /* Create slow-path Mailbox Command Complete Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0500 Failed allocate slow-path mailbox CQ\n");
+ goto out_free_fp_eq;
+ }
+ phba->sli4_hba.mbx_cq = qdesc;
+
+ /* Create slow-path ELS Complete Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0501 Failed allocate slow-path ELS CQ\n");
+ goto out_free_mbx_cq;
+ }
+ phba->sli4_hba.els_cq = qdesc;
+
+ /* Create slow-path Unsolicited Receive Complete Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0502 Failed allocate slow-path USOL RX CQ\n");
+ goto out_free_els_cq;
+ }
+ phba->sli4_hba.rxq_cq = qdesc;
+
+ /* Create fast-path FCP Completion Queue(s), one-to-one with EQs */
+ phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) *
+ phba->cfg_fcp_eq_count), GFP_KERNEL);
+ if (!phba->sli4_hba.fcp_cq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2577 Failed allocate memory for fast-path "
+ "CQ record array\n");
+ goto out_free_rxq_cq;
+ }
+ for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0499 Failed allocate fast-path FCP "
+ "CQ (%d)\n", fcp_cqidx);
+ goto out_free_fcp_cq;
+ }
+ phba->sli4_hba.fcp_cq[fcp_cqidx] = qdesc;
+ }
+
+ /* Create Mailbox Command Queue */
+ phba->sli4_hba.mq_esize = LPFC_MQE_SIZE;
+ phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT;
+
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.mq_esize,
+ phba->sli4_hba.mq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0505 Failed allocate slow-path MQ\n");
+ goto out_free_fcp_cq;
+ }
+ phba->sli4_hba.mbx_wq = qdesc;
+
+ /*
+ * Create all the Work Queues (WQs)
+ */
+ phba->sli4_hba.wq_esize = LPFC_WQE_SIZE;
+ phba->sli4_hba.wq_ecount = LPFC_WQE_DEF_COUNT;
+
+ /* Create slow-path ELS Work Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+ phba->sli4_hba.wq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0504 Failed allocate slow-path ELS WQ\n");
+ goto out_free_mbx_wq;
+ }
+ phba->sli4_hba.els_wq = qdesc;
+
+ /* Create fast-path FCP Work Queue(s) */
+ phba->sli4_hba.fcp_wq = kzalloc((sizeof(struct lpfc_queue *) *
+ phba->cfg_fcp_wq_count), GFP_KERNEL);
+ if (!phba->sli4_hba.fcp_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2578 Failed allocate memory for fast-path "
+ "WQ record array\n");
+ goto out_free_els_wq;
+ }
+ for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+ phba->sli4_hba.wq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0503 Failed allocate fast-path FCP "
+ "WQ (%d)\n", fcp_wqidx);
+ goto out_free_fcp_wq;
+ }
+ phba->sli4_hba.fcp_wq[fcp_wqidx] = qdesc;
+ }
+
+ /*
+ * Create Receive Queue (RQ)
+ */
+ phba->sli4_hba.rq_esize = LPFC_RQE_SIZE;
+ phba->sli4_hba.rq_ecount = LPFC_RQE_DEF_COUNT;
+
+ /* Create Receive Queue for header */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
+ phba->sli4_hba.rq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0506 Failed allocate receive HRQ\n");
+ goto out_free_fcp_wq;
+ }
+ phba->sli4_hba.hdr_rq = qdesc;
+
+ /* Create Receive Queue for data */
+ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
+ phba->sli4_hba.rq_ecount);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0507 Failed allocate receive DRQ\n");
+ goto out_free_hdr_rq;
+ }
+ phba->sli4_hba.dat_rq = qdesc;
+
+ return 0;
+
+out_free_hdr_rq:
+ lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
+ phba->sli4_hba.hdr_rq = NULL;
+out_free_fcp_wq:
+ for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--) {
+ lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_wqidx]);
+ phba->sli4_hba.fcp_wq[fcp_wqidx] = NULL;
+ }
+ kfree(phba->sli4_hba.fcp_wq);
+out_free_els_wq:
+ lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
+ phba->sli4_hba.els_wq = NULL;
+out_free_mbx_wq:
+ lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
+ phba->sli4_hba.mbx_wq = NULL;
+out_free_fcp_cq:
+ for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--) {
+ lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_cqidx]);
+ phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL;
+ }
+ kfree(phba->sli4_hba.fcp_cq);
+out_free_rxq_cq:
+ lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
+ phba->sli4_hba.rxq_cq = NULL;
+out_free_els_cq:
+ lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
+ phba->sli4_hba.els_cq = NULL;
+out_free_mbx_cq:
+ lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
+ phba->sli4_hba.mbx_cq = NULL;
+out_free_fp_eq:
+ for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--) {
+ lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_eqidx]);
+ phba->sli4_hba.fp_eq[fcp_eqidx] = NULL;
+ }
+ kfree(phba->sli4_hba.fp_eq);
+out_free_sp_eq:
+ lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
+ phba->sli4_hba.sp_eq = NULL;
+out_error:
+ return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_queue_destroy - Destroy all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release all the SLI4 queues with the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+static void
+lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
+{
+ int fcp_qidx;
+
+ /* Release mailbox command work queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
+ phba->sli4_hba.mbx_wq = NULL;
+
+ /* Release ELS work queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
+ phba->sli4_hba.els_wq = NULL;
+
+ /* Release FCP work queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
+ lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_qidx]);
+ kfree(phba->sli4_hba.fcp_wq);
+ phba->sli4_hba.fcp_wq = NULL;
+
+ /* Release unsolicited receive queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
+ phba->sli4_hba.hdr_rq = NULL;
+ lpfc_sli4_queue_free(phba->sli4_hba.dat_rq);
+ phba->sli4_hba.dat_rq = NULL;
+
+ /* Release unsolicited receive complete queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
+ phba->sli4_hba.rxq_cq = NULL;
+
+ /* Release ELS complete queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
+ phba->sli4_hba.els_cq = NULL;
+
+ /* Release mailbox command complete queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
+ phba->sli4_hba.mbx_cq = NULL;
+
+ /* Release FCP response complete queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+ lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_qidx]);
+ kfree(phba->sli4_hba.fcp_cq);
+ phba->sli4_hba.fcp_cq = NULL;
+
+ /* Release fast-path event queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+ lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_qidx]);
+ kfree(phba->sli4_hba.fp_eq);
+ phba->sli4_hba.fp_eq = NULL;
+
+ /* Release slow-path event queue */
+ lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
+ phba->sli4_hba.sp_eq = NULL;
+
+ return;
+}
+
+/**
+ * lpfc_sli4_queue_setup - Set up all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up all the SLI4 queues for the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_queue_setup(struct lpfc_hba *phba)
+{
+ int rc = -ENOMEM;
+ int fcp_eqidx, fcp_cqidx, fcp_wqidx;
+ int fcp_cq_index = 0;
+
+ /*
+ * Set up Event Queues (EQs)
+ */
+
+ /* Set up slow-path event queue */
+ if (!phba->sli4_hba.sp_eq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0520 Slow-path EQ not allocated\n");
+ goto out_error;
+ }
+ rc = lpfc_eq_create(phba, phba->sli4_hba.sp_eq,
+ LPFC_SP_DEF_IMAX);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0521 Failed setup of slow-path EQ: "
+ "rc = 0x%x\n", rc);
+ goto out_error;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2583 Slow-path EQ setup: queue-id=%d\n",
+ phba->sli4_hba.sp_eq->queue_id);
+
+ /* Set up fast-path event queue */
+ for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+ if (!phba->sli4_hba.fp_eq[fcp_eqidx]) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0522 Fast-path EQ (%d) not "
+ "allocated\n", fcp_eqidx);
+ goto out_destroy_fp_eq;
+ }
+ rc = lpfc_eq_create(phba, phba->sli4_hba.fp_eq[fcp_eqidx],
+ phba->cfg_fcp_imax);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0523 Failed setup of fast-path EQ "
+ "(%d), rc = 0x%x\n", fcp_eqidx, rc);
+ goto out_destroy_fp_eq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2584 Fast-path EQ setup: "
+ "queue[%d]-id=%d\n", fcp_eqidx,
+ phba->sli4_hba.fp_eq[fcp_eqidx]->queue_id);
+ }
+
+ /*
+ * Set up Complete Queues (CQs)
+ */
+
+ /* Set up slow-path MBOX Complete Queue as the first CQ */
+ if (!phba->sli4_hba.mbx_cq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0528 Mailbox CQ not allocated\n");
+ goto out_destroy_fp_eq;
+ }
+ rc = lpfc_cq_create(phba, phba->sli4_hba.mbx_cq, phba->sli4_hba.sp_eq,
+ LPFC_MCQ, LPFC_MBOX);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0529 Failed setup of slow-path mailbox CQ: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_fp_eq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2585 MBX CQ setup: cq-id=%d, parent eq-id=%d\n",
+ phba->sli4_hba.mbx_cq->queue_id,
+ phba->sli4_hba.sp_eq->queue_id);
+
+ /* Set up slow-path ELS Complete Queue */
+ if (!phba->sli4_hba.els_cq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0530 ELS CQ not allocated\n");
+ goto out_destroy_mbx_cq;
+ }
+ rc = lpfc_cq_create(phba, phba->sli4_hba.els_cq, phba->sli4_hba.sp_eq,
+ LPFC_WCQ, LPFC_ELS);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0531 Failed setup of slow-path ELS CQ: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_mbx_cq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2586 ELS CQ setup: cq-id=%d, parent eq-id=%d\n",
+ phba->sli4_hba.els_cq->queue_id,
+ phba->sli4_hba.sp_eq->queue_id);
+
+ /* Set up slow-path Unsolicited Receive Complete Queue */
+ if (!phba->sli4_hba.rxq_cq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0532 USOL RX CQ not allocated\n");
+ goto out_destroy_els_cq;
+ }
+ rc = lpfc_cq_create(phba, phba->sli4_hba.rxq_cq, phba->sli4_hba.sp_eq,
+ LPFC_RCQ, LPFC_USOL);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0533 Failed setup of slow-path USOL RX CQ: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_els_cq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2587 USL CQ setup: cq-id=%d, parent eq-id=%d\n",
+ phba->sli4_hba.rxq_cq->queue_id,
+ phba->sli4_hba.sp_eq->queue_id);
+
+ /* Set up fast-path FCP Response Complete Queue */
+ for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
+ if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0526 Fast-path FCP CQ (%d) not "
+ "allocated\n", fcp_cqidx);
+ goto out_destroy_fcp_cq;
+ }
+ rc = lpfc_cq_create(phba, phba->sli4_hba.fcp_cq[fcp_cqidx],
+ phba->sli4_hba.fp_eq[fcp_cqidx],
+ LPFC_WCQ, LPFC_FCP);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0527 Failed setup of fast-path FCP "
+ "CQ (%d), rc = 0x%x\n", fcp_cqidx, rc);
+ goto out_destroy_fcp_cq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2588 FCP CQ setup: cq[%d]-id=%d, "
+ "parent eq[%d]-id=%d\n",
+ fcp_cqidx,
+ phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id,
+ fcp_cqidx,
+ phba->sli4_hba.fp_eq[fcp_cqidx]->queue_id);
+ }
+
+ /*
+ * Set up all the Work Queues (WQs)
+ */
+
+ /* Set up Mailbox Command Queue */
+ if (!phba->sli4_hba.mbx_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0538 Slow-path MQ not allocated\n");
+ goto out_destroy_fcp_cq;
+ }
+ rc = lpfc_mq_create(phba, phba->sli4_hba.mbx_wq,
+ phba->sli4_hba.mbx_cq, LPFC_MBOX);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0539 Failed setup of slow-path MQ: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_fcp_cq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n",
+ phba->sli4_hba.mbx_wq->queue_id,
+ phba->sli4_hba.mbx_cq->queue_id);
+
+ /* Set up slow-path ELS Work Queue */
+ if (!phba->sli4_hba.els_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0536 Slow-path ELS WQ not allocated\n");
+ goto out_destroy_mbx_wq;
+ }
+ rc = lpfc_wq_create(phba, phba->sli4_hba.els_wq,
+ phba->sli4_hba.els_cq, LPFC_ELS);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0537 Failed setup of slow-path ELS WQ: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_mbx_wq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2590 ELS WQ setup: wq-id=%d, parent cq-id=%d\n",
+ phba->sli4_hba.els_wq->queue_id,
+ phba->sli4_hba.els_cq->queue_id);
+
+ /* Set up fast-path FCP Work Queue */
+ for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
+ if (!phba->sli4_hba.fcp_wq[fcp_wqidx]) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0534 Fast-path FCP WQ (%d) not "
+ "allocated\n", fcp_wqidx);
+ goto out_destroy_fcp_wq;
+ }
+ rc = lpfc_wq_create(phba, phba->sli4_hba.fcp_wq[fcp_wqidx],
+ phba->sli4_hba.fcp_cq[fcp_cq_index],
+ LPFC_FCP);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0535 Failed setup of fast-path FCP "
+ "WQ (%d), rc = 0x%x\n", fcp_wqidx, rc);
+ goto out_destroy_fcp_wq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2591 FCP WQ setup: wq[%d]-id=%d, "
+ "parent cq[%d]-id=%d\n",
+ fcp_wqidx,
+ phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id,
+ fcp_cq_index,
+ phba->sli4_hba.fcp_cq[fcp_cq_index]->queue_id);
+ /* Round robin FCP Work Queue's Completion Queue assignment */
+ fcp_cq_index = ((fcp_cq_index + 1) % phba->cfg_fcp_eq_count);
+ }
+
+ /*
+ * Create Receive Queue (RQ)
+ */
+ if (!phba->sli4_hba.hdr_rq || !phba->sli4_hba.dat_rq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0540 Receive Queue not allocated\n");
+ goto out_destroy_fcp_wq;
+ }
+ rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
+ phba->sli4_hba.rxq_cq, LPFC_USOL);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0541 Failed setup of Receive Queue: "
+ "rc = 0x%x\n", rc);
+ goto out_destroy_fcp_wq;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2592 USL RQ setup: hdr-rq-id=%d, dat-rq-id=%d "
+ "parent cq-id=%d\n",
+ phba->sli4_hba.hdr_rq->queue_id,
+ phba->sli4_hba.dat_rq->queue_id,
+ phba->sli4_hba.rxq_cq->queue_id);
+ return 0;
+
+out_destroy_fcp_wq:
+ for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--)
+ lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_wqidx]);
+ lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+out_destroy_mbx_wq:
+ lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+out_destroy_fcp_cq:
+ for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--)
+ lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]);
+ lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
+out_destroy_els_cq:
+ lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
+out_destroy_mbx_cq:
+ lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+out_destroy_fp_eq:
+ for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--)
+ lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_eqidx]);
+ lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+out_error:
+ return rc;
+}
+
+/**
+ * lpfc_sli4_queue_unset - Unset all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset all the SLI4 queues with the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+void
+lpfc_sli4_queue_unset(struct lpfc_hba *phba)
+{
+ int fcp_qidx;
+
+ /* Unset mailbox command work queue */
+ lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+ /* Unset ELS work queue */
+ lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+ /* Unset unsolicited receive queue */
+ lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq);
+ /* Unset FCP work queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
+ lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_qidx]);
+ /* Unset mailbox command complete queue */
+ lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+ /* Unset ELS complete queue */
+ lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
+ /* Unset unsolicited receive complete queue */
+ lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
+ /* Unset FCP response complete queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+ lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]);
+ /* Unset fast-path event queue */
+ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+ lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_qidx]);
+ /* Unset slow-path event queue */
+ lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+}
+
+/**
+ * lpfc_sli4_cq_event_pool_create - Create completion-queue event free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and set up a pool of completion queue
+ * events. The body of the completion queue event is a completion queue entry
+ * CQE. For now, this pool is used for the interrupt service routine to queue
+ * the following HBA completion queue events for the worker thread to process:
+ * - Mailbox asynchronous events
+ * - Receive queue completion unsolicited events
+ * Later, this can be used for all the slow-path events.
+ *
+ * Return codes
+ * 0 - sucessful
+ * -ENOMEM - No availble memory
+ **/
+static int
+lpfc_sli4_cq_event_pool_create(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event;
+ int i;
+
+ for (i = 0; i < (4 * phba->sli4_hba.cq_ecount); i++) {
+ cq_event = kmalloc(sizeof(struct lpfc_cq_event), GFP_KERNEL);
+ if (!cq_event)
+ goto out_pool_create_fail;
+ list_add_tail(&cq_event->list,
+ &phba->sli4_hba.sp_cqe_event_pool);
+ }
+ return 0;
+
+out_pool_create_fail:
+ lpfc_sli4_cq_event_pool_destroy(phba);
+ return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_cq_event_pool_destroy - Free completion-queue event free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the pool of completion queue events at
+ * driver unload time. Note that, it is the responsibility of the driver
+ * cleanup routine to free all the outstanding completion-queue events
+ * allocated from this pool back into the pool before invoking this routine
+ * to destroy the pool.
+ **/
+static void
+lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event, *next_cq_event;
+
+ list_for_each_entry_safe(cq_event, next_cq_event,
+ &phba->sli4_hba.sp_cqe_event_pool, list) {
+ list_del(&cq_event->list);
+ kfree(cq_event);
+ }
+}
+
+/**
+ * __lpfc_sli4_cq_event_alloc - Allocate a completion-queue event from free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the lock free version of the API invoked to allocate a
+ * completion-queue event from the free pool.
+ *
+ * Return: Pointer to the newly allocated completion-queue event if successful
+ * NULL otherwise.
+ **/
+struct lpfc_cq_event *
+__lpfc_sli4_cq_event_alloc(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event = NULL;
+
+ list_remove_head(&phba->sli4_hba.sp_cqe_event_pool, cq_event,
+ struct lpfc_cq_event, list);
+ return cq_event;
+}
+
+/**
+ * lpfc_sli4_cq_event_alloc - Allocate a completion-queue event from free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the lock version of the API invoked to allocate a
+ * completion-queue event from the free pool.
+ *
+ * Return: Pointer to the newly allocated completion-queue event if successful
+ * NULL otherwise.
+ **/
+struct lpfc_cq_event *
+lpfc_sli4_cq_event_alloc(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cq_event = __lpfc_sli4_cq_event_alloc(phba);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return cq_event;
+}
+
+/**
+ * __lpfc_sli4_cq_event_release - Release a completion-queue event to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @cq_event: pointer to the completion queue event to be freed.
+ *
+ * This routine is the lock free version of the API invoked to release a
+ * completion-queue event back into the free pool.
+ **/
+void
+__lpfc_sli4_cq_event_release(struct lpfc_hba *phba,
+ struct lpfc_cq_event *cq_event)
+{
+ list_add_tail(&cq_event->list, &phba->sli4_hba.sp_cqe_event_pool);
+}
+
+/**
+ * lpfc_sli4_cq_event_release - Release a completion-queue event to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @cq_event: pointer to the completion queue event to be freed.
+ *
+ * This routine is the lock version of the API invoked to release a
+ * completion-queue event back into the free pool.
+ **/
+void
+lpfc_sli4_cq_event_release(struct lpfc_hba *phba,
+ struct lpfc_cq_event *cq_event)
+{
+ unsigned long iflags;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ __lpfc_sli4_cq_event_release(phba, cq_event);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+}
+
+/**
+ * lpfc_sli4_cq_event_release_all - Release all cq events to the free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to free all the pending completion-queue events to the
+ * back into the free pool for device reset.
+ **/
+static void
+lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba)
+{
+ LIST_HEAD(cqelist);
+ struct lpfc_cq_event *cqe;
+ unsigned long iflags;
+
+ /* Retrieve all the pending WCQEs from pending WCQE lists */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ /* Pending FCP XRI abort events */
+ list_splice_init(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
+ &cqelist);
+ /* Pending ELS XRI abort events */
+ list_splice_init(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
+ &cqelist);
+ /* Pending asynnc events */
+ list_splice_init(&phba->sli4_hba.sp_asynce_work_queue,
+ &cqelist);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ while (!list_empty(&cqelist)) {
+ list_remove_head(&cqelist, cqe, struct lpfc_cq_event, list);
+ lpfc_sli4_cq_event_release(phba, cqe);
+ }
+}
+
+/**
+ * lpfc_pci_function_reset - Reset pci function.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to request a PCI function reset. It will destroys
+ * all resources assigned to the PCI function which originates this request.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No availble memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_pci_function_reset(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ uint32_t rc = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0494 Unable to allocate memory for issuing "
+ "SLI_FUNCTION_RESET mailbox command\n");
+ return -ENOMEM;
+ }
+
+ /* Set up PCI function reset SLI4_CONFIG mailbox-ioctl command */
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_FUNCTION_RESET, 0,
+ LPFC_SLI4_MBX_EMBED);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0495 SLI_FUNCTION_RESET mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_sli4_send_nop_mbox_cmds - Send sli-4 nop mailbox commands
+ * @phba: pointer to lpfc hba data structure.
+ * @cnt: number of nop mailbox commands to send.
+ *
+ * This routine is invoked to send a number @cnt of NOP mailbox command and
+ * wait for each command to complete.
+ *
+ * Return: the number of NOP mailbox command completed.
+ **/
+static int
+lpfc_sli4_send_nop_mbox_cmds(struct lpfc_hba *phba, uint32_t cnt)
+{
+ LPFC_MBOXQ_t *mboxq;
+ int length, cmdsent;
+ uint32_t mbox_tmo;
+ uint32_t rc = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (cnt == 0) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2518 Requested to send 0 NOP mailbox cmd\n");
+ return cnt;
+ }
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2519 Unable to allocate memory for issuing "
+ "NOP mailbox command\n");
+ return 0;
+ }
+
+ /* Set up NOP SLI4_CONFIG mailbox-ioctl command */
+ length = (sizeof(struct lpfc_mbx_nop) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_NOP, length, LPFC_SLI4_MBX_EMBED);
+
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ for (cmdsent = 0; cmdsent < cnt; cmdsent++) {
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ else
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ if (rc == MBX_TIMEOUT)
+ break;
+ /* Check return status */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2520 NOP mailbox command failed "
+ "status x%x add_status x%x mbx "
+ "status x%x\n", shdr_status,
+ shdr_add_status, rc);
+ break;
+ }
+ }
+
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+
+ return cmdsent;
+}
+
+/**
+ * lpfc_sli4_fcfi_unreg - Unregister fcfi to device
* @phba: pointer to lpfc hba data structure.
+ * @fcfi: fcf index.
*
- * This routine is invoked to enable the MSI-X interrupt vectors. The kernel
- * function pci_enable_msix() is called to enable the MSI-X vectors. Note that
- * pci_enable_msix(), once invoked, enables either all or nothing, depending
- * on the current availability of PCI vector resources. The device driver is
- * responsible for calling the individual request_irq() to register each MSI-X
- * vector with a interrupt handler, which is done in this function. Note that
+ * This routine is invoked to unregister a FCFI from device.
+ **/
+void
+lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi)
+{
+ LPFC_MBOXQ_t *mbox;
+ uint32_t mbox_tmo;
+ int rc;
+ unsigned long flags;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+ if (!mbox)
+ return;
+
+ lpfc_unreg_fcfi(mbox, fcfi);
+
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+ }
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ if (rc != MBX_SUCCESS)
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2517 Unregister FCFI command failed "
+ "status %d, mbxStatus x%x\n", rc,
+ bf_get(lpfc_mqe_status, &mbox->u.mqe));
+ else {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* Mark the FCFI is no longer registered */
+ phba->fcf.fcf_flag &=
+ ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_DISCOVERED);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ }
+}
+
+/**
+ * lpfc_sli4_pci_mem_setup - Setup SLI4 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the PCI device memory space for device
+ * with SLI-4 interface spec.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+ unsigned long bar0map_len, bar1map_len, bar2map_len;
+ int error = -ENODEV;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ return error;
+ else
+ pdev = phba->pcidev;
+
+ /* Set the device DMA mask size */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
+ return error;
+
+ /* Get the bus address of SLI4 device Bar0, Bar1, and Bar2 and the
+ * number of bytes required by each mapping. They are actually
+ * mapping to the PCI BAR regions 1, 2, and 4 by the SLI4 device.
+ */
+ phba->pci_bar0_map = pci_resource_start(pdev, LPFC_SLI4_BAR0);
+ bar0map_len = pci_resource_len(pdev, LPFC_SLI4_BAR0);
+
+ phba->pci_bar1_map = pci_resource_start(pdev, LPFC_SLI4_BAR1);
+ bar1map_len = pci_resource_len(pdev, LPFC_SLI4_BAR1);
+
+ phba->pci_bar2_map = pci_resource_start(pdev, LPFC_SLI4_BAR2);
+ bar2map_len = pci_resource_len(pdev, LPFC_SLI4_BAR2);
+
+ /* Map SLI4 PCI Config Space Register base to a kernel virtual addr */
+ phba->sli4_hba.conf_regs_memmap_p =
+ ioremap(phba->pci_bar0_map, bar0map_len);
+ if (!phba->sli4_hba.conf_regs_memmap_p) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "ioremap failed for SLI4 PCI config registers.\n");
+ goto out;
+ }
+
+ /* Map SLI4 HBA Control Register base to a kernel virtual address. */
+ phba->sli4_hba.ctrl_regs_memmap_p =
+ ioremap(phba->pci_bar1_map, bar1map_len);
+ if (!phba->sli4_hba.ctrl_regs_memmap_p) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "ioremap failed for SLI4 HBA control registers.\n");
+ goto out_iounmap_conf;
+ }
+
+ /* Map SLI4 HBA Doorbell Register base to a kernel virtual address. */
+ phba->sli4_hba.drbl_regs_memmap_p =
+ ioremap(phba->pci_bar2_map, bar2map_len);
+ if (!phba->sli4_hba.drbl_regs_memmap_p) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "ioremap failed for SLI4 HBA doorbell registers.\n");
+ goto out_iounmap_ctrl;
+ }
+
+ /* Set up BAR0 PCI config space register memory map */
+ lpfc_sli4_bar0_register_memmap(phba);
+
+ /* Set up BAR1 register memory map */
+ lpfc_sli4_bar1_register_memmap(phba);
+
+ /* Set up BAR2 register memory map */
+ error = lpfc_sli4_bar2_register_memmap(phba, LPFC_VF0);
+ if (error)
+ goto out_iounmap_all;
+
+ return 0;
+
+out_iounmap_all:
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+out_iounmap_ctrl:
+ iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+out_iounmap_conf:
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+out:
+ return error;
+}
+
+/**
+ * lpfc_sli4_pci_mem_unset - Unset SLI4 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the PCI device memory space for device
+ * with SLI-4 interface spec.
+ **/
+static void
+lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev;
+
+ /* Obtain PCI device reference */
+ if (!phba->pcidev)
+ return;
+ else
+ pdev = phba->pcidev;
+
+ /* Free coherent DMA memory allocated */
+
+ /* Unmap I/O memory space */
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+
+ return;
+}
+
+/**
+ * lpfc_sli_enable_msix - Enable MSI-X interrupt mode on SLI-3 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI-X interrupt vectors to device
+ * with SLI-3 interface specs. The kernel function pci_enable_msix() is
+ * called to enable the MSI-X vectors. Note that pci_enable_msix(), once
+ * invoked, enables either all or nothing, depending on the current
+ * availability of PCI vector resources. The device driver is responsible
+ * for calling the individual request_irq() to register each MSI-X vector
+ * with a interrupt handler, which is done in this function. Note that
* later when device is unloading, the driver should always call free_irq()
* on all MSI-X vectors it has done request_irq() on before calling
* pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
@@ -2271,7 +6029,7 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
* other values - error
**/
static int
-lpfc_enable_msix(struct lpfc_hba *phba)
+lpfc_sli_enable_msix(struct lpfc_hba *phba)
{
int rc, i;
LPFC_MBOXQ_t *pmb;
@@ -2287,20 +6045,21 @@ lpfc_enable_msix(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0420 PCI enable MSI-X failed (%d)\n", rc);
goto msi_fail_out;
- } else
- for (i = 0; i < LPFC_MSIX_VECTORS; i++)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0477 MSI-X entry[%d]: vector=x%x "
- "message=%d\n", i,
- phba->msix_entries[i].vector,
- phba->msix_entries[i].entry);
+ }
+ for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0477 MSI-X entry[%d]: vector=x%x "
+ "message=%d\n", i,
+ phba->msix_entries[i].vector,
+ phba->msix_entries[i].entry);
/*
* Assign MSI-X vectors to interrupt handlers
*/
/* vector-0 is associated to slow-path handler */
- rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
- IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
+ rc = request_irq(phba->msix_entries[0].vector,
+ &lpfc_sli_sp_intr_handler, IRQF_SHARED,
+ LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0421 MSI-X slow-path request_irq failed "
@@ -2309,8 +6068,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
}
/* vector-1 is associated to fast-path handler */
- rc = request_irq(phba->msix_entries[1].vector, &lpfc_fp_intr_handler,
- IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
+ rc = request_irq(phba->msix_entries[1].vector,
+ &lpfc_sli_fp_intr_handler, IRQF_SHARED,
+ LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -2339,7 +6099,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"0351 Config MSI mailbox command failed, "
"mbxCmd x%x, mbxStatus x%x\n",
- pmb->mb.mbxCommand, pmb->mb.mbxStatus);
+ pmb->u.mb.mbxCommand, pmb->u.mb.mbxStatus);
goto mbx_fail_out;
}
@@ -2366,14 +6126,14 @@ msi_fail_out:
}
/**
- * lpfc_disable_msix: Disable MSI-X interrupt mode.
+ * lpfc_sli_disable_msix - Disable MSI-X interrupt mode on SLI-3 device.
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to release the MSI-X vectors and then disable the
- * MSI-X interrupt mode.
+ * MSI-X interrupt mode to device with SLI-3 interface spec.
**/
static void
-lpfc_disable_msix(struct lpfc_hba *phba)
+lpfc_sli_disable_msix(struct lpfc_hba *phba)
{
int i;
@@ -2382,23 +6142,26 @@ lpfc_disable_msix(struct lpfc_hba *phba)
free_irq(phba->msix_entries[i].vector, phba);
/* Disable MSI-X */
pci_disable_msix(phba->pcidev);
+
+ return;
}
/**
- * lpfc_enable_msi: Enable MSI interrupt mode.
+ * lpfc_sli_enable_msi - Enable MSI interrupt mode on SLI-3 device.
* @phba: pointer to lpfc hba data structure.
*
- * This routine is invoked to enable the MSI interrupt mode. The kernel
- * function pci_enable_msi() is called to enable the MSI vector. The
- * device driver is responsible for calling the request_irq() to register
- * MSI vector with a interrupt the handler, which is done in this function.
+ * This routine is invoked to enable the MSI interrupt mode to device with
+ * SLI-3 interface spec. The kernel function pci_enable_msi() is called to
+ * enable the MSI vector. The device driver is responsible for calling the
+ * request_irq() to register MSI vector with a interrupt the handler, which
+ * is done in this function.
*
* Return codes
* 0 - sucessful
* other values - error
*/
static int
-lpfc_enable_msi(struct lpfc_hba *phba)
+lpfc_sli_enable_msi(struct lpfc_hba *phba)
{
int rc;
@@ -2412,7 +6175,7 @@ lpfc_enable_msi(struct lpfc_hba *phba)
return rc;
}
- rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
IRQF_SHARED, LPFC_DRIVER_NAME, phba);
if (rc) {
pci_disable_msi(phba->pcidev);
@@ -2423,17 +6186,17 @@ lpfc_enable_msi(struct lpfc_hba *phba)
}
/**
- * lpfc_disable_msi: Disable MSI interrupt mode.
+ * lpfc_sli_disable_msi - Disable MSI interrupt mode to SLI-3 device.
* @phba: pointer to lpfc hba data structure.
*
- * This routine is invoked to disable the MSI interrupt mode. The driver
- * calls free_irq() on MSI vector it has done request_irq() on before
- * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
- * a device will be left with MSI enabled and leaks its vector.
+ * This routine is invoked to disable the MSI interrupt mode to device with
+ * SLI-3 interface spec. The driver calls free_irq() on MSI vector it has
+ * done request_irq() on before calling pci_disable_msi(). Failure to do so
+ * results in a BUG_ON() and a device will be left with MSI enabled and leaks
+ * its vector.
*/
-
static void
-lpfc_disable_msi(struct lpfc_hba *phba)
+lpfc_sli_disable_msi(struct lpfc_hba *phba)
{
free_irq(phba->pcidev->irq, phba);
pci_disable_msi(phba->pcidev);
@@ -2441,80 +6204,298 @@ lpfc_disable_msi(struct lpfc_hba *phba)
}
/**
- * lpfc_log_intr_mode: Log the active interrupt mode
+ * lpfc_sli_enable_intr - Enable device interrupt to SLI-3 device.
* @phba: pointer to lpfc hba data structure.
- * @intr_mode: active interrupt mode adopted.
*
- * This routine it invoked to log the currently used active interrupt mode
- * to the device.
- */
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s) to device with SLI-3 interface
+ * spec. Depends on the interrupt mode configured to the driver, the driver
+ * will try to fallback from the configured interrupt mode to an interrupt
+ * mode which is supported by the platform, kernel, and device in the order
+ * of:
+ * MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static uint32_t
+lpfc_sli_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+ uint32_t intr_mode = LPFC_INTR_ERROR;
+ int retval;
+
+ if (cfg_mode == 2) {
+ /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+ retval = lpfc_sli_config_port(phba, LPFC_SLI_REV3);
+ if (!retval) {
+ /* Now, try to enable MSI-X interrupt mode */
+ retval = lpfc_sli_enable_msix(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI-X mode */
+ phba->intr_type = MSIX;
+ intr_mode = 2;
+ }
+ }
+ }
+
+ /* Fallback to MSI if MSI-X initialization failed */
+ if (cfg_mode >= 1 && phba->intr_type == NONE) {
+ retval = lpfc_sli_enable_msi(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI mode */
+ phba->intr_type = MSI;
+ intr_mode = 1;
+ }
+ }
+
+ /* Fallback to INTx if both MSI-X/MSI initalization failed */
+ if (phba->intr_type == NONE) {
+ retval = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (!retval) {
+ /* Indicate initialization to INTx mode */
+ phba->intr_type = INTx;
+ intr_mode = 0;
+ }
+ }
+ return intr_mode;
+}
+
+/**
+ * lpfc_sli_disable_intr - Disable device interrupt to SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s) to device with
+ * SLI-3 interface spec. Depending on the interrupt mode, the driver will
+ * release the interrupt vector(s) for the message signaled interrupt.
+ **/
static void
-lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+lpfc_sli_disable_intr(struct lpfc_hba *phba)
{
- switch (intr_mode) {
- case 0:
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0470 Enable INTx interrupt mode.\n");
- break;
- case 1:
+ /* Disable the currently initialized interrupt mode */
+ if (phba->intr_type == MSIX)
+ lpfc_sli_disable_msix(phba);
+ else if (phba->intr_type == MSI)
+ lpfc_sli_disable_msi(phba);
+ else if (phba->intr_type == INTx)
+ free_irq(phba->pcidev->irq, phba);
+
+ /* Reset interrupt management states */
+ phba->intr_type = NONE;
+ phba->sli.slistat.sli_intr = 0;
+
+ return;
+}
+
+/**
+ * lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI-X interrupt vectors to device
+ * with SLI-4 interface spec. The kernel function pci_enable_msix() is called
+ * to enable the MSI-X vectors. Note that pci_enable_msix(), once invoked,
+ * enables either all or nothing, depending on the current availability of
+ * PCI vector resources. The device driver is responsible for calling the
+ * individual request_irq() to register each MSI-X vector with a interrupt
+ * handler, which is done in this function. Note that later when device is
+ * unloading, the driver should always call free_irq() on all MSI-X vectors
+ * it has done request_irq() on before calling pci_disable_msix(). Failure
+ * to do so results in a BUG_ON() and a device will be left with MSI-X
+ * enabled and leaks its vectors.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli4_enable_msix(struct lpfc_hba *phba)
+{
+ int rc, index;
+
+ /* Set up MSI-X multi-message vectors */
+ for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
+ phba->sli4_hba.msix_entries[index].entry = index;
+
+ /* Configure MSI-X capability structure */
+ rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries,
+ phba->sli4_hba.cfg_eqn);
+ if (rc) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0481 Enabled MSI interrupt mode.\n");
- break;
- case 2:
+ "0484 PCI enable MSI-X failed (%d)\n", rc);
+ goto msi_fail_out;
+ }
+ /* Log MSI-X vector assignment */
+ for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0480 Enabled MSI-X interrupt mode.\n");
- break;
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0482 Illegal interrupt mode.\n");
- break;
+ "0489 MSI-X entry[%d]: vector=x%x "
+ "message=%d\n", index,
+ phba->sli4_hba.msix_entries[index].vector,
+ phba->sli4_hba.msix_entries[index].entry);
+ /*
+ * Assign MSI-X vectors to interrupt handlers
+ */
+
+ /* The first vector must associated to slow-path handler for MQ */
+ rc = request_irq(phba->sli4_hba.msix_entries[0].vector,
+ &lpfc_sli4_sp_intr_handler, IRQF_SHARED,
+ LPFC_SP_DRIVER_HANDLER_NAME, phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0485 MSI-X slow-path request_irq failed "
+ "(%d)\n", rc);
+ goto msi_fail_out;
}
- return;
+
+ /* The rest of the vector(s) are associated to fast-path handler(s) */
+ for (index = 1; index < phba->sli4_hba.cfg_eqn; index++) {
+ phba->sli4_hba.fcp_eq_hdl[index - 1].idx = index - 1;
+ phba->sli4_hba.fcp_eq_hdl[index - 1].phba = phba;
+ rc = request_irq(phba->sli4_hba.msix_entries[index].vector,
+ &lpfc_sli4_fp_intr_handler, IRQF_SHARED,
+ LPFC_FP_DRIVER_HANDLER_NAME,
+ &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0486 MSI-X fast-path (%d) "
+ "request_irq failed (%d)\n", index, rc);
+ goto cfg_fail_out;
+ }
+ }
+
+ return rc;
+
+cfg_fail_out:
+ /* free the irq already requested */
+ for (--index; index >= 1; index--)
+ free_irq(phba->sli4_hba.msix_entries[index - 1].vector,
+ &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+
+ /* free the irq already requested */
+ free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
+
+msi_fail_out:
+ /* Unconfigure MSI-X capability structure */
+ pci_disable_msix(phba->pcidev);
+ return rc;
}
+/**
+ * lpfc_sli4_disable_msix - Disable MSI-X interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release the MSI-X vectors and then disable the
+ * MSI-X interrupt mode to device with SLI-4 interface spec.
+ **/
static void
-lpfc_stop_port(struct lpfc_hba *phba)
+lpfc_sli4_disable_msix(struct lpfc_hba *phba)
{
- /* Clear all interrupt enable conditions */
- writel(0, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
- /* Clear all pending interrupts */
- writel(0xffffffff, phba->HAregaddr);
- readl(phba->HAregaddr); /* flush */
+ int index;
- /* Reset some HBA SLI setup states */
- lpfc_stop_phba_timers(phba);
- phba->pport->work_port_events = 0;
+ /* Free up MSI-X multi-message vectors */
+ free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
+
+ for (index = 1; index < phba->sli4_hba.cfg_eqn; index++)
+ free_irq(phba->sli4_hba.msix_entries[index].vector,
+ &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+ /* Disable MSI-X */
+ pci_disable_msix(phba->pcidev);
+
+ return;
+}
+
+/**
+ * lpfc_sli4_enable_msi - Enable MSI interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode to device with
+ * SLI-4 interface spec. The kernel function pci_enable_msi() is called
+ * to enable the MSI vector. The device driver is responsible for calling
+ * the request_irq() to register MSI vector with a interrupt the handler,
+ * which is done in this function.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli4_enable_msi(struct lpfc_hba *phba)
+{
+ int rc, index;
+
+ rc = pci_enable_msi(phba->pcidev);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0487 PCI enable MSI mode success.\n");
+ else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0488 PCI enable MSI mode failed (%d)\n", rc);
+ return rc;
+ }
+ rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (rc) {
+ pci_disable_msi(phba->pcidev);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0490 MSI request_irq failed (%d)\n", rc);
+ }
+
+ for (index = 0; index < phba->cfg_fcp_eq_count; index++) {
+ phba->sli4_hba.fcp_eq_hdl[index].idx = index;
+ phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+ }
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_disable_msi - Disable MSI interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode to device with
+ * SLI-4 interface spec. The driver calls free_irq() on MSI vector it has
+ * done request_irq() on before calling pci_disable_msi(). Failure to do so
+ * results in a BUG_ON() and a device will be left with MSI enabled and leaks
+ * its vector.
+ **/
+static void
+lpfc_sli4_disable_msi(struct lpfc_hba *phba)
+{
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msi(phba->pcidev);
return;
}
/**
- * lpfc_enable_intr: Enable device interrupt.
+ * lpfc_sli4_enable_intr - Enable device interrupt to SLI-4 device
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to enable device interrupt and associate driver's
- * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
- * mode configured to the driver, the driver will try to fallback from the
- * configured interrupt mode to an interrupt mode which is supported by the
- * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ * interrupt handler(s) to interrupt vector(s) to device with SLI-4
+ * interface spec. Depends on the interrupt mode configured to the driver,
+ * the driver will try to fallback from the configured interrupt mode to an
+ * interrupt mode which is supported by the platform, kernel, and device in
+ * the order of:
+ * MSI-X -> MSI -> IRQ.
*
* Return codes
- * 0 - sucessful
- * other values - error
+ * 0 - sucessful
+ * other values - error
**/
static uint32_t
-lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
{
uint32_t intr_mode = LPFC_INTR_ERROR;
- int retval;
+ int retval, index;
if (cfg_mode == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- retval = lpfc_sli_config_port(phba, 3);
+ /* Preparation before conf_msi mbox cmd */
+ retval = 0;
if (!retval) {
/* Now, try to enable MSI-X interrupt mode */
- retval = lpfc_enable_msix(phba);
+ retval = lpfc_sli4_enable_msix(phba);
if (!retval) {
/* Indicate initialization to MSI-X mode */
phba->intr_type = MSIX;
@@ -2525,7 +6506,7 @@ lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
/* Fallback to MSI if MSI-X initialization failed */
if (cfg_mode >= 1 && phba->intr_type == NONE) {
- retval = lpfc_enable_msi(phba);
+ retval = lpfc_sli4_enable_msi(phba);
if (!retval) {
/* Indicate initialization to MSI mode */
phba->intr_type = MSI;
@@ -2535,34 +6516,39 @@ lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
/* Fallback to INTx if both MSI-X/MSI initalization failed */
if (phba->intr_type == NONE) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ retval = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
IRQF_SHARED, LPFC_DRIVER_NAME, phba);
if (!retval) {
/* Indicate initialization to INTx mode */
phba->intr_type = INTx;
intr_mode = 0;
+ for (index = 0; index < phba->cfg_fcp_eq_count;
+ index++) {
+ phba->sli4_hba.fcp_eq_hdl[index].idx = index;
+ phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+ }
}
}
return intr_mode;
}
/**
- * lpfc_disable_intr: Disable device interrupt.
+ * lpfc_sli4_disable_intr - Disable device interrupt to SLI-4 device
* @phba: pointer to lpfc hba data structure.
*
- * This routine is invoked to disable device interrupt and disassociate the
- * driver's interrupt handler(s) from interrupt vector(s). Depending on the
- * interrupt mode, the driver will release the interrupt vector(s) for the
- * message signaled interrupt.
+ * This routine is invoked to disable device interrupt and disassociate
+ * the driver's interrupt handler(s) from interrupt vector(s) to device
+ * with SLI-4 interface spec. Depending on the interrupt mode, the driver
+ * will release the interrupt vector(s) for the message signaled interrupt.
**/
static void
-lpfc_disable_intr(struct lpfc_hba *phba)
+lpfc_sli4_disable_intr(struct lpfc_hba *phba)
{
/* Disable the currently initialized interrupt mode */
if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
+ lpfc_sli4_disable_msix(phba);
else if (phba->intr_type == MSI)
- lpfc_disable_msi(phba);
+ lpfc_sli4_disable_msi(phba);
else if (phba->intr_type == INTx)
free_irq(phba->pcidev->irq, phba);
@@ -2574,263 +6560,233 @@ lpfc_disable_intr(struct lpfc_hba *phba)
}
/**
- * lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
- * @pdev: pointer to PCI device
- * @pid: pointer to PCI device identifier
- *
- * This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is presented in PCI bus, the kernel PCI subsystem looks at
- * PCI device-specific information of the device and driver to see if the
- * driver state that it can support this kind of device. If the match is
- * successful, the driver core invokes this routine. If this routine
- * determines it can claim the HBA, it does all the initialization that it
- * needs to do to handle the HBA properly.
+ * lpfc_unset_hba - Unset SLI3 hba device initialization
+ * @phba: pointer to lpfc hba data structure.
*
- * Return code
- * 0 - driver can claim the device
- * negative value - driver can not claim the device
+ * This routine is invoked to unset the HBA device initialization steps to
+ * a device with SLI-3 interface spec.
**/
-static int __devinit
-lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+static void
+lpfc_unset_hba(struct lpfc_hba *phba)
{
- struct lpfc_vport *vport = NULL;
- struct lpfc_hba *phba;
- struct lpfc_sli *psli;
- struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
- struct Scsi_Host *shost = NULL;
- void *ptr;
- unsigned long bar0map_len, bar2map_len;
- int error = -ENODEV, retval;
- int i, hbq_count;
- uint16_t iotag;
- uint32_t cfg_mode, intr_mode;
- int bars = pci_select_bars(pdev, IORESOURCE_MEM);
- struct lpfc_adapter_event_header adapter_event;
-
- if (pci_enable_device_mem(pdev))
- goto out;
- if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
- goto out_disable_device;
-
- phba = kzalloc(sizeof (struct lpfc_hba), GFP_KERNEL);
- if (!phba)
- goto out_release_regions;
+ struct lpfc_vport *vport = phba->pport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- atomic_set(&phba->fast_event_count, 0);
- spin_lock_init(&phba->hbalock);
+ spin_lock_irq(shost->host_lock);
+ vport->load_flag |= FC_UNLOADING;
+ spin_unlock_irq(shost->host_lock);
- /* Initialize ndlp management spinlock */
- spin_lock_init(&phba->ndlp_lock);
+ lpfc_stop_hba_timers(phba);
- phba->pcidev = pdev;
+ phba->pport->work_port_events = 0;
- /* Assign an unused board number */
- if ((phba->brd_no = lpfc_get_instance()) < 0)
- goto out_free_phba;
+ lpfc_sli_hba_down(phba);
- INIT_LIST_HEAD(&phba->port_list);
- init_waitqueue_head(&phba->wait_4_mlo_m_q);
- /*
- * Get all the module params for configuring this host and then
- * establish the host.
- */
- lpfc_get_cfgparam(phba);
- phba->max_vpi = LPFC_MAX_VPI;
+ lpfc_sli_brdrestart(phba);
- /* Initialize timers used by driver */
- init_timer(&phba->hb_tmofunc);
- phba->hb_tmofunc.function = lpfc_hb_timeout;
- phba->hb_tmofunc.data = (unsigned long)phba;
+ lpfc_sli_disable_intr(phba);
- psli = &phba->sli;
- init_timer(&psli->mbox_tmo);
- psli->mbox_tmo.function = lpfc_mbox_timeout;
- psli->mbox_tmo.data = (unsigned long) phba;
- init_timer(&phba->fcp_poll_timer);
- phba->fcp_poll_timer.function = lpfc_poll_timeout;
- phba->fcp_poll_timer.data = (unsigned long) phba;
- init_timer(&phba->fabric_block_timer);
- phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
- phba->fabric_block_timer.data = (unsigned long) phba;
- init_timer(&phba->eratt_poll);
- phba->eratt_poll.function = lpfc_poll_eratt;
- phba->eratt_poll.data = (unsigned long) phba;
+ return;
+}
- pci_set_master(pdev);
- pci_save_state(pdev);
- pci_try_set_mwi(pdev);
+/**
+ * lpfc_sli4_unset_hba - Unset SLI4 hba device initialization.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the HBA device initialization steps to
+ * a device with SLI-4 interface spec.
+ **/
+static void
+lpfc_sli4_unset_hba(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (pci_set_dma_mask(phba->pcidev, DMA_BIT_MASK(64)) != 0)
- if (pci_set_dma_mask(phba->pcidev, DMA_BIT_MASK(32)) != 0)
- goto out_idr_remove;
+ spin_lock_irq(shost->host_lock);
+ vport->load_flag |= FC_UNLOADING;
+ spin_unlock_irq(shost->host_lock);
- /*
- * Get the bus 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->pport->work_port_events = 0;
- phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2);
- bar2map_len = pci_resource_len(phba->pcidev, 2);
+ lpfc_sli4_hba_down(phba);
- /* Map HBA SLIM to a kernel virtual address. */
- phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
- if (!phba->slim_memmap_p) {
- error = -ENODEV;
- dev_printk(KERN_ERR, &pdev->dev,
- "ioremap failed for SLIM memory.\n");
- goto out_idr_remove;
- }
+ lpfc_sli4_disable_intr(phba);
- /* Map HBA Control Registers to a kernel virtual address. */
- phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
- if (!phba->ctrl_regs_memmap_p) {
- error = -ENODEV;
- dev_printk(KERN_ERR, &pdev->dev,
- "ioremap failed for HBA control registers.\n");
- goto out_iounmap_slim;
- }
+ return;
+}
- /* Allocate memory for SLI-2 structures */
- phba->slim2p.virt = dma_alloc_coherent(&phba->pcidev->dev,
- SLI2_SLIM_SIZE,
- &phba->slim2p.phys,
- GFP_KERNEL);
- if (!phba->slim2p.virt)
- goto out_iounmap;
+/**
+ * lpfc_sli4_hba_unset - Unset the fcoe hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called in the SLI4 code path to reset the HBA's FCoE
+ * function. The caller is not required to hold any lock. This routine
+ * issues PCI function reset mailbox command to reset the FCoE function.
+ * At the end of the function, it calls lpfc_hba_down_post function to
+ * free any pending commands.
+ **/
+static void
+lpfc_sli4_hba_unset(struct lpfc_hba *phba)
+{
+ int wait_cnt = 0;
+ LPFC_MBOXQ_t *mboxq;
- memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
- phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
- phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
- phba->IOCBs = (phba->slim2p.virt +
- offsetof(struct lpfc_sli2_slim, IOCBs));
+ lpfc_stop_hba_timers(phba);
+ phba->sli4_hba.intr_enable = 0;
- phba->hbqslimp.virt = dma_alloc_coherent(&phba->pcidev->dev,
- lpfc_sli_hbq_size(),
- &phba->hbqslimp.phys,
- GFP_KERNEL);
- if (!phba->hbqslimp.virt)
- goto out_free_slim;
+ /*
+ * Gracefully wait out the potential current outstanding asynchronous
+ * mailbox command.
+ */
- hbq_count = lpfc_sli_hbq_count();
- ptr = phba->hbqslimp.virt;
- for (i = 0; i < hbq_count; ++i) {
- phba->hbqs[i].hbq_virt = ptr;
- INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
- ptr += (lpfc_hbq_defs[i]->entry_count *
- sizeof(struct lpfc_hbq_entry));
+ /* First, block any pending async mailbox command from posted */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
+ spin_unlock_irq(&phba->hbalock);
+ /* Now, trying to wait it out if we can */
+ while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+ msleep(10);
+ if (++wait_cnt > LPFC_ACTIVE_MBOX_WAIT_CNT)
+ break;
}
- phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_els_hbq_alloc;
- phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_els_hbq_free;
-
- memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
-
- INIT_LIST_HEAD(&phba->hbqbuf_in_list);
-
- /* Initialize the SLI Layer to run with lpfc HBAs. */
- lpfc_sli_setup(phba);
- lpfc_sli_queue_setup(phba);
-
- retval = lpfc_mem_alloc(phba);
- if (retval) {
- error = retval;
- goto out_free_hbqslimp;
+ /* Forcefully release the outstanding mailbox command if timed out */
+ if (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+ spin_lock_irq(&phba->hbalock);
+ mboxq = phba->sli.mbox_active;
+ mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ __lpfc_mbox_cmpl_put(phba, mboxq);
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ spin_unlock_irq(&phba->hbalock);
}
- /* Initialize and populate the iocb list per host. */
- INIT_LIST_HEAD(&phba->lpfc_iocb_list);
- for (i = 0; i < LPFC_IOCB_LIST_CNT; i++) {
- iocbq_entry = kzalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
- if (iocbq_entry == NULL) {
- printk(KERN_ERR "%s: only allocated %d iocbs of "
- "expected %d count. Unloading driver.\n",
- __func__, i, LPFC_IOCB_LIST_CNT);
- error = -ENOMEM;
- goto out_free_iocbq;
- }
+ /* Tear down the queues in the HBA */
+ lpfc_sli4_queue_unset(phba);
- iotag = lpfc_sli_next_iotag(phba, iocbq_entry);
- if (iotag == 0) {
- kfree (iocbq_entry);
- printk(KERN_ERR "%s: failed to allocate IOTAG. "
- "Unloading driver.\n",
- __func__);
- error = -ENOMEM;
- goto out_free_iocbq;
- }
+ /* Disable PCI subsystem interrupt */
+ lpfc_sli4_disable_intr(phba);
- spin_lock_irq(&phba->hbalock);
- list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
- phba->total_iocbq_bufs++;
- spin_unlock_irq(&phba->hbalock);
- }
+ /* Stop kthread signal shall trigger work_done one more time */
+ kthread_stop(phba->worker_thread);
- /* 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;
+ /* Stop the SLI4 device port */
+ phba->pport->work_port_events = 0;
+}
- INIT_LIST_HEAD(&phba->work_list);
- phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT);
- phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
+/**
+ * lpfc_pci_probe_one_s3 - PCI probe func to reg SLI-3 device to PCI subsystem.
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is to be called to attach a device with SLI-3 interface spec
+ * to the PCI subsystem. When an Emulex HBA with SLI-3 interface spec is
+ * presented on PCI bus, the kernel PCI subsystem looks at PCI device-specific
+ * information of the device and driver to see if the driver state that it can
+ * support this kind of device. If the match is successful, the driver core
+ * invokes this routine. If this routine determines it can claim the HBA, it
+ * does all the initialization that it needs to do to handle the HBA properly.
+ *
+ * Return code
+ * 0 - driver can claim the device
+ * negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_vport *vport = NULL;
+ int error;
+ uint32_t cfg_mode, intr_mode;
- /* Initialize the wait queue head for the kernel thread */
- init_waitqueue_head(&phba->work_waitq);
+ /* Allocate memory for HBA structure */
+ phba = lpfc_hba_alloc(pdev);
+ if (!phba)
+ return -ENOMEM;
- /* Startup the kernel thread for this host adapter. */
- phba->worker_thread = kthread_run(lpfc_do_work, phba,
- "lpfc_worker_%d", phba->brd_no);
- if (IS_ERR(phba->worker_thread)) {
- error = PTR_ERR(phba->worker_thread);
- goto out_free_iocbq;
+ /* Perform generic PCI device enabling operation */
+ error = lpfc_enable_pci_dev(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1401 Failed to enable pci device.\n");
+ goto out_free_phba;
}
- /* Initialize the list of scsi buffers used by driver for scsi IO. */
- spin_lock_init(&phba->scsi_buf_list_lock);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
+ /* Set up SLI API function jump table for PCI-device group-0 HBAs */
+ error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_LP);
+ if (error)
+ goto out_disable_pci_dev;
- /* Initialize list of fabric iocbs */
- INIT_LIST_HEAD(&phba->fabric_iocb_list);
+ /* Set up SLI-3 specific device PCI memory space */
+ error = lpfc_sli_pci_mem_setup(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1402 Failed to set up pci memory space.\n");
+ goto out_disable_pci_dev;
+ }
- /* Initialize list to save ELS buffers */
- INIT_LIST_HEAD(&phba->elsbuf);
+ /* Set up phase-1 common device driver resources */
+ error = lpfc_setup_driver_resource_phase1(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1403 Failed to set up driver resource.\n");
+ goto out_unset_pci_mem_s3;
+ }
- vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
- if (!vport)
- goto out_kthread_stop;
+ /* Set up SLI-3 specific device driver resources */
+ error = lpfc_sli_driver_resource_setup(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1404 Failed to set up driver resource.\n");
+ goto out_unset_pci_mem_s3;
+ }
- shost = lpfc_shost_from_vport(vport);
- phba->pport = vport;
- lpfc_debugfs_initialize(vport);
+ /* Initialize and populate the iocb list per host */
+ error = lpfc_init_iocb_list(phba, LPFC_IOCB_LIST_CNT);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1405 Failed to initialize iocb list.\n");
+ goto out_unset_driver_resource_s3;
+ }
- pci_set_drvdata(pdev, shost);
+ /* Set up common device driver resources */
+ error = lpfc_setup_driver_resource_phase2(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1406 Failed to set up driver resource.\n");
+ goto out_free_iocb_list;
+ }
- phba->MBslimaddr = phba->slim_memmap_p;
- phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
- phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
- phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
- phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+ /* Create SCSI host to the physical port */
+ error = lpfc_create_shost(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1407 Failed to create scsi host.\n");
+ goto out_unset_driver_resource;
+ }
/* Configure sysfs attributes */
- if (lpfc_alloc_sysfs_attr(vport)) {
+ vport = phba->pport;
+ error = lpfc_alloc_sysfs_attr(vport);
+ if (error) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1476 Failed to allocate sysfs attr\n");
- error = -ENOMEM;
- goto out_destroy_port;
+ goto out_destroy_shost;
}
+ /* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
while (true) {
+ /* Put device to a known state before enabling interrupt */
+ lpfc_stop_port(phba);
/* Configure and enable interrupt */
- intr_mode = lpfc_enable_intr(phba, cfg_mode);
+ intr_mode = lpfc_sli_enable_intr(phba, cfg_mode);
if (intr_mode == LPFC_INTR_ERROR) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0426 Failed to enable interrupt.\n");
+ "0431 Failed to enable interrupt.\n");
+ error = -ENODEV;
goto out_free_sysfs_attr;
}
- /* HBA SLI setup */
+ /* SLI-3 HBA setup */
if (lpfc_sli_hba_setup(phba)) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1477 Failed to set up hba\n");
@@ -2840,185 +6796,65 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Wait 50ms for the interrupts of previous mailbox commands */
msleep(50);
- /* Check active interrupts received */
- if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+ /* Check active interrupts on message signaled interrupts */
+ if (intr_mode == 0 ||
+ phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
/* Log the current active interrupt mode */
phba->intr_mode = intr_mode;
lpfc_log_intr_mode(phba, intr_mode);
break;
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0451 Configure interrupt mode (%d) "
+ "0447 Configure interrupt mode (%d) "
"failed active interrupt test.\n",
intr_mode);
- if (intr_mode == 0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0479 Failed to enable "
- "interrupt.\n");
- error = -ENODEV;
- goto out_remove_device;
- }
- /* Stop HBA SLI setups */
- lpfc_stop_port(phba);
/* Disable the current interrupt mode */
- lpfc_disable_intr(phba);
+ lpfc_sli_disable_intr(phba);
/* Try next level of interrupt mode */
cfg_mode = --intr_mode;
}
}
- /*
- * hba setup may have changed the hba_queue_depth so we need to adjust
- * the value of can_queue.
- */
- shost->can_queue = phba->cfg_hba_queue_depth - 10;
- if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ /* Perform post initialization setup */
+ lpfc_post_init_setup(phba);
- if (lpfc_prot_mask && lpfc_prot_guard) {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "1478 Registering BlockGuard with the "
- "SCSI layer\n");
-
- scsi_host_set_prot(shost, lpfc_prot_mask);
- scsi_host_set_guard(shost, lpfc_prot_guard);
- }
- }
-
- if (!_dump_buf_data) {
- int pagecnt = 10;
- while (pagecnt) {
- spin_lock_init(&_dump_buf_lock);
- _dump_buf_data =
- (char *) __get_free_pages(GFP_KERNEL, pagecnt);
- if (_dump_buf_data) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
- "_dump_buf_data at 0x%p\n",
- (1 << pagecnt), _dump_buf_data);
- _dump_buf_data_order = pagecnt;
- memset(_dump_buf_data, 0, ((1 << PAGE_SHIFT)
- << pagecnt));
- break;
- } else {
- --pagecnt;
- }
-
- }
-
- if (!_dump_buf_data_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
- "memory for hexdump\n");
-
- } else {
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
- "\n", _dump_buf_data);
- }
-
-
- if (!_dump_buf_dif) {
- int pagecnt = 10;
- while (pagecnt) {
- _dump_buf_dif =
- (char *) __get_free_pages(GFP_KERNEL, pagecnt);
- if (_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
- "_dump_buf_dif at 0x%p\n",
- (1 << pagecnt), _dump_buf_dif);
- _dump_buf_dif_order = pagecnt;
- memset(_dump_buf_dif, 0, ((1 << PAGE_SHIFT)
- << pagecnt));
- break;
- } else {
- --pagecnt;
- }
-
- }
-
- if (!_dump_buf_dif_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
- "memory for hexdump\n");
-
- } else {
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
- _dump_buf_dif);
- }
-
- lpfc_host_attrib_init(shost);
-
- if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
- spin_lock_irq(shost->host_lock);
- lpfc_poll_start_timer(phba);
- spin_unlock_irq(shost->host_lock);
- }
-
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0428 Perform SCSI scan\n");
- /* Send board arrival event to upper layer */
- adapter_event.event_type = FC_REG_ADAPTER_EVENT;
- adapter_event.subcategory = LPFC_EVENT_ARRIVAL;
- fc_host_post_vendor_event(shost, fc_get_event_number(),
- sizeof(adapter_event),
- (char *) &adapter_event,
- LPFC_NL_VENDOR_ID);
+ /* Check if there are static vports to be created. */
+ lpfc_create_static_vport(phba);
return 0;
out_remove_device:
- spin_lock_irq(shost->host_lock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(shost->host_lock);
- lpfc_stop_phba_timers(phba);
- phba->pport->work_port_events = 0;
- lpfc_disable_intr(phba);
- lpfc_sli_hba_down(phba);
- lpfc_sli_brdrestart(phba);
+ lpfc_unset_hba(phba);
out_free_sysfs_attr:
lpfc_free_sysfs_attr(vport);
-out_destroy_port:
- destroy_port(vport);
-out_kthread_stop:
- kthread_stop(phba->worker_thread);
-out_free_iocbq:
- list_for_each_entry_safe(iocbq_entry, iocbq_next,
- &phba->lpfc_iocb_list, list) {
- kfree(iocbq_entry);
- phba->total_iocbq_bufs--;
- }
- lpfc_mem_free(phba);
-out_free_hbqslimp:
- dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
- phba->hbqslimp.virt, phba->hbqslimp.phys);
-out_free_slim:
- dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
- phba->slim2p.virt, phba->slim2p.phys);
-out_iounmap:
- iounmap(phba->ctrl_regs_memmap_p);
-out_iounmap_slim:
- iounmap(phba->slim_memmap_p);
-out_idr_remove:
- idr_remove(&lpfc_hba_index, phba->brd_no);
+out_destroy_shost:
+ lpfc_destroy_shost(phba);
+out_unset_driver_resource:
+ lpfc_unset_driver_resource_phase2(phba);
+out_free_iocb_list:
+ lpfc_free_iocb_list(phba);
+out_unset_driver_resource_s3:
+ lpfc_sli_driver_resource_unset(phba);
+out_unset_pci_mem_s3:
+ lpfc_sli_pci_mem_unset(phba);
+out_disable_pci_dev:
+ lpfc_disable_pci_dev(phba);
out_free_phba:
- kfree(phba);
-out_release_regions:
- pci_release_selected_regions(pdev, bars);
-out_disable_device:
- pci_disable_device(pdev);
-out:
- pci_set_drvdata(pdev, NULL);
- if (shost)
- scsi_host_put(shost);
+ lpfc_hba_free(phba);
return error;
}
/**
- * lpfc_pci_remove_one: lpfc PCI func to unregister device from PCI subsystem.
+ * lpfc_pci_remove_one_s3 - PCI func to unreg SLI-3 device from PCI subsystem.
* @pdev: pointer to PCI device
*
- * This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
- * for the HBA device to be removed from the PCI subsystem properly.
+ * This routine is to be called to disattach a device with SLI-3 interface
+ * spec from PCI subsystem. When an Emulex HBA with SLI-3 interface spec is
+ * removed from PCI bus, it performs all the necessary cleanup for the HBA
+ * device to be removed from the PCI subsystem properly.
**/
static void __devexit
-lpfc_pci_remove_one(struct pci_dev *pdev)
+lpfc_pci_remove_one_s3(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
@@ -3033,12 +6869,10 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_free_sysfs_attr(vport);
- kthread_stop(phba->worker_thread);
-
/* Release all the vports against this physical port */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+ for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
fc_vport_terminate(vports[i]->fc_vport);
lpfc_destroy_vport_work_array(phba, vports);
@@ -3052,10 +6886,15 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
* clears the rings, discards all mailbox commands, and resets
* the HBA.
*/
+
+ /* HBA interrupt will be diabled after this call */
lpfc_sli_hba_down(phba);
+ /* Stop kthread signal shall trigger work_done one more time */
+ kthread_stop(phba->worker_thread);
+ /* Final cleanup of txcmplq and reset the HBA */
lpfc_sli_brdrestart(phba);
- lpfc_stop_phba_timers(phba);
+ lpfc_stop_hba_timers(phba);
spin_lock_irq(&phba->hbalock);
list_del_init(&vport->listentry);
spin_unlock_irq(&phba->hbalock);
@@ -3063,7 +6902,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_debugfs_terminate(vport);
/* Disable interrupt */
- lpfc_disable_intr(phba);
+ lpfc_sli_disable_intr(phba);
pci_set_drvdata(pdev, NULL);
scsi_host_put(shost);
@@ -3073,7 +6912,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
* corresponding pools here.
*/
lpfc_scsi_free(phba);
- lpfc_mem_free(phba);
+ lpfc_mem_free_all(phba);
dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
phba->hbqslimp.virt, phba->hbqslimp.phys);
@@ -3086,36 +6925,35 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
iounmap(phba->ctrl_regs_memmap_p);
iounmap(phba->slim_memmap_p);
- idr_remove(&lpfc_hba_index, phba->brd_no);
-
- kfree(phba);
+ lpfc_hba_free(phba);
pci_release_selected_regions(pdev, bars);
pci_disable_device(pdev);
}
/**
- * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
+ * lpfc_pci_suspend_one_s3 - PCI func to suspend SLI-3 device for power mgmnt
* @pdev: pointer to PCI device
* @msg: power management message
*
- * This routine is to be registered to the kernel's PCI subsystem to support
- * system Power Management (PM). When PM invokes this method, it quiesces the
- * device by stopping the driver's worker thread for the device, turning off
- * device's interrupt and DMA, and bring the device offline. Note that as the
- * driver implements the minimum PM requirements to a power-aware driver's PM
- * support for suspend/resume -- all the possible PM messages (SUSPEND,
- * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
- * and the driver will fully reinitialize its device during resume() method
- * call, the driver will set device to PCI_D3hot state in PCI config space
- * instead of setting it according to the @msg provided by the PM.
+ * This routine is to be called from the kernel's PCI subsystem to support
+ * system Power Management (PM) to device with SLI-3 interface spec. When
+ * PM invokes this method, it quiesces the device by stopping the driver's
+ * worker thread for the device, turning off device's interrupt and DMA,
+ * and bring the device offline. Note that as the driver implements the
+ * minimum PM requirements to a power-aware driver's PM support for the
+ * suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE, FREEZE)
+ * to the suspend() method call will be treated as SUSPEND and the driver will
+ * fully reinitialize its device during resume() method call, the driver will
+ * set device to PCI_D3hot state in PCI config space instead of setting it
+ * according to the @msg provided by the PM.
*
* Return code
- * 0 - driver suspended the device
- * Error otherwise
+ * 0 - driver suspended the device
+ * Error otherwise
**/
static int
-lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+lpfc_pci_suspend_one_s3(struct pci_dev *pdev, pm_message_t msg)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
@@ -3129,7 +6967,7 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
kthread_stop(phba->worker_thread);
/* Disable interrupt from device */
- lpfc_disable_intr(phba);
+ lpfc_sli_disable_intr(phba);
/* Save device state to PCI config space */
pci_save_state(pdev);
@@ -3139,25 +6977,26 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
}
/**
- * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
+ * lpfc_pci_resume_one_s3 - PCI func to resume SLI-3 device for power mgmnt
* @pdev: pointer to PCI device
*
- * This routine is to be registered to the kernel's PCI subsystem to support
- * system Power Management (PM). When PM invokes this method, it restores
- * the device's PCI config space state and fully reinitializes the device
- * and brings it online. Note that as the driver implements the minimum PM
- * requirements to a power-aware driver's PM for suspend/resume -- all
- * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
- * method call will be treated as SUSPEND and the driver will fully
- * reinitialize its device during resume() method call, the device will be
- * set to PCI_D0 directly in PCI config space before restoring the state.
+ * This routine is to be called from the kernel's PCI subsystem to support
+ * system Power Management (PM) to device with SLI-3 interface spec. When PM
+ * invokes this method, it restores the device's PCI config space state and
+ * fully reinitializes the device and brings it online. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's
+ * PM for suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE,
+ * FREEZE) to the suspend() method call will be treated as SUSPEND and the
+ * driver will fully reinitialize its device during resume() method call,
+ * the device will be set to PCI_D0 directly in PCI config space before
+ * restoring the state.
*
* Return code
- * 0 - driver suspended the device
- * Error otherwise
+ * 0 - driver suspended the device
+ * Error otherwise
**/
static int
-lpfc_pci_resume_one(struct pci_dev *pdev)
+lpfc_pci_resume_one_s3(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
@@ -3185,7 +7024,7 @@ lpfc_pci_resume_one(struct pci_dev *pdev)
}
/* Configure and enable interrupt */
- intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0430 PM resume Failed to enable interrupt\n");
@@ -3204,23 +7043,24 @@ lpfc_pci_resume_one(struct pci_dev *pdev)
}
/**
- * lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
+ * lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
*
- * This routine is registered to the PCI subsystem for error handling. This
- * function is called by the PCI subsystem after a PCI bus error affecting
- * this device has been detected. When this function is invoked, it will
- * need to stop all the I/Os and interrupt(s) to the device. Once that is
- * done, it will return PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to
- * perform proper recovery as desired.
+ * This routine is called from the PCI subsystem for I/O error handling to
+ * device with SLI-3 interface spec. This function is called by the PCI
+ * subsystem after a PCI bus error affecting this device has been detected.
+ * When this function is invoked, it will need to stop all the I/Os and
+ * interrupt(s) to the device. Once that is done, it will return
+ * PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to perform proper recovery
+ * as desired.
*
* Return codes
- * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
- * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
**/
-static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
- pci_channel_state_t state)
+static pci_ers_result_t
+lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
@@ -3247,30 +7087,32 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
lpfc_sli_abort_iocb_ring(phba, pring);
/* Disable interrupt */
- lpfc_disable_intr(phba);
+ lpfc_sli_disable_intr(phba);
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
}
/**
- * lpfc_io_slot_reset: Restart a PCI device from scratch.
+ * lpfc_io_slot_reset_s3 - Method for restarting PCI SLI-3 device from scratch.
* @pdev: pointer to PCI device.
*
- * This routine is registered to the PCI subsystem for error handling. This is
- * called after PCI bus has been reset to restart the PCI card from scratch,
- * as if from a cold-boot. During the PCI subsystem error recovery, after the
- * driver returns PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform
- * proper error recovery and then call this routine before calling the .resume
- * method to recover the device. This function will initialize the HBA device,
- * enable the interrupt, but it will just put the HBA to offline state without
- * passing any I/O traffic.
+ * This routine is called from the PCI subsystem for error handling to
+ * device with SLI-3 interface spec. This is called after PCI bus has been
+ * reset to restart the PCI card from scratch, as if from a cold-boot.
+ * During the PCI subsystem error recovery, after driver returns
+ * PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform proper error
+ * recovery and then call this routine before calling the .resume method
+ * to recover the device. This function will initialize the HBA device,
+ * enable the interrupt, but it will just put the HBA to offline state
+ * without passing any I/O traffic.
*
* Return codes
- * PCI_ERS_RESULT_RECOVERED - the device has been recovered
- * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ * PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
*/
-static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
+static pci_ers_result_t
+lpfc_io_slot_reset_s3(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
@@ -3289,11 +7131,11 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
spin_lock_irq(&phba->hbalock);
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
/* Configure and enable interrupt */
- intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0427 Cannot re-enable interrupt after "
@@ -3313,20 +7155,713 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
}
/**
- * lpfc_io_resume: Resume PCI I/O operation.
+ * lpfc_io_resume_s3 - Method for resuming PCI I/O operation on SLI-3 device.
* @pdev: pointer to PCI device
*
- * This routine is registered to the PCI subsystem for error handling. It is
- * called when kernel error recovery tells the lpfc driver that it is ok to
- * resume normal PCI operation after PCI bus error recovery. After this call,
- * traffic can start to flow from this device again.
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-3 interface spec. It is called when kernel error recovery tells
+ * the lpfc driver that it is ok to resume normal PCI operation after PCI bus
+ * error recovery. After this call, traffic can start to flow from this device
+ * again.
*/
-static void lpfc_io_resume(struct pci_dev *pdev)
+static void
+lpfc_io_resume_s3(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ lpfc_online(phba);
+}
+
+/**
+ * lpfc_sli4_get_els_iocb_cnt - Calculate the # of ELS IOCBs to reserve
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * returns the number of ELS/CT IOCBs to reserve
+ **/
+int
+lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
+{
+ int max_xri = phba->sli4_hba.max_cfg_param.max_xri;
+
+ if (max_xri <= 100)
+ return 4;
+ else if (max_xri <= 256)
+ return 8;
+ else if (max_xri <= 512)
+ return 16;
+ else if (max_xri <= 1024)
+ return 32;
+ else
+ return 48;
+}
+
+/**
+ * lpfc_pci_probe_one_s4 - PCI probe func to reg SLI-4 device to PCI subsys
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is called from the kernel's PCI subsystem to device with
+ * SLI-4 interface spec. When an Emulex HBA with SLI-4 interface spec is
+ * presented on PCI bus, the kernel PCI subsystem looks at PCI device-specific
+ * information of the device and driver to see if the driver state that it
+ * can support this kind of device. If the match is successful, the driver
+ * core invokes this routine. If this routine determines it can claim the HBA,
+ * it does all the initialization that it needs to do to handle the HBA
+ * properly.
+ *
+ * Return code
+ * 0 - driver can claim the device
+ * negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_vport *vport = NULL;
+ int error;
+ uint32_t cfg_mode, intr_mode;
+ int mcnt;
+
+ /* Allocate memory for HBA structure */
+ phba = lpfc_hba_alloc(pdev);
+ if (!phba)
+ return -ENOMEM;
+
+ /* Perform generic PCI device enabling operation */
+ error = lpfc_enable_pci_dev(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1409 Failed to enable pci device.\n");
+ goto out_free_phba;
+ }
+
+ /* Set up SLI API function jump table for PCI-device group-1 HBAs */
+ error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_OC);
+ if (error)
+ goto out_disable_pci_dev;
+
+ /* Set up SLI-4 specific device PCI memory space */
+ error = lpfc_sli4_pci_mem_setup(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1410 Failed to set up pci memory space.\n");
+ goto out_disable_pci_dev;
+ }
+
+ /* Set up phase-1 common device driver resources */
+ error = lpfc_setup_driver_resource_phase1(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1411 Failed to set up driver resource.\n");
+ goto out_unset_pci_mem_s4;
+ }
+
+ /* Set up SLI-4 Specific device driver resources */
+ error = lpfc_sli4_driver_resource_setup(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1412 Failed to set up driver resource.\n");
+ goto out_unset_pci_mem_s4;
+ }
+
+ /* Initialize and populate the iocb list per host */
+ error = lpfc_init_iocb_list(phba,
+ phba->sli4_hba.max_cfg_param.max_xri);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1413 Failed to initialize iocb list.\n");
+ goto out_unset_driver_resource_s4;
+ }
+
+ /* Set up common device driver resources */
+ error = lpfc_setup_driver_resource_phase2(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1414 Failed to set up driver resource.\n");
+ goto out_free_iocb_list;
+ }
+
+ /* Create SCSI host to the physical port */
+ error = lpfc_create_shost(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1415 Failed to create scsi host.\n");
+ goto out_unset_driver_resource;
+ }
+
+ /* Configure sysfs attributes */
+ vport = phba->pport;
+ error = lpfc_alloc_sysfs_attr(vport);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1416 Failed to allocate sysfs attr\n");
+ goto out_destroy_shost;
+ }
+
+ /* Now, trying to enable interrupt and bring up the device */
+ cfg_mode = phba->cfg_use_msi;
+ while (true) {
+ /* Put device to a known state before enabling interrupt */
+ lpfc_stop_port(phba);
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_sli4_enable_intr(phba, cfg_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0426 Failed to enable interrupt.\n");
+ error = -ENODEV;
+ goto out_free_sysfs_attr;
+ }
+ /* Set up SLI-4 HBA */
+ if (lpfc_sli4_hba_setup(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1421 Failed to set up hba\n");
+ error = -ENODEV;
+ goto out_disable_intr;
+ }
+
+ /* Send NOP mbx cmds for non-INTx mode active interrupt test */
+ if (intr_mode != 0)
+ mcnt = lpfc_sli4_send_nop_mbox_cmds(phba,
+ LPFC_ACT_INTR_CNT);
+
+ /* Check active interrupts received only for MSI/MSI-X */
+ if (intr_mode == 0 ||
+ phba->sli.slistat.sli_intr >= LPFC_ACT_INTR_CNT) {
+ /* Log the current active interrupt mode */
+ phba->intr_mode = intr_mode;
+ lpfc_log_intr_mode(phba, intr_mode);
+ break;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0451 Configure interrupt mode (%d) "
+ "failed active interrupt test.\n",
+ intr_mode);
+ /* Unset the preivous SLI-4 HBA setup */
+ lpfc_sli4_unset_hba(phba);
+ /* Try next level of interrupt mode */
+ cfg_mode = --intr_mode;
+ }
+
+ /* Perform post initialization setup */
+ lpfc_post_init_setup(phba);
+
+ return 0;
+
+out_disable_intr:
+ lpfc_sli4_disable_intr(phba);
+out_free_sysfs_attr:
+ lpfc_free_sysfs_attr(vport);
+out_destroy_shost:
+ lpfc_destroy_shost(phba);
+out_unset_driver_resource:
+ lpfc_unset_driver_resource_phase2(phba);
+out_free_iocb_list:
+ lpfc_free_iocb_list(phba);
+out_unset_driver_resource_s4:
+ lpfc_sli4_driver_resource_unset(phba);
+out_unset_pci_mem_s4:
+ lpfc_sli4_pci_mem_unset(phba);
+out_disable_pci_dev:
+ lpfc_disable_pci_dev(phba);
+out_free_phba:
+ lpfc_hba_free(phba);
+ return error;
+}
+
+/**
+ * lpfc_pci_remove_one_s4 - PCI func to unreg SLI-4 device from PCI subsystem
+ * @pdev: pointer to PCI device
+ *
+ * This routine is called from the kernel's PCI subsystem to device with
+ * SLI-4 interface spec. When an Emulex HBA with SLI-4 interface spec is
+ * removed from PCI bus, it performs all the necessary cleanup for the HBA
+ * device to be removed from the PCI subsystem properly.
+ **/
+static void __devexit
+lpfc_pci_remove_one_s4(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_vport **vports;
+ struct lpfc_hba *phba = vport->phba;
+ int i;
+
+ /* Mark the device unloading flag */
+ spin_lock_irq(&phba->hbalock);
+ vport->load_flag |= FC_UNLOADING;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Free the HBA sysfs attributes */
+ lpfc_free_sysfs_attr(vport);
+
+ /* Release all the vports against this physical port */
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
+ fc_vport_terminate(vports[i]->fc_vport);
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ /* Remove FC host and then SCSI host with the physical port */
+ fc_remove_host(shost);
+ scsi_remove_host(shost);
+
+ /* Perform cleanup on the physical port */
+ lpfc_cleanup(vport);
+
+ /*
+ * Bring down the SLI Layer. This step disables all interrupts,
+ * clears the rings, discards all mailbox commands, and resets
+ * the HBA FCoE function.
+ */
+ lpfc_debugfs_terminate(vport);
+ lpfc_sli4_hba_unset(phba);
+
+ spin_lock_irq(&phba->hbalock);
+ list_del_init(&vport->listentry);
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Call scsi_free before lpfc_sli4_driver_resource_unset since scsi
+ * buffers are released to their corresponding pools here.
+ */
+ lpfc_scsi_free(phba);
+ lpfc_sli4_driver_resource_unset(phba);
+
+ /* Unmap adapter Control and Doorbell registers */
+ lpfc_sli4_pci_mem_unset(phba);
+
+ /* Release PCI resources and disable device's PCI function */
+ scsi_host_put(shost);
+ lpfc_disable_pci_dev(phba);
+
+ /* Finally, free the driver's device data structure */
+ lpfc_hba_free(phba);
+
+ return;
+}
+
+/**
+ * lpfc_pci_suspend_one_s4 - PCI func to suspend SLI-4 device for power mgmnt
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is called from the kernel's PCI subsystem to support system
+ * Power Management (PM) to device with SLI-4 interface spec. When PM invokes
+ * this method, it quiesces the device by stopping the driver's worker
+ * thread for the device, turning off device's interrupt and DMA, and bring
+ * the device offline. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM support for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the driver will set
+ * device to PCI_D3hot state in PCI config space instead of setting it
+ * according to the @msg provided by the PM.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one_s4(struct pci_dev *pdev, pm_message_t msg)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0298 PCI device Power Management suspend.\n");
+
+ /* Bring down the device */
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ kthread_stop(phba->worker_thread);
+
+ /* Disable interrupt from device */
+ lpfc_sli4_disable_intr(phba);
+
+ /* Save device state to PCI config space */
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+/**
+ * lpfc_pci_resume_one_s4 - PCI func to resume SLI-4 device for power mgmnt
+ * @pdev: pointer to PCI device
+ *
+ * This routine is called from the kernel's PCI subsystem to support system
+ * Power Management (PM) to device with SLI-4 interface spac. When PM invokes
+ * this method, it restores the device's PCI config space state and fully
+ * reinitializes the device and brings it online. Note that as the driver
+ * implements the minimum PM requirements to a power-aware driver's PM for
+ * suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE, FREEZE)
+ * to the suspend() method call will be treated as SUSPEND and the driver
+ * will fully reinitialize its device during resume() method call, the device
+ * will be set to PCI_D0 directly in PCI config space before restoring the
+ * state.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_resume_one_s4(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ uint32_t intr_mode;
+ int error;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0292 PCI device Power Management resume.\n");
+
+ /* Restore device state from PCI config space */
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0293 PM resume failed to start worker "
+ "thread: error=x%x.\n", error);
+ return error;
+ }
+
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0294 PM resume Failed to enable interrupt\n");
+ return -EIO;
+ } else
+ phba->intr_mode = intr_mode;
+ /* Restart HBA and bring it online */
+ lpfc_sli_brdrestart(phba);
lpfc_online(phba);
+
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
+ return 0;
+}
+
+/**
+ * lpfc_io_error_detected_s4 - Method for handling PCI I/O error to SLI-4 device
+ * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. This function is called by the PCI subsystem
+ * after a PCI bus error affecting this device has been detected. When this
+ * function is invoked, it will need to stop all the I/Os and interrupt(s)
+ * to the device. Once that is done, it will return PCI_ERS_RESULT_NEED_RESET
+ * for the PCI subsystem to perform proper recovery as desired.
+ *
+ * Return codes
+ * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * lpfc_io_slot_reset_s4 - Method for restart PCI SLI-4 device from scratch
+ * @pdev: pointer to PCI device.
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. It is called after PCI bus has been reset to
+ * restart the PCI card from scratch, as if from a cold-boot. During the
+ * PCI subsystem error recovery, after the driver returns
+ * PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform proper error
+ * recovery and then call this routine before calling the .resume method to
+ * recover the device. This function will initialize the HBA device, enable
+ * the interrupt, but it will just put the HBA to offline state without
+ * passing any I/O traffic.
+ *
+ * Return codes
+ * PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ */
+static pci_ers_result_t
+lpfc_io_slot_reset_s4(struct pci_dev *pdev)
+{
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * lpfc_io_resume_s4 - Method for resuming PCI I/O operation to SLI-4 device
+ * @pdev: pointer to PCI device
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. It is called when kernel error recovery tells
+ * the lpfc driver that it is ok to resume normal PCI operation after PCI bus
+ * error recovery. After this call, traffic can start to flow from this device
+ * again.
+ **/
+static void
+lpfc_io_resume_s4(struct pci_dev *pdev)
+{
+ return;
+}
+
+/**
+ * lpfc_pci_probe_one - lpfc PCI probe func to reg dev to PCI subsystem
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA device is presented on PCI bus, the kernel PCI subsystem looks
+ * at PCI device-specific information of the device and driver to see if the
+ * driver state that it can support this kind of device. If the match is
+ * successful, the driver core invokes this routine. This routine dispatches
+ * the action to the proper SLI-3 or SLI-4 device probing routine, which will
+ * do all the initialization that it needs to do to handle the HBA device
+ * properly.
+ *
+ * Return code
+ * 0 - driver can claim the device
+ * negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+ int rc;
+ uint16_t dev_id;
+
+ if (pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id))
+ return -ENODEV;
+
+ switch (dev_id) {
+ case PCI_DEVICE_ID_TIGERSHARK:
+ case PCI_DEVICE_ID_TIGERSHARK_S:
+ rc = lpfc_pci_probe_one_s4(pdev, pid);
+ break;
+ default:
+ rc = lpfc_pci_probe_one_s3(pdev, pid);
+ break;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_pci_remove_one - lpfc PCI func to unreg dev from PCI subsystem
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA is removed from PCI bus, the driver core invokes this routine.
+ * This routine dispatches the action to the proper SLI-3 or SLI-4 device
+ * remove routine, which will perform all the necessary cleanup for the
+ * device to be removed from the PCI subsystem properly.
+ **/
+static void __devexit
+lpfc_pci_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ lpfc_pci_remove_one_s3(pdev);
+ break;
+ case LPFC_PCI_DEV_OC:
+ lpfc_pci_remove_one_s4(pdev);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1424 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return;
+}
+
+/**
+ * lpfc_pci_suspend_one - lpfc PCI func to suspend dev for power management
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device suspend routine, which will
+ * suspend the device.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ int rc = -ENODEV;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ rc = lpfc_pci_suspend_one_s3(pdev, msg);
+ break;
+ case LPFC_PCI_DEV_OC:
+ rc = lpfc_pci_suspend_one_s4(pdev, msg);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1425 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_pci_resume_one - lpfc PCI func to resume dev for power management
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device resume routine, which will
+ * resume the device.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ int rc = -ENODEV;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ rc = lpfc_pci_resume_one_s3(pdev);
+ break;
+ case LPFC_PCI_DEV_OC:
+ rc = lpfc_pci_resume_one_s4(pdev);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1426 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_io_error_detected - lpfc method for handling PCI I/O error
+ * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
+ *
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called by the PCI subsystem after a PCI bus error affecting
+ * this device has been detected. When this routine is invoked, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device error detected handling
+ * routine, which will perform the proper error detected operation.
+ *
+ * Return codes
+ * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ rc = lpfc_io_error_detected_s3(pdev, state);
+ break;
+ case LPFC_PCI_DEV_OC:
+ rc = lpfc_io_error_detected_s4(pdev, state);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1427 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_io_slot_reset - lpfc method for restart PCI dev from scratch
+ * @pdev: pointer to PCI device.
+ *
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called after PCI bus has been reset to restart the PCI card
+ * from scratch, as if from a cold-boot. When this routine is invoked, it
+ * dispatches the action to the proper SLI-3 or SLI-4 device reset handling
+ * routine, which will perform the proper device reset.
+ *
+ * Return codes
+ * PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_slot_reset(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ rc = lpfc_io_slot_reset_s3(pdev);
+ break;
+ case LPFC_PCI_DEV_OC:
+ rc = lpfc_io_slot_reset_s4(pdev);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1428 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_io_resume - lpfc method for resuming PCI I/O operation
+ * @pdev: pointer to PCI device
+ *
+ * This routine is registered to the PCI subsystem for error handling. It
+ * is called when kernel error recovery tells the lpfc driver that it is
+ * OK to resume normal PCI operation after PCI bus error recovery. When
+ * this routine is invoked, it dispatches the action to the proper SLI-3
+ * or SLI-4 device io_resume routine, which will resume the device operation.
+ **/
+static void
+lpfc_io_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ switch (phba->pci_dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ lpfc_io_resume_s3(pdev);
+ break;
+ case LPFC_PCI_DEV_OC:
+ lpfc_io_resume_s4(pdev);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1429 Invalid PCI device group: 0x%x\n",
+ phba->pci_dev_grp);
+ break;
+ }
+ return;
}
static struct pci_device_id lpfc_id_table[] = {
@@ -3404,6 +7939,10 @@ static struct pci_device_id lpfc_id_table[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK_S,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }
};
@@ -3421,12 +7960,12 @@ static struct pci_driver lpfc_driver = {
.probe = lpfc_pci_probe_one,
.remove = __devexit_p(lpfc_pci_remove_one),
.suspend = lpfc_pci_suspend_one,
- .resume = lpfc_pci_resume_one,
+ .resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
};
/**
- * lpfc_init: lpfc module initialization routine.
+ * lpfc_init - lpfc module initialization routine
*
* This routine is to be invoked when the lpfc module is loaded into the
* kernel. The special kernel macro module_init() is used to indicate the
@@ -3472,7 +8011,7 @@ lpfc_init(void)
}
/**
- * lpfc_exit: lpfc module removal routine.
+ * lpfc_exit - lpfc module removal routine
*
* This routine is invoked when the lpfc module is removed from the kernel.
* The special kernel macro module_exit() is used to indicate the role of
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index a85b7c196bbc..954ba57970a3 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -18,33 +18,39 @@
* included with this package. *
*******************************************************************/
-#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_TEMP 0x100 /* Temperature sensor events */
-#define LOG_BG 0x200 /* BlockBuard events */
-#define LOG_MISC 0x400 /* Miscellaneous events */
-#define LOG_SLI 0x800 /* SLI events */
-#define LOG_FCP_ERROR 0x1000 /* log errors, not underruns */
-#define LOG_LIBDFC 0x2000 /* Libdfc events */
-#define LOG_VPORT 0x4000 /* NPIV events */
-#define LOG_ALL_MSG 0xffff /* LOG all messages */
+#define LOG_ELS 0x00000001 /* ELS events */
+#define LOG_DISCOVERY 0x00000002 /* Link discovery events */
+#define LOG_MBOX 0x00000004 /* Mailbox events */
+#define LOG_INIT 0x00000008 /* Initialization events */
+#define LOG_LINK_EVENT 0x00000010 /* Link events */
+#define LOG_IP 0x00000020 /* IP traffic history */
+#define LOG_FCP 0x00000040 /* FCP traffic history */
+#define LOG_NODE 0x00000080 /* Node table events */
+#define LOG_TEMP 0x00000100 /* Temperature sensor events */
+#define LOG_BG 0x00000200 /* BlockGuard events */
+#define LOG_MISC 0x00000400 /* Miscellaneous events */
+#define LOG_SLI 0x00000800 /* SLI events */
+#define LOG_FCP_ERROR 0x00001000 /* log errors, not underruns */
+#define LOG_LIBDFC 0x00002000 /* Libdfc events */
+#define LOG_VPORT 0x00004000 /* NPIV events */
+#define LOF_SECURITY 0x00008000 /* Security events */
+#define LOG_EVENT 0x00010000 /* CT,TEMP,DUMP, logging */
+#define LOG_ALL_MSG 0xffffffff /* LOG all messages */
#define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \
- do { \
- { if (((mask) &(vport)->cfg_log_verbose) || (level[1] <= '3')) \
+do { \
+ { if (((mask) & (vport)->cfg_log_verbose) || (level[1] <= '3')) \
dev_printk(level, &((vport)->phba->pcidev)->dev, "%d:(%d):" \
fmt, (vport)->phba->brd_no, vport->vpi, ##arg); } \
- } while (0)
+} while (0)
#define lpfc_printf_log(phba, level, mask, fmt, arg...) \
- do { \
- { if (((mask) &(phba)->pport->cfg_log_verbose) || (level[1] <= '3')) \
+do { \
+ { uint32_t log_verbose = (phba)->pport ? \
+ (phba)->pport->cfg_log_verbose : \
+ (phba)->cfg_log_verbose; \
+ if (((mask) & log_verbose) || (level[1] <= '3')) \
dev_printk(level, &((phba)->pcidev)->dev, "%d:" \
- fmt, phba->brd_no, ##arg); } \
- } while (0)
+ fmt, phba->brd_no, ##arg); \
+ } \
+} while (0)
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 34eeb086a667..b9b451c09010 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -28,8 +28,10 @@
#include <scsi/scsi.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -39,7 +41,45 @@
#include "lpfc_compat.h"
/**
- * lpfc_dump_mem: Prepare a mailbox command for retrieving HBA's VPD memory.
+ * lpfc_dump_static_vport - Dump HBA's static vport information.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ * @offset: offset for dumping vport info.
+ *
+ * The dump mailbox command provides a method for the device driver to obtain
+ * various types of information from the HBA device.
+ *
+ * This routine prepares the mailbox command for dumping list of static
+ * vports to be created.
+ **/
+void
+lpfc_dump_static_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
+ uint16_t offset)
+{
+ MAILBOX_t *mb;
+ void *ctx;
+
+ mb = &pmb->u.mb;
+ ctx = pmb->context2;
+
+ /* Setup to dump vport info 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.entry_index = offset;
+ mb->un.varDmp.region_id = DMP_REGION_VPORT;
+ mb->un.varDmp.word_cnt = DMP_RSP_SIZE/sizeof(uint32_t);
+ mb->un.varDmp.co = 0;
+ mb->un.varDmp.resp_offset = 0;
+ pmb->context2 = ctx;
+ mb->mbxOwner = OWN_HOST;
+
+ return;
+}
+
+/**
+ * lpfc_dump_mem - Prepare a mailbox command for retrieving HBA's VPD memory
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
* @offset: offset for dumping VPD memory mailbox command.
@@ -58,7 +98,7 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
MAILBOX_t *mb;
void *ctx;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
ctx = pmb->context2;
/* Setup to dump VPD region */
@@ -77,9 +117,10 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
}
/**
- * lpfc_dump_mem: Prepare a mailbox command for retrieving wakeup params.
+ * lpfc_dump_wakeup_param - Prepare mailbox command for retrieving wakeup params
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
+ *
* This function create a dump memory mailbox command to dump wake up
* parameters.
*/
@@ -89,7 +130,7 @@ lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
MAILBOX_t *mb;
void *ctx;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
/* Save context so that we can restore after memset */
ctx = pmb->context2;
@@ -109,7 +150,7 @@ lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
/**
- * lpfc_read_nv: Prepare a mailbox command for reading HBA's NVRAM param.
+ * lpfc_read_nv - Prepare a mailbox command for reading HBA's NVRAM param
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -124,7 +165,7 @@ lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxCommand = MBX_READ_NV;
mb->mbxOwner = OWN_HOST;
@@ -132,7 +173,7 @@ lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_config_async: Prepare a mailbox command for enabling HBA async event.
+ * lpfc_config_async - Prepare a mailbox command for enabling HBA async event
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
* @ring: ring number for the asynchronous event to be configured.
@@ -150,7 +191,7 @@ lpfc_config_async(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb,
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxCommand = MBX_ASYNCEVT_ENABLE;
mb->un.varCfgAsyncEvent.ring = ring;
@@ -159,7 +200,7 @@ lpfc_config_async(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb,
}
/**
- * lpfc_heart_beat: Prepare a mailbox command for heart beat.
+ * lpfc_heart_beat - Prepare a mailbox command for heart beat
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -176,7 +217,7 @@ lpfc_heart_beat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxCommand = MBX_HEARTBEAT;
mb->mbxOwner = OWN_HOST;
@@ -184,7 +225,7 @@ lpfc_heart_beat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_read_la: Prepare a mailbox command for reading HBA link attention.
+ * lpfc_read_la - Prepare a mailbox command for reading HBA link attention
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
* @mp: DMA buffer memory for reading the link attention information into.
@@ -210,7 +251,7 @@ lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, struct lpfc_dmabuf *mp)
struct lpfc_sli *psli;
psli = &phba->sli;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
INIT_LIST_HEAD(&mp->list);
@@ -228,7 +269,7 @@ lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, struct lpfc_dmabuf *mp)
}
/**
- * lpfc_clear_la: Prepare a mailbox command for clearing HBA link attention.
+ * lpfc_clear_la - Prepare a mailbox command for clearing HBA link attention
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -247,7 +288,7 @@ lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->un.varClearLA.eventTag = phba->fc_eventTag;
@@ -257,7 +298,7 @@ lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_config_link: Prepare a mailbox command for configuring link on a HBA.
+ * lpfc_config_link - Prepare a mailbox command for configuring link on a HBA
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -274,7 +315,7 @@ void
lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
struct lpfc_vport *vport = phba->pport;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
/* NEW_FEATURE
@@ -305,7 +346,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_config_msi: Prepare a mailbox command for configuring msi-x.
+ * lpfc_config_msi - Prepare a mailbox command for configuring msi-x
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -320,7 +361,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
int
lpfc_config_msi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
uint32_t attentionConditions[2];
/* Sanity check */
@@ -383,7 +424,7 @@ lpfc_config_msi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
/**
- * lpfc_init_link: Prepare a mailbox command for initialize link on a HBA.
+ * lpfc_init_link - Prepare a mailbox command for initialize link on a HBA
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
* @topology: the link topology for the link to be initialized to.
@@ -404,7 +445,7 @@ lpfc_init_link(struct lpfc_hba * phba,
struct lpfc_sli *psli;
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
psli = &phba->sli;
@@ -463,7 +504,7 @@ lpfc_init_link(struct lpfc_hba * phba,
}
/**
- * lpfc_read_sparam: Prepare a mailbox command for reading HBA parameters.
+ * lpfc_read_sparam - Prepare a mailbox command for reading HBA parameters
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
* @vpi: virtual N_Port identifier.
@@ -491,7 +532,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
struct lpfc_sli *psli;
psli = &phba->sli;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxOwner = OWN_HOST;
@@ -514,7 +555,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
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);
- mb->un.varRdSparm.vpi = vpi;
+ mb->un.varRdSparm.vpi = vpi + phba->vpi_base;
/* save address for completion */
pmb->context1 = mp;
@@ -523,7 +564,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
}
/**
- * lpfc_unreg_did: Prepare a mailbox command for unregistering DID.
+ * lpfc_unreg_did - Prepare a mailbox command for unregistering DID
* @phba: pointer to lpfc hba data structure.
* @vpi: virtual N_Port identifier.
* @did: remote port identifier.
@@ -543,10 +584,12 @@ lpfc_unreg_did(struct lpfc_hba * phba, uint16_t vpi, uint32_t did,
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->un.varUnregDID.did = did;
+ if (vpi != 0xffff)
+ vpi += phba->vpi_base;
mb->un.varUnregDID.vpi = vpi;
mb->mbxCommand = MBX_UNREG_D_ID;
@@ -555,7 +598,7 @@ lpfc_unreg_did(struct lpfc_hba * phba, uint16_t vpi, uint32_t did,
}
/**
- * lpfc_read_config: Prepare a mailbox command for reading HBA configuration.
+ * lpfc_read_config - Prepare a mailbox command for reading HBA configuration
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -572,7 +615,7 @@ lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxCommand = MBX_READ_CONFIG;
@@ -581,7 +624,7 @@ lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_read_lnk_stat: Prepare a mailbox command for reading HBA link stats.
+ * lpfc_read_lnk_stat - Prepare a mailbox command for reading HBA link stats
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -597,7 +640,7 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->mbxCommand = MBX_READ_LNK_STAT;
@@ -606,7 +649,7 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_reg_login: Prepare a mailbox command for registering remote login.
+ * lpfc_reg_rpi - Prepare a mailbox command for registering remote login
* @phba: pointer to lpfc hba data structure.
* @vpi: virtual N_Port identifier.
* @did: remote port identifier.
@@ -630,17 +673,23 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
* 1 - DMA memory allocation failed
**/
int
-lpfc_reg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
+lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
uint8_t *param, LPFC_MBOXQ_t *pmb, uint32_t flag)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
uint8_t *sparam;
struct lpfc_dmabuf *mp;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->un.varRegLogin.rpi = 0;
- mb->un.varRegLogin.vpi = vpi;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ mb->un.varRegLogin.rpi = lpfc_sli4_alloc_rpi(phba);
+ if (mb->un.varRegLogin.rpi == LPFC_RPI_ALLOC_ERROR)
+ return 1;
+ }
+
+ mb->un.varRegLogin.vpi = vpi + phba->vpi_base;
mb->un.varRegLogin.did = did;
mb->un.varWords[30] = flag; /* Set flag to issue action on cmpl */
@@ -677,7 +726,7 @@ lpfc_reg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
}
/**
- * lpfc_unreg_login: Prepare a mailbox command for unregistering remote login.
+ * lpfc_unreg_login - Prepare a mailbox command for unregistering remote login
* @phba: pointer to lpfc hba data structure.
* @vpi: virtual N_Port identifier.
* @rpi: remote port identifier
@@ -696,20 +745,21 @@ lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi,
{
MAILBOX_t *mb;
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->un.varUnregLogin.rpi = (uint16_t) rpi;
mb->un.varUnregLogin.rsvd1 = 0;
- mb->un.varUnregLogin.vpi = vpi;
+ mb->un.varUnregLogin.vpi = vpi + phba->vpi_base;
mb->mbxCommand = MBX_UNREG_LOGIN;
mb->mbxOwner = OWN_HOST;
+
return;
}
/**
- * lpfc_reg_vpi: Prepare a mailbox command for registering vport identifier.
+ * lpfc_reg_vpi - Prepare a mailbox command for registering vport identifier
* @phba: pointer to lpfc hba data structure.
* @vpi: virtual N_Port identifier.
* @sid: Fibre Channel S_ID (N_Port_ID assigned to a virtual N_Port).
@@ -724,15 +774,15 @@ lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi,
* This routine prepares the mailbox command for registering a virtual N_Port.
**/
void
-lpfc_reg_vpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t sid,
- LPFC_MBOXQ_t *pmb)
+lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- mb->un.varRegVpi.vpi = vpi;
- mb->un.varRegVpi.sid = sid;
+ mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base;
+ mb->un.varRegVpi.sid = vport->fc_myDID;
+ mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base;
mb->mbxCommand = MBX_REG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -741,7 +791,7 @@ lpfc_reg_vpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t sid,
}
/**
- * lpfc_unreg_vpi: Prepare a mailbox command for unregistering vport id.
+ * lpfc_unreg_vpi - Prepare a mailbox command for unregistering vport id
* @phba: pointer to lpfc hba data structure.
* @vpi: virtual N_Port identifier.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -759,10 +809,10 @@ lpfc_reg_vpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t sid,
void
lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- mb->un.varUnregVpi.vpi = vpi;
+ mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
mb->mbxCommand = MBX_UNREG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -771,7 +821,7 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
}
/**
- * lpfc_config_pcb_setup: Set up IOCB rings in the Port Control Block (PCB)
+ * lpfc_config_pcb_setup - Set up IOCB rings in the Port Control Block (PCB)
* @phba: pointer to lpfc hba data structure.
*
* This routine sets up and initializes the IOCB rings in the Port Control
@@ -835,7 +885,7 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba)
}
/**
- * lpfc_read_rev: Prepare a mailbox command for reading HBA revision.
+ * lpfc_read_rev - Prepare a mailbox command for reading HBA revision
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -851,7 +901,7 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba)
void
lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
mb->un.varRdRev.cv = 1;
mb->un.varRdRev.v3req = 1; /* Request SLI3 info */
@@ -861,7 +911,7 @@ lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_build_hbq_profile2: Set up the HBQ Selection Profile 2.
+ * lpfc_build_hbq_profile2 - Set up the HBQ Selection Profile 2
* @hbqmb: pointer to the HBQ configuration data structure in mailbox command.
* @hbq_desc: pointer to the HBQ selection profile descriptor.
*
@@ -880,7 +930,7 @@ lpfc_build_hbq_profile2(struct config_hbq_var *hbqmb,
}
/**
- * lpfc_build_hbq_profile3: Set up the HBQ Selection Profile 3.
+ * lpfc_build_hbq_profile3 - Set up the HBQ Selection Profile 3
* @hbqmb: pointer to the HBQ configuration data structure in mailbox command.
* @hbq_desc: pointer to the HBQ selection profile descriptor.
*
@@ -902,7 +952,7 @@ lpfc_build_hbq_profile3(struct config_hbq_var *hbqmb,
}
/**
- * lpfc_build_hbq_profile5: Set up the HBQ Selection Profile 5.
+ * lpfc_build_hbq_profile5 - Set up the HBQ Selection Profile 5
* @hbqmb: pointer to the HBQ configuration data structure in mailbox command.
* @hbq_desc: pointer to the HBQ selection profile descriptor.
*
@@ -925,7 +975,7 @@ lpfc_build_hbq_profile5(struct config_hbq_var *hbqmb,
}
/**
- * lpfc_config_hbq: Prepare a mailbox command for configuring an HBQ.
+ * lpfc_config_hbq - Prepare a mailbox command for configuring an HBQ
* @phba: pointer to lpfc hba data structure.
* @id: HBQ identifier.
* @hbq_desc: pointer to the HBA descriptor data structure.
@@ -944,7 +994,7 @@ lpfc_config_hbq(struct lpfc_hba *phba, uint32_t id,
uint32_t hbq_entry_index, LPFC_MBOXQ_t *pmb)
{
int i;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct config_hbq_var *hbqmb = &mb->un.varCfgHbq;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
@@ -999,7 +1049,7 @@ lpfc_config_hbq(struct lpfc_hba *phba, uint32_t id,
}
/**
- * lpfc_config_ring: Prepare a mailbox command for configuring an IOCB ring.
+ * lpfc_config_ring - Prepare a mailbox command for configuring an IOCB ring
* @phba: pointer to lpfc hba data structure.
* @ring:
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -1019,7 +1069,7 @@ void
lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
{
int i;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
struct lpfc_sli *psli;
struct lpfc_sli_ring *pring;
@@ -1057,7 +1107,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_config_port: Prepare a mailbox command for configuring port.
+ * lpfc_config_port - Prepare a mailbox command for configuring port
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -1074,7 +1124,7 @@ void
lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t __iomem *mb_slim = (MAILBOX_t __iomem *) phba->MBslimaddr;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
dma_addr_t pdma_addr;
uint32_t bar_low, bar_high;
size_t offset;
@@ -1098,21 +1148,22 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
/* If HBA supports SLI=3 ask for it */
- if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+ if (phba->sli_rev == LPFC_SLI_REV3 && phba->vpd.sli3Feat.cerbm) {
if (phba->cfg_enable_bg)
mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
+ mb->un.varCfgPort.cdss = 1; /* Configure Security */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.cinb = 1; /* Interrupt Notification Block */
mb->un.varCfgPort.max_hbq = lpfc_sli_hbq_count();
if (phba->max_vpi && phba->cfg_enable_npiv &&
phba->vpd.sli3Feat.cmv) {
- mb->un.varCfgPort.max_vpi = phba->max_vpi;
+ mb->un.varCfgPort.max_vpi = LPFC_MAX_VPI;
mb->un.varCfgPort.cmv = 1;
} else
mb->un.varCfgPort.max_vpi = phba->max_vpi = 0;
} else
- phba->sli_rev = 2;
+ phba->sli_rev = LPFC_SLI_REV2;
mb->un.varCfgPort.sli_mode = phba->sli_rev;
/* Now setup pcb */
@@ -1227,7 +1278,7 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
/**
- * lpfc_kill_board: Prepare a mailbox command for killing board.
+ * lpfc_kill_board - Prepare a mailbox command for killing board
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
@@ -1244,7 +1295,7 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
void
lpfc_kill_board(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
{
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
mb->mbxCommand = MBX_KILL_BOARD;
@@ -1253,7 +1304,7 @@ lpfc_kill_board(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
}
/**
- * lpfc_mbox_put: Put a mailbox cmd into the tail of driver's mailbox queue.
+ * lpfc_mbox_put - Put a mailbox cmd into the tail of driver's mailbox queue
* @phba: pointer to lpfc hba data structure.
* @mbq: pointer to the driver internal queue element for mailbox command.
*
@@ -1277,7 +1328,7 @@ lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
}
/**
- * lpfc_mbox_get: Remove a mailbox cmd from the head of driver's mailbox queue.
+ * lpfc_mbox_get - Remove a mailbox cmd from the head of driver's mailbox queue
* @phba: pointer to lpfc hba data structure.
*
* Driver maintains a internal mailbox command queue implemented as a linked
@@ -1304,30 +1355,99 @@ lpfc_mbox_get(struct lpfc_hba * phba)
}
/**
- * lpfc_mbox_cmpl_put: Put mailbox command into mailbox command complete list.
+ * __lpfc_mbox_cmpl_put - Put mailbox cmd into mailbox cmd complete list
* @phba: pointer to lpfc hba data structure.
* @mbq: pointer to the driver internal queue element for mailbox command.
*
* This routine put the completed mailbox command into the mailbox command
- * complete list. This routine is called from driver interrupt handler
- * context.The mailbox complete list is used by the driver worker thread
- * to process mailbox complete callback functions outside the driver interrupt
- * handler.
+ * complete list. This is the unlocked version of the routine. The mailbox
+ * complete list is used by the driver worker thread to process mailbox
+ * complete callback functions outside the driver interrupt handler.
**/
void
-lpfc_mbox_cmpl_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
+__lpfc_mbox_cmpl_put(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbq)
+{
+ list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
+}
+
+/**
+ * lpfc_mbox_cmpl_put - Put mailbox command into mailbox command complete list
+ * @phba: pointer to lpfc hba data structure.
+ * @mbq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This routine put the completed mailbox command into the mailbox command
+ * complete list. This is the locked version of the routine. The mailbox
+ * complete list is used by the driver worker thread to process mailbox
+ * complete callback functions outside the driver interrupt handler.
+ **/
+void
+lpfc_mbox_cmpl_put(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbq)
{
unsigned long iflag;
/* This function expects to be called from interrupt context */
spin_lock_irqsave(&phba->hbalock, iflag);
- list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
+ __lpfc_mbox_cmpl_put(phba, mbq);
spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
/**
- * lpfc_mbox_tmo_val: Retrieve mailbox command timeout value.
+ * lpfc_mbox_cmd_check - Check the validality of a mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This routine is to check whether a mailbox command is valid to be issued.
+ * This check will be performed by both the mailbox issue API when a client
+ * is to issue a mailbox command to the mailbox transport.
+ *
+ * Return 0 - pass the check, -ENODEV - fail the check
+ **/
+int
+lpfc_mbox_cmd_check(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ /* Mailbox command that have a completion handler must also have a
+ * vport specified.
+ */
+ if (mboxq->mbox_cmpl && mboxq->mbox_cmpl != lpfc_sli_def_mbox_cmpl &&
+ mboxq->mbox_cmpl != lpfc_sli_wake_mbox_wait) {
+ if (!mboxq->vport) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_VPORT,
+ "1814 Mbox x%x failed, no vport\n",
+ mboxq->u.mb.mbxCommand);
+ dump_stack();
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+/**
+ * lpfc_mbox_dev_check - Check the device state for issuing a mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to check whether the HBA device is ready for posting a
+ * mailbox command. It is used by the mailbox transport API at the time the
+ * to post a mailbox command to the device.
+ *
+ * Return 0 - pass the check, -ENODEV - fail the check
+ **/
+int
+lpfc_mbox_dev_check(struct lpfc_hba *phba)
+{
+ /* If the PCI channel is in offline state, do not issue mbox */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return -ENODEV;
+
+ /* If the HBA is in error state, do not issue mbox */
+ if (phba->link_state == LPFC_HBA_ERROR)
+ return -ENODEV;
+
+ return 0;
+}
+
+/**
+ * lpfc_mbox_tmo_val - Retrieve mailbox command timeout value
* @phba: pointer to lpfc hba data structure.
* @cmd: mailbox command code.
*
@@ -1349,6 +1469,475 @@ lpfc_mbox_tmo_val(struct lpfc_hba *phba, int cmd)
case MBX_WRITE_WWN: /* 0x98 */
case MBX_LOAD_EXP_ROM: /* 0x9C */
return LPFC_MBOX_TMO_FLASH_CMD;
+ case MBX_SLI4_CONFIG: /* 0x9b */
+ return LPFC_MBOX_SLI4_CONFIG_TMO;
}
return LPFC_MBOX_TMO;
}
+
+/**
+ * lpfc_sli4_mbx_sge_set - Set a sge entry in non-embedded mailbox command
+ * @mbox: pointer to lpfc mbox command.
+ * @sgentry: sge entry index.
+ * @phyaddr: physical address for the sge
+ * @length: Length of the sge.
+ *
+ * This routine sets up an entry in the non-embedded mailbox command at the sge
+ * index location.
+ **/
+void
+lpfc_sli4_mbx_sge_set(struct lpfcMboxq *mbox, uint32_t sgentry,
+ dma_addr_t phyaddr, uint32_t length)
+{
+ struct lpfc_mbx_nembed_cmd *nembed_sge;
+
+ nembed_sge = (struct lpfc_mbx_nembed_cmd *)
+ &mbox->u.mqe.un.nembed_cmd;
+ nembed_sge->sge[sgentry].pa_lo = putPaddrLow(phyaddr);
+ nembed_sge->sge[sgentry].pa_hi = putPaddrHigh(phyaddr);
+ nembed_sge->sge[sgentry].length = length;
+}
+
+/**
+ * lpfc_sli4_mbx_sge_get - Get a sge entry from non-embedded mailbox command
+ * @mbox: pointer to lpfc mbox command.
+ * @sgentry: sge entry index.
+ *
+ * This routine gets an entry from the non-embedded mailbox command at the sge
+ * index location.
+ **/
+void
+lpfc_sli4_mbx_sge_get(struct lpfcMboxq *mbox, uint32_t sgentry,
+ struct lpfc_mbx_sge *sge)
+{
+ struct lpfc_mbx_nembed_cmd *nembed_sge;
+
+ nembed_sge = (struct lpfc_mbx_nembed_cmd *)
+ &mbox->u.mqe.un.nembed_cmd;
+ sge->pa_lo = nembed_sge->sge[sgentry].pa_lo;
+ sge->pa_hi = nembed_sge->sge[sgentry].pa_hi;
+ sge->length = nembed_sge->sge[sgentry].length;
+}
+
+/**
+ * lpfc_sli4_mbox_cmd_free - Free a sli4 mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ *
+ * This routine frees SLI4 specific mailbox command for sending IOCTL command.
+ **/
+void
+lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+ struct lpfc_mbx_sli4_config *sli4_cfg;
+ struct lpfc_mbx_sge sge;
+ dma_addr_t phyaddr;
+ uint32_t sgecount, sgentry;
+
+ sli4_cfg = &mbox->u.mqe.un.sli4_config;
+
+ /* For embedded mbox command, just free the mbox command */
+ if (bf_get(lpfc_mbox_hdr_emb, &sli4_cfg->header.cfg_mhdr)) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return;
+ }
+
+ /* For non-embedded mbox command, we need to free the pages first */
+ sgecount = bf_get(lpfc_mbox_hdr_sge_cnt, &sli4_cfg->header.cfg_mhdr);
+ /* There is nothing we can do if there is no sge address array */
+ if (unlikely(!mbox->sge_array)) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return;
+ }
+ /* Each non-embedded DMA memory was allocated in the length of a page */
+ for (sgentry = 0; sgentry < sgecount; sgentry++) {
+ lpfc_sli4_mbx_sge_get(mbox, sgentry, &sge);
+ phyaddr = getPaddr(sge.pa_hi, sge.pa_lo);
+ dma_free_coherent(&phba->pcidev->dev, PAGE_SIZE,
+ mbox->sge_array->addr[sgentry], phyaddr);
+ }
+ /* Free the sge address array memory */
+ kfree(mbox->sge_array);
+ /* Finally, free the mailbox command itself */
+ mempool_free(mbox, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_config - Initialize the SLI4 Config Mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ * @subsystem: The sli4 config sub mailbox subsystem.
+ * @opcode: The sli4 config sub mailbox command opcode.
+ * @length: Length of the sli4 config mailbox command.
+ *
+ * This routine sets up the header fields of SLI4 specific mailbox command
+ * for sending IOCTL command.
+ *
+ * Return: the actual length of the mbox command allocated (mostly useful
+ * for none embedded mailbox command).
+ **/
+int
+lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
+ uint8_t subsystem, uint8_t opcode, uint32_t length, bool emb)
+{
+ struct lpfc_mbx_sli4_config *sli4_config;
+ union lpfc_sli4_cfg_shdr *cfg_shdr = NULL;
+ uint32_t alloc_len;
+ uint32_t resid_len;
+ uint32_t pagen, pcount;
+ void *viraddr;
+ dma_addr_t phyaddr;
+
+ /* Set up SLI4 mailbox command header fields */
+ memset(mbox, 0, sizeof(*mbox));
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_SLI4_CONFIG);
+
+ /* Set up SLI4 ioctl command header fields */
+ sli4_config = &mbox->u.mqe.un.sli4_config;
+
+ /* Setup for the embedded mbox command */
+ if (emb) {
+ /* Set up main header fields */
+ bf_set(lpfc_mbox_hdr_emb, &sli4_config->header.cfg_mhdr, 1);
+ sli4_config->header.cfg_mhdr.payload_length =
+ LPFC_MBX_CMD_HDR_LENGTH + length;
+ /* Set up sub-header fields following main header */
+ bf_set(lpfc_mbox_hdr_opcode,
+ &sli4_config->header.cfg_shdr.request, opcode);
+ bf_set(lpfc_mbox_hdr_subsystem,
+ &sli4_config->header.cfg_shdr.request, subsystem);
+ sli4_config->header.cfg_shdr.request.request_length = length;
+ return length;
+ }
+
+ /* Setup for the none-embedded mbox command */
+ pcount = (PAGE_ALIGN(length))/PAGE_SIZE;
+ pcount = (pcount > LPFC_SLI4_MBX_SGE_MAX_PAGES) ?
+ LPFC_SLI4_MBX_SGE_MAX_PAGES : pcount;
+ /* Allocate record for keeping SGE virtual addresses */
+ mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt),
+ GFP_KERNEL);
+ if (!mbox->sge_array)
+ return 0;
+
+ for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) {
+ /* The DMA memory is always allocated in the length of a
+ * page even though the last SGE might not fill up to a
+ * page, this is used as a priori size of PAGE_SIZE for
+ * the later DMA memory free.
+ */
+ viraddr = dma_alloc_coherent(&phba->pcidev->dev, PAGE_SIZE,
+ &phyaddr, GFP_KERNEL);
+ /* In case of malloc fails, proceed with whatever we have */
+ if (!viraddr)
+ break;
+ mbox->sge_array->addr[pagen] = viraddr;
+ /* Keep the first page for later sub-header construction */
+ if (pagen == 0)
+ cfg_shdr = (union lpfc_sli4_cfg_shdr *)viraddr;
+ resid_len = length - alloc_len;
+ if (resid_len > PAGE_SIZE) {
+ lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr,
+ PAGE_SIZE);
+ alloc_len += PAGE_SIZE;
+ } else {
+ lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr,
+ resid_len);
+ alloc_len = length;
+ }
+ }
+
+ /* Set up main header fields in mailbox command */
+ sli4_config->header.cfg_mhdr.payload_length = alloc_len;
+ bf_set(lpfc_mbox_hdr_sge_cnt, &sli4_config->header.cfg_mhdr, pagen);
+
+ /* Set up sub-header fields into the first page */
+ if (pagen > 0) {
+ bf_set(lpfc_mbox_hdr_opcode, &cfg_shdr->request, opcode);
+ bf_set(lpfc_mbox_hdr_subsystem, &cfg_shdr->request, subsystem);
+ cfg_shdr->request.request_length =
+ alloc_len - sizeof(union lpfc_sli4_cfg_shdr);
+ }
+ /* The sub-header is in DMA memory, which needs endian converstion */
+ lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr,
+ sizeof(union lpfc_sli4_cfg_shdr));
+
+ return alloc_len;
+}
+
+/**
+ * lpfc_sli4_mbox_opcode_get - Get the opcode from a sli4 mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ *
+ * This routine gets the opcode from a SLI4 specific mailbox command for
+ * sending IOCTL command. If the mailbox command is not MBX_SLI4_CONFIG
+ * (0x9B) or if the IOCTL sub-header is not present, opcode 0x0 shall be
+ * returned.
+ **/
+uint8_t
+lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+ struct lpfc_mbx_sli4_config *sli4_cfg;
+ union lpfc_sli4_cfg_shdr *cfg_shdr;
+
+ if (mbox->u.mb.mbxCommand != MBX_SLI4_CONFIG)
+ return 0;
+ sli4_cfg = &mbox->u.mqe.un.sli4_config;
+
+ /* For embedded mbox command, get opcode from embedded sub-header*/
+ if (bf_get(lpfc_mbox_hdr_emb, &sli4_cfg->header.cfg_mhdr)) {
+ cfg_shdr = &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+ return bf_get(lpfc_mbox_hdr_opcode, &cfg_shdr->request);
+ }
+
+ /* For non-embedded mbox command, get opcode from first dma page */
+ if (unlikely(!mbox->sge_array))
+ return 0;
+ cfg_shdr = (union lpfc_sli4_cfg_shdr *)mbox->sge_array->addr[0];
+ return bf_get(lpfc_mbox_hdr_opcode, &cfg_shdr->request);
+}
+
+/**
+ * lpfc_request_features: Configure SLI4 REQUEST_FEATURES mailbox
+ * @mboxq: pointer to lpfc mbox command.
+ *
+ * This routine sets up the mailbox for an SLI4 REQUEST_FEATURES
+ * mailbox command.
+ **/
+void
+lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
+{
+ /* Set up SLI4 mailbox command header fields */
+ memset(mboxq, 0, sizeof(LPFC_MBOXQ_t));
+ bf_set(lpfc_mqe_command, &mboxq->u.mqe, MBX_SLI4_REQ_FTRS);
+
+ /* Set up host requested features. */
+ bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
+
+ /* Virtual fabrics and FIPs are not supported yet. */
+ bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0);
+
+ /* Enable DIF (block guard) only if configured to do so. */
+ if (phba->cfg_enable_bg)
+ bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1);
+
+ /* Enable NPIV only if configured to do so. */
+ if (phba->max_vpi && phba->cfg_enable_npiv)
+ bf_set(lpfc_mbx_rq_ftr_rq_npiv, &mboxq->u.mqe.un.req_ftrs, 1);
+
+ return;
+}
+
+/**
+ * lpfc_init_vfi - Initialize the INIT_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vport: Vport associated with the VF.
+ *
+ * This routine initializes @mbox to all zeros and then fills in the mailbox
+ * fields from @vport. INIT_VFI configures virtual fabrics identified by VFI
+ * in the context of an FCF. The driver issues this command to setup a VFI
+ * before issuing a FLOGI to login to the VSAN. The driver should also issue a
+ * REG_VFI after a successful VSAN login.
+ **/
+void
+lpfc_init_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
+{
+ struct lpfc_mbx_init_vfi *init_vfi;
+
+ memset(mbox, 0, sizeof(*mbox));
+ init_vfi = &mbox->u.mqe.un.init_vfi;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VFI);
+ bf_set(lpfc_init_vfi_vr, init_vfi, 1);
+ bf_set(lpfc_init_vfi_vt, init_vfi, 1);
+ bf_set(lpfc_init_vfi_vfi, init_vfi, vport->vfi + vport->phba->vfi_base);
+ bf_set(lpfc_init_vfi_fcfi, init_vfi, vport->phba->fcf.fcfi);
+}
+
+/**
+ * lpfc_reg_vfi - Initialize the REG_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vport: vport associated with the VF.
+ * @phys: BDE DMA bus address used to send the service parameters to the HBA.
+ *
+ * This routine initializes @mbox to all zeros and then fills in the mailbox
+ * fields from @vport, and uses @buf as a DMAable buffer to send the vport's
+ * fc service parameters to the HBA for this VFI. REG_VFI configures virtual
+ * fabrics identified by VFI in the context of an FCF.
+ **/
+void
+lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
+{
+ struct lpfc_mbx_reg_vfi *reg_vfi;
+
+ memset(mbox, 0, sizeof(*mbox));
+ reg_vfi = &mbox->u.mqe.un.reg_vfi;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_VFI);
+ bf_set(lpfc_reg_vfi_vp, reg_vfi, 1);
+ bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base);
+ bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi);
+ bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base);
+ reg_vfi->bde.addrHigh = putPaddrHigh(phys);
+ reg_vfi->bde.addrLow = putPaddrLow(phys);
+ reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
+ reg_vfi->bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bf_set(lpfc_reg_vfi_nport_id, reg_vfi, vport->fc_myDID);
+}
+
+/**
+ * lpfc_init_vpi - Initialize the INIT_VPI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vpi: VPI to be initialized.
+ *
+ * The INIT_VPI mailbox command supports virtual N_Ports. The driver uses the
+ * command to activate a virtual N_Port. The HBA assigns a MAC address to use
+ * with the virtual N Port. The SLI Host issues this command before issuing a
+ * FDISC to connect to the Fabric. The SLI Host should issue a REG_VPI after a
+ * successful virtual NPort login.
+ **/
+void
+lpfc_init_vpi(struct lpfcMboxq *mbox, uint16_t vpi)
+{
+ memset(mbox, 0, sizeof(*mbox));
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VPI);
+ bf_set(lpfc_init_vpi_vpi, &mbox->u.mqe.un.init_vpi, vpi);
+}
+
+/**
+ * lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vfi: VFI to be unregistered.
+ *
+ * The UNREG_VFI mailbox command causes the SLI Host to put a virtual fabric
+ * (logical NPort) into the inactive state. The SLI Host must have logged out
+ * and unregistered all remote N_Ports to abort any activity on the virtual
+ * fabric. The SLI Port posts the mailbox response after marking the virtual
+ * fabric inactive.
+ **/
+void
+lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi)
+{
+ memset(mbox, 0, sizeof(*mbox));
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI);
+ bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, vfi);
+}
+
+/**
+ * lpfc_dump_fcoe_param - Dump config region 23 to get FCoe parameters.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump FCoE
+ * parameters stored in region 23.
+ **/
+int
+lpfc_dump_fcoe_param(struct lpfc_hba *phba,
+ struct lpfcMboxq *mbox)
+{
+ struct lpfc_dmabuf *mp = NULL;
+ MAILBOX_t *mb;
+
+ memset(mbox, 0, sizeof(*mbox));
+ mb = &mbox->u.mb;
+
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (mp)
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+
+ if (!mp || !mp->virt) {
+ kfree(mp);
+ /* dump_fcoe_param failed to allocate memory */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+ "2569 lpfc_dump_fcoe_param: memory"
+ " allocation failed \n");
+ return 1;
+ }
+
+ memset(mp->virt, 0, LPFC_BPL_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+
+ /* save address for completion */
+ mbox->context1 = (uint8_t *) mp;
+
+ mb->mbxCommand = MBX_DUMP_MEMORY;
+ mb->un.varDmp.type = DMP_NV_PARAMS;
+ mb->un.varDmp.region_id = DMP_REGION_FCOEPARAM;
+ mb->un.varDmp.sli4_length = DMP_FCOEPARAM_RGN_SIZE;
+ mb->un.varWords[3] = putPaddrLow(mp->phys);
+ mb->un.varWords[4] = putPaddrHigh(mp->phys);
+ return 0;
+}
+
+/**
+ * lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
+ * @phba: pointer to the hba structure containing the FCF index and RQ ID.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * The REG_FCFI mailbox command supports Fibre Channel Forwarders (FCFs). The
+ * SLI Host uses the command to activate an FCF after it has acquired FCF
+ * information via a READ_FCF mailbox command. This mailbox command also is used
+ * to indicate where received unsolicited frames from this FCF will be sent. By
+ * default this routine will set up the FCF to forward all unsolicited frames
+ * the the RQ ID passed in the @phba. This can be overridden by the caller for
+ * more complicated setups.
+ **/
+void
+lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+ struct lpfc_mbx_reg_fcfi *reg_fcfi;
+
+ memset(mbox, 0, sizeof(*mbox));
+ reg_fcfi = &mbox->u.mqe.un.reg_fcfi;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI);
+ bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, phba->sli4_hba.hdr_rq->queue_id);
+ bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID);
+ bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID);
+ bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID);
+ bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, phba->fcf.fcf_indx);
+ /* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */
+ bf_set(lpfc_reg_fcfi_mam, reg_fcfi,
+ (~phba->fcf.addr_mode) & 0x3);
+ if (phba->fcf.fcf_flag & FCF_VALID_VLAN) {
+ bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1);
+ bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, phba->fcf.vlan_id);
+ }
+}
+
+/**
+ * lpfc_unreg_fcfi - Initialize the UNREG_FCFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @fcfi: FCFI to be unregistered.
+ *
+ * The UNREG_FCFI mailbox command supports Fibre Channel Forwarders (FCFs).
+ * The SLI Host uses the command to inactivate an FCFI.
+ **/
+void
+lpfc_unreg_fcfi(struct lpfcMboxq *mbox, uint16_t fcfi)
+{
+ memset(mbox, 0, sizeof(*mbox));
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_FCFI);
+ bf_set(lpfc_unreg_fcfi, &mbox->u.mqe.un.unreg_fcfi, fcfi);
+}
+
+/**
+ * lpfc_resume_rpi - Initialize the RESUME_RPI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @ndlp: The nodelist structure that describes the RPI to resume.
+ *
+ * The RESUME_RPI mailbox command is used to restart I/O to an RPI after a
+ * link event.
+ **/
+void
+lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_mbx_resume_rpi *resume_rpi;
+
+ memset(mbox, 0, sizeof(*mbox));
+ resume_rpi = &mbox->u.mqe.un.resume_rpi;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_RESUME_RPI);
+ bf_set(lpfc_resume_rpi_rpi, resume_rpi, ndlp->nlp_rpi);
+ bf_set(lpfc_resume_rpi_vpi, resume_rpi,
+ ndlp->vport->vpi + ndlp->vport->phba->vpi_base);
+ bf_set(lpfc_resume_rpi_vfi, resume_rpi,
+ ndlp->vport->vfi + ndlp->vport->phba->vfi_base);
+}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index a4bba2069248..e198c917c13e 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -28,8 +28,10 @@
#include <scsi/scsi.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -41,11 +43,11 @@
/**
- * lpfc_mem_alloc: create and allocate all PCI and memory pools
+ * lpfc_mem_alloc - create and allocate all PCI and memory pools
* @phba: HBA to allocate pools for
*
* Description: Creates and allocates PCI pools lpfc_scsi_dma_buf_pool,
- * lpfc_mbuf_pool, lpfc_hbq_pool. Creates and allocates kmalloc-backed mempools
+ * lpfc_mbuf_pool, lpfc_hrb_pool. Creates and allocates kmalloc-backed mempools
* for LPFC_MBOXQ_t and lpfc_nodelist. Also allocates the VPI bitmask.
*
* Notes: Not interrupt-safe. Must be called with no locks held. If any
@@ -56,19 +58,30 @@
* -ENOMEM on failure (if any memory allocations fail)
**/
int
-lpfc_mem_alloc(struct lpfc_hba * phba)
+lpfc_mem_alloc(struct lpfc_hba *phba, int align)
{
struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
int longs;
int i;
- phba->lpfc_scsi_dma_buf_pool = pci_pool_create("lpfc_scsi_dma_buf_pool",
- phba->pcidev, phba->cfg_sg_dma_buf_size, 8, 0);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ phba->lpfc_scsi_dma_buf_pool =
+ pci_pool_create("lpfc_scsi_dma_buf_pool",
+ phba->pcidev,
+ phba->cfg_sg_dma_buf_size,
+ phba->cfg_sg_dma_buf_size,
+ 0);
+ else
+ phba->lpfc_scsi_dma_buf_pool =
+ pci_pool_create("lpfc_scsi_dma_buf_pool",
+ phba->pcidev, phba->cfg_sg_dma_buf_size,
+ align, 0);
if (!phba->lpfc_scsi_dma_buf_pool)
goto fail;
phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev,
- LPFC_BPL_SIZE, 8,0);
+ LPFC_BPL_SIZE,
+ align, 0);
if (!phba->lpfc_mbuf_pool)
goto fail_free_dma_buf_pool;
@@ -97,23 +110,31 @@ lpfc_mem_alloc(struct lpfc_hba * phba)
sizeof(struct lpfc_nodelist));
if (!phba->nlp_mem_pool)
goto fail_free_mbox_pool;
-
- phba->lpfc_hbq_pool = pci_pool_create("lpfc_hbq_pool",phba->pcidev,
- LPFC_BPL_SIZE, 8, 0);
- if (!phba->lpfc_hbq_pool)
+ phba->lpfc_hrb_pool = pci_pool_create("lpfc_hrb_pool",
+ phba->pcidev,
+ LPFC_HDR_BUF_SIZE, align, 0);
+ if (!phba->lpfc_hrb_pool)
goto fail_free_nlp_mem_pool;
+ phba->lpfc_drb_pool = pci_pool_create("lpfc_drb_pool",
+ phba->pcidev,
+ LPFC_DATA_BUF_SIZE, align, 0);
+ if (!phba->lpfc_drb_pool)
+ goto fail_free_hbq_pool;
/* vpi zero is reserved for the physical port so add 1 to max */
longs = ((phba->max_vpi + 1) + BITS_PER_LONG - 1) / BITS_PER_LONG;
phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), GFP_KERNEL);
if (!phba->vpi_bmask)
- goto fail_free_hbq_pool;
+ goto fail_free_dbq_pool;
return 0;
+ fail_free_dbq_pool:
+ pci_pool_destroy(phba->lpfc_drb_pool);
+ phba->lpfc_drb_pool = NULL;
fail_free_hbq_pool:
- lpfc_sli_hbqbuf_free_all(phba);
- pci_pool_destroy(phba->lpfc_hbq_pool);
+ pci_pool_destroy(phba->lpfc_hrb_pool);
+ phba->lpfc_hrb_pool = NULL;
fail_free_nlp_mem_pool:
mempool_destroy(phba->nlp_mem_pool);
phba->nlp_mem_pool = NULL;
@@ -136,27 +157,73 @@ lpfc_mem_alloc(struct lpfc_hba * phba)
}
/**
- * lpfc_mem_free: Frees all PCI and memory allocated by lpfc_mem_alloc
+ * lpfc_mem_free - Frees memory allocated by lpfc_mem_alloc
* @phba: HBA to free memory for
*
- * Description: Frees PCI pools lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool,
- * lpfc_hbq_pool. Frees kmalloc-backed mempools for LPFC_MBOXQ_t and
- * lpfc_nodelist. Also frees the VPI bitmask.
+ * Description: Free the memory allocated by lpfc_mem_alloc routine. This
+ * routine is a the counterpart of lpfc_mem_alloc.
*
* Returns: None
**/
void
-lpfc_mem_free(struct lpfc_hba * phba)
+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;
- struct lpfc_dmabuf *mp;
int i;
+ struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+ /* Free VPI bitmask memory */
kfree(phba->vpi_bmask);
+
+ /* Free HBQ pools */
lpfc_sli_hbqbuf_free_all(phba);
+ pci_pool_destroy(phba->lpfc_drb_pool);
+ phba->lpfc_drb_pool = NULL;
+ pci_pool_destroy(phba->lpfc_hrb_pool);
+ phba->lpfc_hrb_pool = NULL;
+
+ /* Free NLP memory pool */
+ mempool_destroy(phba->nlp_mem_pool);
+ phba->nlp_mem_pool = NULL;
+
+ /* Free mbox memory pool */
+ mempool_destroy(phba->mbox_mem_pool);
+ phba->mbox_mem_pool = NULL;
+
+ /* Free MBUF memory pool */
+ 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);
+
+ pci_pool_destroy(phba->lpfc_mbuf_pool);
+ phba->lpfc_mbuf_pool = NULL;
+ /* Free DMA buffer memory pool */
+ pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
+ phba->lpfc_scsi_dma_buf_pool = NULL;
+
+ return;
+}
+
+/**
+ * lpfc_mem_free_all - Frees all PCI and driver memory
+ * @phba: HBA to free memory for
+ *
+ * Description: Free memory from PCI and driver memory pools and also those
+ * used : lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees
+ * kmalloc-backed mempools for LPFC_MBOXQ_t and lpfc_nodelist. Also frees
+ * the VPI bitmask.
+ *
+ * Returns: None
+ **/
+void
+lpfc_mem_free_all(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ LPFC_MBOXQ_t *mbox, *next_mbox;
+ struct lpfc_dmabuf *mp;
+
+ /* Free memory used in mailbox queue back to mailbox memory pool */
list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) {
mp = (struct lpfc_dmabuf *) (mbox->context1);
if (mp) {
@@ -166,6 +233,7 @@ lpfc_mem_free(struct lpfc_hba * phba)
list_del(&mbox->list);
mempool_free(mbox, phba->mbox_mem_pool);
}
+ /* Free memory used in mailbox cmpl list back to mailbox memory pool */
list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq_cmpl, list) {
mp = (struct lpfc_dmabuf *) (mbox->context1);
if (mp) {
@@ -175,8 +243,10 @@ lpfc_mem_free(struct lpfc_hba * phba)
list_del(&mbox->list);
mempool_free(mbox, phba->mbox_mem_pool);
}
-
+ /* Free the active mailbox command back to the mailbox memory pool */
+ spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
if (psli->mbox_active) {
mbox = psli->mbox_active;
mp = (struct lpfc_dmabuf *) (mbox->context1);
@@ -188,31 +258,18 @@ lpfc_mem_free(struct lpfc_hba * phba)
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);
-
- pci_pool_destroy(phba->lpfc_hbq_pool);
- mempool_destroy(phba->nlp_mem_pool);
- mempool_destroy(phba->mbox_mem_pool);
-
- pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
- pci_pool_destroy(phba->lpfc_mbuf_pool);
-
- phba->lpfc_hbq_pool = NULL;
- phba->nlp_mem_pool = NULL;
- phba->mbox_mem_pool = NULL;
- phba->lpfc_scsi_dma_buf_pool = NULL;
- phba->lpfc_mbuf_pool = NULL;
+ /* Free and destroy all the allocated memory pools */
+ lpfc_mem_free(phba);
/* Free the iocb lookup array */
kfree(psli->iocbq_lookup);
psli->iocbq_lookup = NULL;
+
+ return;
}
/**
- * lpfc_mbuf_alloc: Allocate an mbuf from the lpfc_mbuf_pool PCI pool
+ * lpfc_mbuf_alloc - Allocate an mbuf from the lpfc_mbuf_pool PCI pool
* @phba: HBA which owns the pool to allocate from
* @mem_flags: indicates if this is a priority (MEM_PRI) allocation
* @handle: used to return the DMA-mapped address of the mbuf
@@ -249,7 +306,7 @@ lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle)
}
/**
- * __lpfc_mem_free: Free an mbuf from the lpfc_mbuf_pool PCI pool (locked)
+ * __lpfc_mbuf_free - Free an mbuf from the lpfc_mbuf_pool PCI pool (locked)
* @phba: HBA which owns the pool to return to
* @virt: mbuf to free
* @dma: the DMA-mapped address of the lpfc_mbuf_pool to be freed
@@ -278,7 +335,7 @@ __lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
}
/**
- * lpfc_mem_free: Free an mbuf from the lpfc_mbuf_pool PCI pool (unlocked)
+ * lpfc_mbuf_free - Free an mbuf from the lpfc_mbuf_pool PCI pool (unlocked)
* @phba: HBA which owns the pool to return to
* @virt: mbuf to free
* @dma: the DMA-mapped address of the lpfc_mbuf_pool to be freed
@@ -291,7 +348,6 @@ __lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
* Returns: None
**/
void
-
lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
{
unsigned long iflags;
@@ -303,10 +359,10 @@ lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
}
/**
- * lpfc_els_hbq_alloc: Allocate an HBQ buffer
+ * lpfc_els_hbq_alloc - Allocate an HBQ buffer
* @phba: HBA to allocate HBQ buffer for
*
- * Description: Allocates a DMA-mapped HBQ buffer from the lpfc_hbq_pool PCI
+ * Description: Allocates a DMA-mapped HBQ buffer from the lpfc_hrb_pool PCI
* pool along a non-DMA-mapped container for it.
*
* Notes: Not interrupt-safe. Must be called with no locks held.
@@ -324,7 +380,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
if (!hbqbp)
return NULL;
- hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hbq_pool, GFP_KERNEL,
+ hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
&hbqbp->dbuf.phys);
if (!hbqbp->dbuf.virt) {
kfree(hbqbp);
@@ -335,7 +391,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
}
/**
- * lpfc_mem_hbq_free: Frees an HBQ buffer allocated with lpfc_els_hbq_alloc
+ * lpfc_els_hbq_free - Frees an HBQ buffer allocated with lpfc_els_hbq_alloc
* @phba: HBA buffer was allocated for
* @hbqbp: HBQ container returned by lpfc_els_hbq_alloc
*
@@ -349,13 +405,74 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
void
lpfc_els_hbq_free(struct lpfc_hba *phba, struct hbq_dmabuf *hbqbp)
{
- pci_pool_free(phba->lpfc_hbq_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
+ pci_pool_free(phba->lpfc_hrb_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
kfree(hbqbp);
return;
}
/**
- * lpfc_in_buf_free: Free a DMA buffer
+ * lpfc_sli4_rb_alloc - Allocate an SLI4 Receive buffer
+ * @phba: HBA to allocate a receive buffer for
+ *
+ * Description: Allocates a DMA-mapped receive buffer from the lpfc_hrb_pool PCI
+ * pool along a non-DMA-mapped container for it.
+ *
+ * Notes: Not interrupt-safe. Must be called with no locks held.
+ *
+ * Returns:
+ * pointer to HBQ on success
+ * NULL on failure
+ **/
+struct hbq_dmabuf *
+lpfc_sli4_rb_alloc(struct lpfc_hba *phba)
+{
+ struct hbq_dmabuf *dma_buf;
+
+ dma_buf = kmalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
+ if (!dma_buf)
+ return NULL;
+
+ dma_buf->hbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
+ &dma_buf->hbuf.phys);
+ if (!dma_buf->hbuf.virt) {
+ kfree(dma_buf);
+ return NULL;
+ }
+ dma_buf->dbuf.virt = pci_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
+ &dma_buf->dbuf.phys);
+ if (!dma_buf->dbuf.virt) {
+ pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt,
+ dma_buf->hbuf.phys);
+ kfree(dma_buf);
+ return NULL;
+ }
+ dma_buf->size = LPFC_BPL_SIZE;
+ return dma_buf;
+}
+
+/**
+ * lpfc_sli4_rb_free - Frees a receive buffer
+ * @phba: HBA buffer was allocated for
+ * @dmab: DMA Buffer container returned by lpfc_sli4_hbq_alloc
+ *
+ * Description: Frees both the container and the DMA-mapped buffers returned by
+ * lpfc_sli4_rb_alloc.
+ *
+ * Notes: Can be called with or without locks held.
+ *
+ * Returns: None
+ **/
+void
+lpfc_sli4_rb_free(struct lpfc_hba *phba, struct hbq_dmabuf *dmab)
+{
+ pci_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys);
+ pci_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys);
+ kfree(dmab);
+ return;
+}
+
+/**
+ * lpfc_in_buf_free - Free a DMA buffer
* @phba: HBA buffer is associated with
* @mp: Buffer to free
*
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 8f548adae9cc..09f659f77bb3 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -28,8 +28,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -192,7 +194,6 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
struct lpfc_iocbq *iocb, *next_iocb;
- IOCB_t *cmd;
/* Abort outstanding I/O on NPort <nlp_DID> */
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_DISCOVERY,
@@ -223,19 +224,10 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
}
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- iocb = list_get_first(&completions, struct lpfc_iocbq, list);
- cmd = &iocb->iocb;
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
+
lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
return 0;
}
@@ -371,7 +363,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!mbox)
goto out;
- rc = lpfc_reg_login(phba, vport->vpi, icmd->un.rcvels.remoteID,
+ rc = lpfc_reg_rpi(phba, vport->vpi, icmd->un.rcvels.remoteID,
(uint8_t *) sp, mbox, 0);
if (rc) {
mempool_free(mbox, phba->mbox_mem_pool);
@@ -505,11 +497,19 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL);
else
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+ if ((ndlp->nlp_type & NLP_FABRIC) &&
+ vport->port_type == LPFC_NPIV_PORT) {
+ lpfc_linkdown_port(vport);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(shost->host_lock);
- if ((!(ndlp->nlp_type & NLP_FABRIC) &&
- ((ndlp->nlp_type & NLP_FCP_TARGET) ||
- !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
- (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
+ ndlp->nlp_last_elscmd = ELS_CMD_FDISC;
+ } else if ((!(ndlp->nlp_type & NLP_FABRIC) &&
+ ((ndlp->nlp_type & NLP_FCP_TARGET) ||
+ !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
+ (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
/* Only try to re-login if this is NOT a Fabric Node */
mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
spin_lock_irq(shost->host_lock);
@@ -577,7 +577,7 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (!ndlp->nlp_rpi) {
+ if (!(ndlp->nlp_flag & NLP_RPI_VALID)) {
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
return 0;
}
@@ -867,7 +867,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
lpfc_unreg_rpi(vport, ndlp);
- if (lpfc_reg_login(phba, vport->vpi, irsp->un.elsreq64.remoteID,
+ if (lpfc_reg_rpi(phba, vport->vpi, irsp->un.elsreq64.remoteID,
(uint8_t *) sp, mbox, 0) == 0) {
switch (ndlp->nlp_DID) {
case NameServer_DID:
@@ -1078,6 +1078,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
struct lpfc_iocbq *cmdiocb, *rspiocb;
IOCB_t *irsp;
ADISC *ap;
+ int rc;
cmdiocb = (struct lpfc_iocbq *) arg;
rspiocb = cmdiocb->context_un.rsp_iocb;
@@ -1103,6 +1104,15 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
return ndlp->nlp_state;
}
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ rc = lpfc_sli4_resume_rpi(ndlp);
+ if (rc) {
+ /* Stay in state and retry. */
+ ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
+ return ndlp->nlp_state;
+ }
+ }
+
if (ndlp->nlp_type & NLP_FCP_TARGET) {
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
@@ -1110,6 +1120,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
}
+
return ndlp->nlp_state;
}
@@ -1200,7 +1211,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
/* cleanup any ndlp on mbox q waiting for reglogin cmpl */
if ((mb = phba->sli.mbox_active)) {
- if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
lpfc_nlp_put(ndlp);
mb->context2 = NULL;
@@ -1210,7 +1221,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
- if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
mp = (struct lpfc_dmabuf *) (mb->context1);
if (mp) {
@@ -1261,7 +1272,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
uint32_t did = mb->un.varWords[1];
if (mb->mbxStatus) {
@@ -1293,6 +1304,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
}
ndlp->nlp_rpi = mb->un.varWords[0];
+ ndlp->nlp_flag |= NLP_RPI_VALID;
/* Only if we are not a fabric nport do we issue PRLI */
if (!(ndlp->nlp_type & NLP_FABRIC)) {
@@ -1888,11 +1900,12 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport,
void *arg, uint32_t evt)
{
LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
- MAILBOX_t *mb = &pmb->mb;
+ MAILBOX_t *mb = &pmb->u.mb;
- if (!mb->mbxStatus)
+ if (!mb->mbxStatus) {
ndlp->nlp_rpi = mb->un.varWords[0];
- else {
+ ndlp->nlp_flag |= NLP_RPI_VALID;
+ } else {
if (ndlp->nlp_flag & NLP_NODEV_REMOVE) {
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index a8fab3977116..e9fa6762044a 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -31,8 +31,10 @@
#include <scsi/scsi_transport_fc.h>
#include "lpfc_version.h"
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -57,6 +59,8 @@ static char *dif_op_str[] = {
"SCSI_PROT_READ_CONVERT",
"SCSI_PROT_WRITE_CONVERT"
};
+static void
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
static void
lpfc_debug_save_data(struct scsi_cmnd *cmnd)
@@ -112,7 +116,7 @@ lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
}
/**
- * lpfc_update_stats: Update statistical data for the command completion.
+ * lpfc_update_stats - Update statistical data for the command completion
* @phba: Pointer to HBA object.
* @lpfc_cmd: lpfc scsi command object pointer.
*
@@ -165,8 +169,7 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
}
/**
- * lpfc_send_sdev_queuedepth_change_event: Posts a queuedepth change
- * event.
+ * lpfc_send_sdev_queuedepth_change_event - Posts a queuedepth change event
* @phba: Pointer to HBA context object.
* @vport: Pointer to vport object.
* @ndlp: Pointer to FC node associated with the target.
@@ -220,7 +223,7 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
}
/**
- * lpfc_rampdown_queue_depth: Post RAMP_DOWN_QUEUE event to worker thread.
+ * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
* @phba: The Hba for which this call is being executed.
*
* This routine is called when there is resource error in driver or firmware.
@@ -261,7 +264,7 @@ lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
}
/**
- * lpfc_rampup_queue_depth: Post RAMP_UP_QUEUE event for worker thread.
+ * lpfc_rampup_queue_depth - Post RAMP_UP_QUEUE event for worker thread
* @phba: The Hba for which this call is being executed.
*
* This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine
@@ -273,14 +276,14 @@ lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
**/
static inline void
lpfc_rampup_queue_depth(struct lpfc_vport *vport,
- struct scsi_device *sdev)
+ uint32_t queue_depth)
{
unsigned long flags;
struct lpfc_hba *phba = vport->phba;
uint32_t evt_posted;
atomic_inc(&phba->num_cmd_success);
- if (vport->cfg_lun_queue_depth <= sdev->queue_depth)
+ if (vport->cfg_lun_queue_depth <= queue_depth)
return;
spin_lock_irqsave(&phba->hbalock, flags);
if (((phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) > jiffies) ||
@@ -303,7 +306,7 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
}
/**
- * lpfc_ramp_down_queue_handler: WORKER_RAMP_DOWN_QUEUE event handler.
+ * lpfc_ramp_down_queue_handler - WORKER_RAMP_DOWN_QUEUE event handler
* @phba: The Hba for which this call is being executed.
*
* This routine is called to process WORKER_RAMP_DOWN_QUEUE event for worker
@@ -326,7 +329,7 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
shost_for_each_device(sdev, shost) {
new_queue_depth =
@@ -361,7 +364,7 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
}
/**
- * lpfc_ramp_up_queue_handler: WORKER_RAMP_UP_QUEUE event handler.
+ * lpfc_ramp_up_queue_handler - WORKER_RAMP_UP_QUEUE event handler
* @phba: The Hba for which this call is being executed.
*
* This routine is called to process WORKER_RAMP_UP_QUEUE event for worker
@@ -380,7 +383,7 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
shost_for_each_device(sdev, shost) {
if (vports[i]->cfg_lun_queue_depth <=
@@ -410,7 +413,7 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
}
/**
- * lpfc_scsi_dev_block: set all scsi hosts to block state.
+ * lpfc_scsi_dev_block - set all scsi hosts to block state
* @phba: Pointer to HBA context object.
*
* This function walks vport list and set each SCSI host to block state
@@ -428,7 +431,7 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
shost_for_each_device(sdev, shost) {
rport = starget_to_rport(scsi_target(sdev));
@@ -439,22 +442,23 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
}
/**
- * lpfc_new_scsi_buf: Scsi buffer allocator.
+ * lpfc_new_scsi_buf_s3 - Scsi buffer allocator for HBA with SLI3 IF spec
* @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
*
- * This routine allocates a scsi buffer, which contains all the necessary
- * information needed to initiate a SCSI I/O. The non-DMAable buffer region
- * contains information to build the IOCB. The DMAable region contains
- * memory for the FCP CMND, FCP RSP, and the initial BPL. In addition to
- * allocating memory, the FCP CMND and FCP RSP BDEs are setup in the BPL
- * and the BPL BDE is setup in the IOCB.
+ * This routine allocates a scsi buffer for device with SLI-3 interface spec,
+ * the scsi buffer contains all the necessary information needed to initiate
+ * a SCSI I/O. The non-DMAable buffer region contains information to build
+ * the IOCB. The DMAable region contains memory for the FCP CMND, FCP RSP,
+ * and the initial BPL. In addition to allocating memory, the FCP CMND and
+ * FCP RSP BDEs are setup in the BPL and the BPL BDE is setup in the IOCB.
*
* Return codes:
- * NULL - Error
- * Pointer to lpfc_scsi_buf data structure - Success
+ * int - number of scsi buffers that were allocated.
+ * 0 = failure, less than num_to_alloc is a partial failure.
**/
-static struct lpfc_scsi_buf *
-lpfc_new_scsi_buf(struct lpfc_vport *vport)
+static int
+lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_scsi_buf *psb;
@@ -464,107 +468,401 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
dma_addr_t pdma_phys_fcp_rsp;
dma_addr_t pdma_phys_bpl;
uint16_t iotag;
+ int bcnt;
- psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
- if (!psb)
- return NULL;
+ for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+ psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+ if (!psb)
+ break;
+
+ /*
+ * Get memory from the pci pool to map the virt space to pci
+ * bus space for an I/O. The DMA buffer includes space for the
+ * struct fcp_cmnd, struct fcp_rsp and the number of bde's
+ * necessary to support the sg_tablesize.
+ */
+ psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool,
+ GFP_KERNEL, &psb->dma_handle);
+ if (!psb->data) {
+ kfree(psb);
+ break;
+ }
+
+ /* Initialize virtual ptrs to dma_buf region. */
+ memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+
+ /* Allocate iotag for psb->cur_iocbq. */
+ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
+ if (iotag == 0) {
+ pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
+ psb->data, psb->dma_handle);
+ kfree(psb);
+ break;
+ }
+ psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+
+ psb->fcp_cmnd = psb->data;
+ psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
+ psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp);
+
+ /* Initialize local short-hand pointers. */
+ bpl = psb->fcp_bpl;
+ pdma_phys_fcp_cmd = psb->dma_handle;
+ pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
+ pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp);
+
+ /*
+ * The first two bdes are the FCP_CMD and FCP_RSP. The balance
+ * are sg list bdes. Initialize the first two and leave the
+ * rest for queuecommand.
+ */
+ bpl[0].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_cmd));
+ bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
+ bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
+ bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
+
+ /* Setup the physical region for the FCP RSP */
+ bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
+ bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
+ bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
+ bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
+
+ /*
+ * Since the IOCB for the FCP I/O is built into this
+ * lpfc_scsi_buf, initialize it with all known data now.
+ */
+ iocb = &psb->cur_iocbq.iocb;
+ iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+ if ((phba->sli_rev == 3) &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
+ /* fill in immediate fcp command BDE */
+ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
+ iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
+ iocb->un.fcpi64.bdl.addrLow = offsetof(IOCB_t,
+ unsli3.fcp_ext.icd);
+ iocb->un.fcpi64.bdl.addrHigh = 0;
+ iocb->ulpBdeCount = 0;
+ iocb->ulpLe = 0;
+ /* fill in responce BDE */
+ iocb->unsli3.fcp_ext.rbde.tus.f.bdeFlags =
+ BUFF_TYPE_BDE_64;
+ iocb->unsli3.fcp_ext.rbde.tus.f.bdeSize =
+ sizeof(struct fcp_rsp);
+ iocb->unsli3.fcp_ext.rbde.addrLow =
+ putPaddrLow(pdma_phys_fcp_rsp);
+ iocb->unsli3.fcp_ext.rbde.addrHigh =
+ putPaddrHigh(pdma_phys_fcp_rsp);
+ } else {
+ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ iocb->un.fcpi64.bdl.bdeSize =
+ (2 * sizeof(struct ulp_bde64));
+ iocb->un.fcpi64.bdl.addrLow =
+ putPaddrLow(pdma_phys_bpl);
+ iocb->un.fcpi64.bdl.addrHigh =
+ putPaddrHigh(pdma_phys_bpl);
+ iocb->ulpBdeCount = 1;
+ iocb->ulpLe = 1;
+ }
+ iocb->ulpClass = CLASS3;
+ psb->status = IOSTAT_SUCCESS;
+ /* Put it back into the SCSI buffer list */
+ lpfc_release_scsi_buf_s4(phba, psb);
- /*
- * Get memory from the pci pool to map the virt space to pci bus space
- * for an I/O. The DMA buffer includes space for the struct fcp_cmnd,
- * struct fcp_rsp and the number of bde's necessary to support the
- * sg_tablesize.
- */
- psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool, GFP_KERNEL,
- &psb->dma_handle);
- if (!psb->data) {
- kfree(psb);
- return NULL;
}
- /* Initialize virtual ptrs to dma_buf region. */
- memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+ return bcnt;
+}
- /* Allocate iotag for psb->cur_iocbq. */
- iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
- if (iotag == 0) {
- pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree (psb);
- return NULL;
+/**
+ * lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the fcp xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 fast-path
+ * FCP aborted xri.
+ **/
+void
+lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri)
+{
+ uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+ struct lpfc_scsi_buf *psb, *next_psb;
+ unsigned long iflag = 0;
+
+ spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag);
+ list_for_each_entry_safe(psb, next_psb,
+ &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
+ if (psb->cur_iocbq.sli4_xritag == xri) {
+ list_del(&psb->list);
+ psb->status = IOSTAT_SUCCESS;
+ spin_unlock_irqrestore(
+ &phba->sli4_hba.abts_scsi_buf_list_lock,
+ iflag);
+ lpfc_release_scsi_buf_s4(phba, psb);
+ return;
+ }
}
- psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
+ iflag);
+}
- psb->fcp_cmnd = psb->data;
- psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
- psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
- sizeof(struct fcp_rsp);
+/**
+ * lpfc_sli4_repost_scsi_sgl_list - Repsot the Scsi buffers sgl pages as block
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine walks the list of scsi buffers that have been allocated and
+ * repost them to the HBA by using SGL block post. This is needed after a
+ * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
+ * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list
+ * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers.
+ *
+ * Returns: 0 = success, non-zero failure.
+ **/
+int
+lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
+{
+ struct lpfc_scsi_buf *psb;
+ int index, status, bcnt = 0, rcnt = 0, rc = 0;
+ LIST_HEAD(sblist);
+
+ for (index = 0; index < phba->sli4_hba.scsi_xri_cnt; index++) {
+ psb = phba->sli4_hba.lpfc_scsi_psb_array[index];
+ if (psb) {
+ /* Remove from SCSI buffer list */
+ list_del(&psb->list);
+ /* Add it to a local SCSI buffer list */
+ list_add_tail(&psb->list, &sblist);
+ if (++rcnt == LPFC_NEMBED_MBOX_SGL_CNT) {
+ bcnt = rcnt;
+ rcnt = 0;
+ }
+ } else
+ /* A hole present in the XRI array, need to skip */
+ bcnt = rcnt;
- /* Initialize local short-hand pointers. */
- bpl = psb->fcp_bpl;
- pdma_phys_fcp_cmd = psb->dma_handle;
- pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
- pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
- sizeof(struct fcp_rsp);
+ if (index == phba->sli4_hba.scsi_xri_cnt - 1)
+ /* End of XRI array for SCSI buffer, complete */
+ bcnt = rcnt;
- /*
- * The first two bdes are the FCP_CMD and FCP_RSP. The balance are sg
- * list bdes. Initialize the first two and leave the rest for
- * queuecommand.
- */
- bpl[0].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_cmd));
- bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
- bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
- bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
-
- /* Setup the physical region for the FCP RSP */
- bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
- bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
- bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
- bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
+ /* Continue until collect up to a nembed page worth of sgls */
+ if (bcnt == 0)
+ continue;
+ /* Now, post the SCSI buffer list sgls as a block */
+ status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt);
+ /* Reset SCSI buffer count for next round of posting */
+ bcnt = 0;
+ while (!list_empty(&sblist)) {
+ list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
+ list);
+ if (status) {
+ /* Put this back on the abort scsi list */
+ psb->status = IOSTAT_LOCAL_REJECT;
+ psb->result = IOERR_ABORT_REQUESTED;
+ rc++;
+ } else
+ psb->status = IOSTAT_SUCCESS;
+ /* Put it back into the SCSI buffer list */
+ lpfc_release_scsi_buf_s4(phba, psb);
+ }
+ }
+ return rc;
+}
- /*
- * Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
- * initialize it with all known data now.
- */
- iocb = &psb->cur_iocbq.iocb;
- iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- if ((phba->sli_rev == 3) &&
- !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
- /* fill in immediate fcp command BDE */
- iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
+/**
+ * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec
+ * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
+ *
+ * This routine allocates a scsi buffer for device with SLI-4 interface spec,
+ * the scsi buffer contains all the necessary information needed to initiate
+ * a SCSI I/O.
+ *
+ * Return codes:
+ * int - number of scsi buffers that were allocated.
+ * 0 = failure, less than num_to_alloc is a partial failure.
+ **/
+static int
+lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_scsi_buf *psb;
+ struct sli4_sge *sgl;
+ IOCB_t *iocb;
+ dma_addr_t pdma_phys_fcp_cmd;
+ dma_addr_t pdma_phys_fcp_rsp;
+ dma_addr_t pdma_phys_bpl, pdma_phys_bpl1;
+ uint16_t iotag, last_xritag = NO_XRI;
+ int status = 0, index;
+ int bcnt;
+ int non_sequential_xri = 0;
+ int rc = 0;
+ LIST_HEAD(sblist);
+
+ for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+ psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+ if (!psb)
+ break;
+
+ /*
+ * Get memory from the pci pool to map the virt space to pci bus
+ * space for an I/O. The DMA buffer includes space for the
+ * struct fcp_cmnd, struct fcp_rsp and the number of bde's
+ * necessary to support the sg_tablesize.
+ */
+ psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool,
+ GFP_KERNEL, &psb->dma_handle);
+ if (!psb->data) {
+ kfree(psb);
+ break;
+ }
+
+ /* Initialize virtual ptrs to dma_buf region. */
+ memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+
+ /* Allocate iotag for psb->cur_iocbq. */
+ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
+ if (iotag == 0) {
+ kfree(psb);
+ break;
+ }
+
+ psb->cur_iocbq.sli4_xritag = lpfc_sli4_next_xritag(phba);
+ if (psb->cur_iocbq.sli4_xritag == NO_XRI) {
+ pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
+ psb->data, psb->dma_handle);
+ kfree(psb);
+ break;
+ }
+ if (last_xritag != NO_XRI
+ && psb->cur_iocbq.sli4_xritag != (last_xritag+1)) {
+ non_sequential_xri = 1;
+ } else
+ list_add_tail(&psb->list, &sblist);
+ last_xritag = psb->cur_iocbq.sli4_xritag;
+
+ index = phba->sli4_hba.scsi_xri_cnt++;
+ psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+
+ psb->fcp_bpl = psb->data;
+ psb->fcp_cmnd = (psb->data + phba->cfg_sg_dma_buf_size)
+ - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+ psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
+ sizeof(struct fcp_cmnd));
+
+ /* Initialize local short-hand pointers. */
+ sgl = (struct sli4_sge *)psb->fcp_bpl;
+ pdma_phys_bpl = psb->dma_handle;
+ pdma_phys_fcp_cmd =
+ (psb->dma_handle + phba->cfg_sg_dma_buf_size)
+ - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+ pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
+
+ /*
+ * The first two bdes are the FCP_CMD and FCP_RSP. The balance
+ * are sg list bdes. Initialize the first two and leave the
+ * rest for queuecommand.
+ */
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
+ bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_cmnd));
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl++;
+
+ /* Setup the physical region for the FCP RSP */
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
+ bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_rsp));
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->word3 = cpu_to_le32(sgl->word3);
+
+ /*
+ * Since the IOCB for the FCP I/O is built into this
+ * lpfc_scsi_buf, initialize it with all known data now.
+ */
+ iocb = &psb->cur_iocbq.iocb;
+ iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
+ /* setting the BLP size to 2 * sizeof BDE may not be correct.
+ * We are setting the bpl to point to out sgl. An sgl's
+ * entries are 16 bytes, a bpl entries are 12 bytes.
+ */
iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
- iocb->un.fcpi64.bdl.addrLow = offsetof(IOCB_t,
- unsli3.fcp_ext.icd);
- iocb->un.fcpi64.bdl.addrHigh = 0;
- iocb->ulpBdeCount = 0;
- iocb->ulpLe = 0;
- /* fill in responce BDE */
- iocb->unsli3.fcp_ext.rbde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- iocb->unsli3.fcp_ext.rbde.tus.f.bdeSize =
- sizeof(struct fcp_rsp);
- iocb->unsli3.fcp_ext.rbde.addrLow =
- putPaddrLow(pdma_phys_fcp_rsp);
- iocb->unsli3.fcp_ext.rbde.addrHigh =
- putPaddrHigh(pdma_phys_fcp_rsp);
- } else {
- iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
- iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_bpl);
- iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_bpl);
+ iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
+ iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
iocb->ulpBdeCount = 1;
iocb->ulpLe = 1;
+ iocb->ulpClass = CLASS3;
+ if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
+ pdma_phys_bpl1 = pdma_phys_bpl + SGL_PAGE_SIZE;
+ else
+ pdma_phys_bpl1 = 0;
+ psb->dma_phys_bpl = pdma_phys_bpl;
+ phba->sli4_hba.lpfc_scsi_psb_array[index] = psb;
+ if (non_sequential_xri) {
+ status = lpfc_sli4_post_sgl(phba, pdma_phys_bpl,
+ pdma_phys_bpl1,
+ psb->cur_iocbq.sli4_xritag);
+ if (status) {
+ /* Put this back on the abort scsi list */
+ psb->status = IOSTAT_LOCAL_REJECT;
+ psb->result = IOERR_ABORT_REQUESTED;
+ rc++;
+ } else
+ psb->status = IOSTAT_SUCCESS;
+ /* Put it back into the SCSI buffer list */
+ lpfc_release_scsi_buf_s4(phba, psb);
+ break;
+ }
+ }
+ if (bcnt) {
+ status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt);
+ /* Reset SCSI buffer count for next round of posting */
+ while (!list_empty(&sblist)) {
+ list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
+ list);
+ if (status) {
+ /* Put this back on the abort scsi list */
+ psb->status = IOSTAT_LOCAL_REJECT;
+ psb->result = IOERR_ABORT_REQUESTED;
+ rc++;
+ } else
+ psb->status = IOSTAT_SUCCESS;
+ /* Put it back into the SCSI buffer list */
+ lpfc_release_scsi_buf_s4(phba, psb);
+ }
}
- iocb->ulpClass = CLASS3;
- return psb;
+ return bcnt + non_sequential_xri - rc;
}
/**
- * lpfc_get_scsi_buf: Get a scsi buffer from lpfc_scsi_buf_list list of Hba.
- * @phba: The Hba for which this call is being executed.
+ * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator
+ * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
+ *
+ * This routine wraps the actual SCSI buffer allocator function pointer from
+ * the lpfc_hba struct.
+ *
+ * Return codes:
+ * int - number of scsi buffers that were allocated.
+ * 0 = failure, less than num_to_alloc is a partial failure.
+ **/
+static inline int
+lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
+{
+ return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc);
+}
+
+/**
+ * lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
+ * @phba: The HBA for which this call is being executed.
*
* This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
* and returns to caller.
@@ -592,7 +890,7 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
}
/**
- * lpfc_release_scsi_buf: Return a scsi buffer back to hba lpfc_scsi_buf_list list.
+ * lpfc_release_scsi_buf - Return a scsi buffer back to hba scsi buf list
* @phba: The Hba for which this call is being executed.
* @psb: The scsi buffer which is being released.
*
@@ -600,7 +898,7 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
* lpfc_scsi_buf_list list.
**/
static void
-lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
unsigned long iflag = 0;
@@ -611,21 +909,69 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
}
/**
- * lpfc_scsi_prep_dma_buf: Routine to do DMA mapping for scsi buffer.
+ * lpfc_release_scsi_buf_s4: Return a scsi buffer back to hba scsi buf list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer
+ * and cannot be reused for at least RA_TOV amount of time if it was
+ * aborted.
+ **/
+static void
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+ unsigned long iflag = 0;
+
+ if (psb->status == IOSTAT_LOCAL_REJECT
+ && psb->result == IOERR_ABORT_REQUESTED) {
+ spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
+ iflag);
+ psb->pCmd = NULL;
+ list_add_tail(&psb->list,
+ &phba->sli4_hba.lpfc_abts_scsi_buf_list);
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
+ iflag);
+ } else {
+
+ spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+ psb->pCmd = NULL;
+ list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+ }
+}
+
+/**
+ * lpfc_release_scsi_buf: Return a scsi buffer back to hba scsi buf list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list.
+ **/
+static void
+lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+
+ phba->lpfc_release_scsi_buf(phba, psb);
+}
+
+/**
+ * lpfc_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
* @phba: The Hba for which this call is being executed.
* @lpfc_cmd: The scsi buffer which is going to be mapped.
*
* This routine does the pci dma mapping for scatter-gather list of scsi cmnd
- * field of @lpfc_cmd. This routine scans through sg elements and format the
- * bdea. This routine also initializes all IOCB fields which are dependent on
- * scsi command request buffer.
+ * field of @lpfc_cmd for device with SLI-3 interface spec. This routine scans
+ * through sg elements and format the bdea. This routine also initializes all
+ * IOCB fields which are dependent on scsi command request buffer.
*
* Return codes:
* 1 - Error
* 0 - Success
**/
static int
-lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct scatterlist *sgel = NULL;
@@ -738,7 +1084,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* Due to difference in data length between DIF/non-DIF paths,
* we need to set word 4 of IOCB here
*/
- iocb_cmd->un.fcpi.fcpi_parm = le32_to_cpu(scsi_bufflen(scsi_cmnd));
+ iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
return 0;
}
@@ -823,9 +1169,9 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
/**
* lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
* @sc: in: SCSI command
- * @apptagmask out: app tag mask
- * @apptagval out: app tag value
- * @reftag out: ref tag (reference tag)
+ * @apptagmask: out: app tag mask
+ * @apptagval: out: app tag value
+ * @reftag: out: ref tag (reference tag)
*
* Description:
* Extract DIF paramters from the command if possible. Otherwise,
@@ -1413,7 +1759,134 @@ out:
}
/**
- * lpfc_send_scsi_error_event: Posts an event when there is SCSI error.
+ * lpfc_scsi_prep_dma_buf_s4 - DMA mapping for scsi buffer to SLI4 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
+ * field of @lpfc_cmd for device with SLI-4 interface spec.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
+static int
+lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct scatterlist *sgel = NULL;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ dma_addr_t physaddr;
+ uint32_t num_bde = 0;
+ uint32_t dma_len;
+ uint32_t dma_offset = 0;
+ int nseg;
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither. Start the lpfc command prep by
+ * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+ * data bde entry.
+ */
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+
+ nseg = scsi_dma_map(scsi_cmnd);
+ if (unlikely(!nseg))
+ return 1;
+ sgl += 1;
+ /* clear the last flag in the fcp_rsp map entry */
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl += 1;
+
+ lpfc_cmd->seg_cnt = nseg;
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ /*
+ * The driver established a maximum scatter-gather segment count
+ * during probe that limits the number of sg elements in any
+ * single scsi command. Just run through the seg_cnt and format
+ * the sge's.
+ * When using SLI-3 the driver will try to fit all the BDEs into
+ * the IOCB. If it can't then the BDEs get added to a BPL as it
+ * does for SLI-2 mode.
+ */
+ scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
+ physaddr = sg_dma_address(sgel);
+ dma_len = sg_dma_len(sgel);
+ bf_set(lpfc_sli4_sge_len, sgl, sg_dma_len(sgel));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+ if ((num_bde + 1) == nseg)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ else
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->word3 = cpu_to_le32(sgl->word3);
+ dma_offset += dma_len;
+ sgl++;
+ }
+ } else {
+ sgl += 1;
+ /* clear the last flag in the fcp_rsp map entry */
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are dependent on the
+ * scsi_cmnd request_buffer. Note that for SLI-2 the bdeSize is
+ * explicitly reinitialized.
+ * all iocb memory resources are reused.
+ */
+ fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
+ return 0;
+}
+
+/**
+ * lpfc_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine wraps the actual DMA mapping function pointer from the
+ * lpfc_hba struct.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
+static inline int
+lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+{
+ return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+}
+
+/**
+ * lpfc_send_scsi_error_event - Posts an event when there is SCSI error
* @phba: Pointer to hba context object.
* @vport: Pointer to vport object.
* @lpfc_cmd: Pointer to lpfc scsi command which reported the error.
@@ -1505,15 +1978,15 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
}
/**
- * lpfc_scsi_unprep_dma_buf: Routine to un-map DMA mapping of scatter gather.
- * @phba: The Hba for which this call is being executed.
+ * lpfc_scsi_unprep_dma_buf_s3 - Un-map DMA mapping of SG-list for SLI3 dev
+ * @phba: The HBA for which this call is being executed.
* @psb: The scsi buffer which is going to be un-mapped.
*
* This routine does DMA un-mapping of scatter gather list of scsi command
- * field of @lpfc_cmd.
+ * field of @lpfc_cmd for device with SLI-3 interface spec.
**/
static void
-lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
+lpfc_scsi_unprep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
/*
* There are only two special cases to consider. (1) the scsi command
@@ -1530,7 +2003,37 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
}
/**
- * lpfc_handler_fcp_err: FCP response handler.
+ * lpfc_scsi_unprep_dma_buf_s4 - Un-map DMA mapping of SG-list for SLI4 dev
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd for device with SLI-4 interface spec. If we have to
+ * remove the sgl for this scsi buffer then we will do it here. For now
+ * we should be able to just call the sli3 unprep routine.
+ **/
+static void
+lpfc_scsi_unprep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+ lpfc_scsi_unprep_dma_buf_s3(phba, psb);
+}
+
+/**
+ * lpfc_scsi_unprep_dma_buf - Wrapper function for unmap DMA mapping of SG-list
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd for device with SLI-4 interface spec.
+ **/
+static void
+lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+ phba->lpfc_scsi_unprep_dma_buf(phba, psb);
+}
+
+/**
+ * lpfc_handler_fcp_err - FCP response handler
* @vport: The virtual port for which this call is being executed.
* @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
* @rsp_iocb: The response IOCB which contains FCP error.
@@ -1674,10 +2177,10 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
/**
- * lpfc_scsi_cmd_iocb_cmpl: Scsi cmnd IOCB completion routine.
+ * lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine
* @phba: The Hba for which this call is being executed.
* @pIocbIn: The command IOCBQ for the scsi cmnd.
- * @pIocbOut: The response IOCBQ for the scsi cmnd .
+ * @pIocbOut: The response IOCBQ for the scsi cmnd.
*
* This routine assigns scsi command result by looking into response IOCB
* status field appropriately. This routine handles QUEUE FULL condition as
@@ -1694,10 +2197,12 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_nodelist *pnode = rdata->pnode;
struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
int result;
- struct scsi_device *sdev, *tmp_sdev;
+ struct scsi_device *tmp_sdev;
int depth = 0;
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
+ struct Scsi_Host *shost = cmd->device->host;
+ uint32_t queue_depth, scsi_id;
lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
@@ -1808,11 +2313,10 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_update_stats(phba, lpfc_cmd);
result = cmd->result;
- sdev = cmd->device;
if (vport->cfg_max_scsicmpl_time &&
time_after(jiffies, lpfc_cmd->start_time +
msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
- spin_lock_irqsave(sdev->host->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
if (pnode && NLP_CHK_NODE_ACT(pnode)) {
if (pnode->cmd_qdepth >
atomic_read(&pnode->cmd_pending) &&
@@ -1825,22 +2329,26 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
pnode->last_change_time = jiffies;
}
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
} else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
time_after(jiffies, pnode->last_change_time +
msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
- spin_lock_irqsave(sdev->host->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
pnode->cmd_qdepth += pnode->cmd_qdepth *
LPFC_TGTQ_RAMPUP_PCENT / 100;
if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
pnode->last_change_time = jiffies;
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+
+ /* The sdev is not guaranteed to be valid post scsi_done upcall. */
+ queue_depth = cmd->device->queue_depth;
+ scsi_id = cmd->device->id;
cmd->scsi_done(cmd);
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
@@ -1848,28 +2356,28 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
* If there is a thread waiting for command completion
* wake up the thread.
*/
- spin_lock_irqsave(sdev->host->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
lpfc_cmd->pCmd = NULL;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
lpfc_release_scsi_buf(phba, lpfc_cmd);
return;
}
if (!result)
- lpfc_rampup_queue_depth(vport, sdev);
+ lpfc_rampup_queue_depth(vport, queue_depth);
if (!result && pnode && NLP_CHK_NODE_ACT(pnode) &&
((jiffies - pnode->last_ramp_up_time) >
LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
((jiffies - pnode->last_q_full_time) >
LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
- (vport->cfg_lun_queue_depth > sdev->queue_depth)) {
- shost_for_each_device(tmp_sdev, sdev->host) {
+ (vport->cfg_lun_queue_depth > queue_depth)) {
+ shost_for_each_device(tmp_sdev, shost) {
if (vport->cfg_lun_queue_depth > tmp_sdev->queue_depth){
- if (tmp_sdev->id != sdev->id)
+ if (tmp_sdev->id != scsi_id)
continue;
if (tmp_sdev->ordered_tags)
scsi_adjust_queue_depth(tmp_sdev,
@@ -1885,7 +2393,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode,
0xFFFFFFFF,
- sdev->queue_depth - 1, sdev->queue_depth);
+ queue_depth , queue_depth + 1);
}
/*
@@ -1896,8 +2404,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
NLP_CHK_NODE_ACT(pnode)) {
pnode->last_q_full_time = jiffies;
- shost_for_each_device(tmp_sdev, sdev->host) {
- if (tmp_sdev->id != sdev->id)
+ shost_for_each_device(tmp_sdev, shost) {
+ if (tmp_sdev->id != scsi_id)
continue;
depth = scsi_track_queue_full(tmp_sdev,
tmp_sdev->queue_depth - 1);
@@ -1909,7 +2417,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
* scsi_track_queue_full.
*/
if (depth == -1)
- depth = sdev->host->cmd_per_lun;
+ depth = shost->cmd_per_lun;
if (depth) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
@@ -1925,17 +2433,17 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
* If there is a thread waiting for command completion
* wake up the thread.
*/
- spin_lock_irqsave(sdev->host->host_lock, flags);
+ spin_lock_irqsave(shost->host_lock, flags);
lpfc_cmd->pCmd = NULL;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ spin_unlock_irqrestore(shost->host_lock, flags);
lpfc_release_scsi_buf(phba, lpfc_cmd);
}
/**
- * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB.
+ * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB
* @data: A pointer to the immediate command data portion of the IOCB.
* @fcp_cmnd: The FCP Command that is provided by the SCSI layer.
*
@@ -1953,16 +2461,16 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
}
/**
- * lpfc_scsi_prep_cmnd: Routine to convert scsi cmnd to FCP information unit.
+ * lpfc_scsi_prep_cmnd_s3 - Convert scsi cmnd to FCP infor unit for SLI3 dev
* @vport: The virtual port for which this call is being executed.
* @lpfc_cmd: The scsi command which needs to send.
* @pnode: Pointer to lpfc_nodelist.
*
* This routine initializes fcp_cmnd and iocb data structure from scsi command
- * to transfer.
+ * to transfer for device with SLI3 interface spec.
**/
static void
-lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+lpfc_scsi_prep_cmnd_s3(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_nodelist *pnode)
{
struct lpfc_hba *phba = vport->phba;
@@ -2009,8 +2517,11 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
if (scsi_sg_count(scsi_cmnd)) {
if (datadir == DMA_TO_DEVICE) {
iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
- iocb_cmd->un.fcpi.fcpi_parm = 0;
- iocb_cmd->ulpPU = 0;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ iocb_cmd->un.fcpi.fcpi_parm = 0;
+ iocb_cmd->ulpPU = 0;
+ } else
+ iocb_cmd->ulpPU = PARM_READ_CHECK;
fcp_cmnd->fcpCntl3 = WRITE_DATA;
phba->fc4OutputRequests++;
} else {
@@ -2047,20 +2558,60 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
/**
- * lpfc_scsi_prep_task_mgmt_cmnd: Convert scsi TM cmnd to FCP information unit.
+ * lpfc_scsi_prep_cmnd_s4 - Convert scsi cmnd to FCP infor unit for SLI4 dev
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer for device with SLI4 interface spec.
+ **/
+static void
+lpfc_scsi_prep_cmnd_s4(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_nodelist *pnode)
+{
+ /*
+ * The prep cmnd routines do not touch the sgl or its
+ * entries. We may not have to do anything different.
+ * I will leave this function in place until we can
+ * run some IO through the driver and determine if changes
+ * are needed.
+ */
+ return lpfc_scsi_prep_cmnd_s3(vport, lpfc_cmd, pnode);
+}
+
+/**
+ * lpfc_scsi_prep_cmnd - Wrapper func for convert scsi cmnd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine wraps the actual convert SCSI cmnd function pointer from
+ * the lpfc_hba struct.
+ **/
+static inline void
+lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_nodelist *pnode)
+{
+ vport->phba->lpfc_scsi_prep_cmnd(vport, lpfc_cmd, pnode);
+}
+
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd_s3 - Convert SLI3 scsi TM cmd to FCP info unit
* @vport: The virtual port for which this call is being executed.
* @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
* @lun: Logical unit number.
* @task_mgmt_cmd: SCSI task management command.
*
- * This routine creates FCP information unit corresponding to @task_mgmt_cmd.
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd
+ * for device with SLI-3 interface spec.
*
* Return codes:
* 0 - Error
* 1 - Success
**/
static int
-lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
+lpfc_scsi_prep_task_mgmt_cmd_s3(struct lpfc_vport *vport,
struct lpfc_scsi_buf *lpfc_cmd,
unsigned int lun,
uint8_t task_mgmt_cmd)
@@ -2110,7 +2661,108 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
}
/**
- * lpc_taskmgmt_def_cmpl: IOCB completion routine for task management command.
+ * lpfc_scsi_prep_task_mgmt_cmnd_s4 - Convert SLI4 scsi TM cmd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd
+ * for device with SLI-4 interface spec.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
+static int
+lpfc_scsi_prep_task_mgmt_cmd_s4(struct lpfc_vport *vport,
+ struct lpfc_scsi_buf *lpfc_cmd,
+ unsigned int lun,
+ uint8_t task_mgmt_cmd)
+{
+ /*
+ * The prep cmnd routines do not touch the sgl or its
+ * entries. We may not have to do anything different.
+ * I will leave this function in place until we can
+ * run some IO through the driver and determine if changes
+ * are needed.
+ */
+ return lpfc_scsi_prep_task_mgmt_cmd_s3(vport, lpfc_cmd, lun,
+ task_mgmt_cmd);
+}
+
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd - Wrapper func convert scsi TM cmd to FCP info
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine wraps the actual convert SCSI TM to FCP information unit
+ * function pointer from the lpfc_hba struct.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
+static inline int
+lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
+ struct lpfc_scsi_buf *lpfc_cmd,
+ unsigned int lun,
+ uint8_t task_mgmt_cmd)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return phba->lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
+ task_mgmt_cmd);
+}
+
+/**
+ * lpfc_scsi_api_table_setup - Set up scsi api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the SCSI interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+ switch (dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
+ phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
+ phba->lpfc_scsi_prep_cmnd = lpfc_scsi_prep_cmnd_s3;
+ phba->lpfc_scsi_unprep_dma_buf = lpfc_scsi_unprep_dma_buf_s3;
+ phba->lpfc_scsi_prep_task_mgmt_cmd =
+ lpfc_scsi_prep_task_mgmt_cmd_s3;
+ phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
+ break;
+ case LPFC_PCI_DEV_OC:
+ phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
+ phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
+ phba->lpfc_scsi_prep_cmnd = lpfc_scsi_prep_cmnd_s4;
+ phba->lpfc_scsi_unprep_dma_buf = lpfc_scsi_unprep_dma_buf_s4;
+ phba->lpfc_scsi_prep_task_mgmt_cmd =
+ lpfc_scsi_prep_task_mgmt_cmd_s4;
+ phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1418 Invalid HBA PCI-device group: 0x%x\n",
+ dev_grp);
+ return -ENODEV;
+ break;
+ }
+ phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf;
+ phba->lpfc_rampdown_queue_depth = lpfc_rampdown_queue_depth;
+ return 0;
+}
+
+/**
+ * lpfc_taskmgmt_def_cmpl - IOCB completion routine for task management command
* @phba: The Hba for which this call is being executed.
* @cmdiocbq: Pointer to lpfc_iocbq data structure.
* @rspiocbq: Pointer to lpfc_iocbq data structure.
@@ -2131,7 +2783,7 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
}
/**
- * lpfc_scsi_tgt_reset: Target reset handler.
+ * lpfc_scsi_tgt_reset - Target reset handler
* @lpfc_cmd: Pointer to lpfc_scsi_buf data structure
* @vport: The virtual port for which this call is being executed.
* @tgt_id: Target ID.
@@ -2174,9 +2826,8 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0702 Issue Target Reset to TGT %d Data: x%x x%x\n",
tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag);
- status = lpfc_sli_issue_iocb_wait(phba,
- &phba->sli.ring[phba->sli.fcp_ring],
- iocbq, iocbqrsp, lpfc_cmd->timeout);
+ status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING,
+ iocbq, iocbqrsp, lpfc_cmd->timeout);
if (status != IOCB_SUCCESS) {
if (status == IOCB_TIMEDOUT) {
iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
@@ -2198,7 +2849,7 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
}
/**
- * lpfc_info: Info entry point of scsi_host_template data structure.
+ * lpfc_info - Info entry point of scsi_host_template data structure
* @host: The scsi host for which this call is being executed.
*
* This routine provides module information about hba.
@@ -2236,7 +2887,7 @@ lpfc_info(struct Scsi_Host *host)
}
/**
- * lpfc_poll_rearm_time: Routine to modify fcp_poll timer of hba.
+ * lpfc_poll_rearm_time - Routine to modify fcp_poll timer of hba
* @phba: The Hba for which this call is being executed.
*
* This routine modifies fcp_poll_timer field of @phba by cfg_poll_tmo.
@@ -2253,7 +2904,7 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
}
/**
- * lpfc_poll_start_timer: Routine to start fcp_poll_timer of HBA.
+ * lpfc_poll_start_timer - Routine to start fcp_poll_timer of HBA
* @phba: The Hba for which this call is being executed.
*
* This routine starts the fcp_poll_timer of @phba.
@@ -2264,7 +2915,7 @@ void lpfc_poll_start_timer(struct lpfc_hba * phba)
}
/**
- * lpfc_poll_timeout: Restart polling timer.
+ * lpfc_poll_timeout - Restart polling timer
* @ptr: Map to lpfc_hba data structure pointer.
*
* This routine restarts fcp_poll timer, when FCP ring polling is enable
@@ -2283,8 +2934,7 @@ void lpfc_poll_timeout(unsigned long ptr)
}
/**
- * lpfc_queuecommand: Queuecommand entry point of Scsi Host Templater data
- * structure.
+ * lpfc_queuecommand - scsi_host_template queuecommand entry point
* @cmnd: Pointer to scsi_cmnd data structure.
* @done: Pointer to done routine.
*
@@ -2302,7 +2952,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli *psli = &phba->sli;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
struct lpfc_nodelist *ndlp = rdata->pnode;
struct lpfc_scsi_buf *lpfc_cmd;
@@ -2424,7 +3073,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
atomic_inc(&ndlp->cmd_pending);
- err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
+ err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
if (err) {
atomic_dec(&ndlp->cmd_pending);
@@ -2450,7 +3099,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
}
/**
- * lpfc_block_error_handler: Routine to block error handler.
+ * lpfc_block_error_handler - Routine to block error handler
* @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
@@ -2472,8 +3121,7 @@ lpfc_block_error_handler(struct scsi_cmnd *cmnd)
}
/**
- * lpfc_abort_handler: Eh_abort_handler entry point of Scsi Host Template data
- *structure.
+ * lpfc_abort_handler - scsi_host_template eh_abort_handler entry point
* @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine aborts @cmnd pending in base driver.
@@ -2488,7 +3136,6 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring];
struct lpfc_iocbq *iocb;
struct lpfc_iocbq *abtsiocb;
struct lpfc_scsi_buf *lpfc_cmd;
@@ -2529,7 +3176,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
icmd = &abtsiocb->iocb;
icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
icmd->un.acxri.abortContextTag = cmd->ulpContext;
- icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ icmd->un.acxri.abortIoTag = iocb->sli4_xritag;
+ else
+ icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
icmd->ulpLe = 1;
icmd->ulpClass = cmd->ulpClass;
@@ -2540,7 +3190,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
abtsiocb->vport = vport;
- if (lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0) == IOCB_ERROR) {
+ if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) ==
+ IOCB_ERROR) {
lpfc_sli_release_iocbq(phba, abtsiocb);
ret = FAILED;
goto out;
@@ -2578,8 +3229,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
}
/**
- * lpfc_device_reset_handler: eh_device_reset entry point of Scsi Host Template
- *data structure.
+ * lpfc_device_reset_handler - scsi_host_template eh_device_reset entry point
* @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine does a device reset by sending a TARGET_RESET task management
@@ -2587,7 +3237,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
*
* Return code :
* 0x2003 - Error
- * 0ex2002 - Success
+ * 0x2002 - Success
**/
static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
@@ -2667,8 +3317,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
"0703 Issue target reset to TGT %d LUN %d "
"rpi x%x nlp_flag x%x\n", cmnd->device->id,
cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag);
- status = lpfc_sli_issue_iocb_wait(phba,
- &phba->sli.ring[phba->sli.fcp_ring],
+ status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING,
iocbq, iocbqrsp, lpfc_cmd->timeout);
if (status == IOCB_TIMEDOUT) {
iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
@@ -2707,8 +3356,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
}
/**
- * lpfc_bus_reset_handler: eh_bus_reset_handler entry point of Scsi Host
- * Template data structure.
+ * lpfc_bus_reset_handler - scsi_host_template eh_bus_reset_handler entry point
* @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine does target reset to all target on @cmnd->device->host.
@@ -2808,8 +3456,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
}
/**
- * lpfc_slave_alloc: slave_alloc entry point of Scsi Host Template data
- * structure.
+ * lpfc_slave_alloc - scsi_host_template slave_alloc entry point
* @sdev: Pointer to scsi_device.
*
* This routine populates the cmds_per_lun count + 2 scsi_bufs into this host's
@@ -2826,11 +3473,10 @@ lpfc_slave_alloc(struct scsi_device *sdev)
{
struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *scsi_buf = NULL;
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- uint32_t total = 0, i;
+ uint32_t total = 0;
uint32_t num_to_alloc = 0;
- unsigned long flags;
+ int num_allocated = 0;
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
@@ -2864,27 +3510,19 @@ lpfc_slave_alloc(struct scsi_device *sdev)
(phba->cfg_hba_queue_depth - total));
num_to_alloc = phba->cfg_hba_queue_depth - total;
}
-
- for (i = 0; i < num_to_alloc; i++) {
- scsi_buf = lpfc_new_scsi_buf(vport);
- if (!scsi_buf) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0706 Failed to allocate "
- "command buffer\n");
- break;
- }
-
- spin_lock_irqsave(&phba->scsi_buf_list_lock, flags);
- phba->total_scsi_bufs++;
- list_add_tail(&scsi_buf->list, &phba->lpfc_scsi_buf_list);
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, flags);
+ num_allocated = lpfc_new_scsi_buf(vport, num_to_alloc);
+ if (num_to_alloc != num_allocated) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "0708 Allocation request of %d "
+ "command buffers did not succeed. "
+ "Allocated %d buffers.\n",
+ num_to_alloc, num_allocated);
}
return 0;
}
/**
- * lpfc_slave_configure: slave_configure entry point of Scsi Host Templater data
- * structure.
+ * lpfc_slave_configure - scsi_host_template slave_configure entry point
* @sdev: Pointer to scsi_device.
*
* This routine configures following items
@@ -2925,7 +3563,7 @@ lpfc_slave_configure(struct scsi_device *sdev)
}
/**
- * lpfc_slave_destroy: slave_destroy entry point of SHT data structure.
+ * lpfc_slave_destroy - slave_destroy entry point of SHT data structure
* @sdev: Pointer to scsi_device.
*
* This routine sets @sdev hostatdata filed to null.
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index c7c440d5fa29..65dfc8bd5b49 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -140,6 +140,8 @@ struct lpfc_scsi_buf {
struct fcp_rsp *fcp_rsp;
struct ulp_bde64 *fcp_bpl;
+ dma_addr_t dma_phys_bpl;
+
/* cur_iocbq has phys of the dma-able buffer.
* Iotag is in here
*/
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index a36a120561e2..ff04daf18f48 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -29,9 +29,12 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -40,24 +43,7 @@
#include "lpfc_logmsg.h"
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
-
-/*
- * 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, pmbox, 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", \
- pmbox->vport ? pmbox->vport->vpi : 0, \
- pmbox->mb.mbxCommand, \
- phba->pport->port_state, \
- psli->sli_flag, \
- flag)
-
+#include "lpfc_vport.h"
/* There are only four IOCB completion types. */
typedef enum _lpfc_iocb_type {
@@ -67,8 +53,352 @@ typedef enum _lpfc_iocb_type {
LPFC_ABORT_IOCB
} lpfc_iocb_type;
+
+/* Provide function prototypes local to this module. */
+static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
+ uint32_t);
+static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
+ uint8_t *, uint32_t *);
+
+static IOCB_t *
+lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
+{
+ return &iocbq->iocb;
+}
+
+/**
+ * lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue
+ * @q: The Work Queue to operate on.
+ * @wqe: The work Queue Entry to put on the Work queue.
+ *
+ * This routine will copy the contents of @wqe to the next available entry on
+ * the @q. This function will then ring the Work Queue Doorbell to signal the
+ * HBA to start processing the Work Queue Entry. This function returns 0 if
+ * successful. If no entries are available on @q then this function will return
+ * -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static uint32_t
+lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
+{
+ union lpfc_wqe *temp_wqe = q->qe[q->host_index].wqe;
+ struct lpfc_register doorbell;
+ uint32_t host_index;
+
+ /* If the host has not yet processed the next entry then we are done */
+ if (((q->host_index + 1) % q->entry_count) == q->hba_index)
+ return -ENOMEM;
+ /* set consumption flag every once in a while */
+ if (!((q->host_index + 1) % LPFC_RELEASE_NOTIFICATION_INTERVAL))
+ bf_set(lpfc_wqe_gen_wqec, &wqe->generic, 1);
+
+ lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
+
+ /* Update the host index before invoking device */
+ host_index = q->host_index;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+
+ /* Ring Doorbell */
+ doorbell.word0 = 0;
+ bf_set(lpfc_wq_doorbell_num_posted, &doorbell, 1);
+ bf_set(lpfc_wq_doorbell_index, &doorbell, host_index);
+ bf_set(lpfc_wq_doorbell_id, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.WQDBregaddr);
+ readl(q->phba->sli4_hba.WQDBregaddr); /* Flush */
+
+ return 0;
+}
+
+/**
+ * lpfc_sli4_wq_release - Updates internal hba index for WQ
+ * @q: The Work Queue to operate on.
+ * @index: The index to advance the hba index to.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * Work Queue Entries by the HBA. When the HBA indicates that it has consumed
+ * an entry the host calls this function to update the queue's internal
+ * pointers. This routine returns the number of entries that were consumed by
+ * the HBA.
+ **/
+static uint32_t
+lpfc_sli4_wq_release(struct lpfc_queue *q, uint32_t index)
+{
+ uint32_t released = 0;
+
+ if (q->hba_index == index)
+ return 0;
+ do {
+ q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ released++;
+ } while (q->hba_index != index);
+ return released;
+}
+
+/**
+ * lpfc_sli4_mq_put - Put a Mailbox Queue Entry on an Mailbox Queue
+ * @q: The Mailbox Queue to operate on.
+ * @wqe: The Mailbox Queue Entry to put on the Work queue.
+ *
+ * This routine will copy the contents of @mqe to the next available entry on
+ * the @q. This function will then ring the Work Queue Doorbell to signal the
+ * HBA to start processing the Work Queue Entry. This function returns 0 if
+ * successful. If no entries are available on @q then this function will return
+ * -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static uint32_t
+lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
+{
+ struct lpfc_mqe *temp_mqe = q->qe[q->host_index].mqe;
+ struct lpfc_register doorbell;
+ uint32_t host_index;
+
+ /* If the host has not yet processed the next entry then we are done */
+ if (((q->host_index + 1) % q->entry_count) == q->hba_index)
+ return -ENOMEM;
+ lpfc_sli_pcimem_bcopy(mqe, temp_mqe, q->entry_size);
+ /* Save off the mailbox pointer for completion */
+ q->phba->mbox = (MAILBOX_t *)temp_mqe;
+
+ /* Update the host index before invoking device */
+ host_index = q->host_index;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+
+ /* Ring Doorbell */
+ doorbell.word0 = 0;
+ bf_set(lpfc_mq_doorbell_num_posted, &doorbell, 1);
+ bf_set(lpfc_mq_doorbell_id, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.MQDBregaddr);
+ readl(q->phba->sli4_hba.MQDBregaddr); /* Flush */
+ return 0;
+}
+
+/**
+ * lpfc_sli4_mq_release - Updates internal hba index for MQ
+ * @q: The Mailbox Queue to operate on.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * a Mailbox Queue Entry by the HBA. When the HBA indicates that it has consumed
+ * an entry the host calls this function to update the queue's internal
+ * pointers. This routine returns the number of entries that were consumed by
+ * the HBA.
+ **/
+static uint32_t
+lpfc_sli4_mq_release(struct lpfc_queue *q)
+{
+ /* Clear the mailbox pointer for completion */
+ q->phba->mbox = NULL;
+ q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ return 1;
+}
+
/**
- * lpfc_cmd_iocb: Get next command iocb entry in the ring.
+ * lpfc_sli4_eq_get - Gets the next valid EQE from a EQ
+ * @q: The Event Queue to get the first valid EQE from
+ *
+ * This routine will get the first valid Event Queue Entry from @q, update
+ * the queue's internal hba index, and return the EQE. If no valid EQEs are in
+ * the Queue (no more work to do), or the Queue is full of EQEs that have been
+ * processed, but not popped back to the HBA then this routine will return NULL.
+ **/
+static struct lpfc_eqe *
+lpfc_sli4_eq_get(struct lpfc_queue *q)
+{
+ struct lpfc_eqe *eqe = q->qe[q->hba_index].eqe;
+
+ /* If the next EQE is not valid then we are done */
+ if (!bf_get(lpfc_eqe_valid, eqe))
+ return NULL;
+ /* If the host has not yet processed the next entry then we are done */
+ if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+ return NULL;
+
+ q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ return eqe;
+}
+
+/**
+ * lpfc_sli4_eq_release - Indicates the host has finished processing an EQ
+ * @q: The Event Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Event Queue Entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of EQEs that were popped.
+ **/
+uint32_t
+lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
+{
+ uint32_t released = 0;
+ struct lpfc_eqe *temp_eqe;
+ struct lpfc_register doorbell;
+
+ /* while there are valid entries */
+ while (q->hba_index != q->host_index) {
+ temp_eqe = q->qe[q->host_index].eqe;
+ bf_set(lpfc_eqe_valid, temp_eqe, 0);
+ released++;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+ }
+ if (unlikely(released == 0 && !arm))
+ return 0;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm) {
+ bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
+ }
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
+ bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ return released;
+}
+
+/**
+ * lpfc_sli4_cq_get - Gets the next valid CQE from a CQ
+ * @q: The Completion Queue to get the first valid CQE from
+ *
+ * This routine will get the first valid Completion Queue Entry from @q, update
+ * the queue's internal hba index, and return the CQE. If no valid CQEs are in
+ * the Queue (no more work to do), or the Queue is full of CQEs that have been
+ * processed, but not popped back to the HBA then this routine will return NULL.
+ **/
+static struct lpfc_cqe *
+lpfc_sli4_cq_get(struct lpfc_queue *q)
+{
+ struct lpfc_cqe *cqe;
+
+ /* If the next CQE is not valid then we are done */
+ if (!bf_get(lpfc_cqe_valid, q->qe[q->hba_index].cqe))
+ return NULL;
+ /* If the host has not yet processed the next entry then we are done */
+ if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+ return NULL;
+
+ cqe = q->qe[q->hba_index].cqe;
+ q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ return cqe;
+}
+
+/**
+ * lpfc_sli4_cq_release - Indicates the host has finished processing a CQ
+ * @q: The Completion Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Completion queue entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of CQEs that were released.
+ **/
+uint32_t
+lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
+{
+ uint32_t released = 0;
+ struct lpfc_cqe *temp_qe;
+ struct lpfc_register doorbell;
+
+ /* while there are valid entries */
+ while (q->hba_index != q->host_index) {
+ temp_qe = q->qe[q->host_index].cqe;
+ bf_set(lpfc_cqe_valid, temp_qe, 0);
+ released++;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+ }
+ if (unlikely(released == 0 && !arm))
+ return 0;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm)
+ bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
+ bf_set(lpfc_eqcq_doorbell_cqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ return released;
+}
+
+/**
+ * lpfc_sli4_rq_put - Put a Receive Buffer Queue Entry on a Receive Queue
+ * @q: The Header Receive Queue to operate on.
+ * @wqe: The Receive Queue Entry to put on the Receive queue.
+ *
+ * This routine will copy the contents of @wqe to the next available entry on
+ * the @q. This function will then ring the Receive Queue Doorbell to signal the
+ * HBA to start processing the Receive Queue Entry. This function returns the
+ * index that the rqe was copied to if successful. If no entries are available
+ * on @q then this function will return -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static int
+lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
+ struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe)
+{
+ struct lpfc_rqe *temp_hrqe = hq->qe[hq->host_index].rqe;
+ struct lpfc_rqe *temp_drqe = dq->qe[dq->host_index].rqe;
+ struct lpfc_register doorbell;
+ int put_index = hq->host_index;
+
+ if (hq->type != LPFC_HRQ || dq->type != LPFC_DRQ)
+ return -EINVAL;
+ if (hq->host_index != dq->host_index)
+ return -EINVAL;
+ /* If the host has not yet processed the next entry then we are done */
+ if (((hq->host_index + 1) % hq->entry_count) == hq->hba_index)
+ return -EBUSY;
+ lpfc_sli_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size);
+ lpfc_sli_pcimem_bcopy(drqe, temp_drqe, dq->entry_size);
+
+ /* Update the host index to point to the next slot */
+ hq->host_index = ((hq->host_index + 1) % hq->entry_count);
+ dq->host_index = ((dq->host_index + 1) % dq->entry_count);
+
+ /* Ring The Header Receive Queue Doorbell */
+ if (!(hq->host_index % LPFC_RQ_POST_BATCH)) {
+ doorbell.word0 = 0;
+ bf_set(lpfc_rq_doorbell_num_posted, &doorbell,
+ LPFC_RQ_POST_BATCH);
+ bf_set(lpfc_rq_doorbell_id, &doorbell, hq->queue_id);
+ writel(doorbell.word0, hq->phba->sli4_hba.RQDBregaddr);
+ }
+ return put_index;
+}
+
+/**
+ * lpfc_sli4_rq_release - Updates internal hba index for RQ
+ * @q: The Header Receive Queue to operate on.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * one Receive Queue Entry by the HBA. When the HBA indicates that it has
+ * consumed an entry the host calls this function to update the queue's
+ * internal pointers. This routine returns the number of entries that were
+ * consumed by the HBA.
+ **/
+static uint32_t
+lpfc_sli4_rq_release(struct lpfc_queue *hq, struct lpfc_queue *dq)
+{
+ if ((hq->type != LPFC_HRQ) || (dq->type != LPFC_DRQ))
+ return 0;
+ hq->hba_index = ((hq->hba_index + 1) % hq->entry_count);
+ dq->hba_index = ((dq->hba_index + 1) % dq->entry_count);
+ return 1;
+}
+
+/**
+ * lpfc_cmd_iocb - Get next command iocb entry in the ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -85,7 +415,7 @@ lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_resp_iocb: Get next response iocb entry in the ring.
+ * lpfc_resp_iocb - Get next response iocb entry in the ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -102,7 +432,7 @@ lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * __lpfc_sli_get_iocbq: Allocates an iocb object from iocb pool.
+ * __lpfc_sli_get_iocbq - Allocates an iocb object from iocb pool
* @phba: Pointer to HBA context object.
*
* This function is called with hbalock held. This function
@@ -121,7 +451,77 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_get_iocbq: Allocates an iocb object from iocb pool.
+ * __lpfc_clear_active_sglq - Remove the active sglq for this XRI.
+ * @phba: Pointer to HBA context object.
+ * @xritag: XRI value.
+ *
+ * This function clears the sglq pointer from the array of acive
+ * sglq's. The xritag that is passed in is used to index into the
+ * array. Before the xritag can be used it needs to be adjusted
+ * by subtracting the xribase.
+ *
+ * Returns sglq ponter = success, NULL = Failure.
+ **/
+static struct lpfc_sglq *
+__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
+{
+ uint16_t adj_xri;
+ struct lpfc_sglq *sglq;
+ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
+ if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri)
+ return NULL;
+ sglq = phba->sli4_hba.lpfc_sglq_active_list[adj_xri];
+ phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = NULL;
+ return sglq;
+}
+
+/**
+ * __lpfc_get_active_sglq - Get the active sglq for this XRI.
+ * @phba: Pointer to HBA context object.
+ * @xritag: XRI value.
+ *
+ * This function returns the sglq pointer from the array of acive
+ * sglq's. The xritag that is passed in is used to index into the
+ * array. Before the xritag can be used it needs to be adjusted
+ * by subtracting the xribase.
+ *
+ * Returns sglq ponter = success, NULL = Failure.
+ **/
+static struct lpfc_sglq *
+__lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
+{
+ uint16_t adj_xri;
+ struct lpfc_sglq *sglq;
+ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
+ if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri)
+ return NULL;
+ sglq = phba->sli4_hba.lpfc_sglq_active_list[adj_xri];
+ return sglq;
+}
+
+/**
+ * __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called with hbalock held. This function
+ * Gets a new driver sglq object from the sglq list. If the
+ * list is not empty then it is successful, it returns pointer to the newly
+ * allocated sglq object else it returns NULL.
+ **/
+static struct lpfc_sglq *
+__lpfc_sli_get_sglq(struct lpfc_hba *phba)
+{
+ struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list;
+ struct lpfc_sglq *sglq = NULL;
+ uint16_t adj_xri;
+ list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+ adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
+ phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
+ return sglq;
+}
+
+/**
+ * lpfc_sli_get_iocbq - Allocates an iocb object from iocb pool
* @phba: Pointer to HBA context object.
*
* This function is called with no lock held. This function
@@ -142,7 +542,7 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
}
/**
- * __lpfc_sli_release_iocbq: Release iocb to the iocb pool.
+ * __lpfc_sli_release_iocbq_s4 - Release iocb to the iocb pool
* @phba: Pointer to HBA context object.
* @iocbq: Pointer to driver iocb object.
*
@@ -150,9 +550,62 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
* iocb object to the iocb pool. The iotag in the iocb object
* does not change for each use of the iocb object. This function
* clears all other fields of the iocb object when it is freed.
+ * The sqlq structure that holds the xritag and phys and virtual
+ * mappings for the scatter gather list is retrieved from the
+ * active array of sglq. The get of the sglq pointer also clears
+ * the entry in the array. If the status of the IO indiactes that
+ * this IO was aborted then the sglq entry it put on the
+ * lpfc_abts_els_sgl_list until the CQ_ABORTED_XRI is received. If the
+ * IO has good status or fails for any other reason then the sglq
+ * entry is added to the free list (lpfc_sgl_list).
**/
static void
-__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+__lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ struct lpfc_sglq *sglq;
+ size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
+ unsigned long iflag;
+
+ if (iocbq->sli4_xritag == NO_XRI)
+ sglq = NULL;
+ else
+ sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
+ if (sglq) {
+ if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
+ || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
+ && (iocbq->iocb.un.ulpWord[4]
+ == IOERR_SLI_ABORTED))) {
+ spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
+ iflag);
+ list_add(&sglq->list,
+ &phba->sli4_hba.lpfc_abts_els_sgl_list);
+ spin_unlock_irqrestore(
+ &phba->sli4_hba.abts_sgl_list_lock, iflag);
+ } else
+ list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list);
+ }
+
+
+ /*
+ * Clean all volatile data fields, preserve iotag and node struct.
+ */
+ memset((char *)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+ iocbq->sli4_xritag = NO_XRI;
+ list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
+}
+
+/**
+ * __lpfc_sli_release_iocbq_s3 - Release iocb to the iocb pool
+ * @phba: Pointer to HBA context object.
+ * @iocbq: Pointer to driver iocb object.
+ *
+ * This function is called with hbalock held to release driver
+ * iocb object to the iocb pool. The iotag in the iocb object
+ * does not change for each use of the iocb object. This function
+ * clears all other fields of the iocb object when it is freed.
+ **/
+static void
+__lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
@@ -160,11 +613,28 @@ __lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
* Clean all volatile data fields, preserve iotag and node struct.
*/
memset((char*)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+ iocbq->sli4_xritag = NO_XRI;
list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
}
/**
- * lpfc_sli_release_iocbq: Release iocb to the iocb pool.
+ * __lpfc_sli_release_iocbq - Release iocb to the iocb pool
+ * @phba: Pointer to HBA context object.
+ * @iocbq: Pointer to driver iocb object.
+ *
+ * This function is called with hbalock held to release driver
+ * iocb object to the iocb pool. The iotag in the iocb object
+ * does not change for each use of the iocb object. This function
+ * clears all other fields of the iocb object when it is freed.
+ **/
+static void
+__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ phba->__lpfc_sli_release_iocbq(phba, iocbq);
+}
+
+/**
+ * lpfc_sli_release_iocbq - Release iocb to the iocb pool
* @phba: Pointer to HBA context object.
* @iocbq: Pointer to driver iocb object.
*
@@ -185,8 +655,40 @@ lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
}
/**
- * lpfc_sli_iocb_cmd_type: Get the iocb type.
- * @iocb_cmnd : iocb command code.
+ * lpfc_sli_cancel_iocbs - Cancel all iocbs from a list.
+ * @phba: Pointer to HBA context object.
+ * @iocblist: List of IOCBs.
+ * @ulpstatus: ULP status in IOCB command field.
+ * @ulpWord4: ULP word-4 in IOCB command field.
+ *
+ * This function is called with a list of IOCBs to cancel. It cancels the IOCB
+ * on the list by invoking the complete callback function associated with the
+ * IOCB with the provided @ulpstatus and @ulpword4 set to the IOCB commond
+ * fields.
+ **/
+void
+lpfc_sli_cancel_iocbs(struct lpfc_hba *phba, struct list_head *iocblist,
+ uint32_t ulpstatus, uint32_t ulpWord4)
+{
+ struct lpfc_iocbq *piocb;
+
+ while (!list_empty(iocblist)) {
+ list_remove_head(iocblist, piocb, struct lpfc_iocbq, list);
+
+ if (!piocb->iocb_cmpl)
+ lpfc_sli_release_iocbq(phba, piocb);
+ else {
+ piocb->iocb.ulpStatus = ulpstatus;
+ piocb->iocb.un.ulpWord[4] = ulpWord4;
+ (piocb->iocb_cmpl) (phba, piocb, piocb);
+ }
+ }
+ return;
+}
+
+/**
+ * lpfc_sli_iocb_cmd_type - Get the iocb type
+ * @iocb_cmnd: iocb command code.
*
* This function is called by ring event handler function to get the iocb type.
* This function translates the iocb command to an iocb command type used to
@@ -249,6 +751,14 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
case CMD_GEN_REQUEST64_CR:
case CMD_GEN_REQUEST64_CX:
case CMD_XMIT_ELS_RSP64_CX:
+ case DSSCMD_IWRITE64_CR:
+ case DSSCMD_IWRITE64_CX:
+ case DSSCMD_IREAD64_CR:
+ case DSSCMD_IREAD64_CX:
+ case DSSCMD_INVALIDATE_DEK:
+ case DSSCMD_SET_KEK:
+ case DSSCMD_GET_KEK_ID:
+ case DSSCMD_GEN_XFER:
type = LPFC_SOL_IOCB;
break;
case CMD_ABORT_XRI_CN:
@@ -295,7 +805,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
}
/**
- * lpfc_sli_ring_map: Issue config_ring mbox for all rings.
+ * lpfc_sli_ring_map - Issue config_ring mbox for all rings
* @phba: Pointer to HBA context object.
*
* This function is called from SLI initialization code
@@ -316,7 +826,7 @@ lpfc_sli_ring_map(struct lpfc_hba *phba)
pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb)
return -ENOMEM;
- pmbox = &pmb->mb;
+ pmbox = &pmb->u.mb;
phba->link_state = LPFC_INIT_MBX_CMDS;
for (i = 0; i < psli->num_rings; i++) {
lpfc_config_ring(phba, i, pmb);
@@ -338,7 +848,7 @@ lpfc_sli_ring_map(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_ringtxcmpl_put: Adds new iocb to the txcmplq.
+ * lpfc_sli_ringtxcmpl_put - Adds new iocb to the txcmplq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to the driver iocb object.
@@ -370,7 +880,7 @@ lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_ringtx_get: Get first element of the txq.
+ * lpfc_sli_ringtx_get - Get first element of the txq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -391,7 +901,7 @@ lpfc_sli_ringtx_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_sli_next_iocb_slot: Get next iocb slot in the ring.
+ * lpfc_sli_next_iocb_slot - Get next iocb slot in the ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -445,7 +955,7 @@ lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_sli_next_iotag: Get an iotag for the iocb.
+ * lpfc_sli_next_iotag - Get an iotag for the iocb
* @phba: Pointer to HBA context object.
* @iocbq: Pointer to driver iocb object.
*
@@ -520,7 +1030,7 @@ lpfc_sli_next_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
}
/**
- * lpfc_sli_submit_iocb: Submit an iocb to the firmware.
+ * lpfc_sli_submit_iocb - Submit an iocb to the firmware
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @iocb: Pointer to iocb slot in the ring.
@@ -577,7 +1087,7 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_update_full_ring: Update the chip attention register.
+ * lpfc_sli_update_full_ring - Update the chip attention register
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -608,7 +1118,7 @@ lpfc_sli_update_full_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_sli_update_ring: Update chip attention register.
+ * lpfc_sli_update_ring - Update chip attention register
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -632,7 +1142,7 @@ lpfc_sli_update_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_sli_resume_iocb: Process iocbs in the txq.
+ * lpfc_sli_resume_iocb - Process iocbs in the txq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -672,7 +1182,7 @@ lpfc_sli_resume_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_sli_next_hbq_slot: Get next hbq entry for the HBQ.
+ * lpfc_sli_next_hbq_slot - Get next hbq entry for the HBQ
* @phba: Pointer to HBA context object.
* @hbqno: HBQ number.
*
@@ -717,7 +1227,7 @@ lpfc_sli_next_hbq_slot(struct lpfc_hba *phba, uint32_t hbqno)
}
/**
- * lpfc_sli_hbqbuf_free_all: Free all the hbq buffers.
+ * lpfc_sli_hbqbuf_free_all - Free all the hbq buffers
* @phba: Pointer to HBA context object.
*
* This function is called with no lock held to free all the
@@ -747,8 +1257,8 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
phba->hbqs[i].buffer_count = 0;
}
/* Return all HBQ buffer that are in-fly */
- list_for_each_entry_safe(dmabuf, next_dmabuf,
- &phba->hbqbuf_in_list, list) {
+ list_for_each_entry_safe(dmabuf, next_dmabuf, &phba->rb_pend_list,
+ list) {
hbq_buf = container_of(dmabuf, struct hbq_dmabuf, dbuf);
list_del(&hbq_buf->dbuf.list);
if (hbq_buf->tag == -1) {
@@ -771,7 +1281,7 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_hbq_to_firmware: Post the hbq buffer to firmware.
+ * lpfc_sli_hbq_to_firmware - Post the hbq buffer to firmware
* @phba: Pointer to HBA context object.
* @hbqno: HBQ number.
* @hbq_buf: Pointer to HBQ buffer.
@@ -782,10 +1292,28 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
* pointer to the hbq entry if it successfully post the buffer
* else it will return NULL.
**/
-static struct lpfc_hbq_entry *
+static int
lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
struct hbq_dmabuf *hbq_buf)
{
+ return phba->lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buf);
+}
+
+/**
+ * lpfc_sli_hbq_to_firmware_s3 - Post the hbq buffer to SLI3 firmware
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ * @hbq_buf: Pointer to HBQ buffer.
+ *
+ * This function is called with the hbalock held to post a hbq buffer to the
+ * firmware. If the function finds an empty slot in the HBQ, it will post the
+ * buffer and place it on the hbq_buffer_list. The function will return zero if
+ * it successfully post the buffer else it will return an error.
+ **/
+static int
+lpfc_sli_hbq_to_firmware_s3(struct lpfc_hba *phba, uint32_t hbqno,
+ struct hbq_dmabuf *hbq_buf)
+{
struct lpfc_hbq_entry *hbqe;
dma_addr_t physaddr = hbq_buf->dbuf.phys;
@@ -806,8 +1334,40 @@ lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
/* flush */
readl(phba->hbq_put + hbqno);
list_add_tail(&hbq_buf->dbuf.list, &hbqp->hbq_buffer_list);
- }
- return hbqe;
+ return 0;
+ } else
+ return -ENOMEM;
+}
+
+/**
+ * lpfc_sli_hbq_to_firmware_s4 - Post the hbq buffer to SLI4 firmware
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ * @hbq_buf: Pointer to HBQ buffer.
+ *
+ * This function is called with the hbalock held to post an RQE to the SLI4
+ * firmware. If able to post the RQE to the RQ it will queue the hbq entry to
+ * the hbq_buffer_list and return zero, otherwise it will return an error.
+ **/
+static int
+lpfc_sli_hbq_to_firmware_s4(struct lpfc_hba *phba, uint32_t hbqno,
+ struct hbq_dmabuf *hbq_buf)
+{
+ int rc;
+ struct lpfc_rqe hrqe;
+ struct lpfc_rqe drqe;
+
+ hrqe.address_lo = putPaddrLow(hbq_buf->hbuf.phys);
+ hrqe.address_hi = putPaddrHigh(hbq_buf->hbuf.phys);
+ drqe.address_lo = putPaddrLow(hbq_buf->dbuf.phys);
+ drqe.address_hi = putPaddrHigh(hbq_buf->dbuf.phys);
+ rc = lpfc_sli4_rq_put(phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
+ &hrqe, &drqe);
+ if (rc < 0)
+ return rc;
+ hbq_buf->tag = rc;
+ list_add_tail(&hbq_buf->dbuf.list, &phba->hbqs[hbqno].hbq_buffer_list);
+ return 0;
}
/* HBQ for ELS and CT traffic. */
@@ -818,8 +1378,8 @@ static struct lpfc_hbq_init lpfc_els_hbq = {
.profile = 0,
.ring_mask = (1 << LPFC_ELS_RING),
.buffer_count = 0,
- .init_count = 20,
- .add_count = 5,
+ .init_count = 40,
+ .add_count = 40,
};
/* HBQ for the extra ring if needed */
@@ -841,7 +1401,7 @@ struct lpfc_hbq_init *lpfc_hbq_defs[] = {
};
/**
- * lpfc_sli_hbqbuf_fill_hbqs: Post more hbq buffers to HBQ.
+ * lpfc_sli_hbqbuf_fill_hbqs - Post more hbq buffers to HBQ
* @phba: Pointer to HBA context object.
* @hbqno: HBQ number.
* @count: Number of HBQ buffers to be posted.
@@ -882,7 +1442,7 @@ lpfc_sli_hbqbuf_fill_hbqs(struct lpfc_hba *phba, uint32_t hbqno, uint32_t count)
dbuf.list);
hbq_buffer->tag = (phba->hbqs[hbqno].buffer_count |
(hbqno << 16));
- if (lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
+ if (!lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
phba->hbqs[hbqno].buffer_count++;
posted++;
} else
@@ -901,7 +1461,7 @@ err:
}
/**
- * lpfc_sli_hbqbuf_add_hbqs: Post more HBQ buffers to firmware.
+ * lpfc_sli_hbqbuf_add_hbqs - Post more HBQ buffers to firmware
* @phba: Pointer to HBA context object.
* @qno: HBQ number.
*
@@ -917,7 +1477,7 @@ lpfc_sli_hbqbuf_add_hbqs(struct lpfc_hba *phba, uint32_t qno)
}
/**
- * lpfc_sli_hbqbuf_init_hbqs: Post initial buffers to the HBQ.
+ * lpfc_sli_hbqbuf_init_hbqs - Post initial buffers to the HBQ
* @phba: Pointer to HBA context object.
* @qno: HBQ queue number.
*
@@ -933,7 +1493,26 @@ lpfc_sli_hbqbuf_init_hbqs(struct lpfc_hba *phba, uint32_t qno)
}
/**
- * lpfc_sli_hbqbuf_find: Find the hbq buffer associated with a tag.
+ * lpfc_sli_hbqbuf_get - Remove the first hbq off of an hbq list
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ *
+ * This function removes the first hbq buffer on an hbq list and returns a
+ * pointer to that buffer. If it finds no buffers on the list it returns NULL.
+ **/
+static struct hbq_dmabuf *
+lpfc_sli_hbqbuf_get(struct list_head *rb_list)
+{
+ struct lpfc_dmabuf *d_buf;
+
+ list_remove_head(rb_list, d_buf, struct lpfc_dmabuf, list);
+ if (!d_buf)
+ return NULL;
+ return container_of(d_buf, struct hbq_dmabuf, dbuf);
+}
+
+/**
+ * lpfc_sli_hbqbuf_find - Find the hbq buffer associated with a tag
* @phba: Pointer to HBA context object.
* @tag: Tag of the hbq buffer.
*
@@ -953,12 +1532,15 @@ lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
if (hbqno >= LPFC_MAX_HBQS)
return NULL;
+ spin_lock_irq(&phba->hbalock);
list_for_each_entry(d_buf, &phba->hbqs[hbqno].hbq_buffer_list, list) {
hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
if (hbq_buf->tag == tag) {
+ spin_unlock_irq(&phba->hbalock);
return hbq_buf;
}
}
+ spin_unlock_irq(&phba->hbalock);
lpfc_printf_log(phba, KERN_ERR, LOG_SLI | LOG_VPORT,
"1803 Bad hbq tag. Data: x%x x%x\n",
tag, phba->hbqs[tag >> 16].buffer_count);
@@ -966,7 +1548,7 @@ lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
}
/**
- * lpfc_sli_free_hbq: Give back the hbq buffer to firmware.
+ * lpfc_sli_free_hbq - Give back the hbq buffer to firmware
* @phba: Pointer to HBA context object.
* @hbq_buffer: Pointer to HBQ buffer.
*
@@ -981,14 +1563,13 @@ lpfc_sli_free_hbq(struct lpfc_hba *phba, struct hbq_dmabuf *hbq_buffer)
if (hbq_buffer) {
hbqno = hbq_buffer->tag >> 16;
- if (!lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
+ if (lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer))
(phba->hbqs[hbqno].hbq_free_buffer)(phba, hbq_buffer);
- }
}
}
/**
- * lpfc_sli_chk_mbx_command: Check if the mailbox is a legitimate mailbox.
+ * lpfc_sli_chk_mbx_command - Check if the mailbox is a legitimate mailbox
* @mbxCommand: mailbox command code.
*
* This function is called by the mailbox event handler function to verify
@@ -1054,6 +1635,15 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
case MBX_HEARTBEAT:
case MBX_PORT_CAPABILITIES:
case MBX_PORT_IOV_CONTROL:
+ case MBX_SLI4_CONFIG:
+ case MBX_SLI4_REQ_FTRS:
+ case MBX_REG_FCFI:
+ case MBX_UNREG_FCFI:
+ case MBX_REG_VFI:
+ case MBX_UNREG_VFI:
+ case MBX_INIT_VPI:
+ case MBX_INIT_VFI:
+ case MBX_RESUME_RPI:
ret = mbxCommand;
break;
default:
@@ -1064,8 +1654,7 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
}
/**
- * lpfc_sli_wake_mbox_wait: Completion handler for mbox issued from
- * lpfc_sli_issue_mbox_wait.
+ * lpfc_sli_wake_mbox_wait - lpfc_sli_issue_mbox_wait mbox completion handler
* @phba: Pointer to HBA context object.
* @pmboxq: Pointer to mailbox command.
*
@@ -1075,7 +1664,7 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
* will wake up thread waiting on the wait queue pointed by context1
* of the mailbox.
**/
-static void
+void
lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
wait_queue_head_t *pdone_q;
@@ -1096,7 +1685,7 @@ lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
/**
- * lpfc_sli_def_mbox_cmpl: Default mailbox completion handler.
+ * lpfc_sli_def_mbox_cmpl - Default mailbox completion handler
* @phba: Pointer to HBA context object.
* @pmb: Pointer to mailbox object.
*
@@ -1109,7 +1698,7 @@ void
lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_dmabuf *mp;
- uint16_t rpi;
+ uint16_t rpi, vpi;
int rc;
mp = (struct lpfc_dmabuf *) (pmb->context1);
@@ -1119,28 +1708,34 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
kfree(mp);
}
+ if ((pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) &&
+ (phba->sli_rev == LPFC_SLI_REV4))
+ lpfc_sli4_free_rpi(phba, pmb->u.mb.un.varUnregLogin.rpi);
+
/*
* If a REG_LOGIN succeeded after node is destroyed or node
* is in re-discovery driver need to cleanup the RPI.
*/
if (!(phba->pport->load_flag & FC_UNLOADING) &&
- pmb->mb.mbxCommand == MBX_REG_LOGIN64 &&
- !pmb->mb.mbxStatus) {
-
- rpi = pmb->mb.un.varWords[0];
- lpfc_unreg_login(phba, pmb->mb.un.varRegLogin.vpi, rpi, pmb);
+ pmb->u.mb.mbxCommand == MBX_REG_LOGIN64 &&
+ !pmb->u.mb.mbxStatus) {
+ rpi = pmb->u.mb.un.varWords[0];
+ vpi = pmb->u.mb.un.varRegLogin.vpi - phba->vpi_base;
+ lpfc_unreg_login(phba, vpi, rpi, pmb);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if (rc != MBX_NOT_FINISHED)
return;
}
- mempool_free(pmb, phba->mbox_mem_pool);
- return;
+ if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG)
+ lpfc_sli4_mbox_cmd_free(phba, pmb);
+ else
+ mempool_free(pmb, phba->mbox_mem_pool);
}
/**
- * lpfc_sli_handle_mb_event: Handle mailbox completions from firmware.
+ * lpfc_sli_handle_mb_event - Handle mailbox completions from firmware
* @phba: Pointer to HBA context object.
*
* This function is called with no lock held. This function processes all
@@ -1173,7 +1768,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
if (pmb == NULL)
break;
- pmbox = &pmb->mb;
+ pmbox = &pmb->u.mb;
if (pmbox->mbxCommand != MBX_HEARTBEAT) {
if (pmb->vport) {
@@ -1202,9 +1797,10 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
/* Unknow mailbox command compl */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"(%d):0323 Unknown Mailbox command "
- "%x Cmpl\n",
+ "x%x (x%x) Cmpl\n",
pmb->vport ? pmb->vport->vpi : 0,
- pmbox->mbxCommand);
+ pmbox->mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, pmb));
phba->link_state = LPFC_HBA_ERROR;
phba->work_hs = HS_FFER3;
lpfc_handle_eratt(phba);
@@ -1219,29 +1815,29 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
LOG_MBOX | LOG_SLI,
"(%d):0305 Mbox cmd cmpl "
"error - RETRYing Data: x%x "
- "x%x x%x x%x\n",
+ "(x%x) x%x x%x x%x\n",
pmb->vport ? pmb->vport->vpi :0,
pmbox->mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba,
+ pmb),
pmbox->mbxStatus,
pmbox->un.varWords[0],
pmb->vport->port_state);
pmbox->mbxStatus = 0;
pmbox->mbxOwner = OWN_HOST;
- spin_lock_irq(&phba->hbalock);
- phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
- spin_unlock_irq(&phba->hbalock);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
- if (rc == MBX_SUCCESS)
+ if (rc != MBX_NOT_FINISHED)
continue;
}
}
/* Mailbox cmd <cmd> Cmpl <cmpl> */
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
- "(%d):0307 Mailbox cmd x%x Cmpl x%p "
+ "(%d):0307 Mailbox cmd x%x (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",
pmb->vport ? pmb->vport->vpi : 0,
pmbox->mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, pmb),
pmb->mbox_cmpl,
*((uint32_t *) pmbox),
pmbox->un.varWords[0],
@@ -1260,7 +1856,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_get_buff: Get the buffer associated with the buffer tag.
+ * lpfc_sli_get_buff - Get the buffer associated with the buffer tag
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @tag: buffer tag.
@@ -1286,9 +1882,48 @@ lpfc_sli_get_buff(struct lpfc_hba *phba,
return &hbq_entry->dbuf;
}
+/**
+ * lpfc_complete_unsol_iocb - Complete an unsolicited sequence
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @saveq: Pointer to the iocbq struct representing the sequence starting frame.
+ * @fch_r_ctl: the r_ctl for the first frame of the sequence.
+ * @fch_type: the type for the first frame of the sequence.
+ *
+ * This function is called with no lock held. This function uses the r_ctl and
+ * type of the received sequence to find the correct callback function to call
+ * to process the sequence.
+ **/
+static int
+lpfc_complete_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *saveq, uint32_t fch_r_ctl,
+ uint32_t fch_type)
+{
+ int i;
+
+ /* unSolicited Responses */
+ if (pring->prt[0].profile) {
+ if (pring->prt[0].lpfc_sli_rcv_unsol_event)
+ (pring->prt[0].lpfc_sli_rcv_unsol_event) (phba, pring,
+ saveq);
+ return 1;
+ }
+ /* We must search, based on rctl / type
+ for the right routine */
+ for (i = 0; i < pring->num_mask; i++) {
+ if ((pring->prt[i].rctl == fch_r_ctl) &&
+ (pring->prt[i].type == fch_type)) {
+ if (pring->prt[i].lpfc_sli_rcv_unsol_event)
+ (pring->prt[i].lpfc_sli_rcv_unsol_event)
+ (phba, pring, saveq);
+ return 1;
+ }
+ }
+ return 0;
+}
/**
- * lpfc_sli_process_unsol_iocb: Unsolicited iocb handler.
+ * lpfc_sli_process_unsol_iocb - Unsolicited iocb handler
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @saveq: Pointer to the unsolicited iocb.
@@ -1308,7 +1943,7 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
IOCB_t * irsp;
WORD5 * w5p;
uint32_t Rctl, Type;
- uint32_t match, i;
+ uint32_t match;
struct lpfc_iocbq *iocbq;
struct lpfc_dmabuf *dmzbuf;
@@ -1451,40 +2086,17 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
- /* unSolicited Responses */
- if (pring->prt[0].profile) {
- if (pring->prt[0].lpfc_sli_rcv_unsol_event)
- (pring->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 < pring->num_mask; i++) {
- if ((pring->prt[i].rctl == Rctl)
- && (pring->prt[i].type == Type)) {
- if (pring->prt[i].lpfc_sli_rcv_unsol_event)
- (pring->prt[i].lpfc_sli_rcv_unsol_event)
- (phba, pring, saveq);
- match = 1;
- break;
- }
- }
- }
- if (match == 0) {
- /* Unexpected Rctl / Type received */
- /* Ring <ringno> handler: unexpected
- Rctl <Rctl> Type <Type> received */
+ if (!lpfc_complete_unsol_iocb(phba, pring, saveq, Rctl, Type))
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0313 Ring %d handler: unexpected Rctl x%x "
"Type x%x received\n",
pring->ringno, Rctl, Type);
- }
+
return 1;
}
/**
- * lpfc_sli_iocbq_lookup: Find command iocb for the given response iocb.
+ * lpfc_sli_iocbq_lookup - Find command iocb for the given response iocb
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @prspiocb: Pointer to response iocb object.
@@ -1521,7 +2133,38 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
}
/**
- * lpfc_sli_process_sol_iocb: process solicited iocb completion.
+ * lpfc_sli_iocbq_lookup_by_tag - Find command iocb for the iotag
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @iotag: IOCB tag.
+ *
+ * This function looks up the iocb_lookup table to get the command iocb
+ * corresponding to the given iotag. This function is called with the
+ * hbalock held.
+ * This function returns the command iocb object if it finds the command
+ * iocb else returns NULL.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba,
+ struct lpfc_sli_ring *pring, uint16_t iotag)
+{
+ struct lpfc_iocbq *cmd_iocb;
+
+ if (iotag != 0 && iotag <= phba->sli.last_iotag) {
+ cmd_iocb = phba->sli.iocbq_lookup[iotag];
+ list_del_init(&cmd_iocb->list);
+ pring->txcmplq_cnt--;
+ return cmd_iocb;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0372 iotag x%x is out off range: max iotag (x%x)\n",
+ iotag, phba->sli.last_iotag);
+ return NULL;
+}
+
+/**
+ * lpfc_sli_process_sol_iocb - process solicited iocb completion
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @saveq: Pointer to the response iocb to be processed.
@@ -1597,7 +2240,7 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* Ring <ringno> handler: unexpected completion IoTag
* <IoTag>
*/
- lpfc_printf_vlog(cmdiocbp->vport, KERN_WARNING, LOG_SLI,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0322 Ring %d handler: "
"unexpected completion IoTag x%x "
"Data: x%x x%x x%x x%x\n",
@@ -1614,7 +2257,7 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_rsp_pointers_error: Response ring pointer error handler.
+ * lpfc_sli_rsp_pointers_error - Response ring pointer error handler
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -1652,7 +2295,7 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
}
/**
- * lpfc_poll_eratt: Error attention polling timer timeout handler.
+ * lpfc_poll_eratt - Error attention polling timer timeout handler
* @ptr: Pointer to address of HBA context object.
*
* This function is invoked by the Error Attention polling timer when the
@@ -1682,7 +2325,7 @@ void lpfc_poll_eratt(unsigned long ptr)
}
/**
- * lpfc_sli_poll_fcp_ring: Handle FCP ring completion in polling mode.
+ * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode
* @phba: Pointer to HBA context object.
*
* This function is called from lpfc_queuecommand, lpfc_poll_timeout,
@@ -1845,7 +2488,7 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_handle_fast_ring_event: Handle ring events on FCP ring.
+ * lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @mask: Host attention register mask for this ring.
@@ -1923,7 +2566,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_rampdown_queue_depth(phba);
+ phba->lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2037,39 +2680,215 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
}
/**
- * lpfc_sli_handle_slow_ring_event: Handle ring events for non-FCP rings.
+ * lpfc_sli_sp_handle_rspiocb - Handle slow-path response iocb
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @rspiocbp: Pointer to driver response IOCB object.
+ *
+ * This function is called from the worker thread when there is a slow-path
+ * response IOCB to process. This function chains all the response iocbs until
+ * seeing the iocb with the LE bit set. The function will call
+ * lpfc_sli_process_sol_iocb function if the response iocb indicates a
+ * completion of a command iocb. The function will call the
+ * lpfc_sli_process_unsol_iocb function if this is an unsolicited iocb.
+ * The function frees the resources or calls the completion handler if this
+ * iocb is an abort completion. The function returns NULL when the response
+ * iocb has the LE bit set and all the chained iocbs are processed, otherwise
+ * this function shall chain the iocb on to the iocb_continueq and return the
+ * response iocb passed in.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli_sp_handle_rspiocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *rspiocbp)
+{
+ struct lpfc_iocbq *saveq;
+ struct lpfc_iocbq *cmdiocbp;
+ struct lpfc_iocbq *next_iocb;
+ IOCB_t *irsp = NULL;
+ uint32_t free_saveq;
+ uint8_t iocb_cmd_type;
+ lpfc_iocb_type type;
+ unsigned long iflag;
+ int rc;
+
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ /* First add the response iocb to the countinueq list */
+ list_add_tail(&rspiocbp->list, &(pring->iocb_continueq));
+ pring->iocb_continueq_cnt++;
+
+ /* Now, determine whetehr the list is completed for processing */
+ irsp = &rspiocbp->iocb;
+ if (irsp->ulpLe) {
+ /*
+ * By default, the driver expects to free all resources
+ * associated with this iocb completion.
+ */
+ free_saveq = 1;
+ saveq = list_get_first(&pring->iocb_continueq,
+ struct lpfc_iocbq, list);
+ irsp = &(saveq->iocb);
+ list_del_init(&pring->iocb_continueq);
+ pring->iocb_continueq_cnt = 0;
+
+ pring->stats.iocb_rsp++;
+
+ /*
+ * If resource errors reported from HBA, reduce
+ * queuedepths of the SCSI device.
+ */
+ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ phba->lpfc_rampdown_queue_depth(phba);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ }
+
+ if (irsp->ulpStatus) {
+ /* Rsp ring <ringno> error: IOCB */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0328 Rsp Ring %d error: "
+ "IOCB Data: "
+ "x%x x%x x%x x%x "
+ "x%x x%x x%x x%x "
+ "x%x x%x x%x x%x "
+ "x%x x%x x%x x%x\n",
+ pring->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),
+ *(((uint32_t *) irsp) + 8),
+ *(((uint32_t *) irsp) + 9),
+ *(((uint32_t *) irsp) + 10),
+ *(((uint32_t *) irsp) + 11),
+ *(((uint32_t *) irsp) + 12),
+ *(((uint32_t *) irsp) + 13),
+ *(((uint32_t *) irsp) + 14),
+ *(((uint32_t *) irsp) + 15));
+ }
+
+ /*
+ * Fetch the IOCB command type and call the correct completion
+ * routine. Solicited and Unsolicited IOCBs on the ELS ring
+ * get freed back to the lpfc_iocb_list by the discovery
+ * kernel thread.
+ */
+ iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
+ type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
+ switch (type) {
+ case LPFC_SOL_IOCB:
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ rc = lpfc_sli_process_sol_iocb(phba, pring, saveq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ break;
+
+ case LPFC_UNSOL_IOCB:
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ rc = lpfc_sli_process_unsol_iocb(phba, pring, saveq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (!rc)
+ free_saveq = 0;
+ break;
+
+ case LPFC_ABORT_IOCB:
+ cmdiocbp = NULL;
+ if (irsp->ulpCommand != CMD_XRI_ABORTED_CX)
+ cmdiocbp = lpfc_sli_iocbq_lookup(phba, pring,
+ saveq);
+ if (cmdiocbp) {
+ /* Call the specified completion routine */
+ if (cmdiocbp->iocb_cmpl) {
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
+ (cmdiocbp->iocb_cmpl)(phba, cmdiocbp,
+ saveq);
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
+ } else
+ __lpfc_sli_release_iocbq(phba,
+ cmdiocbp);
+ }
+ break;
+
+ case 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\n",
+ phba->brd_no, adaptermsg);
+ } else {
+ /* Unknown IOCB command */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0335 Unknown IOCB "
+ "command Data: x%x "
+ "x%x x%x x%x\n",
+ irsp->ulpCommand,
+ irsp->ulpStatus,
+ irsp->ulpIoTag,
+ irsp->ulpContext);
+ }
+ break;
+ }
+
+ if (free_saveq) {
+ list_for_each_entry_safe(rspiocbp, next_iocb,
+ &saveq->list, list) {
+ list_del(&rspiocbp->list);
+ __lpfc_sli_release_iocbq(phba, rspiocbp);
+ }
+ __lpfc_sli_release_iocbq(phba, saveq);
+ }
+ rspiocbp = NULL;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return rspiocbp;
+}
+
+/**
+ * lpfc_sli_handle_slow_ring_event - Wrapper func for handling slow-path iocbs
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @mask: Host attention register mask for this ring.
*
- * This function is called from the worker thread when there is a ring
- * event for non-fcp rings. The caller does not hold any lock .
- * The function processes each response iocb in the response ring until it
- * finds an iocb with LE bit set and chains all the iocbs upto the iocb with
- * LE bit set. The function will call lpfc_sli_process_sol_iocb function if the
- * response iocb indicates a completion of a command iocb. The function
- * will call lpfc_sli_process_unsol_iocb function if this is an unsolicited
- * iocb. The function frees the resources or calls the completion handler if
- * this iocb is an abort completion. The function returns 0 when the allocated
- * iocbs are not freed, otherwise returns 1.
+ * This routine wraps the actual slow_ring event process routine from the
+ * API jump table function pointer from the lpfc_hba struct.
**/
-int
+void
lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
+ phba->lpfc_sli_handle_slow_ring_event(phba, pring, mask);
+}
+
+/**
+ * lpfc_sli_handle_slow_ring_event_s3 - Handle SLI3 ring event for non-FCP rings
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @mask: Host attention register mask for this ring.
+ *
+ * This function is called from the worker thread when there is a ring event
+ * for non-fcp rings. The caller does not hold any lock. The function will
+ * remove each response iocb in the response ring and calls the handle
+ * response iocb routine (lpfc_sli_sp_handle_rspiocb) to process it.
+ **/
+static void
+lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
+ struct lpfc_sli_ring *pring, uint32_t mask)
+{
struct lpfc_pgp *pgp;
IOCB_t *entry;
IOCB_t *irsp = NULL;
struct lpfc_iocbq *rspiocbp = NULL;
- struct lpfc_iocbq *next_iocb;
- struct lpfc_iocbq *cmdiocbp;
- struct lpfc_iocbq *saveq;
- uint8_t iocb_cmd_type;
- lpfc_iocb_type type;
- uint32_t status, free_saveq;
uint32_t portRspPut, portRspMax;
- int rc = 1;
unsigned long iflag;
+ uint32_t status;
pgp = &phba->port_gp[pring->ringno];
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -2097,7 +2916,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
phba->work_hs = HS_FFER3;
lpfc_handle_eratt(phba);
- return 1;
+ return;
}
rmb();
@@ -2142,138 +2961,10 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
- list_add_tail(&rspiocbp->list, &(pring->iocb_continueq));
-
- pring->iocb_continueq_cnt++;
- if (irsp->ulpLe) {
- /*
- * By default, the driver expects to free all resources
- * associated with this iocb completion.
- */
- free_saveq = 1;
- saveq = list_get_first(&pring->iocb_continueq,
- struct lpfc_iocbq, list);
- irsp = &(saveq->iocb);
- list_del_init(&pring->iocb_continueq);
- pring->iocb_continueq_cnt = 0;
-
- pring->stats.iocb_rsp++;
-
- /*
- * If resource errors reported from HBA, reduce
- * queuedepths of the SCSI device.
- */
- if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_rampdown_queue_depth(phba);
- spin_lock_irqsave(&phba->hbalock, iflag);
- }
-
- if (irsp->ulpStatus) {
- /* Rsp ring <ringno> error: IOCB */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0328 Rsp Ring %d error: "
- "IOCB Data: "
- "x%x x%x x%x x%x "
- "x%x x%x x%x x%x "
- "x%x x%x x%x x%x "
- "x%x x%x x%x x%x\n",
- pring->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),
- *(((uint32_t *) irsp) + 8),
- *(((uint32_t *) irsp) + 9),
- *(((uint32_t *) irsp) + 10),
- *(((uint32_t *) irsp) + 11),
- *(((uint32_t *) irsp) + 12),
- *(((uint32_t *) irsp) + 13),
- *(((uint32_t *) irsp) + 14),
- *(((uint32_t *) irsp) + 15));
- }
-
- /*
- * Fetch the IOCB command type and call the correct
- * completion routine. Solicited and Unsolicited
- * IOCBs on the ELS ring get freed back to the
- * lpfc_iocb_list by the discovery kernel thread.
- */
- iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
- type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
- if (type == LPFC_SOL_IOCB) {
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- rc = lpfc_sli_process_sol_iocb(phba, pring,
- saveq);
- spin_lock_irqsave(&phba->hbalock, iflag);
- } else if (type == LPFC_UNSOL_IOCB) {
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- rc = lpfc_sli_process_unsol_iocb(phba, pring,
- saveq);
- spin_lock_irqsave(&phba->hbalock, iflag);
- if (!rc)
- free_saveq = 0;
- } else if (type == LPFC_ABORT_IOCB) {
- if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) &&
- ((cmdiocbp =
- lpfc_sli_iocbq_lookup(phba, pring,
- saveq)))) {
- /* Call the specified completion
- routine */
- if (cmdiocbp->iocb_cmpl) {
- spin_unlock_irqrestore(
- &phba->hbalock,
- iflag);
- (cmdiocbp->iocb_cmpl) (phba,
- cmdiocbp, saveq);
- spin_lock_irqsave(
- &phba->hbalock,
- iflag);
- } else
- __lpfc_sli_release_iocbq(phba,
- cmdiocbp);
- }
- } 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\n",
- phba->brd_no, adaptermsg);
- } else {
- /* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0335 Unknown IOCB "
- "command Data: x%x "
- "x%x x%x x%x\n",
- irsp->ulpCommand,
- irsp->ulpStatus,
- irsp->ulpIoTag,
- irsp->ulpContext);
- }
- }
-
- if (free_saveq) {
- list_for_each_entry_safe(rspiocbp, next_iocb,
- &saveq->list, list) {
- list_del(&rspiocbp->list);
- __lpfc_sli_release_iocbq(phba,
- rspiocbp);
- }
- __lpfc_sli_release_iocbq(phba, saveq);
- }
- rspiocbp = NULL;
- }
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ /* Handle the response IOCB */
+ rspiocbp = lpfc_sli_sp_handle_rspiocb(phba, pring, rspiocbp);
+ spin_lock_irqsave(&phba->hbalock, iflag);
/*
* If the port response put pointer has not been updated, sync
@@ -2307,11 +2998,41 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
}
spin_unlock_irqrestore(&phba->hbalock, iflag);
- return rc;
+ return;
}
/**
- * lpfc_sli_abort_iocb_ring: Abort all iocbs in the ring.
+ * lpfc_sli_handle_slow_ring_event_s4 - Handle SLI4 slow-path els events
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @mask: Host attention register mask for this ring.
+ *
+ * This function is called from the worker thread when there is a pending
+ * ELS response iocb on the driver internal slow-path response iocb worker
+ * queue. The caller does not hold any lock. The function will remove each
+ * response iocb from the response worker queue and calls the handle
+ * response iocb routine (lpfc_sli_sp_handle_rspiocb) to process it.
+ **/
+static void
+lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
+ struct lpfc_sli_ring *pring, uint32_t mask)
+{
+ struct lpfc_iocbq *irspiocbq;
+ unsigned long iflag;
+
+ while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+ /* Get the response iocb from the head of work queue */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue,
+ irspiocbq, struct lpfc_iocbq, list);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ /* Process the response iocb */
+ lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+ }
+}
+
+/**
+ * lpfc_sli_abort_iocb_ring - Abort all iocbs in the ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
*
@@ -2325,7 +3046,6 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
LIST_HEAD(completions);
struct lpfc_iocbq *iocb, *next_iocb;
- IOCB_t *cmd = NULL;
if (pring->ringno == LPFC_ELS_RING) {
lpfc_fabric_abort_hba(phba);
@@ -2344,23 +3064,13 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
spin_unlock_irq(&phba->hbalock);
- while (!list_empty(&completions)) {
- iocb = list_get_first(&completions, struct lpfc_iocbq, list);
- cmd = &iocb->iocb;
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
}
/**
- * lpfc_sli_flush_fcp_rings: flush all iocbs in the fcp ring.
+ * lpfc_sli_flush_fcp_rings - flush all iocbs in the fcp ring
* @phba: Pointer to HBA context object.
*
* This function flushes all iocbs in the fcp ring and frees all the iocb
@@ -2374,8 +3084,6 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
{
LIST_HEAD(txq);
LIST_HEAD(txcmplq);
- struct lpfc_iocbq *iocb;
- IOCB_t *cmd = NULL;
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
@@ -2393,38 +3101,16 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
/* Flush the txq */
- while (!list_empty(&txq)) {
- iocb = list_get_first(&txq, struct lpfc_iocbq, list);
- cmd = &iocb->iocb;
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_DOWN;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
/* Flush the txcmpq */
- while (!list_empty(&txcmplq)) {
- iocb = list_get_first(&txcmplq, struct lpfc_iocbq, list);
- cmd = &iocb->iocb;
- list_del_init(&iocb->list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_DOWN;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
}
/**
- * lpfc_sli_brdready: Check for host status bits.
+ * lpfc_sli_brdready_s3 - Check for sli3 host ready status
* @phba: Pointer to HBA context object.
* @mask: Bit mask to be checked.
*
@@ -2436,8 +3122,8 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
* function returns 1 when HBA fail to restart otherwise returns
* zero.
**/
-int
-lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
+static int
+lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask)
{
uint32_t status;
int i = 0;
@@ -2481,10 +3167,60 @@ lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
return retval;
}
+/**
+ * lpfc_sli_brdready_s4 - Check for sli4 host ready status
+ * @phba: Pointer to HBA context object.
+ * @mask: Bit mask to be checked.
+ *
+ * This function checks the host status register to check if HBA is
+ * ready. This function will wait in a loop for the HBA to be ready
+ * If the HBA is not ready , the function will will reset the HBA PCI
+ * function again. The function returns 1 when HBA fail to be ready
+ * otherwise returns zero.
+ **/
+static int
+lpfc_sli_brdready_s4(struct lpfc_hba *phba, uint32_t mask)
+{
+ uint32_t status;
+ int retval = 0;
+
+ /* Read the HBA Host Status Register */
+ status = lpfc_sli4_post_status_check(phba);
+
+ if (status) {
+ phba->pport->port_state = LPFC_VPORT_UNKNOWN;
+ lpfc_sli_brdrestart(phba);
+ status = lpfc_sli4_post_status_check(phba);
+ }
+
+ /* Check to see if any errors occurred during init */
+ if (status) {
+ phba->link_state = LPFC_HBA_ERROR;
+ retval = 1;
+ } else
+ phba->sli4_hba.intr_enable = 0;
+
+ return retval;
+}
+
+/**
+ * lpfc_sli_brdready - Wrapper func for checking the hba readyness
+ * @phba: Pointer to HBA context object.
+ * @mask: Bit mask to be checked.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba readyness check routine
+ * from the API jump table function pointer from the lpfc_hba struct.
+ **/
+int
+lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
+{
+ return phba->lpfc_sli_brdready(phba, mask);
+}
+
#define BARRIER_TEST_PATTERN (0xdeadbeef)
/**
- * lpfc_reset_barrier: Make HBA ready for HBA reset.
+ * lpfc_reset_barrier - Make HBA ready for HBA reset
* @phba: Pointer to HBA context object.
*
* This function is called before resetting an HBA. This
@@ -2536,7 +3272,7 @@ void lpfc_reset_barrier(struct lpfc_hba *phba)
mdelay(1);
if (readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN)) {
- if (phba->sli.sli_flag & LPFC_SLI2_ACTIVE ||
+ if (phba->sli.sli_flag & LPFC_SLI_ACTIVE ||
phba->pport->stopped)
goto restore_hc;
else
@@ -2564,7 +3300,7 @@ restore_hc:
}
/**
- * lpfc_sli_brdkill: Issue a kill_board mailbox command.
+ * lpfc_sli_brdkill - Issue a kill_board mailbox command
* @phba: Pointer to HBA context object.
*
* This function issues a kill_board mailbox command and waits for
@@ -2617,7 +3353,9 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
return 1;
}
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ spin_lock_irq(&phba->hbalock);
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
mempool_free(pmb, phba->mbox_mem_pool);
@@ -2640,10 +3378,10 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
}
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ psli->mbox_active = NULL;
phba->link_flag &= ~LS_IGNORE_ERATT;
spin_unlock_irq(&phba->hbalock);
- psli->mbox_active = NULL;
lpfc_hba_down_post(phba);
phba->link_state = LPFC_HBA_ERROR;
@@ -2651,7 +3389,7 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_brdreset: Reset the HBA.
+ * lpfc_sli_brdreset - Reset a sli-2 or sli-3 HBA
* @phba: Pointer to HBA context object.
*
* This function resets the HBA by writing HC_INITFF to the control
@@ -2687,7 +3425,8 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
(cfg_value &
~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
- psli->sli_flag &= ~(LPFC_SLI2_ACTIVE | LPFC_PROCESS_LA);
+ psli->sli_flag &= ~(LPFC_SLI_ACTIVE | LPFC_PROCESS_LA);
+
/* Now toggle INITFF bit in the Host Control Register */
writel(HC_INITFF, phba->HCregaddr);
mdelay(1);
@@ -2714,7 +3453,66 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_brdrestart: Restart the HBA.
+ * lpfc_sli4_brdreset - Reset a sli-4 HBA
+ * @phba: Pointer to HBA context object.
+ *
+ * This function resets a SLI4 HBA. This function disables PCI layer parity
+ * checking during resets the device. The caller is not required to hold
+ * any locks.
+ *
+ * This function returns 0 always.
+ **/
+int
+lpfc_sli4_brdreset(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ uint16_t cfg_value;
+ uint8_t qindx;
+
+ /* Reset HBA */
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0295 Reset HBA Data: x%x x%x\n",
+ phba->pport->port_state, psli->sli_flag);
+
+ /* perform board reset */
+ phba->fc_eventTag = 0;
+ phba->pport->fc_myDID = 0;
+ phba->pport->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)));
+
+ spin_lock_irq(&phba->hbalock);
+ psli->sli_flag &= ~(LPFC_PROCESS_LA);
+ phba->fcf.fcf_flag = 0;
+ /* Clean up the child queue list for the CQs */
+ list_del_init(&phba->sli4_hba.mbx_wq->list);
+ list_del_init(&phba->sli4_hba.els_wq->list);
+ list_del_init(&phba->sli4_hba.hdr_rq->list);
+ list_del_init(&phba->sli4_hba.dat_rq->list);
+ list_del_init(&phba->sli4_hba.mbx_cq->list);
+ list_del_init(&phba->sli4_hba.els_cq->list);
+ list_del_init(&phba->sli4_hba.rxq_cq->list);
+ for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++)
+ list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list);
+ for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++)
+ list_del_init(&phba->sli4_hba.fcp_cq[qindx]->list);
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Now physically reset the device */
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0389 Performing PCI function reset!\n");
+ /* Perform FCoE PCI function reset */
+ lpfc_pci_function_reset(phba);
+
+ return 0;
+}
+
+/**
+ * lpfc_sli_brdrestart_s3 - Restart a sli-3 hba
* @phba: Pointer to HBA context object.
*
* This function is called in the SLI initialization code path to
@@ -2726,8 +3524,8 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
* The function does not guarantee completion of MBX_RESTART mailbox
* command before the return of this function.
**/
-int
-lpfc_sli_brdrestart(struct lpfc_hba *phba)
+static int
+lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
{
MAILBOX_t *mb;
struct lpfc_sli *psli;
@@ -2766,7 +3564,7 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
lpfc_sli_brdreset(phba);
phba->pport->stopped = 0;
phba->link_state = LPFC_INIT_START;
-
+ phba->hba_flag = 0;
spin_unlock_irq(&phba->hbalock);
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
@@ -2781,7 +3579,56 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_chipset_init: Wait for the restart of the HBA after a restart.
+ * lpfc_sli_brdrestart_s4 - Restart the sli-4 hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called in the SLI initialization code path to restart
+ * a SLI4 HBA. The caller is not required to hold any lock.
+ * At the end of the function, it calls lpfc_hba_down_post function to
+ * free any pending commands.
+ **/
+static int
+lpfc_sli_brdrestart_s4(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+
+
+ /* Restart HBA */
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0296 Restart HBA Data: x%x x%x\n",
+ phba->pport->port_state, psli->sli_flag);
+
+ lpfc_sli4_brdreset(phba);
+
+ spin_lock_irq(&phba->hbalock);
+ phba->pport->stopped = 0;
+ phba->link_state = LPFC_INIT_START;
+ phba->hba_flag = 0;
+ spin_unlock_irq(&phba->hbalock);
+
+ memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
+ psli->stats_start = get_seconds();
+
+ lpfc_hba_down_post(phba);
+
+ return 0;
+}
+
+/**
+ * lpfc_sli_brdrestart - Wrapper func for restarting hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba restart routine from the
+ * API jump table function pointer from the lpfc_hba struct.
+**/
+int
+lpfc_sli_brdrestart(struct lpfc_hba *phba)
+{
+ return phba->lpfc_sli_brdrestart(phba);
+}
+
+/**
+ * lpfc_sli_chipset_init - Wait for the restart of the HBA after a restart
* @phba: Pointer to HBA context object.
*
* This function is called after a HBA restart to wait for successful
@@ -2876,7 +3723,7 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_hbq_count: Get the number of HBQs to be configured.
+ * lpfc_sli_hbq_count - Get the number of HBQs to be configured
*
* This function calculates and returns the number of HBQs required to be
* configured.
@@ -2888,7 +3735,7 @@ lpfc_sli_hbq_count(void)
}
/**
- * lpfc_sli_hbq_entry_count: Calculate total number of hbq entries.
+ * lpfc_sli_hbq_entry_count - Calculate total number of hbq entries
*
* This function adds the number of hbq entries in every HBQ to get
* the total number of hbq entries required for the HBA and returns
@@ -2907,7 +3754,7 @@ lpfc_sli_hbq_entry_count(void)
}
/**
- * lpfc_sli_hbq_size: Calculate memory required for all hbq entries.
+ * lpfc_sli_hbq_size - Calculate memory required for all hbq entries
*
* This function calculates amount of memory required for all hbq entries
* to be configured and returns the total memory required.
@@ -2919,7 +3766,7 @@ lpfc_sli_hbq_size(void)
}
/**
- * lpfc_sli_hbq_setup: configure and initialize HBQs.
+ * lpfc_sli_hbq_setup - configure and initialize HBQs
* @phba: Pointer to HBA context object.
*
* This function is called during the SLI initialization to configure
@@ -2944,7 +3791,7 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba)
if (!pmb)
return -ENOMEM;
- pmbox = &pmb->mb;
+ pmbox = &pmb->u.mb;
/* Initialize the struct lpfc_sli_hbq structure for each hbq */
phba->link_state = LPFC_INIT_MBX_CMDS;
@@ -2988,7 +3835,27 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_config_port: Issue config port mailbox command.
+ * lpfc_sli4_rb_setup - Initialize and post RBs to HBA
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called during the SLI initialization to configure
+ * all the HBQs and post buffers to the HBQ. The caller is not
+ * required to hold any locks. This function will return zero if successful
+ * else it will return negative error code.
+ **/
+static int
+lpfc_sli4_rb_setup(struct lpfc_hba *phba)
+{
+ phba->hbq_in_use = 1;
+ phba->hbqs[0].entry_count = lpfc_hbq_defs[0]->entry_count;
+ phba->hbq_count = 1;
+ /* Initially populate or replenish the HBQs */
+ lpfc_sli_hbqbuf_init_hbqs(phba, 0);
+ return 0;
+}
+
+/**
+ * lpfc_sli_config_port - Issue config port mailbox command
* @phba: Pointer to HBA context object.
* @sli_mode: sli mode - 2/3
*
@@ -3051,33 +3918,43 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0442 Adapter failed to init, mbxCmd x%x "
"CONFIG_PORT, mbxStatus x%x Data: x%x\n",
- pmb->mb.mbxCommand, pmb->mb.mbxStatus, 0);
+ pmb->u.mb.mbxCommand, pmb->u.mb.mbxStatus, 0);
spin_lock_irq(&phba->hbalock);
- phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE;
+ phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
rc = -ENXIO;
- } else
+ } else {
+ /* Allow asynchronous mailbox command to go through */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
+ spin_unlock_irq(&phba->hbalock);
done = 1;
+ }
}
if (!done) {
rc = -EINVAL;
goto do_prep_failed;
}
- if (pmb->mb.un.varCfgPort.sli_mode == 3) {
- if (!pmb->mb.un.varCfgPort.cMA) {
+ if (pmb->u.mb.un.varCfgPort.sli_mode == 3) {
+ if (!pmb->u.mb.un.varCfgPort.cMA) {
rc = -ENXIO;
goto do_prep_failed;
}
- if (phba->max_vpi && pmb->mb.un.varCfgPort.gmv) {
+ if (phba->max_vpi && pmb->u.mb.un.varCfgPort.gmv) {
phba->sli3_options |= LPFC_SLI3_NPIV_ENABLED;
- phba->max_vpi = pmb->mb.un.varCfgPort.max_vpi;
+ phba->max_vpi = pmb->u.mb.un.varCfgPort.max_vpi;
+ phba->max_vports = (phba->max_vpi > phba->max_vports) ?
+ phba->max_vpi : phba->max_vports;
+
} else
phba->max_vpi = 0;
- if (pmb->mb.un.varCfgPort.gerbm)
+ if (pmb->u.mb.un.varCfgPort.gdss)
+ phba->sli3_options |= LPFC_SLI3_DSS_ENABLED;
+ if (pmb->u.mb.un.varCfgPort.gerbm)
phba->sli3_options |= LPFC_SLI3_HBQ_ENABLED;
- if (pmb->mb.un.varCfgPort.gcrp)
+ if (pmb->u.mb.un.varCfgPort.gcrp)
phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
- if (pmb->mb.un.varCfgPort.ginb) {
+ if (pmb->u.mb.un.varCfgPort.ginb) {
phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
@@ -3093,7 +3970,7 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
}
if (phba->cfg_enable_bg) {
- if (pmb->mb.un.varCfgPort.gbg)
+ if (pmb->u.mb.un.varCfgPort.gbg)
phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
else
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -3114,7 +3991,7 @@ do_prep_failed:
/**
- * lpfc_sli_hba_setup: SLI intialization function.
+ * lpfc_sli_hba_setup - SLI intialization function
* @phba: Pointer to HBA context object.
*
* This function is the main SLI intialization function. This function
@@ -3188,8 +4065,9 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
if (rc)
goto lpfc_sli_hba_setup_error;
}
-
+ spin_lock_irq(&phba->hbalock);
phba->sli.sli_flag |= LPFC_PROCESS_LA;
+ spin_unlock_irq(&phba->hbalock);
rc = lpfc_config_port_post(phba);
if (rc)
@@ -3204,9 +4082,491 @@ lpfc_sli_hba_setup_error:
return rc;
}
+/**
+ * lpfc_sli4_read_fcoe_params - Read fcoe params from conf region
+ * @phba: Pointer to HBA context object.
+ * @mboxq: mailbox pointer.
+ * This function issue a dump mailbox command to read config region
+ * 23 and parse the records in the region and populate driver
+ * data structure.
+ **/
+static int
+lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
+ LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_dmabuf *mp;
+ struct lpfc_mqe *mqe;
+ uint32_t data_length;
+ int rc;
+
+ /* Program the default value of vlan_id and fc_map */
+ phba->valid_vlan = 0;
+ phba->fc_map[0] = LPFC_FCOE_FCF_MAP0;
+ phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
+ phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
+
+ mqe = &mboxq->u.mqe;
+ if (lpfc_dump_fcoe_param(phba, mboxq))
+ return -ENOMEM;
+
+ mp = (struct lpfc_dmabuf *) mboxq->context1;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "(%d):2571 Mailbox cmd x%x Status x%x "
+ "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+ "x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+ "CQ: x%x x%x x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ bf_get(lpfc_mqe_command, mqe),
+ bf_get(lpfc_mqe_status, mqe),
+ mqe->un.mb_words[0], mqe->un.mb_words[1],
+ mqe->un.mb_words[2], mqe->un.mb_words[3],
+ mqe->un.mb_words[4], mqe->un.mb_words[5],
+ mqe->un.mb_words[6], mqe->un.mb_words[7],
+ mqe->un.mb_words[8], mqe->un.mb_words[9],
+ mqe->un.mb_words[10], mqe->un.mb_words[11],
+ mqe->un.mb_words[12], mqe->un.mb_words[13],
+ mqe->un.mb_words[14], mqe->un.mb_words[15],
+ mqe->un.mb_words[16], mqe->un.mb_words[50],
+ mboxq->mcqe.word0,
+ mboxq->mcqe.mcqe_tag0, mboxq->mcqe.mcqe_tag1,
+ mboxq->mcqe.trailer);
+
+ if (rc) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ return -EIO;
+ }
+ data_length = mqe->un.mb_words[5];
+ if (data_length > DMP_FCOEPARAM_RGN_SIZE)
+ return -EIO;
+
+ lpfc_parse_fcoe_conf(phba, mp->virt, data_length);
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ return 0;
+}
/**
- * lpfc_mbox_timeout: Timeout call back function for mbox timer.
+ * lpfc_sli4_read_rev - Issue READ_REV and collect vpd data
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to the LPFC_MBOXQ_t structure.
+ * @vpd: pointer to the memory to hold resulting port vpd data.
+ * @vpd_size: On input, the number of bytes allocated to @vpd.
+ * On output, the number of data bytes in @vpd.
+ *
+ * This routine executes a READ_REV SLI4 mailbox command. In
+ * addition, this routine gets the port vpd data.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - could not allocated memory.
+ **/
+static int
+lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
+ uint8_t *vpd, uint32_t *vpd_size)
+{
+ int rc = 0;
+ uint32_t dma_size;
+ struct lpfc_dmabuf *dmabuf;
+ struct lpfc_mqe *mqe;
+
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ /*
+ * Get a DMA buffer for the vpd data resulting from the READ_REV
+ * mailbox command.
+ */
+ dma_size = *vpd_size;
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+ dma_size,
+ &dmabuf->phys,
+ GFP_KERNEL);
+ if (!dmabuf->virt) {
+ kfree(dmabuf);
+ return -ENOMEM;
+ }
+ memset(dmabuf->virt, 0, dma_size);
+
+ /*
+ * The SLI4 implementation of READ_REV conflicts at word1,
+ * bits 31:16 and SLI4 adds vpd functionality not present
+ * in SLI3. This code corrects the conflicts.
+ */
+ lpfc_read_rev(phba, mboxq);
+ mqe = &mboxq->u.mqe;
+ mqe->un.read_rev.vpd_paddr_high = putPaddrHigh(dmabuf->phys);
+ mqe->un.read_rev.vpd_paddr_low = putPaddrLow(dmabuf->phys);
+ mqe->un.read_rev.word1 &= 0x0000FFFF;
+ bf_set(lpfc_mbx_rd_rev_vpd, &mqe->un.read_rev, 1);
+ bf_set(lpfc_mbx_rd_rev_avail_len, &mqe->un.read_rev, dma_size);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc) {
+ dma_free_coherent(&phba->pcidev->dev, dma_size,
+ dmabuf->virt, dmabuf->phys);
+ return -EIO;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "(%d):0380 Mailbox cmd x%x Status x%x "
+ "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+ "x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+ "CQ: x%x x%x x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ bf_get(lpfc_mqe_command, mqe),
+ bf_get(lpfc_mqe_status, mqe),
+ mqe->un.mb_words[0], mqe->un.mb_words[1],
+ mqe->un.mb_words[2], mqe->un.mb_words[3],
+ mqe->un.mb_words[4], mqe->un.mb_words[5],
+ mqe->un.mb_words[6], mqe->un.mb_words[7],
+ mqe->un.mb_words[8], mqe->un.mb_words[9],
+ mqe->un.mb_words[10], mqe->un.mb_words[11],
+ mqe->un.mb_words[12], mqe->un.mb_words[13],
+ mqe->un.mb_words[14], mqe->un.mb_words[15],
+ mqe->un.mb_words[16], mqe->un.mb_words[50],
+ mboxq->mcqe.word0,
+ mboxq->mcqe.mcqe_tag0, mboxq->mcqe.mcqe_tag1,
+ mboxq->mcqe.trailer);
+
+ /*
+ * The available vpd length cannot be bigger than the
+ * DMA buffer passed to the port. Catch the less than
+ * case and update the caller's size.
+ */
+ if (mqe->un.read_rev.avail_vpd_len < *vpd_size)
+ *vpd_size = mqe->un.read_rev.avail_vpd_len;
+
+ lpfc_sli_pcimem_bcopy(dmabuf->virt, vpd, *vpd_size);
+ dma_free_coherent(&phba->pcidev->dev, dma_size,
+ dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ return 0;
+}
+
+/**
+ * lpfc_sli4_arm_cqeq_intr - Arm sli-4 device completion and event queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to explicitly arm the SLI4 device's completion and
+ * event queues
+ **/
+static void
+lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
+{
+ uint8_t fcp_eqidx;
+
+ lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
+ lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
+ lpfc_sli4_cq_release(phba->sli4_hba.rxq_cq, LPFC_QUEUE_REARM);
+ for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
+ lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx],
+ LPFC_QUEUE_REARM);
+ lpfc_sli4_eq_release(phba->sli4_hba.sp_eq, LPFC_QUEUE_REARM);
+ for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
+ lpfc_sli4_eq_release(phba->sli4_hba.fp_eq[fcp_eqidx],
+ LPFC_QUEUE_REARM);
+}
+
+/**
+ * lpfc_sli4_hba_setup - SLI4 device intialization PCI function
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is the main SLI4 device intialization PCI function. This
+ * function is called by the HBA intialization code, HBA reset code and
+ * HBA error attention handler code. Caller is not required to hold any
+ * locks.
+ **/
+int
+lpfc_sli4_hba_setup(struct lpfc_hba *phba)
+{
+ int rc;
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mqe *mqe;
+ uint8_t *vpd;
+ uint32_t vpd_size;
+ uint32_t ftr_rsp = 0;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
+ struct lpfc_vport *vport = phba->pport;
+ struct lpfc_dmabuf *mp;
+
+ /* Perform a PCI function reset to start from clean */
+ rc = lpfc_pci_function_reset(phba);
+ if (unlikely(rc))
+ return -ENODEV;
+
+ /* Check the HBA Host Status Register for readyness */
+ rc = lpfc_sli4_post_status_check(phba);
+ if (unlikely(rc))
+ return -ENODEV;
+ else {
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag |= LPFC_SLI_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
+ }
+
+ /*
+ * Allocate a single mailbox container for initializing the
+ * port.
+ */
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ /*
+ * Continue initialization with default values even if driver failed
+ * to read FCoE param config regions
+ */
+ if (lpfc_sli4_read_fcoe_params(phba, mboxq))
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+ "2570 Failed to read FCoE parameters \n");
+
+ /* Issue READ_REV to collect vpd and FW information. */
+ vpd_size = PAGE_SIZE;
+ vpd = kzalloc(vpd_size, GFP_KERNEL);
+ if (!vpd) {
+ rc = -ENOMEM;
+ goto out_free_mbox;
+ }
+
+ rc = lpfc_sli4_read_rev(phba, mboxq, vpd, &vpd_size);
+ if (unlikely(rc))
+ goto out_free_vpd;
+
+ mqe = &mboxq->u.mqe;
+ if ((bf_get(lpfc_mbx_rd_rev_sli_lvl,
+ &mqe->un.read_rev) != LPFC_SLI_REV4) ||
+ (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev) == 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0376 READ_REV Error. SLI Level %d "
+ "FCoE enabled %d\n",
+ bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev),
+ bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev));
+ rc = -EIO;
+ goto out_free_vpd;
+ }
+ /* Single threaded at this point, no need for lock */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= HBA_FCOE_SUPPORT;
+ spin_unlock_irq(&phba->hbalock);
+ /*
+ * Evaluate the read rev and vpd data. Populate the driver
+ * state with the results. If this routine fails, the failure
+ * is not fatal as the driver will use generic values.
+ */
+ rc = lpfc_parse_vpd(phba, vpd, vpd_size);
+ if (unlikely(!rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0377 Error %d parsing vpd. "
+ "Using defaults.\n", rc);
+ rc = 0;
+ }
+
+ /* By now, we should determine the SLI revision, hard code for now */
+ phba->sli_rev = LPFC_SLI_REV4;
+
+ /*
+ * Discover the port's supported feature set and match it against the
+ * hosts requests.
+ */
+ lpfc_request_features(phba, mboxq);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (unlikely(rc)) {
+ rc = -EIO;
+ goto out_free_vpd;
+ }
+
+ /*
+ * The port must support FCP initiator mode as this is the
+ * only mode running in the host.
+ */
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_fcpi, &mqe->un.req_ftrs))) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "0378 No support for fcpi mode.\n");
+ ftr_rsp++;
+ }
+
+ /*
+ * If the port cannot support the host's requested features
+ * then turn off the global config parameters to disable the
+ * feature in the driver. This is not a fatal error.
+ */
+ if ((phba->cfg_enable_bg) &&
+ !(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
+ ftr_rsp++;
+
+ if (phba->max_vpi && phba->cfg_enable_npiv &&
+ !(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
+ ftr_rsp++;
+
+ if (ftr_rsp) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "0379 Feature Mismatch Data: x%08x %08x "
+ "x%x x%x x%x\n", mqe->un.req_ftrs.word2,
+ mqe->un.req_ftrs.word3, phba->cfg_enable_bg,
+ phba->cfg_enable_npiv, phba->max_vpi);
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
+ phba->cfg_enable_bg = 0;
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
+ phba->cfg_enable_npiv = 0;
+ }
+
+ /* These SLI3 features are assumed in SLI4 */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli3_options |= (LPFC_SLI3_NPIV_ENABLED | LPFC_SLI3_HBQ_ENABLED);
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Read the port's service parameters. */
+ lpfc_read_sparam(phba, mboxq, vport->vpi);
+ mboxq->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ mp = (struct lpfc_dmabuf *) mboxq->context1;
+ if (rc == MBX_SUCCESS) {
+ memcpy(&vport->fc_sparam, mp->virt, sizeof(struct serv_parm));
+ rc = 0;
+ }
+
+ /*
+ * This memory was allocated by the lpfc_read_sparam routine. Release
+ * it to the mbuf pool.
+ */
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ mboxq->context1 = NULL;
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0382 READ_SPARAM command failed "
+ "status %d, mbxStatus x%x\n",
+ rc, bf_get(lpfc_mqe_status, mqe));
+ phba->link_state = LPFC_HBA_ERROR;
+ rc = -EIO;
+ goto out_free_vpd;
+ }
+
+ if (phba->cfg_soft_wwnn)
+ u64_to_wwn(phba->cfg_soft_wwnn,
+ vport->fc_sparam.nodeName.u.wwn);
+ if (phba->cfg_soft_wwpn)
+ u64_to_wwn(phba->cfg_soft_wwpn,
+ vport->fc_sparam.portName.u.wwn);
+ memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+
+ /* Update the fc_host data structures with new wwn. */
+ fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
+ fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
+
+ /* Register SGL pool to the device using non-embedded mailbox command */
+ rc = lpfc_sli4_post_sgl_list(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0582 Error %d during sgl post operation", rc);
+ rc = -ENODEV;
+ goto out_free_vpd;
+ }
+
+ /* Register SCSI SGL pool to the device */
+ rc = lpfc_sli4_repost_scsi_sgl_list(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "0383 Error %d during scsi sgl post opeation",
+ rc);
+ /* Some Scsi buffers were moved to the abort scsi list */
+ /* A pci function reset will repost them */
+ rc = -ENODEV;
+ goto out_free_vpd;
+ }
+
+ /* Post the rpi header region to the device. */
+ rc = lpfc_sli4_post_all_rpi_hdrs(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0393 Error %d during rpi post operation\n",
+ rc);
+ rc = -ENODEV;
+ goto out_free_vpd;
+ }
+ /* Temporary initialization of lpfc_fip_flag to non-fip */
+ bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0);
+
+ /* Set up all the queues to the device */
+ rc = lpfc_sli4_queue_setup(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0381 Error %d during queue setup.\n ", rc);
+ goto out_stop_timers;
+ }
+
+ /* Arm the CQs and then EQs on device */
+ lpfc_sli4_arm_cqeq_intr(phba);
+
+ /* Indicate device interrupt mode */
+ phba->sli4_hba.intr_enable = 1;
+
+ /* Allow asynchronous mailbox command to go through */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Post receive buffers to the device */
+ lpfc_sli4_rb_setup(phba);
+
+ /* Start the ELS watchdog timer */
+ /*
+ * The driver for SLI4 is not yet ready to process timeouts
+ * or interrupts. Once it is, the comment bars can be removed.
+ */
+ /* mod_timer(&vport->els_tmofunc,
+ * jiffies + HZ * (phba->fc_ratov*2)); */
+
+ /* Start heart beat timer */
+ mod_timer(&phba->hb_tmofunc,
+ jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ phba->hb_outstanding = 0;
+ phba->last_completion_time = jiffies;
+
+ /* Start error attention (ERATT) polling timer */
+ mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+
+ /*
+ * The port is ready, set the host's link state to LINK_DOWN
+ * in preparation for link interrupts.
+ */
+ lpfc_init_link(phba, mboxq, phba->cfg_topology, phba->cfg_link_speed);
+ mboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ lpfc_set_loopback_flag(phba);
+ /* Change driver state to LPFC_LINK_DOWN right before init link */
+ spin_lock_irq(&phba->hbalock);
+ phba->link_state = LPFC_LINK_DOWN;
+ spin_unlock_irq(&phba->hbalock);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (unlikely(rc != MBX_NOT_FINISHED)) {
+ kfree(vpd);
+ return 0;
+ } else
+ rc = -EIO;
+
+ /* Unset all the queues set up in this routine when error out */
+ if (rc)
+ lpfc_sli4_queue_unset(phba);
+
+out_stop_timers:
+ if (rc)
+ lpfc_stop_hba_timers(phba);
+out_free_vpd:
+ kfree(vpd);
+out_free_mbox:
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
+ * lpfc_mbox_timeout - Timeout call back function for mbox timer
* @ptr: context object - pointer to hba structure.
*
* This is the callback function for mailbox timer. The mailbox
@@ -3237,7 +4597,7 @@ lpfc_mbox_timeout(unsigned long ptr)
/**
- * lpfc_mbox_timeout_handler: Worker thread function to handle mailbox timeout.
+ * lpfc_mbox_timeout_handler - Worker thread function to handle mailbox timeout
* @phba: Pointer to HBA context object.
*
* This function is called from worker thread when a mailbox command times out.
@@ -3248,10 +4608,25 @@ void
lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
{
LPFC_MBOXQ_t *pmbox = phba->sli.mbox_active;
- MAILBOX_t *mb = &pmbox->mb;
+ MAILBOX_t *mb = &pmbox->u.mb;
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
+ /* Check the pmbox pointer first. There is a race condition
+ * between the mbox timeout handler getting executed in the
+ * worklist and the mailbox actually completing. When this
+ * race condition occurs, the mbox_active will be NULL.
+ */
+ spin_lock_irq(&phba->hbalock);
+ if (pmbox == NULL) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_MBOX | LOG_SLI,
+ "0353 Active Mailbox cleared - mailbox timeout "
+ "exiting\n");
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+
/* Mbox cmd <mbxCommand> timeout */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
@@ -3259,6 +4634,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
phba->pport->port_state,
phba->sli.sli_flag,
phba->sli.mbox_active);
+ spin_unlock_irq(&phba->hbalock);
/* Setting state unknown so lpfc_sli_abort_iocb_ring
* would get IOCB_ERROR from lpfc_sli_issue_iocb, allowing
@@ -3269,7 +4645,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
spin_unlock_irq(&phba->pport->work_port_lock);
spin_lock_irq(&phba->hbalock);
phba->link_state = LPFC_LINK_UNKNOWN;
- psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
pring = &psli->ring[psli->fcp_ring];
@@ -3277,32 +4653,20 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0345 Resetting board due to mailbox timeout\n");
- /*
- * lpfc_offline calls lpfc_sli_hba_down which will clean up
- * on oustanding mailbox commands.
- */
- /* If resets are disabled then set error state and return. */
- if (!phba->cfg_enable_hba_reset) {
- phba->link_state = LPFC_HBA_ERROR;
- return;
- }
- lpfc_offline_prep(phba);
- lpfc_offline(phba);
- lpfc_sli_brdrestart(phba);
- lpfc_online(phba);
- lpfc_unblock_mgmt_io(phba);
- return;
+
+ /* Reset the HBA device */
+ lpfc_reset_hba(phba);
}
/**
- * lpfc_sli_issue_mbox: Issue a mailbox command to firmware.
+ * lpfc_sli_issue_mbox_s3 - Issue an SLI3 mailbox command to firmware
* @phba: Pointer to HBA context object.
* @pmbox: Pointer to mailbox object.
* @flag: Flag indicating how the mailbox need to be processed.
*
* This function is called by discovery code and HBA management code
- * to submit a mailbox command to firmware. This function gets the
- * hbalock to protect the data structures.
+ * to submit a mailbox command to firmware with SLI-3 interface spec. This
+ * function gets the hbalock to protect the data structures.
* The mailbox command can be submitted in polling mode, in which case
* this function will wait in a polling loop for the completion of the
* mailbox.
@@ -3320,8 +4684,9 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
* return codes the caller owns the mailbox command after the return of
* the function.
**/
-int
-lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
+static int
+lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
+ uint32_t flag)
{
MAILBOX_t *mb;
struct lpfc_sli *psli = &phba->sli;
@@ -3337,6 +4702,10 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
spin_lock_irqsave(&phba->hbalock, drvr_flag);
if (!pmbox) {
/* processing mbox queue from intr_handler */
+ if (unlikely(psli->sli_flag & LPFC_SLI_ASYNC_MBX_BLK)) {
+ spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
+ return MBX_SUCCESS;
+ }
processing_queue = 1;
phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
pmbox = lpfc_mbox_get(phba);
@@ -3353,7 +4722,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
lpfc_printf_log(phba, KERN_ERR,
LOG_MBOX | LOG_VPORT,
"1806 Mbox x%x failed. No vport\n",
- pmbox->mb.mbxCommand);
+ pmbox->u.mb.mbxCommand);
dump_stack();
goto out_not_finished;
}
@@ -3365,23 +4734,37 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
goto out_not_finished;
}
+ /* If HBA has a deferred error attention, fail the iocb. */
+ if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
+ goto out_not_finished;
+ }
+
psli = &phba->sli;
- mb = &pmbox->mb;
+ mb = &pmbox->u.mb;
status = MBX_SUCCESS;
if (phba->link_state == LPFC_HBA_ERROR) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):0311 Mailbox command x%x cannot "
+ "issue Data: x%x x%x\n",
+ pmbox->vport ? pmbox->vport->vpi : 0,
+ pmbox->u.mb.mbxCommand, psli->sli_flag, flag);
goto out_not_finished;
}
if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT &&
!(readl(phba->HCregaddr) & HC_MBINT_ENA)) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
- LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2528 Mailbox command x%x cannot "
+ "issue Data: x%x x%x\n",
+ pmbox->vport ? pmbox->vport->vpi : 0,
+ pmbox->u.mb.mbxCommand, psli->sli_flag, flag);
goto out_not_finished;
}
@@ -3395,14 +4778,24 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2529 Mailbox command x%x "
+ "cannot issue Data: x%x x%x\n",
+ pmbox->vport ? pmbox->vport->vpi : 0,
+ pmbox->u.mb.mbxCommand,
+ psli->sli_flag, flag);
goto out_not_finished;
}
- if (!(psli->sli_flag & LPFC_SLI2_ACTIVE)) {
+ if (!(psli->sli_flag & LPFC_SLI_ACTIVE)) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2530 Mailbox command x%x "
+ "cannot issue Data: x%x x%x\n",
+ pmbox->vport ? pmbox->vport->vpi : 0,
+ pmbox->u.mb.mbxCommand,
+ psli->sli_flag, flag);
goto out_not_finished;
}
@@ -3444,12 +4837,17 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
/* If we are not polling, we MUST be in SLI2 mode */
if (flag != MBX_POLL) {
- if (!(psli->sli_flag & LPFC_SLI2_ACTIVE) &&
+ if (!(psli->sli_flag & LPFC_SLI_ACTIVE) &&
(mb->mbxCommand != MBX_KILL_BOARD)) {
psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2531 Mailbox command x%x "
+ "cannot issue Data: x%x x%x\n",
+ pmbox->vport ? pmbox->vport->vpi : 0,
+ pmbox->u.mb.mbxCommand,
+ psli->sli_flag, flag);
goto out_not_finished;
}
/* timeout active mbox command */
@@ -3488,7 +4886,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
/* next set own bit for the adapter and copy over command word */
mb->mbxOwner = OWN_CHIP;
- if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* First copy command data to host SLIM area */
lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE);
} else {
@@ -3511,7 +4909,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
if (mb->mbxCommand == MBX_CONFIG_PORT) {
/* switch over to host mailbox */
- psli->sli_flag |= LPFC_SLI2_ACTIVE;
+ psli->sli_flag |= LPFC_SLI_ACTIVE;
}
}
@@ -3534,7 +4932,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
writel(CA_MBATT, phba->CAregaddr);
readl(phba->CAregaddr); /* flush */
- if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* First read mbox status word */
word0 = *((uint32_t *)phba->mbox);
word0 = le32_to_cpu(word0);
@@ -3573,7 +4971,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
spin_lock_irqsave(&phba->hbalock, drvr_flag);
}
- if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* First copy command data */
word0 = *((uint32_t *)phba->mbox);
word0 = le32_to_cpu(word0);
@@ -3586,7 +4984,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
if (((slimword0 & OWN_CHIP) != OWN_CHIP)
&& slimmb->mbxStatus) {
psli->sli_flag &=
- ~LPFC_SLI2_ACTIVE;
+ ~LPFC_SLI_ACTIVE;
word0 = slimword0;
}
}
@@ -3598,7 +4996,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
ha_copy = readl(phba->HAregaddr);
}
- if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+ if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* copy results back to user */
lpfc_sli_pcimem_bcopy(phba->mbox, mb, MAILBOX_CMD_SIZE);
} else {
@@ -3625,14 +5023,421 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
out_not_finished:
if (processing_queue) {
- pmbox->mb.mbxStatus = MBX_NOT_FINISHED;
+ pmbox->u.mb.mbxStatus = MBX_NOT_FINISHED;
lpfc_mbox_cmpl_put(phba, pmbox);
}
return MBX_NOT_FINISHED;
}
/**
- * __lpfc_sli_ringtx_put: Add an iocb to the txq.
+ * lpfc_sli4_post_sync_mbox - Post an SLI4 mailbox to the bootstrap mailbox
+ * @phba: Pointer to HBA context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * The function posts a mailbox to the port. The mailbox is expected
+ * to be comletely filled in and ready for the port to operate on it.
+ * This routine executes a synchronous completion operation on the
+ * mailbox by polling for its completion.
+ *
+ * The caller must not be holding any locks when calling this routine.
+ *
+ * Returns:
+ * MBX_SUCCESS - mailbox posted successfully
+ * Any of the MBX error values.
+ **/
+static int
+lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ int rc = MBX_SUCCESS;
+ unsigned long iflag;
+ uint32_t db_ready;
+ uint32_t mcqe_status;
+ uint32_t mbx_cmnd;
+ unsigned long timeout;
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_mqe *mb = &mboxq->u.mqe;
+ struct lpfc_bmbx_create *mbox_rgn;
+ struct dma_address *dma_address;
+ struct lpfc_register bmbx_reg;
+
+ /*
+ * Only one mailbox can be active to the bootstrap mailbox region
+ * at a time and there is no queueing provided.
+ */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2532 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, MBX_POLL);
+ return MBXERR_ERROR;
+ }
+ /* The server grabs the token and owns it until release */
+ psli->sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = mboxq;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+
+ /*
+ * Initialize the bootstrap memory region to avoid stale data areas
+ * in the mailbox post. Then copy the caller's mailbox contents to
+ * the bmbx mailbox region.
+ */
+ mbx_cmnd = bf_get(lpfc_mqe_command, mb);
+ memset(phba->sli4_hba.bmbx.avirt, 0, sizeof(struct lpfc_bmbx_create));
+ lpfc_sli_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt,
+ sizeof(struct lpfc_mqe));
+
+ /* Post the high mailbox dma address to the port and wait for ready. */
+ dma_address = &phba->sli4_hba.bmbx.dma_address;
+ writel(dma_address->addr_hi, phba->sli4_hba.BMBXregaddr);
+
+ timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
+ * 1000) + jiffies;
+ do {
+ bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
+ db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
+ if (!db_ready)
+ msleep(2);
+
+ if (time_after(jiffies, timeout)) {
+ rc = MBXERR_ERROR;
+ goto exit;
+ }
+ } while (!db_ready);
+
+ /* Post the low mailbox dma address to the port. */
+ writel(dma_address->addr_lo, phba->sli4_hba.BMBXregaddr);
+ timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
+ * 1000) + jiffies;
+ do {
+ bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
+ db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
+ if (!db_ready)
+ msleep(2);
+
+ if (time_after(jiffies, timeout)) {
+ rc = MBXERR_ERROR;
+ goto exit;
+ }
+ } while (!db_ready);
+
+ /*
+ * Read the CQ to ensure the mailbox has completed.
+ * If so, update the mailbox status so that the upper layers
+ * can complete the request normally.
+ */
+ lpfc_sli_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb,
+ sizeof(struct lpfc_mqe));
+ mbox_rgn = (struct lpfc_bmbx_create *) phba->sli4_hba.bmbx.avirt;
+ lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe,
+ sizeof(struct lpfc_mcqe));
+ mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe);
+
+ /* Prefix the mailbox status with range x4000 to note SLI4 status. */
+ if (mcqe_status != MB_CQE_STATUS_SUCCESS) {
+ bf_set(lpfc_mqe_status, mb, LPFC_MBX_ERROR_RANGE | mcqe_status);
+ rc = MBXERR_ERROR;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "(%d):0356 Mailbox cmd x%x (x%x) Status x%x "
+ "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x"
+ " x%x x%x CQ: x%x x%x x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mbx_cmnd, lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ bf_get(lpfc_mqe_status, mb),
+ mb->un.mb_words[0], mb->un.mb_words[1],
+ mb->un.mb_words[2], mb->un.mb_words[3],
+ mb->un.mb_words[4], mb->un.mb_words[5],
+ mb->un.mb_words[6], mb->un.mb_words[7],
+ mb->un.mb_words[8], mb->un.mb_words[9],
+ mb->un.mb_words[10], mb->un.mb_words[11],
+ mb->un.mb_words[12], mboxq->mcqe.word0,
+ mboxq->mcqe.mcqe_tag0, mboxq->mcqe.mcqe_tag1,
+ mboxq->mcqe.trailer);
+exit:
+ /* We are holding the token, no needed for lock when release */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return rc;
+}
+
+/**
+ * lpfc_sli_issue_mbox_s4 - Issue an SLI4 mailbox command to firmware
+ * @phba: Pointer to HBA context object.
+ * @pmbox: Pointer to mailbox object.
+ * @flag: Flag indicating how the mailbox need to be processed.
+ *
+ * This function is called by discovery code and HBA management code to submit
+ * a mailbox command to firmware with SLI-4 interface spec.
+ *
+ * Return codes the caller owns the mailbox command after the return of the
+ * function.
+ **/
+static int
+lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
+ uint32_t flag)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ unsigned long iflags;
+ int rc;
+
+ /* Detect polling mode and jump to a handler */
+ if (!phba->sli4_hba.intr_enable) {
+ if (flag == MBX_POLL)
+ rc = lpfc_sli4_post_sync_mbox(phba, mboxq);
+ else
+ rc = -EIO;
+ if (rc != MBX_SUCCESS)
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2541 Mailbox command x%x "
+ "(x%x) cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, flag);
+ return rc;
+ } else if (flag == MBX_POLL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2542 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, flag);
+ return -EIO;
+ }
+
+ /* Now, interrupt mode asynchrous mailbox command */
+ rc = lpfc_mbox_cmd_check(phba, mboxq);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2543 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, flag);
+ goto out_not_finished;
+ }
+ rc = lpfc_mbox_dev_check(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2544 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, flag);
+ goto out_not_finished;
+ }
+
+ /* Put the mailbox command to the driver internal FIFO */
+ psli->slistat.mbox_busy++;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ lpfc_mbox_put(phba, mboxq);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "(%d):0354 Mbox cmd issue - Enqueue Data: "
+ "x%x (x%x) x%x x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0xffffff,
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ phba->pport->port_state,
+ psli->sli_flag, MBX_NOWAIT);
+ /* Wake up worker thread to transport mailbox command from head */
+ lpfc_worker_wake_up(phba);
+
+ return MBX_BUSY;
+
+out_not_finished:
+ return MBX_NOT_FINISHED;
+}
+
+/**
+ * lpfc_sli4_post_async_mbox - Post an SLI4 mailbox command to device
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called by worker thread to send a mailbox command to
+ * SLI4 HBA firmware.
+ *
+ **/
+int
+lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ LPFC_MBOXQ_t *mboxq;
+ int rc = MBX_SUCCESS;
+ unsigned long iflags;
+ struct lpfc_mqe *mqe;
+ uint32_t mbx_cmnd;
+
+ /* Check interrupt mode before post async mailbox command */
+ if (unlikely(!phba->sli4_hba.intr_enable))
+ return MBX_NOT_FINISHED;
+
+ /* Check for mailbox command service token */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (unlikely(psli->sli_flag & LPFC_SLI_ASYNC_MBX_BLK)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return MBX_NOT_FINISHED;
+ }
+ if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return MBX_NOT_FINISHED;
+ }
+ if (unlikely(phba->sli.mbox_active)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "0384 There is pending active mailbox cmd\n");
+ return MBX_NOT_FINISHED;
+ }
+ /* Take the mailbox command service token */
+ psli->sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+
+ /* Get the next mailbox command from head of queue */
+ mboxq = lpfc_mbox_get(phba);
+
+ /* If no more mailbox command waiting for post, we're done */
+ if (!mboxq) {
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return MBX_SUCCESS;
+ }
+ phba->sli.mbox_active = mboxq;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ /* Check device readiness for posting mailbox command */
+ rc = lpfc_mbox_dev_check(phba);
+ if (unlikely(rc))
+ /* Driver clean routine will clean up pending mailbox */
+ goto out_not_finished;
+
+ /* Prepare the mbox command to be posted */
+ mqe = &mboxq->u.mqe;
+ mbx_cmnd = bf_get(lpfc_mqe_command, mqe);
+
+ /* Start timer for the mbox_tmo and log some mailbox post messages */
+ mod_timer(&psli->mbox_tmo, (jiffies +
+ (HZ * lpfc_mbox_tmo_val(phba, mbx_cmnd))));
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "(%d):0355 Mailbox cmd x%x (x%x) issue Data: "
+ "x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0, mbx_cmnd,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ phba->pport->port_state, psli->sli_flag);
+
+ if (mbx_cmnd != MBX_HEARTBEAT) {
+ if (mboxq->vport) {
+ lpfc_debugfs_disc_trc(mboxq->vport,
+ LPFC_DISC_TRC_MBOX_VPORT,
+ "MBOX Send vport: cmd:x%x mb:x%x x%x",
+ mbx_cmnd, mqe->un.mb_words[0],
+ mqe->un.mb_words[1]);
+ } else {
+ lpfc_debugfs_disc_trc(phba->pport,
+ LPFC_DISC_TRC_MBOX,
+ "MBOX Send: cmd:x%x mb:x%x x%x",
+ mbx_cmnd, mqe->un.mb_words[0],
+ mqe->un.mb_words[1]);
+ }
+ }
+ psli->slistat.mbox_cmd++;
+
+ /* Post the mailbox command to the port */
+ rc = lpfc_sli4_mq_put(phba->sli4_hba.mbx_wq, mqe);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2533 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, MBX_NOWAIT);
+ goto out_not_finished;
+ }
+
+ return rc;
+
+out_not_finished:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ __lpfc_mbox_cmpl_put(phba, mboxq);
+ /* Release the token */
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ return MBX_NOT_FINISHED;
+}
+
+/**
+ * lpfc_sli_issue_mbox - Wrapper func for issuing mailbox command
+ * @phba: Pointer to HBA context object.
+ * @pmbox: Pointer to mailbox object.
+ * @flag: Flag indicating how the mailbox need to be processed.
+ *
+ * This routine wraps the actual SLI3 or SLI4 mailbox issuing routine from
+ * the API jump table function pointer from the lpfc_hba struct.
+ *
+ * Return codes the caller owns the mailbox command after the return of the
+ * function.
+ **/
+int
+lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
+{
+ return phba->lpfc_sli_issue_mbox(phba, pmbox, flag);
+}
+
+/**
+ * lpfc_mbox_api_table_setup - Set up mbox api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the mbox interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_mbox_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+ switch (dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ phba->lpfc_sli_issue_mbox = lpfc_sli_issue_mbox_s3;
+ phba->lpfc_sli_handle_slow_ring_event =
+ lpfc_sli_handle_slow_ring_event_s3;
+ phba->lpfc_sli_hbq_to_firmware = lpfc_sli_hbq_to_firmware_s3;
+ phba->lpfc_sli_brdrestart = lpfc_sli_brdrestart_s3;
+ phba->lpfc_sli_brdready = lpfc_sli_brdready_s3;
+ break;
+ case LPFC_PCI_DEV_OC:
+ phba->lpfc_sli_issue_mbox = lpfc_sli_issue_mbox_s4;
+ phba->lpfc_sli_handle_slow_ring_event =
+ lpfc_sli_handle_slow_ring_event_s4;
+ phba->lpfc_sli_hbq_to_firmware = lpfc_sli_hbq_to_firmware_s4;
+ phba->lpfc_sli_brdrestart = lpfc_sli_brdrestart_s4;
+ phba->lpfc_sli_brdready = lpfc_sli_brdready_s4;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1420 Invalid HBA PCI-device group: 0x%x\n",
+ dev_grp);
+ return -ENODEV;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * __lpfc_sli_ringtx_put - Add an iocb to the txq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to address of newly added command iocb.
@@ -3651,7 +5456,7 @@ __lpfc_sli_ringtx_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_next_iocb: Get the next iocb in the txq.
+ * lpfc_sli_next_iocb - Get the next iocb in the txq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to address of newly added command iocb.
@@ -3683,35 +5488,34 @@ lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * __lpfc_sli_issue_iocb: Lockless version of lpfc_sli_issue_iocb.
+ * __lpfc_sli_issue_iocb_s3 - SLI3 device lockless ver of lpfc_sli_issue_iocb
* @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
+ * @ring_number: SLI ring number to issue iocb on.
* @piocb: Pointer to command iocb.
* @flag: Flag indicating if this command can be put into txq.
*
- * __lpfc_sli_issue_iocb is used by other functions in the driver
- * to issue an iocb command to the HBA. If the PCI slot is recovering
- * from error state or if HBA is resetting or if LPFC_STOP_IOCB_EVENT
- * flag is turned on, the function returns IOCB_ERROR.
- * When the link is down, this function allows only iocbs for
- * posting buffers.
- * This function finds next available slot in the command ring and
- * posts the command to the available slot and writes the port
- * attention register to request HBA start processing new iocb.
- * If there is no slot available in the ring and
- * flag & SLI_IOCB_RET_IOCB is set, the new iocb is added to the
- * txq, otherwise the function returns IOCB_BUSY.
- *
- * This function is called with hbalock held.
- * The function will return success after it successfully submit the
- * iocb to firmware or after adding to the txq.
+ * __lpfc_sli_issue_iocb_s3 is used by other functions in the driver to issue
+ * an iocb command to an HBA with SLI-3 interface spec. If the PCI slot is
+ * recovering from error state, if HBA is resetting or if LPFC_STOP_IOCB_EVENT
+ * flag is turned on, the function returns IOCB_ERROR. When the link is down,
+ * this function allows only iocbs for posting buffers. This function finds
+ * next available slot in the command ring and posts the command to the
+ * available slot and writes the port attention register to request HBA start
+ * processing new iocb. If there is no slot available in the ring and
+ * flag & SLI_IOCB_RET_IOCB is set, the new iocb is added to the txq, otherwise
+ * the function returns IOCB_BUSY.
+ *
+ * This function is called with hbalock held. The function will return success
+ * after it successfully submit the iocb to firmware or after adding to the
+ * txq.
**/
static int
-__lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+__lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
struct lpfc_iocbq *nextiocb;
IOCB_t *iocb;
+ struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
if (piocb->iocb_cmpl && (!piocb->vport) &&
(piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
@@ -3729,6 +5533,10 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (unlikely(pci_channel_offline(phba->pcidev)))
return IOCB_ERROR;
+ /* If HBA has a deferred error attention, fail the iocb. */
+ if (unlikely(phba->hba_flag & DEFER_ERATT))
+ return IOCB_ERROR;
+
/*
* We should never get an IOCB if we are in a < LINK_DOWN state
*/
@@ -3811,9 +5619,501 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
return IOCB_BUSY;
}
+/**
+ * lpfc_sli4_bpl2sgl - Convert the bpl/bde to a sgl.
+ * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ * @sglq: Pointer to the scatter gather queue object.
+ *
+ * This routine converts the bpl or bde that is in the IOCB
+ * to a sgl list for the sli4 hardware. The physical address
+ * of the bpl/bde is converted back to a virtual address.
+ * If the IOCB contains a BPL then the list of BDE's is
+ * converted to sli4_sge's. If the IOCB contains a single
+ * BDE then it is converted to a single sli_sge.
+ * The IOCB is still in cpu endianess so the contents of
+ * the bpl can be used without byte swapping.
+ *
+ * Returns valid XRI = Success, NO_XRI = Failure.
+**/
+static uint16_t
+lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
+ struct lpfc_sglq *sglq)
+{
+ uint16_t xritag = NO_XRI;
+ struct ulp_bde64 *bpl = NULL;
+ struct ulp_bde64 bde;
+ struct sli4_sge *sgl = NULL;
+ IOCB_t *icmd;
+ int numBdes = 0;
+ int i = 0;
+
+ if (!piocbq || !sglq)
+ return xritag;
+
+ sgl = (struct sli4_sge *)sglq->sgl;
+ icmd = &piocbq->iocb;
+ if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+ numBdes = icmd->un.genreq64.bdl.bdeSize /
+ sizeof(struct ulp_bde64);
+ /* The addrHigh and addrLow fields within the IOCB
+ * have not been byteswapped yet so there is no
+ * need to swap them back.
+ */
+ bpl = (struct ulp_bde64 *)
+ ((struct lpfc_dmabuf *)piocbq->context3)->virt;
+
+ if (!bpl)
+ return xritag;
+
+ for (i = 0; i < numBdes; i++) {
+ /* Should already be byte swapped. */
+ sgl->addr_hi = bpl->addrHigh;
+ sgl->addr_lo = bpl->addrLow;
+ /* swap the size field back to the cpu so we
+ * can assign it to the sgl.
+ */
+ bde.tus.w = le32_to_cpu(bpl->tus.w);
+ bf_set(lpfc_sli4_sge_len, sgl, bde.tus.f.bdeSize);
+ if ((i+1) == numBdes)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ else
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->word3 = cpu_to_le32(sgl->word3);
+ bpl++;
+ sgl++;
+ }
+ } else if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BDE_64) {
+ /* The addrHigh and addrLow fields of the BDE have not
+ * been byteswapped yet so they need to be swapped
+ * before putting them in the sgl.
+ */
+ sgl->addr_hi =
+ cpu_to_le32(icmd->un.genreq64.bdl.addrHigh);
+ sgl->addr_lo =
+ cpu_to_le32(icmd->un.genreq64.bdl.addrLow);
+ bf_set(lpfc_sli4_sge_len, sgl,
+ icmd->un.genreq64.bdl.bdeSize);
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->word3 = cpu_to_le32(sgl->word3);
+ }
+ return sglq->sli4_xritag;
+}
+
+/**
+ * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
+ * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ *
+ * This routine performs a round robin SCSI command to SLI4 FCP WQ index
+ * distribution.
+ *
+ * Return: index into SLI4 fast-path FCP queue index.
+ **/
+static uint32_t
+lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
+{
+ static uint32_t fcp_qidx;
+
+ return fcp_qidx++ % phba->cfg_fcp_wq_count;
+}
+
+/**
+ * lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry.
+ * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ * @wqe: Pointer to the work queue entry.
+ *
+ * This routine converts the iocb command to its Work Queue Entry
+ * equivalent. The wqe pointer should not have any fields set when
+ * this routine is called because it will memcpy over them.
+ * This routine does not set the CQ_ID or the WQEC bits in the
+ * wqe.
+ *
+ * Returns: 0 = Success, IOCB_ERROR = Failure.
+ **/
+static int
+lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
+ union lpfc_wqe *wqe)
+{
+ uint32_t payload_len = 0;
+ uint8_t ct = 0;
+ uint32_t fip;
+ uint32_t abort_tag;
+ uint8_t command_type = ELS_COMMAND_NON_FIP;
+ uint8_t cmnd;
+ uint16_t xritag;
+ struct ulp_bde64 *bpl = NULL;
+
+ fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+ /* The fcp commands will set command type */
+ if ((!(iocbq->iocb_flag & LPFC_IO_FCP)) && (!fip))
+ command_type = ELS_COMMAND_NON_FIP;
+ else if (!(iocbq->iocb_flag & LPFC_IO_FCP))
+ command_type = ELS_COMMAND_FIP;
+ else if (iocbq->iocb_flag & LPFC_IO_FCP)
+ command_type = FCP_COMMAND;
+ else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2019 Invalid cmd 0x%x\n",
+ iocbq->iocb.ulpCommand);
+ return IOCB_ERROR;
+ }
+ /* Some of the fields are in the right position already */
+ memcpy(wqe, &iocbq->iocb, sizeof(union lpfc_wqe));
+ abort_tag = (uint32_t) iocbq->iotag;
+ xritag = iocbq->sli4_xritag;
+ wqe->words[7] = 0; /* The ct field has moved so reset */
+ /* words0-2 bpl convert bde */
+ if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+ bpl = (struct ulp_bde64 *)
+ ((struct lpfc_dmabuf *)iocbq->context3)->virt;
+ if (!bpl)
+ return IOCB_ERROR;
+
+ /* Should already be byte swapped. */
+ wqe->generic.bde.addrHigh = le32_to_cpu(bpl->addrHigh);
+ wqe->generic.bde.addrLow = le32_to_cpu(bpl->addrLow);
+ /* swap the size field back to the cpu so we
+ * can assign it to the sgl.
+ */
+ wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w);
+ payload_len = wqe->generic.bde.tus.f.bdeSize;
+ } else
+ payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+
+ iocbq->iocb.ulpIoTag = iocbq->iotag;
+ cmnd = iocbq->iocb.ulpCommand;
+
+ switch (iocbq->iocb.ulpCommand) {
+ case CMD_ELS_REQUEST64_CR:
+ if (!iocbq->iocb.ulpLe) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2007 Only Limited Edition cmd Format"
+ " supported 0x%x\n",
+ iocbq->iocb.ulpCommand);
+ return IOCB_ERROR;
+ }
+ wqe->els_req.payload_len = payload_len;
+ /* Els_reguest64 has a TMO */
+ bf_set(wqe_tmo, &wqe->els_req.wqe_com,
+ iocbq->iocb.ulpTimeout);
+ /* Need a VF for word 4 set the vf bit*/
+ bf_set(els_req64_vf, &wqe->els_req, 0);
+ /* And a VFID for word 12 */
+ bf_set(els_req64_vfid, &wqe->els_req, 0);
+ /*
+ * Set ct field to 3, indicates that the context_tag field
+ * contains the FCFI and remote N_Port_ID is
+ * in word 5.
+ */
+
+ ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
+ bf_set(lpfc_wqe_gen_context, &wqe->generic,
+ iocbq->iocb.ulpContext);
+
+ if (iocbq->vport->fc_myDID != 0) {
+ bf_set(els_req64_sid, &wqe->els_req,
+ iocbq->vport->fc_myDID);
+ bf_set(els_req64_sp, &wqe->els_req, 1);
+ }
+ bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct);
+ bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
+ /* CCP CCPE PV PRI in word10 were set in the memcpy */
+ break;
+ case CMD_XMIT_SEQUENCE64_CR:
+ /* word3 iocb=io_tag32 wqe=payload_offset */
+ /* payload offset used for multilpe outstanding
+ * sequences on the same exchange
+ */
+ wqe->words[3] = 0;
+ /* word4 relative_offset memcpy */
+ /* word5 r_ctl/df_ctl memcpy */
+ bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
+ wqe->xmit_sequence.xmit_len = payload_len;
+ break;
+ case CMD_XMIT_BCAST64_CN:
+ /* word3 iocb=iotag32 wqe=payload_len */
+ wqe->words[3] = 0; /* no definition for this in wqe */
+ /* word4 iocb=rsvd wqe=rsvd */
+ /* word5 iocb=rctl/type/df_ctl wqe=rctl/type/df_ctl memcpy */
+ /* word6 iocb=ctxt_tag/io_tag wqe=ctxt_tag/xri */
+ bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+ ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+ break;
+ case CMD_FCP_IWRITE64_CR:
+ command_type = FCP_COMMAND_DATA_OUT;
+ /* The struct for wqe fcp_iwrite has 3 fields that are somewhat
+ * confusing.
+ * word3 is payload_len: byte offset to the sgl entry for the
+ * fcp_command.
+ * word4 is total xfer len, same as the IOCB->ulpParameter.
+ * word5 is initial xfer len 0 = wait for xfer-ready
+ */
+
+ /* Always wait for xfer-ready before sending data */
+ wqe->fcp_iwrite.initial_xfer_len = 0;
+ /* word 4 (xfer length) should have been set on the memcpy */
+
+ /* allow write to fall through to read */
+ case CMD_FCP_IREAD64_CR:
+ /* FCP_CMD is always the 1st sgl entry */
+ wqe->fcp_iread.payload_len =
+ payload_len + sizeof(struct fcp_rsp);
+
+ /* word 4 (xfer length) should have been set on the memcpy */
+
+ bf_set(lpfc_wqe_gen_erp, &wqe->generic,
+ iocbq->iocb.ulpFCP2Rcvy);
+ bf_set(lpfc_wqe_gen_lnk, &wqe->generic, iocbq->iocb.ulpXS);
+ /* The XC bit and the XS bit are similar. The driver never
+ * tracked whether or not the exchange was previouslly open.
+ * XC = Exchange create, 0 is create. 1 is already open.
+ * XS = link cmd: 1 do not close the exchange after command.
+ * XS = 0 close exchange when command completes.
+ * The only time we would not set the XC bit is when the XS bit
+ * is set and we are sending our 2nd or greater command on
+ * this exchange.
+ */
+
+ /* ALLOW read & write to fall through to ICMD64 */
+ case CMD_FCP_ICMND64_CR:
+ /* Always open the exchange */
+ bf_set(wqe_xc, &wqe->fcp_iread.wqe_com, 0);
+
+ wqe->words[10] &= 0xffff0000; /* zero out ebde count */
+ bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+ break;
+ case CMD_GEN_REQUEST64_CR:
+ /* word3 command length is described as byte offset to the
+ * rsp_data. Would always be 16, sizeof(struct sli4_sge)
+ * sgl[0] = cmnd
+ * sgl[1] = rsp.
+ *
+ */
+ wqe->gen_req.command_len = payload_len;
+ /* Word4 parameter copied in the memcpy */
+ /* Word5 [rctl, type, df_ctl, la] copied in memcpy */
+ /* word6 context tag copied in memcpy */
+ if (iocbq->iocb.ulpCt_h || iocbq->iocb.ulpCt_l) {
+ ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2015 Invalid CT %x command 0x%x\n",
+ ct, iocbq->iocb.ulpCommand);
+ return IOCB_ERROR;
+ }
+ bf_set(lpfc_wqe_gen_ct, &wqe->generic, 0);
+ bf_set(wqe_tmo, &wqe->gen_req.wqe_com,
+ iocbq->iocb.ulpTimeout);
+
+ bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_XMIT_ELS_RSP64_CX:
+ /* words0-2 BDE memcpy */
+ /* word3 iocb=iotag32 wqe=rsvd */
+ wqe->words[3] = 0;
+ /* word4 iocb=did wge=rsvd. */
+ wqe->words[4] = 0;
+ /* word5 iocb=rsvd wge=did */
+ bf_set(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest,
+ iocbq->iocb.un.elsreq64.remoteID);
+
+ bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+ ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+
+ bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+ bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+ if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l)
+ bf_set(lpfc_wqe_gen_context, &wqe->generic,
+ iocbq->vport->vpi + phba->vpi_base);
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_CLOSE_XRI_CN:
+ case CMD_ABORT_XRI_CN:
+ case CMD_ABORT_XRI_CX:
+ /* words 0-2 memcpy should be 0 rserved */
+ /* port will send abts */
+ if (iocbq->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+ /*
+ * The link is down so the fw does not need to send abts
+ * on the wire.
+ */
+ bf_set(abort_cmd_ia, &wqe->abort_cmd, 1);
+ else
+ bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
+ bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
+ abort_tag = iocbq->iocb.un.acxri.abortIoTag;
+ wqe->words[5] = 0;
+ bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+ ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+ abort_tag = iocbq->iocb.un.acxri.abortIoTag;
+ wqe->generic.abort_tag = abort_tag;
+ /*
+ * The abort handler will send us CMD_ABORT_XRI_CN or
+ * CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX
+ */
+ bf_set(lpfc_wqe_gen_command, &wqe->generic, CMD_ABORT_XRI_CX);
+ cmnd = CMD_ABORT_XRI_CX;
+ command_type = OTHER_COMMAND;
+ xritag = 0;
+ break;
+ case CMD_XRI_ABORTED_CX:
+ case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
+ /* words0-2 are all 0's no bde */
+ /* word3 and word4 are rsvrd */
+ wqe->words[3] = 0;
+ wqe->words[4] = 0;
+ /* word5 iocb=rsvd wge=did */
+ /* There is no remote port id in the IOCB? */
+ /* Let this fall through and fail */
+ case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */
+ case CMD_FCP_TSEND64_CX: /* Target mode send xfer-ready */
+ case CMD_FCP_TRSP64_CX: /* Target mode rcv */
+ case CMD_FCP_AUTO_TRSP_CX: /* Auto target rsp */
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2014 Invalid command 0x%x\n",
+ iocbq->iocb.ulpCommand);
+ return IOCB_ERROR;
+ break;
+
+ }
+ bf_set(lpfc_wqe_gen_xri, &wqe->generic, xritag);
+ bf_set(lpfc_wqe_gen_request_tag, &wqe->generic, iocbq->iotag);
+ wqe->generic.abort_tag = abort_tag;
+ bf_set(lpfc_wqe_gen_cmd_type, &wqe->generic, command_type);
+ bf_set(lpfc_wqe_gen_command, &wqe->generic, cmnd);
+ bf_set(lpfc_wqe_gen_class, &wqe->generic, iocbq->iocb.ulpClass);
+ bf_set(lpfc_wqe_gen_cq_id, &wqe->generic, LPFC_WQE_CQ_ID_DEFAULT);
+
+ return 0;
+}
+
+/**
+ * __lpfc_sli_issue_iocb_s4 - SLI4 device lockless ver of lpfc_sli_issue_iocb
+ * @phba: Pointer to HBA context object.
+ * @ring_number: SLI ring number to issue iocb on.
+ * @piocb: Pointer to command iocb.
+ * @flag: Flag indicating if this command can be put into txq.
+ *
+ * __lpfc_sli_issue_iocb_s4 is used by other functions in the driver to issue
+ * an iocb command to an HBA with SLI-4 interface spec.
+ *
+ * This function is called with hbalock held. The function will return success
+ * after it successfully submit the iocb to firmware or after adding to the
+ * txq.
+ **/
+static int
+__lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag)
+{
+ struct lpfc_sglq *sglq;
+ uint16_t xritag;
+ union lpfc_wqe wqe;
+ struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
+ uint32_t fcp_wqidx;
+
+ if (piocb->sli4_xritag == NO_XRI) {
+ if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
+ piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+ sglq = NULL;
+ else {
+ sglq = __lpfc_sli_get_sglq(phba);
+ if (!sglq)
+ return IOCB_ERROR;
+ piocb->sli4_xritag = sglq->sli4_xritag;
+ }
+ } else if (piocb->iocb_flag & LPFC_IO_FCP) {
+ sglq = NULL; /* These IO's already have an XRI and
+ * a mapped sgl.
+ */
+ } else {
+ /* This is a continuation of a commandi,(CX) so this
+ * sglq is on the active list
+ */
+ sglq = __lpfc_get_active_sglq(phba, piocb->sli4_xritag);
+ if (!sglq)
+ return IOCB_ERROR;
+ }
+
+ if (sglq) {
+ xritag = lpfc_sli4_bpl2sgl(phba, piocb, sglq);
+ if (xritag != sglq->sli4_xritag)
+ return IOCB_ERROR;
+ }
+
+ if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
+ return IOCB_ERROR;
+
+ if (piocb->iocb_flag & LPFC_IO_FCP) {
+ fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba, piocb);
+ if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+ return IOCB_ERROR;
+ } else {
+ if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
+ return IOCB_ERROR;
+ }
+ lpfc_sli_ringtxcmpl_put(phba, pring, piocb);
+
+ return 0;
+}
+
+/**
+ * __lpfc_sli_issue_iocb - Wrapper func of lockless version for issuing iocb
+ *
+ * This routine wraps the actual lockless version for issusing IOCB function
+ * pointer from the lpfc_hba struct.
+ *
+ * Return codes:
+ * IOCB_ERROR - Error
+ * IOCB_SUCCESS - Success
+ * IOCB_BUSY - Busy
+ **/
+static inline int
+__lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag)
+{
+ return phba->__lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
+}
+
+/**
+ * lpfc_sli_api_table_setup - Set up sli api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the SLI interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+ switch (dev_grp) {
+ case LPFC_PCI_DEV_LP:
+ phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s3;
+ phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s3;
+ break;
+ case LPFC_PCI_DEV_OC:
+ phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s4;
+ phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s4;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1419 Invalid HBA PCI-device group: 0x%x\n",
+ dev_grp);
+ return -ENODEV;
+ break;
+ }
+ phba->lpfc_get_iocb_from_iocbq = lpfc_get_iocb_from_iocbq;
+ return 0;
+}
/**
- * lpfc_sli_issue_iocb: Wrapper function for __lpfc_sli_issue_iocb.
+ * lpfc_sli_issue_iocb - Wrapper function for __lpfc_sli_issue_iocb
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to command iocb.
@@ -3826,21 +6126,21 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* functions which do not hold hbalock.
**/
int
-lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
unsigned long iflags;
int rc;
spin_lock_irqsave(&phba->hbalock, iflags);
- rc = __lpfc_sli_issue_iocb(phba, pring, piocb, flag);
+ rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
spin_unlock_irqrestore(&phba->hbalock, iflags);
return rc;
}
/**
- * lpfc_extra_ring_setup: Extra ring setup function.
+ * lpfc_extra_ring_setup - Extra ring setup function
* @phba: Pointer to HBA context object.
*
* This function is called while driver attaches with the
@@ -3886,7 +6186,7 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba)
}
/**
- * lpfc_sli_async_event_handler: ASYNC iocb handler function.
+ * lpfc_sli_async_event_handler - ASYNC iocb handler function
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @iocbq: Pointer to iocb object.
@@ -3907,6 +6207,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
uint16_t temp;
struct temp_event temp_event_data;
struct Scsi_Host *shost;
+ uint32_t *iocb_w;
icmd = &iocbq->iocb;
evt_code = icmd->un.asyncstat.evt_code;
@@ -3914,13 +6215,23 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
if ((evt_code != ASYNC_TEMP_WARN) &&
(evt_code != ASYNC_TEMP_SAFE)) {
+ iocb_w = (uint32_t *) icmd;
lpfc_printf_log(phba,
KERN_ERR,
LOG_SLI,
"0346 Ring %d handler: unexpected ASYNC_STATUS"
- " evt_code 0x%x\n",
+ " evt_code 0x%x \n"
+ "W0 0x%08x W1 0x%08x W2 0x%08x W3 0x%08x\n"
+ "W4 0x%08x W5 0x%08x W6 0x%08x W7 0x%08x\n"
+ "W8 0x%08x W9 0x%08x W10 0x%08x W11 0x%08x\n"
+ "W12 0x%08x W13 0x%08x W14 0x%08x W15 0x%08x\n",
pring->ringno,
- icmd->un.asyncstat.evt_code);
+ icmd->un.asyncstat.evt_code,
+ iocb_w[0], iocb_w[1], iocb_w[2], iocb_w[3],
+ iocb_w[4], iocb_w[5], iocb_w[6], iocb_w[7],
+ iocb_w[8], iocb_w[9], iocb_w[10], iocb_w[11],
+ iocb_w[12], iocb_w[13], iocb_w[14], iocb_w[15]);
+
return;
}
temp_event_data.data = (uint32_t)temp;
@@ -3954,7 +6265,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
/**
- * lpfc_sli_setup: SLI ring setup function.
+ * lpfc_sli_setup - SLI ring setup function
* @phba: Pointer to HBA context object.
*
* lpfc_sli_setup sets up rings of the SLI interface with
@@ -4076,7 +6387,7 @@ lpfc_sli_setup(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_queue_setup: Queue initialization function.
+ * lpfc_sli_queue_setup - Queue initialization function
* @phba: Pointer to HBA context object.
*
* lpfc_sli_queue_setup sets up mailbox queues and iocb queues for each
@@ -4115,7 +6426,53 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_host_down: Vport cleanup function.
+ * lpfc_sli_mbox_sys_flush - Flush mailbox command sub-system
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine flushes the mailbox command subsystem. It will unconditionally
+ * flush all the mailbox commands in the three possible stages in the mailbox
+ * command sub-system: pending mailbox command queue; the outstanding mailbox
+ * command; and completed mailbox command queue. It is caller's responsibility
+ * to make sure that the driver is in the proper state to flush the mailbox
+ * command sub-system. Namely, the posting of mailbox commands into the
+ * pending mailbox command queue from the various clients must be stopped;
+ * either the HBA is in a state that it will never works on the outstanding
+ * mailbox command (such as in EEH or ERATT conditions) or the outstanding
+ * mailbox command has been completed.
+ **/
+static void
+lpfc_sli_mbox_sys_flush(struct lpfc_hba *phba)
+{
+ LIST_HEAD(completions);
+ struct lpfc_sli *psli = &phba->sli;
+ LPFC_MBOXQ_t *pmb;
+ unsigned long iflag;
+
+ /* Flush all the mailbox commands in the mbox system */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ /* The pending mailbox command queue */
+ list_splice_init(&phba->sli.mboxq, &completions);
+ /* The outstanding active mailbox command */
+ if (psli->mbox_active) {
+ list_add_tail(&psli->mbox_active->list, &completions);
+ psli->mbox_active = NULL;
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ }
+ /* The completed mailbox command queue */
+ list_splice_init(&phba->sli.mboxq_cmpl, &completions);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+
+ /* Return all flushed mailbox commands with MBX_NOT_FINISHED status */
+ while (!list_empty(&completions)) {
+ list_remove_head(&completions, pmb, LPFC_MBOXQ_t, list);
+ pmb->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ if (pmb->mbox_cmpl)
+ pmb->mbox_cmpl(phba, pmb);
+ }
+}
+
+/**
+ * lpfc_sli_host_down - Vport cleanup function
* @vport: Pointer to virtual port object.
*
* lpfc_sli_host_down is called to clean up the resources
@@ -4179,22 +6536,14 @@ lpfc_sli_host_down(struct lpfc_vport *vport)
spin_unlock_irqrestore(&phba->hbalock, flags);
- while (!list_empty(&completions)) {
- list_remove_head(&completions, iocb, struct lpfc_iocbq, list);
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
- iocb->iocb.un.ulpWord[4] = IOERR_SLI_DOWN;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
return 1;
}
/**
- * lpfc_sli_hba_down: Resource cleanup function for the HBA.
+ * lpfc_sli_hba_down - Resource cleanup function for the HBA
* @phba: Pointer to HBA context object.
*
* This function cleans up all iocb, buffers, mailbox commands
@@ -4215,11 +6564,11 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
struct lpfc_dmabuf *buf_ptr;
- LPFC_MBOXQ_t *pmb;
- struct lpfc_iocbq *iocb;
- IOCB_t *cmd = NULL;
- int i;
unsigned long flags = 0;
+ int i;
+
+ /* Shutdown the mailbox command sub-system */
+ lpfc_sli_mbox_sys_shutdown(phba);
lpfc_hba_down_prep(phba);
@@ -4245,18 +6594,9 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
}
spin_unlock_irqrestore(&phba->hbalock, flags);
- while (!list_empty(&completions)) {
- list_remove_head(&completions, iocb, struct lpfc_iocbq, list);
- cmd = &iocb->iocb;
-
- if (!iocb->iocb_cmpl)
- lpfc_sli_release_iocbq(phba, iocb);
- else {
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_DOWN;
- (iocb->iocb_cmpl) (phba, iocb, iocb);
- }
- }
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
spin_lock_irqsave(&phba->hbalock, flags);
list_splice_init(&phba->elsbuf, &completions);
@@ -4273,33 +6613,47 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
/* Return any active mbox cmds */
del_timer_sync(&psli->mbox_tmo);
- spin_lock_irqsave(&phba->hbalock, flags);
- spin_lock(&phba->pport->work_port_lock);
+ spin_lock_irqsave(&phba->pport->work_port_lock, flags);
phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
- spin_unlock(&phba->pport->work_port_lock);
+ spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
- /* Return any pending or completed mbox cmds */
- list_splice_init(&phba->sli.mboxq, &completions);
- if (psli->mbox_active) {
- list_add_tail(&psli->mbox_active->list, &completions);
- psli->mbox_active = NULL;
- psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
- }
- list_splice_init(&phba->sli.mboxq_cmpl, &completions);
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ return 1;
+}
+
+/**
+ * lpfc_sli4_hba_down - PCI function resource cleanup for the SLI4 HBA
+ * @phba: Pointer to HBA context object.
+ *
+ * This function cleans up all queues, iocb, buffers, mailbox commands while
+ * shutting down the SLI4 HBA FCoE function. This function is called with no
+ * lock held and always returns 1.
+ *
+ * This function does the following to cleanup driver FCoE function resources:
+ * - Free discovery resources for each virtual port
+ * - Cleanup any pending fabric iocbs
+ * - Iterate through the iocb txq and free each entry in the list.
+ * - Free up any buffer posted to the HBA.
+ * - Clean up all the queue entries: WQ, RQ, MQ, EQ, CQ, etc.
+ * - Free mailbox commands in the mailbox queue.
+ **/
+int
+lpfc_sli4_hba_down(struct lpfc_hba *phba)
+{
+ /* Stop the SLI4 device port */
+ lpfc_stop_port(phba);
+
+ /* Tear down the queues in the HBA */
+ lpfc_sli4_queue_unset(phba);
+
+ /* unregister default FCFI from the HBA */
+ lpfc_sli4_fcfi_unreg(phba, phba->fcf.fcfi);
- while (!list_empty(&completions)) {
- list_remove_head(&completions, pmb, LPFC_MBOXQ_t, list);
- pmb->mb.mbxStatus = MBX_NOT_FINISHED;
- if (pmb->mbox_cmpl)
- pmb->mbox_cmpl(phba,pmb);
- }
return 1;
}
/**
- * lpfc_sli_pcimem_bcopy: SLI memory copy function.
+ * lpfc_sli_pcimem_bcopy - SLI memory copy function
* @srcp: Source memory pointer.
* @destp: Destination memory pointer.
* @cnt: Number of words required to be copied.
@@ -4329,7 +6683,7 @@ lpfc_sli_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt)
/**
- * lpfc_sli_ringpostbuf_put: Function to add a buffer to postbufq.
+ * lpfc_sli_ringpostbuf_put - Function to add a buffer to postbufq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @mp: Pointer to driver buffer object.
@@ -4352,8 +6706,7 @@ lpfc_sli_ringpostbuf_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_get_buffer_tag: Tag allocation function for a buffer posted
- * using CMD_QUE_XRI64_CX iocb.
+ * lpfc_sli_get_buffer_tag - allocates a tag for a CMD_QUE_XRI64_CX buffer
* @phba: Pointer to HBA context object.
*
* When HBQ is enabled, buffers are searched based on tags. This function
@@ -4378,8 +6731,7 @@ lpfc_sli_get_buffer_tag(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_ring_taggedbuf_get: Search HBQ buffer associated with
- * posted using CMD_QUE_XRI64_CX iocb.
+ * lpfc_sli_ring_taggedbuf_get - find HBQ buffer associated with given tag
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @tag: Buffer tag.
@@ -4422,8 +6774,7 @@ lpfc_sli_ring_taggedbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_ringpostbuf_get: SLI2 buffer search function for
- * unsolicited ct and els events.
+ * lpfc_sli_ringpostbuf_get - search buffers for unsolicited CT and ELS events
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @phys: DMA address of the buffer.
@@ -4466,7 +6817,7 @@ lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_abort_els_cmpl: Completion handler for the els abort iocbs.
+ * lpfc_sli_abort_els_cmpl - Completion handler for the els abort iocbs
* @phba: Pointer to HBA context object.
* @cmdiocb: Pointer to driver command iocb object.
* @rspiocb: Pointer to driver response iocb object.
@@ -4542,7 +6893,7 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_ignore_els_cmpl: Completion handler for aborted ELS command.
+ * lpfc_ignore_els_cmpl - Completion handler for aborted ELS command
* @phba: Pointer to HBA context object.
* @cmdiocb: Pointer to driver command iocb object.
* @rspiocb: Pointer to driver response iocb object.
@@ -4572,7 +6923,7 @@ lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_sli_issue_abort_iotag: Abort function for a command iocb.
+ * lpfc_sli_issue_abort_iotag - Abort function for a command iocb
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @cmdiocb: Pointer to driver command iocb object.
@@ -4628,7 +6979,10 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
iabt = &abtsiocbp->iocb;
iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
iabt->un.acxri.abortContextTag = icmd->ulpContext;
- iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+ else
+ iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
iabt->ulpLe = 1;
iabt->ulpClass = icmd->ulpClass;
@@ -4644,7 +6998,7 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
"abort cmd iotag x%x\n",
iabt->un.acxri.abortContextTag,
iabt->un.acxri.abortIoTag, abtsiocbp->iotag);
- retval = __lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0);
+ retval = __lpfc_sli_issue_iocb(phba, pring->ringno, abtsiocbp, 0);
if (retval)
__lpfc_sli_release_iocbq(phba, abtsiocbp);
@@ -4658,15 +7012,14 @@ abort_iotag_exit:
}
/**
- * lpfc_sli_validate_fcp_iocb: Filtering function, used to find commands
- * associated with a vport/SCSI target/lun.
+ * lpfc_sli_validate_fcp_iocb - find commands associated with a vport or LUN
* @iocbq: Pointer to driver iocb object.
* @vport: Pointer to driver virtual port object.
* @tgt_id: SCSI ID of the target.
* @lun_id: LUN ID of the scsi device.
* @ctx_cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST
*
- * This function acts as iocb filter for functions which abort or count
+ * This function acts as an iocb filter for functions which abort or count
* all FCP iocbs pending on a lun/SCSI target/SCSI host. It will return
* 0 if the filtering criteria is met for the given iocb and will return
* 1 if the filtering criteria is not met.
@@ -4724,7 +7077,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
}
/**
- * lpfc_sli_sum_iocb: Function to count the number of FCP iocbs pending.
+ * lpfc_sli_sum_iocb - Function to count the number of FCP iocbs pending
* @vport: Pointer to virtual port.
* @tgt_id: SCSI ID of the target.
* @lun_id: LUN ID of the scsi device.
@@ -4762,8 +7115,7 @@ lpfc_sli_sum_iocb(struct lpfc_vport *vport, uint16_t tgt_id, uint64_t lun_id,
}
/**
- * lpfc_sli_abort_fcp_cmpl: Completion handler function for an aborted
- * FCP iocb.
+ * lpfc_sli_abort_fcp_cmpl - Completion handler function for aborted FCP IOCBs
* @phba: Pointer to HBA context object
* @cmdiocb: Pointer to command iocb object.
* @rspiocb: Pointer to response iocb object.
@@ -4781,8 +7133,7 @@ lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
/**
- * lpfc_sli_abort_iocb: This function issue abort for all SCSI commands
- * pending on a SCSI host(vport)/target/lun.
+ * lpfc_sli_abort_iocb - issue abort for all commands on a host/target/LUN
* @vport: Pointer to virtual port.
* @pring: Pointer to driver SLI ring object.
* @tgt_id: SCSI ID of the target.
@@ -4830,7 +7181,10 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
cmd = &iocbq->iocb;
abtsiocb->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
abtsiocb->iocb.un.acxri.abortContextTag = cmd->ulpContext;
- abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ abtsiocb->iocb.un.acxri.abortIoTag = iocbq->sli4_xritag;
+ else
+ abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
abtsiocb->iocb.ulpLe = 1;
abtsiocb->iocb.ulpClass = cmd->ulpClass;
abtsiocb->vport = phba->pport;
@@ -4842,7 +7196,8 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
/* Setup callback routine and issue the command. */
abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
- ret_val = lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0);
+ ret_val = lpfc_sli_issue_iocb(phba, pring->ringno,
+ abtsiocb, 0);
if (ret_val == IOCB_ERROR) {
lpfc_sli_release_iocbq(phba, abtsiocb);
errcnt++;
@@ -4854,8 +7209,7 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
}
/**
- * lpfc_sli_wake_iocb_wait: iocb completion handler for iocb issued using
- * lpfc_sli_issue_iocb_wait.
+ * lpfc_sli_wake_iocb_wait - lpfc_sli_issue_iocb_wait's completion handler
* @phba: Pointer to HBA context object.
* @cmdiocbq: Pointer to command iocb.
* @rspiocbq: Pointer to response iocb.
@@ -4893,7 +7247,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
}
/**
- * lpfc_sli_issue_iocb_wait: Synchronous function to issue iocb commands.
+ * lpfc_sli_issue_iocb_wait - Synchronous function to issue iocb commands
* @phba: Pointer to HBA context object..
* @pring: Pointer to sli ring.
* @piocb: Pointer to command iocb.
@@ -4924,7 +7278,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
**/
int
lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
- struct lpfc_sli_ring *pring,
+ uint32_t ring_number,
struct lpfc_iocbq *piocb,
struct lpfc_iocbq *prspiocbq,
uint32_t timeout)
@@ -4955,7 +7309,7 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
readl(phba->HCregaddr); /* flush */
}
- retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0);
+ retval = lpfc_sli_issue_iocb(phba, ring_number, piocb, 0);
if (retval == IOCB_SUCCESS) {
timeout_req = timeout * HZ;
timeleft = wait_event_timeout(done_q,
@@ -5000,7 +7354,7 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
}
/**
- * lpfc_sli_issue_mbox_wait: Synchronous function to issue mailbox.
+ * lpfc_sli_issue_mbox_wait - Synchronous function to issue mailbox
* @phba: Pointer to HBA context object.
* @pmboxq: Pointer to driver mailbox object.
* @timeout: Timeout in number of seconds.
@@ -5070,53 +7424,156 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
}
/**
- * lpfc_sli_flush_mbox_queue: mailbox queue cleanup function.
+ * lpfc_sli_mbox_sys_shutdown - shutdown mailbox command sub-system
* @phba: Pointer to HBA context.
*
- * This function is called to cleanup any pending mailbox
- * objects in the driver queue before bringing the HBA offline.
- * This function is called while resetting the HBA.
- * The function is called without any lock held. The function
- * takes hbalock to update SLI data structure.
- * This function returns 1 when there is an active mailbox
- * command pending else returns 0.
+ * This function is called to shutdown the driver's mailbox sub-system.
+ * It first marks the mailbox sub-system is in a block state to prevent
+ * the asynchronous mailbox command from issued off the pending mailbox
+ * command queue. If the mailbox command sub-system shutdown is due to
+ * HBA error conditions such as EEH or ERATT, this routine shall invoke
+ * the mailbox sub-system flush routine to forcefully bring down the
+ * mailbox sub-system. Otherwise, if it is due to normal condition (such
+ * as with offline or HBA function reset), this routine will wait for the
+ * outstanding mailbox command to complete before invoking the mailbox
+ * sub-system flush routine to gracefully bring down mailbox sub-system.
**/
-int
-lpfc_sli_flush_mbox_queue(struct lpfc_hba * phba)
+void
+lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
{
- struct lpfc_vport *vport = phba->pport;
- int i = 0;
- uint32_t ha_copy;
+ struct lpfc_sli *psli = &phba->sli;
+ uint8_t actcmd = MBX_HEARTBEAT;
+ unsigned long timeout;
- while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE && !vport->stopped) {
- if (i++ > LPFC_MBOX_TMO * 1000)
- return 1;
+ spin_lock_irq(&phba->hbalock);
+ psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
+ spin_unlock_irq(&phba->hbalock);
- /*
- * Call lpfc_sli_handle_mb_event only if a mailbox cmd
- * did finish. This way we won't get the misleading
- * "Stray Mailbox Interrupt" message.
- */
+ if (psli->sli_flag & LPFC_SLI_ACTIVE) {
spin_lock_irq(&phba->hbalock);
- ha_copy = phba->work_ha;
- phba->work_ha &= ~HA_MBATT;
+ if (phba->sli.mbox_active)
+ actcmd = phba->sli.mbox_active->u.mb.mbxCommand;
spin_unlock_irq(&phba->hbalock);
+ /* Determine how long we might wait for the active mailbox
+ * command to be gracefully completed by firmware.
+ */
+ timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, actcmd) *
+ 1000) + jiffies;
+ while (phba->sli.mbox_active) {
+ /* Check active mailbox complete status every 2ms */
+ msleep(2);
+ if (time_after(jiffies, timeout))
+ /* Timeout, let the mailbox flush routine to
+ * forcefully release active mailbox command
+ */
+ break;
+ }
+ }
+ lpfc_sli_mbox_sys_flush(phba);
+}
+
+/**
+ * lpfc_sli_eratt_read - read sli-3 error attention events
+ * @phba: Pointer to HBA context.
+ *
+ * This function is called to read the SLI3 device error attention registers
+ * for possible error attention events. The caller must hold the hostlock
+ * with spin_lock_irq().
+ *
+ * This fucntion returns 1 when there is Error Attention in the Host Attention
+ * Register and returns 0 otherwise.
+ **/
+static int
+lpfc_sli_eratt_read(struct lpfc_hba *phba)
+{
+ uint32_t ha_copy;
+
+ /* Read chip Host Attention (HA) register */
+ ha_copy = readl(phba->HAregaddr);
+ if (ha_copy & HA_ERATT) {
+ /* Read host status register to retrieve error event */
+ lpfc_sli_read_hs(phba);
- if (ha_copy & HA_MBATT)
- if (lpfc_sli_handle_mb_event(phba) == 0)
- i = 0;
+ /* Check if there is a deferred error condition is active */
+ if ((HS_FFER1 & phba->work_hs) &&
+ ((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
+ HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= DEFER_ERATT;
+ spin_unlock_irq(&phba->hbalock);
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr);
+ }
- msleep(1);
+ /* Set the driver HA work bitmap */
+ spin_lock_irq(&phba->hbalock);
+ phba->work_ha |= HA_ERATT;
+ /* Indicate polling handles this ERATT */
+ phba->hba_flag |= HBA_ERATT_HANDLED;
+ spin_unlock_irq(&phba->hbalock);
+ return 1;
}
+ return 0;
+}
- return (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) ? 1 : 0;
+/**
+ * lpfc_sli4_eratt_read - read sli-4 error attention events
+ * @phba: Pointer to HBA context.
+ *
+ * This function is called to read the SLI4 device error attention registers
+ * for possible error attention events. The caller must hold the hostlock
+ * with spin_lock_irq().
+ *
+ * This fucntion returns 1 when there is Error Attention in the Host Attention
+ * Register and returns 0 otherwise.
+ **/
+static int
+lpfc_sli4_eratt_read(struct lpfc_hba *phba)
+{
+ uint32_t uerr_sta_hi, uerr_sta_lo;
+ uint32_t onlnreg0, onlnreg1;
+
+ /* For now, use the SLI4 device internal unrecoverable error
+ * registers for error attention. This can be changed later.
+ */
+ onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
+ onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
+ if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
+ uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+ uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+ if (uerr_sta_lo || uerr_sta_hi) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1423 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "online0_reg=0x%x, online1_reg=0x%x\n",
+ uerr_sta_lo, uerr_sta_hi,
+ onlnreg0, onlnreg1);
+ /* TEMP: as the driver error recover logic is not
+ * fully developed, we just log the error message
+ * and the device error attention action is now
+ * temporarily disabled.
+ */
+ return 0;
+ phba->work_status[0] = uerr_sta_lo;
+ phba->work_status[1] = uerr_sta_hi;
+ spin_lock_irq(&phba->hbalock);
+ /* Set the driver HA work bitmap */
+ phba->work_ha |= HA_ERATT;
+ /* Indicate polling handles this ERATT */
+ phba->hba_flag |= HBA_ERATT_HANDLED;
+ spin_unlock_irq(&phba->hbalock);
+ return 1;
+ }
+ }
+ return 0;
}
/**
- * lpfc_sli_check_eratt: check error attention events
+ * lpfc_sli_check_eratt - check error attention events
* @phba: Pointer to HBA context.
*
- * This function is called form timer soft interrupt context to check HBA's
+ * This function is called from timer soft interrupt context to check HBA's
* error attention register bit for error attention events.
*
* This fucntion returns 1 when there is Error Attention in the Host Attention
@@ -5127,10 +7584,6 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
{
uint32_t ha_copy;
- /* If PCI channel is offline, don't process it */
- if (unlikely(pci_channel_offline(phba->pcidev)))
- return 0;
-
/* If somebody is waiting to handle an eratt, don't process it
* here. The brdkill function will do this.
*/
@@ -5145,45 +7598,93 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
return 0;
}
- /* Read chip Host Attention (HA) register */
- ha_copy = readl(phba->HAregaddr);
- if (ha_copy & HA_ERATT) {
- /* Read host status register to retrieve error event */
- lpfc_sli_read_hs(phba);
- /* Set the driver HA work bitmap */
- phba->work_ha |= HA_ERATT;
- /* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ /*
+ * If there is deferred error attention, do not check for error
+ * attention
+ */
+ if (unlikely(phba->hba_flag & DEFER_ERATT)) {
spin_unlock_irq(&phba->hbalock);
- return 1;
+ return 0;
+ }
+
+ /* If PCI channel is offline, don't process it */
+ if (unlikely(pci_channel_offline(phba->pcidev))) {
+ spin_unlock_irq(&phba->hbalock);
+ return 0;
+ }
+
+ switch (phba->sli_rev) {
+ case LPFC_SLI_REV2:
+ case LPFC_SLI_REV3:
+ /* Read chip Host Attention (HA) register */
+ ha_copy = lpfc_sli_eratt_read(phba);
+ break;
+ case LPFC_SLI_REV4:
+ /* Read devcie Uncoverable Error (UERR) registers */
+ ha_copy = lpfc_sli4_eratt_read(phba);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0299 Invalid SLI revision (%d)\n",
+ phba->sli_rev);
+ ha_copy = 0;
+ break;
}
spin_unlock_irq(&phba->hbalock);
+
+ return ha_copy;
+}
+
+/**
+ * lpfc_intr_state_check - Check device state for interrupt handling
+ * @phba: Pointer to HBA context.
+ *
+ * This inline routine checks whether a device or its PCI slot is in a state
+ * that the interrupt should be handled.
+ *
+ * This function returns 0 if the device or the PCI slot is in a state that
+ * interrupt should be handled, otherwise -EIO.
+ */
+static inline int
+lpfc_intr_state_check(struct lpfc_hba *phba)
+{
+ /* If the pci channel is offline, ignore all the interrupts */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return -EIO;
+
+ /* Update device level interrupt statistics */
+ phba->sli.slistat.sli_intr++;
+
+ /* Ignore all interrupts during initialization. */
+ if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+ return -EIO;
+
return 0;
}
/**
- * lpfc_sp_intr_handler: The slow-path interrupt handler of lpfc driver.
+ * lpfc_sli_sp_intr_handler - Slow-path interrupt handler to SLI-3 device
* @irq: Interrupt number.
* @dev_id: The device context pointer.
*
* This function is directly called from the PCI layer as an interrupt
- * service routine when the device is enabled with MSI-X multi-message
- * interrupt mode and there are slow-path events in the HBA. However,
- * when the device is enabled with either MSI or Pin-IRQ interrupt mode,
- * this function is called as part of the device-level interrupt handler.
- * When the PCI slot is in error recovery or the HBA is undergoing
- * initialization, the interrupt handler will not process the interrupt.
- * The link attention and ELS ring attention events are handled by the
- * worker thread. The interrupt handler signals the worker thread and
- * and returns for these events. This function is called without any
- * lock held. It gets the hbalock to access and update SLI data
+ * service routine when device with SLI-3 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there are slow-path events in
+ * the HBA. However, when the device is enabled with either MSI or Pin-IRQ
+ * interrupt mode, this function is called as part of the device-level
+ * interrupt handler. When the PCI slot is in error recovery or the HBA
+ * is undergoing initialization, the interrupt handler will not process
+ * the interrupt. The link attention and ELS ring attention events are
+ * handled by the worker thread. The interrupt handler signals the worker
+ * thread and returns for these events. This function is called without
+ * any lock held. It gets the hbalock to access and update SLI data
* structures.
*
* This function returns IRQ_HANDLED when interrupt is handled else it
* returns IRQ_NONE.
**/
irqreturn_t
-lpfc_sp_intr_handler(int irq, void *dev_id)
+lpfc_sli_sp_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
uint32_t ha_copy;
@@ -5213,13 +7714,8 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
* individual interrupt handler in MSI-X multi-message interrupt mode
*/
if (phba->intr_type == MSIX) {
- /* If the pci channel is offline, ignore all the interrupts */
- if (unlikely(pci_channel_offline(phba->pcidev)))
- return IRQ_NONE;
- /* Update device-level interrupt statistics */
- phba->sli.slistat.sli_intr++;
- /* Ignore all interrupts during initialization. */
- if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+ /* Check device state for handling interrupt */
+ if (lpfc_intr_state_check(phba))
return IRQ_NONE;
/* Need to read HA REG for slow-path events */
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -5238,6 +7734,16 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
/* Indicate interrupt handler handles ERATT */
phba->hba_flag |= HBA_ERATT_HANDLED;
}
+
+ /*
+ * If there is deferred error attention, do not check for any
+ * interrupt.
+ */
+ if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return IRQ_NONE;
+ }
+
/* Clear up only attention source related to slow-path */
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
@@ -5309,11 +7815,25 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
}
}
spin_lock_irqsave(&phba->hbalock, iflag);
- if (work_ha_copy & HA_ERATT)
+ if (work_ha_copy & HA_ERATT) {
lpfc_sli_read_hs(phba);
+ /*
+ * Check if there is a deferred error condition
+ * is active
+ */
+ if ((HS_FFER1 & phba->work_hs) &&
+ ((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
+ HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+ phba->hba_flag |= DEFER_ERATT;
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr);
+ }
+ }
+
if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
pmb = phba->sli.mbox_active;
- pmbox = &pmb->mb;
+ pmbox = &pmb->u.mb;
mbox = phba->mbox;
vport = pmb->vport;
@@ -5383,7 +7903,8 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
LOG_MBOX | LOG_SLI,
"0350 rc should have"
"been MBX_BUSY");
- goto send_current_mbox;
+ if (rc != MBX_NOT_FINISHED)
+ goto send_current_mbox;
}
}
spin_lock_irqsave(
@@ -5420,29 +7941,29 @@ send_current_mbox:
}
return IRQ_HANDLED;
-} /* lpfc_sp_intr_handler */
+} /* lpfc_sli_sp_intr_handler */
/**
- * lpfc_fp_intr_handler: The fast-path interrupt handler of lpfc driver.
+ * lpfc_sli_fp_intr_handler - Fast-path interrupt handler to SLI-3 device.
* @irq: Interrupt number.
* @dev_id: The device context pointer.
*
* This function is directly called from the PCI layer as an interrupt
- * service routine when the device is enabled with MSI-X multi-message
- * interrupt mode and there is a fast-path FCP IOCB ring event in the
- * HBA. However, when the device is enabled with either MSI or Pin-IRQ
- * interrupt mode, this function is called as part of the device-level
- * interrupt handler. When the PCI slot is in error recovery or the HBA
- * is undergoing initialization, the interrupt handler will not process
- * the interrupt. The SCSI FCP fast-path ring event are handled in the
- * intrrupt context. This function is called without any lock held. It
- * gets the hbalock to access and update SLI data structures.
+ * service routine when device with SLI-3 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there is a fast-path FCP IOCB
+ * ring event in the HBA. However, when the device is enabled with either
+ * MSI or Pin-IRQ interrupt mode, this function is called as part of the
+ * device-level interrupt handler. When the PCI slot is in error recovery
+ * or the HBA is undergoing initialization, the interrupt handler will not
+ * process the interrupt. The SCSI FCP fast-path ring event are handled in
+ * the intrrupt context. This function is called without any lock held.
+ * It gets the hbalock to access and update SLI data structures.
*
* This function returns IRQ_HANDLED when interrupt is handled else it
* returns IRQ_NONE.
**/
irqreturn_t
-lpfc_fp_intr_handler(int irq, void *dev_id)
+lpfc_sli_fp_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
uint32_t ha_copy;
@@ -5462,18 +7983,21 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
* individual interrupt handler in MSI-X multi-message interrupt mode
*/
if (phba->intr_type == MSIX) {
- /* If pci channel is offline, ignore all the interrupts */
- if (unlikely(pci_channel_offline(phba->pcidev)))
- return IRQ_NONE;
- /* Update device-level interrupt statistics */
- phba->sli.slistat.sli_intr++;
- /* Ignore all interrupts during initialization. */
- if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+ /* Check device state for handling interrupt */
+ if (lpfc_intr_state_check(phba))
return IRQ_NONE;
/* Need to read HA REG for FCP ring and other ring events */
ha_copy = readl(phba->HAregaddr);
/* Clear up only attention source related to fast-path */
spin_lock_irqsave(&phba->hbalock, iflag);
+ /*
+ * If there is deferred error attention, do not check for
+ * any interrupt.
+ */
+ if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return IRQ_NONE;
+ }
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
@@ -5507,26 +8031,27 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
}
}
return IRQ_HANDLED;
-} /* lpfc_fp_intr_handler */
+} /* lpfc_sli_fp_intr_handler */
/**
- * lpfc_intr_handler: The device-level interrupt handler of lpfc driver.
+ * lpfc_sli_intr_handler - Device-level interrupt handler to SLI-3 device
* @irq: Interrupt number.
* @dev_id: The device context pointer.
*
- * This function is the device-level interrupt handler called from the PCI
- * layer when either MSI or Pin-IRQ interrupt mode is enabled and there is
- * an event in the HBA which requires driver attention. This function
- * invokes the slow-path interrupt attention handling function and fast-path
- * interrupt attention handling function in turn to process the relevant
- * HBA attention events. This function is called without any lock held. It
- * gets the hbalock to access and update SLI data structures.
+ * This function is the HBA device-level interrupt handler to device with
+ * SLI-3 interface spec, called from the PCI layer when either MSI or
+ * Pin-IRQ interrupt mode is enabled and there is an event in the HBA which
+ * requires driver attention. This function invokes the slow-path interrupt
+ * attention handling function and fast-path interrupt attention handling
+ * function in turn to process the relevant HBA attention events. This
+ * function is called without any lock held. It gets the hbalock to access
+ * and update SLI data structures.
*
* This function returns IRQ_HANDLED when interrupt is handled, else it
* returns IRQ_NONE.
**/
irqreturn_t
-lpfc_intr_handler(int irq, void *dev_id)
+lpfc_sli_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
irqreturn_t sp_irq_rc, fp_irq_rc;
@@ -5541,15 +8066,8 @@ lpfc_intr_handler(int irq, void *dev_id)
if (unlikely(!phba))
return IRQ_NONE;
- /* If the pci channel is offline, ignore all the interrupts. */
- if (unlikely(pci_channel_offline(phba->pcidev)))
- return IRQ_NONE;
-
- /* Update device level interrupt statistics */
- phba->sli.slistat.sli_intr++;
-
- /* Ignore all interrupts during initialization. */
- if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+ /* Check device state for handling interrupt */
+ if (lpfc_intr_state_check(phba))
return IRQ_NONE;
spin_lock(&phba->hbalock);
@@ -5566,6 +8084,14 @@ lpfc_intr_handler(int irq, void *dev_id)
phba->hba_flag |= HBA_ERATT_HANDLED;
}
+ /*
+ * If there is deferred error attention, do not check for any interrupt.
+ */
+ if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ spin_unlock_irq(&phba->hbalock);
+ return IRQ_NONE;
+ }
+
/* Clear attention sources except link and error attentions */
writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
@@ -5583,7 +8109,7 @@ lpfc_intr_handler(int irq, void *dev_id)
status2 >>= (4*LPFC_ELS_RING);
if (status1 || (status2 & HA_RXMASK))
- sp_irq_rc = lpfc_sp_intr_handler(irq, dev_id);
+ sp_irq_rc = lpfc_sli_sp_intr_handler(irq, dev_id);
else
sp_irq_rc = IRQ_NONE;
@@ -5603,10 +8129,3322 @@ lpfc_intr_handler(int irq, void *dev_id)
status2 = 0;
if ((status1 & HA_RXMASK) || (status2 & HA_RXMASK))
- fp_irq_rc = lpfc_fp_intr_handler(irq, dev_id);
+ fp_irq_rc = lpfc_sli_fp_intr_handler(irq, dev_id);
else
fp_irq_rc = IRQ_NONE;
/* Return device-level interrupt handling status */
return (sp_irq_rc == IRQ_HANDLED) ? sp_irq_rc : fp_irq_rc;
-} /* lpfc_intr_handler */
+} /* lpfc_sli_intr_handler */
+
+/**
+ * lpfc_sli4_fcp_xri_abort_event_proc - Process fcp xri abort event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 FCP abort XRI events.
+ **/
+void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event;
+
+ /* First, declare the fcp xri abort event has been handled */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~FCP_XRI_ABORT_EVENT;
+ spin_unlock_irq(&phba->hbalock);
+ /* Now, handle all the fcp xri abort events */
+ while (!list_empty(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue)) {
+ /* Get the first event from the head of the event queue */
+ spin_lock_irq(&phba->hbalock);
+ list_remove_head(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
+ cq_event, struct lpfc_cq_event, list);
+ spin_unlock_irq(&phba->hbalock);
+ /* Notify aborted XRI for FCP work queue */
+ lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
+ /* Free the event processed back to the free pool */
+ lpfc_sli4_cq_event_release(phba, cq_event);
+ }
+}
+
+/**
+ * lpfc_sli4_els_xri_abort_event_proc - Process els xri abort event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 els abort xri events.
+ **/
+void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba)
+{
+ struct lpfc_cq_event *cq_event;
+
+ /* First, declare the els xri abort event has been handled */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~ELS_XRI_ABORT_EVENT;
+ spin_unlock_irq(&phba->hbalock);
+ /* Now, handle all the els xri abort events */
+ while (!list_empty(&phba->sli4_hba.sp_els_xri_aborted_work_queue)) {
+ /* Get the first event from the head of the event queue */
+ spin_lock_irq(&phba->hbalock);
+ list_remove_head(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
+ cq_event, struct lpfc_cq_event, list);
+ spin_unlock_irq(&phba->hbalock);
+ /* Notify aborted XRI for ELS work queue */
+ lpfc_sli4_els_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
+ /* Free the event processed back to the free pool */
+ lpfc_sli4_cq_event_release(phba, cq_event);
+ }
+}
+
+static void
+lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
+ struct lpfc_iocbq *pIocbOut,
+ struct lpfc_wcqe_complete *wcqe)
+{
+ size_t offset = offsetof(struct lpfc_iocbq, iocb);
+
+ memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
+ sizeof(struct lpfc_iocbq) - offset);
+ memset(&pIocbIn->sli4_info, 0,
+ sizeof(struct lpfc_sli4_rspiocb_info));
+ /* Map WCQE parameters into irspiocb parameters */
+ pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
+ if (pIocbOut->iocb_flag & LPFC_IO_FCP)
+ if (pIocbIn->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
+ pIocbIn->iocb.un.fcpi.fcpi_parm =
+ pIocbOut->iocb.un.fcpi.fcpi_parm -
+ wcqe->total_data_placed;
+ else
+ pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+ else
+ pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+ /* Load in additional WCQE parameters */
+ pIocbIn->sli4_info.hw_status = bf_get(lpfc_wcqe_c_hw_status, wcqe);
+ pIocbIn->sli4_info.bfield = 0;
+ if (bf_get(lpfc_wcqe_c_xb, wcqe))
+ pIocbIn->sli4_info.bfield |= LPFC_XB;
+ if (bf_get(lpfc_wcqe_c_pv, wcqe)) {
+ pIocbIn->sli4_info.bfield |= LPFC_PV;
+ pIocbIn->sli4_info.priority =
+ bf_get(lpfc_wcqe_c_priority, wcqe);
+ }
+}
+
+/**
+ * lpfc_sli4_sp_handle_async_event - Handle an asynchroous event
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry with asynchrous
+ * event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_async_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
+{
+ struct lpfc_cq_event *cq_event;
+ unsigned long iflags;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0392 Async Event: word0:x%x, word1:x%x, "
+ "word2:x%x, word3:x%x\n", mcqe->word0,
+ mcqe->mcqe_tag0, mcqe->mcqe_tag1, mcqe->trailer);
+
+ /* Allocate a new internal CQ_EVENT entry */
+ cq_event = lpfc_sli4_cq_event_alloc(phba);
+ if (!cq_event) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0394 Failed to allocate CQ_EVENT entry\n");
+ return false;
+ }
+
+ /* Move the CQE into an asynchronous event entry */
+ memcpy(&cq_event->cqe, mcqe, sizeof(struct lpfc_mcqe));
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_add_tail(&cq_event->list, &phba->sli4_hba.sp_asynce_work_queue);
+ /* Set the async event flag */
+ phba->hba_flag |= ASYNC_EVENT;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ return true;
+}
+
+/**
+ * lpfc_sli4_sp_handle_mbox_event - Handle a mailbox completion event
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry with mailbox
+ * completion event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
+{
+ uint32_t mcqe_status;
+ MAILBOX_t *mbox, *pmbox;
+ struct lpfc_mqe *mqe;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_dmabuf *mp;
+ unsigned long iflags;
+ LPFC_MBOXQ_t *pmb;
+ bool workposted = false;
+ int rc;
+
+ /* If not a mailbox complete MCQE, out by checking mailbox consume */
+ if (!bf_get(lpfc_trailer_completed, mcqe))
+ goto out_no_mqe_complete;
+
+ /* Get the reference to the active mbox command */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pmb = phba->sli.mbox_active;
+ if (unlikely(!pmb)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "1832 No pending MBOX command to handle\n");
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ goto out_no_mqe_complete;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ mqe = &pmb->u.mqe;
+ pmbox = (MAILBOX_t *)&pmb->u.mqe;
+ mbox = phba->mbox;
+ vport = pmb->vport;
+
+ /* Reset heartbeat timer */
+ phba->last_completion_time = jiffies;
+ del_timer(&phba->sli.mbox_tmo);
+
+ /* Move mbox data to caller's mailbox region, do endian swapping */
+ if (pmb->mbox_cmpl && mbox)
+ lpfc_sli_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe));
+ /* Set the mailbox status with SLI4 range 0x4000 */
+ mcqe_status = bf_get(lpfc_mcqe_status, mcqe);
+ if (mcqe_status != MB_CQE_STATUS_SUCCESS)
+ bf_set(lpfc_mqe_status, mqe,
+ (LPFC_MBX_ERROR_RANGE | mcqe_status));
+
+ if (pmb->mbox_flag & LPFC_MBX_IMED_UNREG) {
+ pmb->mbox_flag &= ~LPFC_MBX_IMED_UNREG;
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_MBOX_VPORT,
+ "MBOX dflt rpi: status:x%x rpi:x%x",
+ mcqe_status,
+ pmbox->un.varWords[0], 0);
+ if (mcqe_status == MB_CQE_STATUS_SUCCESS) {
+ mp = (struct lpfc_dmabuf *)(pmb->context1);
+ ndlp = (struct lpfc_nodelist *)pmb->context2;
+ /* Reg_LOGIN of dflt RPI was successful. Now lets get
+ * RID of the PPI using the same mbox buffer.
+ */
+ lpfc_unreg_login(phba, vport->vpi,
+ pmbox->un.varWords[0], pmb);
+ pmb->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;
+ pmb->context1 = mp;
+ pmb->context2 = ndlp;
+ pmb->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc != MBX_BUSY)
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
+ LOG_SLI, "0385 rc should "
+ "have been MBX_BUSY\n");
+ if (rc != MBX_NOT_FINISHED)
+ goto send_current_mbox;
+ }
+ }
+ spin_lock_irqsave(&phba->pport->work_port_lock, iflags);
+ phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
+ spin_unlock_irqrestore(&phba->pport->work_port_lock, iflags);
+
+ /* There is mailbox completion work to do */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ __lpfc_mbox_cmpl_put(phba, pmb);
+ phba->work_ha |= HA_MBATT;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+
+send_current_mbox:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ /* Release the mailbox command posting token */
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ /* Setting active mailbox pointer need to be in sync to flag clear */
+ phba->sli.mbox_active = NULL;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ /* Wake up worker thread to post the next pending mailbox command */
+ lpfc_worker_wake_up(phba);
+out_no_mqe_complete:
+ if (bf_get(lpfc_trailer_consumed, mcqe))
+ lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_mcqe - Process a mailbox completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry, it invokes the
+ * proper mailbox complete handling or asynchrous event handling routine
+ * according to the MCQE's async bit.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+{
+ struct lpfc_mcqe mcqe;
+ bool workposted;
+
+ /* Copy the mailbox MCQE and convert endian order as needed */
+ lpfc_sli_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe));
+
+ /* Invoke the proper event handling routine */
+ if (!bf_get(lpfc_trailer_async, &mcqe))
+ workposted = lpfc_sli4_sp_handle_mbox_event(phba, &mcqe);
+ else
+ workposted = lpfc_sli4_sp_handle_async_event(phba, &mcqe);
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_els_wcqe - Handle els work-queue completion event
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
+ struct lpfc_wcqe_complete *wcqe)
+{
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_iocbq *irspiocbq;
+ unsigned long iflags;
+ bool workposted = false;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring->stats.iocb_event++;
+ /* Look up the ELS command IOCB and create pseudo response IOCB */
+ cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ if (unlikely(!cmdiocbq)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0386 ELS complete with no corresponding "
+ "cmdiocb: iotag (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ return workposted;
+ }
+
+ /* Fake the irspiocbq and copy necessary response information */
+ irspiocbq = lpfc_sli_get_iocbq(phba);
+ if (!irspiocbq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0387 Failed to allocate an iocbq\n");
+ return workposted;
+ }
+ lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+
+ /* Add the irspiocb to the response IOCB work list */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_add_tail(&irspiocbq->list, &phba->sli4_hba.sp_rspiocb_work_queue);
+ /* Indicate ELS ring attention */
+ phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_rel_wcqe - Handle slow-path WQ entry consumed event
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles slow-path WQ entry comsumed event by invoking the
+ * proper WQ release routine to the slow-path WQ.
+ **/
+static void
+lpfc_sli4_sp_handle_rel_wcqe(struct lpfc_hba *phba,
+ struct lpfc_wcqe_release *wcqe)
+{
+ /* Check for the slow-path ELS work queue */
+ if (bf_get(lpfc_wcqe_r_wq_id, wcqe) == phba->sli4_hba.els_wq->queue_id)
+ lpfc_sli4_wq_release(phba->sli4_hba.els_wq,
+ bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+ else
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2579 Slow-path wqe consume event carries "
+ "miss-matched qid: wcqe-qid=x%x, sp-qid=x%x\n",
+ bf_get(lpfc_wcqe_r_wqe_index, wcqe),
+ phba->sli4_hba.els_wq->queue_id);
+}
+
+/**
+ * lpfc_sli4_sp_handle_abort_xri_wcqe - Handle a xri abort event
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to a WQ completion queue.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an XRI abort event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
+ struct lpfc_queue *cq,
+ struct sli4_wcqe_xri_aborted *wcqe)
+{
+ bool workposted = false;
+ struct lpfc_cq_event *cq_event;
+ unsigned long iflags;
+
+ /* Allocate a new internal CQ_EVENT entry */
+ cq_event = lpfc_sli4_cq_event_alloc(phba);
+ if (!cq_event) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0602 Failed to allocate CQ_EVENT entry\n");
+ return false;
+ }
+
+ /* Move the CQE into the proper xri abort event list */
+ memcpy(&cq_event->cqe, wcqe, sizeof(struct sli4_wcqe_xri_aborted));
+ switch (cq->subtype) {
+ case LPFC_FCP:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_add_tail(&cq_event->list,
+ &phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
+ /* Set the fcp xri abort event flag */
+ phba->hba_flag |= FCP_XRI_ABORT_EVENT;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+ break;
+ case LPFC_ELS:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_add_tail(&cq_event->list,
+ &phba->sli4_hba.sp_els_xri_aborted_work_queue);
+ /* Set the els xri abort event flag */
+ phba->hba_flag |= ELS_XRI_ABORT_EVENT;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0603 Invalid work queue CQE subtype (x%x)\n",
+ cq->subtype);
+ workposted = false;
+ break;
+ }
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_wcqe - Process a work-queue completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to the completion queue.
+ * @wcqe: Pointer to a completion queue entry.
+ *
+ * This routine process a slow-path work-queue completion queue entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ struct lpfc_wcqe_complete wcqe;
+ bool workposted = false;
+
+ /* Copy the work queue CQE and convert endian order if needed */
+ lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+
+ /* Check and process for different type of WCQE and dispatch */
+ switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
+ case CQE_CODE_COMPL_WQE:
+ /* Process the WQ complete event */
+ workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+ (struct lpfc_wcqe_complete *)&wcqe);
+ break;
+ case CQE_CODE_RELEASE_WQE:
+ /* Process the WQ release event */
+ lpfc_sli4_sp_handle_rel_wcqe(phba,
+ (struct lpfc_wcqe_release *)&wcqe);
+ break;
+ case CQE_CODE_XRI_ABORTED:
+ /* Process the WQ XRI abort event */
+ workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+ (struct sli4_wcqe_xri_aborted *)&wcqe);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0388 Not a valid WCQE code: x%x\n",
+ bf_get(lpfc_wcqe_c_code, &wcqe));
+ break;
+ }
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @rcqe: Pointer to receive-queue completion queue entry.
+ *
+ * This routine process a receive-queue completion queue entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+{
+ struct lpfc_rcqe rcqe;
+ bool workposted = false;
+ struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
+ struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
+ struct hbq_dmabuf *dma_buf;
+ uint32_t status;
+ unsigned long iflags;
+
+ /* Copy the receive queue CQE and convert endian order if needed */
+ lpfc_sli_pcimem_bcopy(cqe, &rcqe, sizeof(struct lpfc_rcqe));
+ lpfc_sli4_rq_release(hrq, drq);
+ if (bf_get(lpfc_rcqe_code, &rcqe) != CQE_CODE_RECEIVE)
+ goto out;
+ if (bf_get(lpfc_rcqe_rq_id, &rcqe) != hrq->queue_id)
+ goto out;
+
+ status = bf_get(lpfc_rcqe_status, &rcqe);
+ switch (status) {
+ case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2537 Receive Frame Truncated!!\n");
+ case FC_STATUS_RQ_SUCCESS:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
+ if (!dma_buf) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ goto out;
+ }
+ memcpy(&dma_buf->rcqe, &rcqe, sizeof(rcqe));
+ /* save off the frame for the word thread to process */
+ list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list);
+ /* Frame received */
+ phba->hba_flag |= HBA_RECEIVE_BUFFER;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+ break;
+ case FC_STATUS_INSUFF_BUF_NEED_BUF:
+ case FC_STATUS_INSUFF_BUF_FRM_DISC:
+ /* Post more buffers if possible */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ phba->hba_flag |= HBA_POST_RECEIVE_BUFFER;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ workposted = true;
+ break;
+ }
+out:
+ return workposted;
+
+}
+
+/**
+ * lpfc_sli4_sp_handle_eqe - Process a slow-path event queue entry
+ * @phba: Pointer to HBA context object.
+ * @eqe: Pointer to fast-path event queue entry.
+ *
+ * This routine process a event queue entry from the slow-path event queue.
+ * It will check the MajorCode and MinorCode to determine this is for a
+ * completion event on a completion queue, if not, an error shall be logged
+ * and just return. Otherwise, it will get to the corresponding completion
+ * queue and process all the entries on that completion queue, rearm the
+ * completion queue, and then return.
+ *
+ **/
+static void
+lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
+{
+ struct lpfc_queue *cq = NULL, *childq, *speq;
+ struct lpfc_cqe *cqe;
+ bool workposted = false;
+ int ecount = 0;
+ uint16_t cqid;
+
+ if (bf_get(lpfc_eqe_major_code, eqe) != 0 ||
+ bf_get(lpfc_eqe_minor_code, eqe) != 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0359 Not a valid slow-path completion "
+ "event: majorcode=x%x, minorcode=x%x\n",
+ bf_get(lpfc_eqe_major_code, eqe),
+ bf_get(lpfc_eqe_minor_code, eqe));
+ return;
+ }
+
+ /* Get the reference to the corresponding CQ */
+ cqid = bf_get(lpfc_eqe_resource_id, eqe);
+
+ /* Search for completion queue pointer matching this cqid */
+ speq = phba->sli4_hba.sp_eq;
+ list_for_each_entry(childq, &speq->child_list, list) {
+ if (childq->queue_id == cqid) {
+ cq = childq;
+ break;
+ }
+ }
+ if (unlikely(!cq)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0365 Slow-path CQ identifier (%d) does "
+ "not exist\n", cqid);
+ return;
+ }
+
+ /* Process all the entries to the CQ */
+ switch (cq->type) {
+ case LPFC_MCQ:
+ while ((cqe = lpfc_sli4_cq_get(cq))) {
+ workposted |= lpfc_sli4_sp_handle_mcqe(phba, cqe);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+ }
+ break;
+ case LPFC_WCQ:
+ while ((cqe = lpfc_sli4_cq_get(cq))) {
+ workposted |= lpfc_sli4_sp_handle_wcqe(phba, cq, cqe);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+ }
+ break;
+ case LPFC_RCQ:
+ while ((cqe = lpfc_sli4_cq_get(cq))) {
+ workposted |= lpfc_sli4_sp_handle_rcqe(phba, cqe);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+ }
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0370 Invalid completion queue type (%d)\n",
+ cq->type);
+ return;
+ }
+
+ /* Catch the no cq entry condition, log an error */
+ if (unlikely(ecount == 0))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0371 No entry from the CQ: identifier "
+ "(x%x), type (%d)\n", cq->queue_id, cq->type);
+
+ /* In any case, flash and re-arm the RCQ */
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+
+ /* wake up worker thread if there are works to be done */
+ if (workposted)
+ lpfc_worker_wake_up(phba);
+}
+
+/**
+ * lpfc_sli4_fp_handle_fcp_wcqe - Process fast-path work queue completion entry
+ * @eqe: Pointer to fast-path completion queue entry.
+ *
+ * This routine process a fast-path work queue completion entry from fast-path
+ * event queue for FCP command response completion.
+ **/
+static void
+lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
+ struct lpfc_wcqe_complete *wcqe)
+{
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_FCP_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_iocbq irspiocbq;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring->stats.iocb_event++;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ /* Check for response status */
+ if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) {
+ /* If resource errors reported from HBA, reduce queue
+ * depth of the SCSI device.
+ */
+ if ((bf_get(lpfc_wcqe_c_status, wcqe) ==
+ IOSTAT_LOCAL_REJECT) &&
+ (wcqe->parameter == IOERR_NO_RESOURCES)) {
+ phba->lpfc_rampdown_queue_depth(phba);
+ }
+ /* Log the error status */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0373 FCP complete error: status=x%x, "
+ "hw_status=x%x, total_data_specified=%d, "
+ "parameter=x%x, word3=x%x\n",
+ bf_get(lpfc_wcqe_c_status, wcqe),
+ bf_get(lpfc_wcqe_c_hw_status, wcqe),
+ wcqe->total_data_placed, wcqe->parameter,
+ wcqe->word3);
+ }
+
+ /* Look up the FCP command IOCB and create pseudo response IOCB */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ if (unlikely(!cmdiocbq)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0374 FCP complete with no corresponding "
+ "cmdiocb: iotag (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ return;
+ }
+ if (unlikely(!cmdiocbq->iocb_cmpl)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0375 FCP cmdiocb not callback function "
+ "iotag: (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ return;
+ }
+
+ /* Fake the irspiocb and copy necessary response information */
+ lpfc_sli4_iocb_param_transfer(&irspiocbq, cmdiocbq, wcqe);
+
+ /* Pass the cmd_iocb and the rsp state to the upper layer */
+ (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);
+}
+
+/**
+ * lpfc_sli4_fp_handle_rel_wcqe - Handle fast-path WQ entry consumed event
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to completion queue.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an fast-path WQ entry comsumed event by invoking the
+ * proper WQ release routine to the slow-path WQ.
+ **/
+static void
+lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_wcqe_release *wcqe)
+{
+ struct lpfc_queue *childwq;
+ bool wqid_matched = false;
+ uint16_t fcp_wqid;
+
+ /* Check for fast-path FCP work queue release */
+ fcp_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe);
+ list_for_each_entry(childwq, &cq->child_list, list) {
+ if (childwq->queue_id == fcp_wqid) {
+ lpfc_sli4_wq_release(childwq,
+ bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+ wqid_matched = true;
+ break;
+ }
+ }
+ /* Report warning log message if no match found */
+ if (wqid_matched != true)
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2580 Fast-path wqe consume event carries "
+ "miss-matched qid: wcqe-qid=x%x\n", fcp_wqid);
+}
+
+/**
+ * lpfc_sli4_fp_handle_wcqe - Process fast-path work queue completion entry
+ * @cq: Pointer to the completion queue.
+ * @eqe: Pointer to fast-path completion queue entry.
+ *
+ * This routine process a fast-path work queue completion entry from fast-path
+ * event queue for FCP command response completion.
+ **/
+static int
+lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ struct lpfc_wcqe_release wcqe;
+ bool workposted = false;
+
+ /* Copy the work queue CQE and convert endian order if needed */
+ lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+
+ /* Check and process for different type of WCQE and dispatch */
+ switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
+ case CQE_CODE_COMPL_WQE:
+ /* Process the WQ complete event */
+ lpfc_sli4_fp_handle_fcp_wcqe(phba,
+ (struct lpfc_wcqe_complete *)&wcqe);
+ break;
+ case CQE_CODE_RELEASE_WQE:
+ /* Process the WQ release event */
+ lpfc_sli4_fp_handle_rel_wcqe(phba, cq,
+ (struct lpfc_wcqe_release *)&wcqe);
+ break;
+ case CQE_CODE_XRI_ABORTED:
+ /* Process the WQ XRI abort event */
+ workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+ (struct sli4_wcqe_xri_aborted *)&wcqe);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0144 Not a valid WCQE code: x%x\n",
+ bf_get(lpfc_wcqe_c_code, &wcqe));
+ break;
+ }
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_fp_handle_eqe - Process a fast-path event queue entry
+ * @phba: Pointer to HBA context object.
+ * @eqe: Pointer to fast-path event queue entry.
+ *
+ * This routine process a event queue entry from the fast-path event queue.
+ * It will check the MajorCode and MinorCode to determine this is for a
+ * completion event on a completion queue, if not, an error shall be logged
+ * and just return. Otherwise, it will get to the corresponding completion
+ * queue and process all the entries on the completion queue, rearm the
+ * completion queue, and then return.
+ **/
+static void
+lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
+ uint32_t fcp_cqidx)
+{
+ struct lpfc_queue *cq;
+ struct lpfc_cqe *cqe;
+ bool workposted = false;
+ uint16_t cqid;
+ int ecount = 0;
+
+ if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0) ||
+ unlikely(bf_get(lpfc_eqe_minor_code, eqe) != 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0366 Not a valid fast-path completion "
+ "event: majorcode=x%x, minorcode=x%x\n",
+ bf_get(lpfc_eqe_major_code, eqe),
+ bf_get(lpfc_eqe_minor_code, eqe));
+ return;
+ }
+
+ cq = phba->sli4_hba.fcp_cq[fcp_cqidx];
+ if (unlikely(!cq)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0367 Fast-path completion queue does not "
+ "exist\n");
+ return;
+ }
+
+ /* Get the reference to the corresponding CQ */
+ cqid = bf_get(lpfc_eqe_resource_id, eqe);
+ if (unlikely(cqid != cq->queue_id)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0368 Miss-matched fast-path completion "
+ "queue identifier: eqcqid=%d, fcpcqid=%d\n",
+ cqid, cq->queue_id);
+ return;
+ }
+
+ /* Process all the entries to the CQ */
+ while ((cqe = lpfc_sli4_cq_get(cq))) {
+ workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+ }
+
+ /* Catch the no cq entry condition */
+ if (unlikely(ecount == 0))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0369 No entry from fast-path completion "
+ "queue fcpcqid=%d\n", cq->queue_id);
+
+ /* In any case, flash and re-arm the CQ */
+ lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+
+ /* wake up worker thread if there are works to be done */
+ if (workposted)
+ lpfc_worker_wake_up(phba);
+}
+
+static void
+lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+ struct lpfc_eqe *eqe;
+
+ /* walk all the EQ entries and drop on the floor */
+ while ((eqe = lpfc_sli4_eq_get(eq)))
+ ;
+
+ /* Clear and re-arm the EQ */
+ lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM);
+}
+
+/**
+ * lpfc_sli4_sp_intr_handler - Slow-path interrupt handler to SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is directly called from the PCI layer as an interrupt
+ * service routine when device with SLI-4 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there are slow-path events in
+ * the HBA. However, when the device is enabled with either MSI or Pin-IRQ
+ * interrupt mode, this function is called as part of the device-level
+ * interrupt handler. When the PCI slot is in error recovery or the HBA is
+ * undergoing initialization, the interrupt handler will not process the
+ * interrupt. The link attention and ELS ring attention events are handled
+ * by the worker thread. The interrupt handler signals the worker thread
+ * and returns for these events. This function is called without any lock
+ * held. It gets the hbalock to access and update SLI data structures.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_sp_intr_handler(int irq, void *dev_id)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_queue *speq;
+ struct lpfc_eqe *eqe;
+ unsigned long iflag;
+ int ecount = 0;
+
+ /*
+ * Get the driver's phba structure from the dev_id
+ */
+ phba = (struct lpfc_hba *)dev_id;
+
+ if (unlikely(!phba))
+ return IRQ_NONE;
+
+ /* Get to the EQ struct associated with this vector */
+ speq = phba->sli4_hba.sp_eq;
+
+ /* Check device state for handling interrupt */
+ if (unlikely(lpfc_intr_state_check(phba))) {
+ /* Check again for link_state with lock held */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->link_state < LPFC_LINK_DOWN)
+ /* Flush, clear interrupt, and rearm the EQ */
+ lpfc_sli4_eq_flush(phba, speq);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return IRQ_NONE;
+ }
+
+ /*
+ * Process all the event on FCP slow-path EQ
+ */
+ while ((eqe = lpfc_sli4_eq_get(speq))) {
+ lpfc_sli4_sp_handle_eqe(phba, eqe);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_eq_release(speq, LPFC_QUEUE_NOARM);
+ }
+
+ /* Always clear and re-arm the slow-path EQ */
+ lpfc_sli4_eq_release(speq, LPFC_QUEUE_REARM);
+
+ /* Catch the no cq entry condition */
+ if (unlikely(ecount == 0)) {
+ if (phba->intr_type == MSIX)
+ /* MSI-X treated interrupt served as no EQ share INT */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0357 MSI-X interrupt with no EQE\n");
+ else
+ /* Non MSI-X treated on interrupt as EQ share INT */
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+} /* lpfc_sli4_sp_intr_handler */
+
+/**
+ * lpfc_sli4_fp_intr_handler - Fast-path interrupt handler to SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is directly called from the PCI layer as an interrupt
+ * service routine when device with SLI-4 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there is a fast-path FCP IOCB
+ * ring event in the HBA. However, when the device is enabled with either
+ * MSI or Pin-IRQ interrupt mode, this function is called as part of the
+ * device-level interrupt handler. When the PCI slot is in error recovery
+ * or the HBA is undergoing initialization, the interrupt handler will not
+ * process the interrupt. The SCSI FCP fast-path ring event are handled in
+ * the intrrupt context. This function is called without any lock held.
+ * It gets the hbalock to access and update SLI data structures. Note that,
+ * the FCP EQ to FCP CQ are one-to-one map such that the FCP EQ index is
+ * equal to that of FCP CQ index.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_fp_intr_handler(int irq, void *dev_id)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_fcp_eq_hdl *fcp_eq_hdl;
+ struct lpfc_queue *fpeq;
+ struct lpfc_eqe *eqe;
+ unsigned long iflag;
+ int ecount = 0;
+ uint32_t fcp_eqidx;
+
+ /* Get the driver's phba structure from the dev_id */
+ fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id;
+ phba = fcp_eq_hdl->phba;
+ fcp_eqidx = fcp_eq_hdl->idx;
+
+ if (unlikely(!phba))
+ return IRQ_NONE;
+
+ /* Get to the EQ struct associated with this vector */
+ fpeq = phba->sli4_hba.fp_eq[fcp_eqidx];
+
+ /* Check device state for handling interrupt */
+ if (unlikely(lpfc_intr_state_check(phba))) {
+ /* Check again for link_state with lock held */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->link_state < LPFC_LINK_DOWN)
+ /* Flush, clear interrupt, and rearm the EQ */
+ lpfc_sli4_eq_flush(phba, fpeq);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return IRQ_NONE;
+ }
+
+ /*
+ * Process all the event on FCP fast-path EQ
+ */
+ while ((eqe = lpfc_sli4_eq_get(fpeq))) {
+ lpfc_sli4_fp_handle_eqe(phba, eqe, fcp_eqidx);
+ if (!(++ecount % LPFC_GET_QE_REL_INT))
+ lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_NOARM);
+ }
+
+ /* Always clear and re-arm the fast-path EQ */
+ lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+
+ if (unlikely(ecount == 0)) {
+ if (phba->intr_type == MSIX)
+ /* MSI-X treated interrupt served as no EQ share INT */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0358 MSI-X interrupt with no EQE\n");
+ else
+ /* Non MSI-X treated on interrupt as EQ share INT */
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+} /* lpfc_sli4_fp_intr_handler */
+
+/**
+ * lpfc_sli4_intr_handler - Device-level interrupt handler for SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is the device-level interrupt handler to device with SLI-4
+ * interface spec, called from the PCI layer when either MSI or Pin-IRQ
+ * interrupt mode is enabled and there is an event in the HBA which requires
+ * driver attention. This function invokes the slow-path interrupt attention
+ * handling function and fast-path interrupt attention handling function in
+ * turn to process the relevant HBA attention events. This function is called
+ * without any lock held. It gets the hbalock to access and update SLI data
+ * structures.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled, else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_intr_handler(int irq, void *dev_id)
+{
+ struct lpfc_hba *phba;
+ irqreturn_t sp_irq_rc, fp_irq_rc;
+ bool fp_handled = false;
+ uint32_t fcp_eqidx;
+
+ /* Get the driver's phba structure from the dev_id */
+ phba = (struct lpfc_hba *)dev_id;
+
+ if (unlikely(!phba))
+ return IRQ_NONE;
+
+ /*
+ * Invokes slow-path host attention interrupt handling as appropriate.
+ */
+ sp_irq_rc = lpfc_sli4_sp_intr_handler(irq, dev_id);
+
+ /*
+ * Invoke fast-path host attention interrupt handling as appropriate.
+ */
+ for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+ fp_irq_rc = lpfc_sli4_fp_intr_handler(irq,
+ &phba->sli4_hba.fcp_eq_hdl[fcp_eqidx]);
+ if (fp_irq_rc == IRQ_HANDLED)
+ fp_handled |= true;
+ }
+
+ return (fp_handled == true) ? IRQ_HANDLED : sp_irq_rc;
+} /* lpfc_sli4_intr_handler */
+
+/**
+ * lpfc_sli4_queue_free - free a queue structure and associated memory
+ * @queue: The queue structure to free.
+ *
+ * This function frees a queue structure and the DMAable memeory used for
+ * the host resident queue. This function must be called after destroying the
+ * queue on the HBA.
+ **/
+void
+lpfc_sli4_queue_free(struct lpfc_queue *queue)
+{
+ struct lpfc_dmabuf *dmabuf;
+
+ if (!queue)
+ return;
+
+ while (!list_empty(&queue->page_list)) {
+ list_remove_head(&queue->page_list, dmabuf, struct lpfc_dmabuf,
+ list);
+ dma_free_coherent(&queue->phba->pcidev->dev, PAGE_SIZE,
+ dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ }
+ kfree(queue);
+ return;
+}
+
+/**
+ * lpfc_sli4_queue_alloc - Allocate and initialize a queue structure
+ * @phba: The HBA that this queue is being created on.
+ * @entry_size: The size of each queue entry for this queue.
+ * @entry count: The number of entries that this queue will handle.
+ *
+ * This function allocates a queue structure and the DMAable memory used for
+ * the host resident queue. This function must be called before creating the
+ * queue on the HBA.
+ **/
+struct lpfc_queue *
+lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size,
+ uint32_t entry_count)
+{
+ struct lpfc_queue *queue;
+ struct lpfc_dmabuf *dmabuf;
+ int x, total_qe_count;
+ void *dma_pointer;
+
+
+ queue = kzalloc(sizeof(struct lpfc_queue) +
+ (sizeof(union sli4_qe) * entry_count), GFP_KERNEL);
+ if (!queue)
+ return NULL;
+ queue->page_count = (PAGE_ALIGN(entry_size * entry_count))/PAGE_SIZE;
+ INIT_LIST_HEAD(&queue->list);
+ INIT_LIST_HEAD(&queue->page_list);
+ INIT_LIST_HEAD(&queue->child_list);
+ for (x = 0, total_qe_count = 0; x < queue->page_count; x++) {
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf)
+ goto out_fail;
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+ PAGE_SIZE, &dmabuf->phys,
+ GFP_KERNEL);
+ if (!dmabuf->virt) {
+ kfree(dmabuf);
+ goto out_fail;
+ }
+ dmabuf->buffer_tag = x;
+ list_add_tail(&dmabuf->list, &queue->page_list);
+ /* initialize queue's entry array */
+ dma_pointer = dmabuf->virt;
+ for (; total_qe_count < entry_count &&
+ dma_pointer < (PAGE_SIZE + dmabuf->virt);
+ total_qe_count++, dma_pointer += entry_size) {
+ queue->qe[total_qe_count].address = dma_pointer;
+ }
+ }
+ queue->entry_size = entry_size;
+ queue->entry_count = entry_count;
+ queue->phba = phba;
+
+ return queue;
+out_fail:
+ lpfc_sli4_queue_free(queue);
+ return NULL;
+}
+
+/**
+ * lpfc_eq_create - Create an Event Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @eq: The queue structure to use to create the event queue.
+ * @imax: The maximum interrupt per second limit.
+ *
+ * This function creates an event queue, as detailed in @eq, on a port,
+ * described by @phba by sending an EQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @eq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. This
+ * function will send the EQ_CREATE mailbox command to the HBA to setup the
+ * event queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
+{
+ struct lpfc_mbx_eq_create *eq_create;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ struct lpfc_dmabuf *dmabuf;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint16_t dmult;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_eq_create) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_EQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ eq_create = &mbox->u.mqe.un.eq_create;
+ bf_set(lpfc_mbx_eq_create_num_pages, &eq_create->u.request,
+ eq->page_count);
+ bf_set(lpfc_eq_context_size, &eq_create->u.request.context,
+ LPFC_EQE_SIZE);
+ bf_set(lpfc_eq_context_valid, &eq_create->u.request.context, 1);
+ /* Calculate delay multiper from maximum interrupt per second */
+ dmult = LPFC_DMULT_CONST/imax - 1;
+ bf_set(lpfc_eq_context_delay_multi, &eq_create->u.request.context,
+ dmult);
+ switch (eq->entry_count) {
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0360 Unsupported EQ count. (%d)\n",
+ eq->entry_count);
+ if (eq->entry_count < 256)
+ return -EINVAL;
+ /* otherwise default to smallest count (drop through) */
+ case 256:
+ bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+ LPFC_EQ_CNT_256);
+ break;
+ case 512:
+ bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+ LPFC_EQ_CNT_512);
+ break;
+ case 1024:
+ bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+ LPFC_EQ_CNT_1024);
+ break;
+ case 2048:
+ bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+ LPFC_EQ_CNT_2048);
+ break;
+ case 4096:
+ bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+ LPFC_EQ_CNT_4096);
+ break;
+ }
+ list_for_each_entry(dmabuf, &eq->page_list, list) {
+ eq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ eq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->context1 = NULL;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2500 EQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+ eq->type = LPFC_EQ;
+ eq->subtype = LPFC_NONE;
+ eq->queue_id = bf_get(lpfc_mbx_eq_create_q_id, &eq_create->u.response);
+ if (eq->queue_id == 0xFFFF)
+ status = -ENXIO;
+ eq->host_index = 0;
+ eq->hba_index = 0;
+
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_cq_create - Create a Completion Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @cq: The queue structure to use to create the completion queue.
+ * @eq: The event queue to bind this completion queue to.
+ *
+ * This function creates a completion queue, as detailed in @wq, on a port,
+ * described by @phba by sending a CQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @cq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. The @eq
+ * is used to indicate which event queue to bind this completion queue to. This
+ * function will send the CQ_CREATE mailbox command to the HBA to setup the
+ * completion queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_queue *eq, uint32_t type, uint32_t subtype)
+{
+ struct lpfc_mbx_cq_create *cq_create;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_cq_create) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_CQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ cq_create = &mbox->u.mqe.un.cq_create;
+ bf_set(lpfc_mbx_cq_create_num_pages, &cq_create->u.request,
+ cq->page_count);
+ bf_set(lpfc_cq_context_event, &cq_create->u.request.context, 1);
+ bf_set(lpfc_cq_context_valid, &cq_create->u.request.context, 1);
+ bf_set(lpfc_cq_eq_id, &cq_create->u.request.context, eq->queue_id);
+ switch (cq->entry_count) {
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0361 Unsupported CQ count. (%d)\n",
+ cq->entry_count);
+ if (cq->entry_count < 256)
+ return -EINVAL;
+ /* otherwise default to smallest count (drop through) */
+ case 256:
+ bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+ LPFC_CQ_CNT_256);
+ break;
+ case 512:
+ bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+ LPFC_CQ_CNT_512);
+ break;
+ case 1024:
+ bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+ LPFC_CQ_CNT_1024);
+ break;
+ }
+ list_for_each_entry(dmabuf, &cq->page_list, list) {
+ cq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ cq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &cq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2501 CQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
+ if (cq->queue_id == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+ /* link the cq onto the parent eq child list */
+ list_add_tail(&cq->list, &eq->child_list);
+ /* Set up completion queue's type and subtype */
+ cq->type = type;
+ cq->subtype = subtype;
+ cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
+ cq->host_index = 0;
+ cq->hba_index = 0;
+out:
+
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_mq_create - Create a mailbox Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @mq: The queue structure to use to create the mailbox queue.
+ *
+ * This function creates a mailbox queue, as detailed in @mq, on a port,
+ * described by @phba by sending a MQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @cq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. This
+ * function will send the MQ_CREATE mailbox command to the HBA to setup the
+ * mailbox queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
+ struct lpfc_queue *cq, uint32_t subtype)
+{
+ struct lpfc_mbx_mq_create *mq_create;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_mq_create) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_MQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ mq_create = &mbox->u.mqe.un.mq_create;
+ bf_set(lpfc_mbx_mq_create_num_pages, &mq_create->u.request,
+ mq->page_count);
+ bf_set(lpfc_mq_context_cq_id, &mq_create->u.request.context,
+ cq->queue_id);
+ bf_set(lpfc_mq_context_valid, &mq_create->u.request.context, 1);
+ switch (mq->entry_count) {
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0362 Unsupported MQ count. (%d)\n",
+ mq->entry_count);
+ if (mq->entry_count < 16)
+ return -EINVAL;
+ /* otherwise default to smallest count (drop through) */
+ case 16:
+ bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+ LPFC_MQ_CNT_16);
+ break;
+ case 32:
+ bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+ LPFC_MQ_CNT_32);
+ break;
+ case 64:
+ bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+ LPFC_MQ_CNT_64);
+ break;
+ case 128:
+ bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+ LPFC_MQ_CNT_128);
+ break;
+ }
+ list_for_each_entry(dmabuf, &mq->page_list, list) {
+ mq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ mq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &mq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2502 MQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ mq->queue_id = bf_get(lpfc_mbx_mq_create_q_id, &mq_create->u.response);
+ if (mq->queue_id == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+ mq->type = LPFC_MQ;
+ mq->subtype = subtype;
+ mq->host_index = 0;
+ mq->hba_index = 0;
+
+ /* link the mq onto the parent cq child list */
+ list_add_tail(&mq->list, &cq->child_list);
+out:
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_wq_create - Create a Work Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @wq: The queue structure to use to create the work queue.
+ * @cq: The completion queue to bind this work queue to.
+ * @subtype: The subtype of the work queue indicating its functionality.
+ *
+ * This function creates a work queue, as detailed in @wq, on a port, described
+ * by @phba by sending a WQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @wq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. The @cq
+ * is used to indicate which completion queue to bind this work queue to. This
+ * function will send the WQ_CREATE mailbox command to the HBA to setup the
+ * work queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
+ struct lpfc_queue *cq, uint32_t subtype)
+{
+ struct lpfc_mbx_wq_create *wq_create;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_wq_create) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_WQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ wq_create = &mbox->u.mqe.un.wq_create;
+ bf_set(lpfc_mbx_wq_create_num_pages, &wq_create->u.request,
+ wq->page_count);
+ bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request,
+ cq->queue_id);
+ list_for_each_entry(dmabuf, &wq->page_list, list) {
+ wq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ wq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &wq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2503 WQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id, &wq_create->u.response);
+ if (wq->queue_id == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+ wq->type = LPFC_WQ;
+ wq->subtype = subtype;
+ wq->host_index = 0;
+ wq->hba_index = 0;
+
+ /* link the wq onto the parent cq child list */
+ list_add_tail(&wq->list, &cq->child_list);
+out:
+ if (rc == MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_rq_create - Create a Receive Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @hrq: The queue structure to use to create the header receive queue.
+ * @drq: The queue structure to use to create the data receive queue.
+ * @cq: The completion queue to bind this work queue to.
+ *
+ * This function creates a receive buffer queue pair , as detailed in @hrq and
+ * @drq, on a port, described by @phba by sending a RQ_CREATE mailbox command
+ * to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @drq and @hrq
+ * struct is used to get the entry count that is necessary to determine the
+ * number of pages to use for this queue. The @cq is used to indicate which
+ * completion queue to bind received buffers that are posted to these queues to.
+ * This function will send the RQ_CREATE mailbox command to the HBA to setup the
+ * receive queue pair. This function is asynchronous and will wait for the
+ * mailbox command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+ struct lpfc_queue *drq, struct lpfc_queue *cq, uint32_t subtype)
+{
+ struct lpfc_mbx_rq_create *rq_create;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (hrq->entry_count != drq->entry_count)
+ return -EINVAL;
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_rq_create) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_RQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ rq_create = &mbox->u.mqe.un.rq_create;
+ switch (hrq->entry_count) {
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2535 Unsupported RQ count. (%d)\n",
+ hrq->entry_count);
+ if (hrq->entry_count < 512)
+ return -EINVAL;
+ /* otherwise default to smallest count (drop through) */
+ case 512:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_512);
+ break;
+ case 1024:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_1024);
+ break;
+ case 2048:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_2048);
+ break;
+ case 4096:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_4096);
+ break;
+ }
+ bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context,
+ cq->queue_id);
+ bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request,
+ hrq->page_count);
+ bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context,
+ LPFC_HDR_BUF_SIZE);
+ list_for_each_entry(dmabuf, &hrq->page_list, list) {
+ rq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2504 RQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ hrq->queue_id = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response);
+ if (hrq->queue_id == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+ hrq->type = LPFC_HRQ;
+ hrq->subtype = subtype;
+ hrq->host_index = 0;
+ hrq->hba_index = 0;
+
+ /* now create the data queue */
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_RQ_CREATE,
+ length, LPFC_SLI4_MBX_EMBED);
+ switch (drq->entry_count) {
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2536 Unsupported RQ count. (%d)\n",
+ drq->entry_count);
+ if (drq->entry_count < 512)
+ return -EINVAL;
+ /* otherwise default to smallest count (drop through) */
+ case 512:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_512);
+ break;
+ case 1024:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_1024);
+ break;
+ case 2048:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_2048);
+ break;
+ case 4096:
+ bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+ LPFC_RQ_RING_SIZE_4096);
+ break;
+ }
+ bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context,
+ cq->queue_id);
+ bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request,
+ drq->page_count);
+ bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context,
+ LPFC_DATA_BUF_SIZE);
+ list_for_each_entry(dmabuf, &drq->page_list, list) {
+ rq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ status = -ENXIO;
+ goto out;
+ }
+ drq->queue_id = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response);
+ if (drq->queue_id == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+ drq->type = LPFC_DRQ;
+ drq->subtype = subtype;
+ drq->host_index = 0;
+ drq->hba_index = 0;
+
+ /* link the header and data RQs onto the parent cq child list */
+ list_add_tail(&hrq->list, &cq->child_list);
+ list_add_tail(&drq->list, &cq->child_list);
+
+out:
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_eq_destroy - Destroy an event Queue on the HBA
+ * @eq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @eq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @eq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (!eq)
+ return -ENODEV;
+ mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_eq_destroy) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_EQ_DESTROY,
+ length, LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_eq_destroy_q_id, &mbox->u.mqe.un.eq_destroy.u.request,
+ eq->queue_id);
+ mbox->vport = eq->phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+
+ rc = lpfc_sli_issue_mbox(eq->phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.eq_destroy.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2505 EQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+
+ /* Remove eq from any list */
+ list_del_init(&eq->list);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, eq->phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_cq_destroy - Destroy a Completion Queue on the HBA
+ * @cq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @cq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @cq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (!cq)
+ return -ENODEV;
+ mbox = mempool_alloc(cq->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_cq_destroy) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_CQ_DESTROY,
+ length, LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_cq_destroy_q_id, &mbox->u.mqe.un.cq_destroy.u.request,
+ cq->queue_id);
+ mbox->vport = cq->phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(cq->phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.wq_create.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2506 CQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+ /* Remove cq from any list */
+ list_del_init(&cq->list);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, cq->phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_mq_destroy - Destroy a Mailbox Queue on the HBA
+ * @qm: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @mq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @mq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (!mq)
+ return -ENODEV;
+ mbox = mempool_alloc(mq->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_mq_destroy) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_MQ_DESTROY,
+ length, LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_mq_destroy_q_id, &mbox->u.mqe.un.mq_destroy.u.request,
+ mq->queue_id);
+ mbox->vport = mq->phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(mq->phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.mq_destroy.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2507 MQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+ /* Remove mq from any list */
+ list_del_init(&mq->list);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, mq->phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_wq_destroy - Destroy a Work Queue on the HBA
+ * @wq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @wq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @wq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (!wq)
+ return -ENODEV;
+ mbox = mempool_alloc(wq->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_wq_destroy) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_WQ_DESTROY,
+ length, LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_wq_destroy_q_id, &mbox->u.mqe.un.wq_destroy.u.request,
+ wq->queue_id);
+ mbox->vport = wq->phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(wq->phba, mbox, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.wq_destroy.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2508 WQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+ /* Remove wq from any list */
+ list_del_init(&wq->list);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, wq->phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_rq_destroy - Destroy a Receive Queue on the HBA
+ * @rq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @rq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @rq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+ struct lpfc_queue *drq)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (!hrq || !drq)
+ return -ENODEV;
+ mbox = mempool_alloc(hrq->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_rq_destroy) -
+ sizeof(struct mbox_header));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_RQ_DESTROY,
+ length, LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_rq_destroy_q_id, &mbox->u.mqe.un.rq_destroy.u.request,
+ hrq->queue_id);
+ mbox->vport = hrq->phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(hrq->phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.rq_destroy.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2509 RQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, hrq->phba->mbox_mem_pool);
+ return -ENXIO;
+ }
+ bf_set(lpfc_mbx_rq_destroy_q_id, &mbox->u.mqe.un.rq_destroy.u.request,
+ drq->queue_id);
+ rc = lpfc_sli_issue_mbox(drq->phba, mbox, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.rq_destroy.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2510 RQ_DESTROY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ }
+ list_del_init(&hrq->list);
+ list_del_init(&drq->list);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, hrq->phba->mbox_mem_pool);
+ return status;
+}
+
+/**
+ * lpfc_sli4_post_sgl - Post scatter gather list for an XRI to HBA
+ * @phba: The virtual port for which this call being executed.
+ * @pdma_phys_addr0: Physical address of the 1st SGL page.
+ * @pdma_phys_addr1: Physical address of the 2nd SGL page.
+ * @xritag: the xritag that ties this io to the SGL pages.
+ *
+ * This routine will post the sgl pages for the IO that has the xritag
+ * that is in the iocbq structure. The xritag is assigned during iocbq
+ * creation and persists for as long as the driver is loaded.
+ * if the caller has fewer than 256 scatter gather segments to map then
+ * pdma_phys_addr1 should be 0.
+ * If the caller needs to map more than 256 scatter gather segment then
+ * pdma_phys_addr1 should be a valid physical address.
+ * physical address for SGLs must be 64 byte aligned.
+ * If you are going to map 2 SGL's then the first one must have 256 entries
+ * the second sgl can have between 1 and 256 entries.
+ *
+ * Return codes:
+ * 0 - Success
+ * -ENXIO, -ENOMEM - Failure
+ **/
+int
+lpfc_sli4_post_sgl(struct lpfc_hba *phba,
+ dma_addr_t pdma_phys_addr0,
+ dma_addr_t pdma_phys_addr1,
+ uint16_t xritag)
+{
+ struct lpfc_mbx_post_sgl_pages *post_sgl_pages;
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (xritag == NO_XRI) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0364 Invalid param:\n");
+ return -EINVAL;
+ }
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
+ sizeof(struct lpfc_mbx_post_sgl_pages) -
+ sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
+
+ post_sgl_pages = (struct lpfc_mbx_post_sgl_pages *)
+ &mbox->u.mqe.un.post_sgl_pages;
+ bf_set(lpfc_post_sgl_pages_xri, post_sgl_pages, xritag);
+ bf_set(lpfc_post_sgl_pages_xricnt, post_sgl_pages, 1);
+
+ post_sgl_pages->sgl_pg_pairs[0].sgl_pg0_addr_lo =
+ cpu_to_le32(putPaddrLow(pdma_phys_addr0));
+ post_sgl_pages->sgl_pg_pairs[0].sgl_pg0_addr_hi =
+ cpu_to_le32(putPaddrHigh(pdma_phys_addr0));
+
+ post_sgl_pages->sgl_pg_pairs[0].sgl_pg1_addr_lo =
+ cpu_to_le32(putPaddrLow(pdma_phys_addr1));
+ post_sgl_pages->sgl_pg_pairs[0].sgl_pg1_addr_hi =
+ cpu_to_le32(putPaddrHigh(pdma_phys_addr1));
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) &post_sgl_pages->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2511 POST_SGL mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return 0;
+}
+/**
+ * lpfc_sli4_remove_all_sgl_pages - Post scatter gather list for an XRI to HBA
+ * @phba: The virtual port for which this call being executed.
+ *
+ * This routine will remove all of the sgl pages registered with the hba.
+ *
+ * Return codes:
+ * 0 - Success
+ * -ENXIO, -ENOMEM - Failure
+ **/
+int
+lpfc_sli4_remove_all_sgl_pages(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_REMOVE_SGL_PAGES, 0,
+ LPFC_SLI4_MBX_EMBED);
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2512 REMOVE_ALL_SGL_PAGES mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_sli4_next_xritag - Get an xritag for the io
+ * @phba: Pointer to HBA context object.
+ *
+ * This function gets an xritag for the iocb. If there is no unused xritag
+ * it will return 0xffff.
+ * The function returns the allocated xritag if successful, else returns zero.
+ * Zero is not a valid xritag.
+ * The caller is not required to hold any lock.
+ **/
+uint16_t
+lpfc_sli4_next_xritag(struct lpfc_hba *phba)
+{
+ uint16_t xritag;
+
+ spin_lock_irq(&phba->hbalock);
+ xritag = phba->sli4_hba.next_xri;
+ if ((xritag != (uint16_t) -1) && xritag <
+ (phba->sli4_hba.max_cfg_param.max_xri
+ + phba->sli4_hba.max_cfg_param.xri_base)) {
+ phba->sli4_hba.next_xri++;
+ phba->sli4_hba.max_cfg_param.xri_used++;
+ spin_unlock_irq(&phba->hbalock);
+ return xritag;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2004 Failed to allocate XRI.last XRITAG is %d"
+ " Max XRI is %d, Used XRI is %d\n",
+ phba->sli4_hba.next_xri,
+ phba->sli4_hba.max_cfg_param.max_xri,
+ phba->sli4_hba.max_cfg_param.xri_used);
+ return -1;
+}
+
+/**
+ * lpfc_sli4_post_sgl_list - post a block of sgl list to the firmware.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post a block of driver's sgl pages to the
+ * HBA using non-embedded mailbox command. No Lock is held. This routine
+ * is only called when the driver is loading and after all IO has been
+ * stopped.
+ **/
+int
+lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
+{
+ struct lpfc_sglq *sglq_entry;
+ struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
+ struct sgl_page_pairs *sgl_pg_pairs;
+ void *viraddr;
+ LPFC_MBOXQ_t *mbox;
+ uint32_t reqlen, alloclen, pg_pairs;
+ uint32_t mbox_tmo;
+ uint16_t xritag_start = 0;
+ int els_xri_cnt, rc = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ /* The number of sgls to be posted */
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+
+ reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) +
+ sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
+ if (reqlen > PAGE_SIZE) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "2559 Block sgl registration required DMA "
+ "size (%d) great than a page\n", reqlen);
+ return -ENOMEM;
+ }
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2560 Failed to allocate mbox cmd memory\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate DMA memory and set up the non-embedded mailbox command */
+ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
+ LPFC_SLI4_MBX_NEMBED);
+
+ if (alloclen < reqlen) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0285 Allocated DMA memory size (%d) is "
+ "less than the requested DMA memory "
+ "size (%d)\n", alloclen, reqlen);
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return -ENOMEM;
+ }
+
+ /* Get the first SGE entry from the non-embedded DMA memory */
+ if (unlikely(!mbox->sge_array)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2525 Failed to get the non-embedded SGE "
+ "virtual address\n");
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return -ENOMEM;
+ }
+ viraddr = mbox->sge_array->addr[0];
+
+ /* Set up the SGL pages in the non-embedded DMA pages */
+ sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
+ sgl_pg_pairs = &sgl->sgl_pg_pairs;
+
+ for (pg_pairs = 0; pg_pairs < els_xri_cnt; pg_pairs++) {
+ sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[pg_pairs];
+ /* Set up the sge entry */
+ sgl_pg_pairs->sgl_pg0_addr_lo =
+ cpu_to_le32(putPaddrLow(sglq_entry->phys));
+ sgl_pg_pairs->sgl_pg0_addr_hi =
+ cpu_to_le32(putPaddrHigh(sglq_entry->phys));
+ sgl_pg_pairs->sgl_pg1_addr_lo =
+ cpu_to_le32(putPaddrLow(0));
+ sgl_pg_pairs->sgl_pg1_addr_hi =
+ cpu_to_le32(putPaddrHigh(0));
+ /* Keep the first xritag on the list */
+ if (pg_pairs == 0)
+ xritag_start = sglq_entry->sli4_xritag;
+ sgl_pg_pairs++;
+ }
+ bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
+ pg_pairs = (pg_pairs > 0) ? (pg_pairs - 1) : pg_pairs;
+ bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+ /* Perform endian conversion if necessary */
+ sgl->word0 = cpu_to_le32(sgl->word0);
+
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+ }
+ shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2513 POST_SGL_BLOCK mailbox command failed "
+ "status x%x add_status x%x mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_sli4_post_scsi_sgl_block - post a block of scsi sgl list to firmware
+ * @phba: pointer to lpfc hba data structure.
+ * @sblist: pointer to scsi buffer list.
+ * @count: number of scsi buffers on the list.
+ *
+ * This routine is invoked to post a block of @count scsi sgl pages from a
+ * SCSI buffer list @sblist to the HBA using non-embedded mailbox command.
+ * No Lock is held.
+ *
+ **/
+int
+lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
+ int cnt)
+{
+ struct lpfc_scsi_buf *psb;
+ struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
+ struct sgl_page_pairs *sgl_pg_pairs;
+ void *viraddr;
+ LPFC_MBOXQ_t *mbox;
+ uint32_t reqlen, alloclen, pg_pairs;
+ uint32_t mbox_tmo;
+ uint16_t xritag_start = 0;
+ int rc = 0;
+ uint32_t shdr_status, shdr_add_status;
+ dma_addr_t pdma_phys_bpl1;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ /* Calculate the requested length of the dma memory */
+ reqlen = cnt * sizeof(struct sgl_page_pairs) +
+ sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
+ if (reqlen > PAGE_SIZE) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0217 Block sgl registration required DMA "
+ "size (%d) great than a page\n", reqlen);
+ return -ENOMEM;
+ }
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0283 Failed to allocate mbox cmd memory\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate DMA memory and set up the non-embedded mailbox command */
+ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
+ LPFC_SLI4_MBX_NEMBED);
+
+ if (alloclen < reqlen) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2561 Allocated DMA memory size (%d) is "
+ "less than the requested DMA memory "
+ "size (%d)\n", alloclen, reqlen);
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return -ENOMEM;
+ }
+
+ /* Get the first SGE entry from the non-embedded DMA memory */
+ if (unlikely(!mbox->sge_array)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2565 Failed to get the non-embedded SGE "
+ "virtual address\n");
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return -ENOMEM;
+ }
+ viraddr = mbox->sge_array->addr[0];
+
+ /* Set up the SGL pages in the non-embedded DMA pages */
+ sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
+ sgl_pg_pairs = &sgl->sgl_pg_pairs;
+
+ pg_pairs = 0;
+ list_for_each_entry(psb, sblist, list) {
+ /* Set up the sge entry */
+ sgl_pg_pairs->sgl_pg0_addr_lo =
+ cpu_to_le32(putPaddrLow(psb->dma_phys_bpl));
+ sgl_pg_pairs->sgl_pg0_addr_hi =
+ cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl));
+ if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
+ pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE;
+ else
+ pdma_phys_bpl1 = 0;
+ sgl_pg_pairs->sgl_pg1_addr_lo =
+ cpu_to_le32(putPaddrLow(pdma_phys_bpl1));
+ sgl_pg_pairs->sgl_pg1_addr_hi =
+ cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
+ /* Keep the first xritag on the list */
+ if (pg_pairs == 0)
+ xritag_start = psb->cur_iocbq.sli4_xritag;
+ sgl_pg_pairs++;
+ pg_pairs++;
+ }
+ bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
+ bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+ /* Perform endian conversion if necessary */
+ sgl->word0 = cpu_to_le32(sgl->word0);
+
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+ }
+ shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2564 POST_SGL_BLOCK mailbox command failed "
+ "status x%x add_status x%x mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_fc_frame_check - Check that this frame is a valid frame to handle
+ * @phba: pointer to lpfc_hba struct that the frame was received on
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ *
+ * This function checks the fields in the @fc_hdr to see if the FC frame is a
+ * valid type of frame that the LPFC driver will handle. This function will
+ * return a zero if the frame is a valid frame or a non zero value when the
+ * frame does not pass the check.
+ **/
+static int
+lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
+{
+ char *rctl_names[] = FC_RCTL_NAMES_INIT;
+ char *type_names[] = FC_TYPE_NAMES_INIT;
+ struct fc_vft_header *fc_vft_hdr;
+
+ switch (fc_hdr->fh_r_ctl) {
+ case FC_RCTL_DD_UNCAT: /* uncategorized information */
+ case FC_RCTL_DD_SOL_DATA: /* solicited data */
+ case FC_RCTL_DD_UNSOL_CTL: /* unsolicited control */
+ case FC_RCTL_DD_SOL_CTL: /* solicited control or reply */
+ case FC_RCTL_DD_UNSOL_DATA: /* unsolicited data */
+ case FC_RCTL_DD_DATA_DESC: /* data descriptor */
+ case FC_RCTL_DD_UNSOL_CMD: /* unsolicited command */
+ case FC_RCTL_DD_CMD_STATUS: /* command status */
+ case FC_RCTL_ELS_REQ: /* extended link services request */
+ case FC_RCTL_ELS_REP: /* extended link services reply */
+ case FC_RCTL_ELS4_REQ: /* FC-4 ELS request */
+ case FC_RCTL_ELS4_REP: /* FC-4 ELS reply */
+ case FC_RCTL_BA_NOP: /* basic link service NOP */
+ case FC_RCTL_BA_ABTS: /* basic link service abort */
+ case FC_RCTL_BA_RMC: /* remove connection */
+ case FC_RCTL_BA_ACC: /* basic accept */
+ case FC_RCTL_BA_RJT: /* basic reject */
+ case FC_RCTL_BA_PRMT:
+ case FC_RCTL_ACK_1: /* acknowledge_1 */
+ case FC_RCTL_ACK_0: /* acknowledge_0 */
+ case FC_RCTL_P_RJT: /* port reject */
+ case FC_RCTL_F_RJT: /* fabric reject */
+ case FC_RCTL_P_BSY: /* port busy */
+ case FC_RCTL_F_BSY: /* fabric busy to data frame */
+ case FC_RCTL_F_BSYL: /* fabric busy to link control frame */
+ case FC_RCTL_LCR: /* link credit reset */
+ case FC_RCTL_END: /* end */
+ break;
+ case FC_RCTL_VFTH: /* Virtual Fabric tagging Header */
+ fc_vft_hdr = (struct fc_vft_header *)fc_hdr;
+ fc_hdr = &((struct fc_frame_header *)fc_vft_hdr)[1];
+ return lpfc_fc_frame_check(phba, fc_hdr);
+ default:
+ goto drop;
+ }
+ switch (fc_hdr->fh_type) {
+ case FC_TYPE_BLS:
+ case FC_TYPE_ELS:
+ case FC_TYPE_FCP:
+ case FC_TYPE_CT:
+ break;
+ case FC_TYPE_IP:
+ case FC_TYPE_ILS:
+ default:
+ goto drop;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "2538 Received frame rctl:%s type:%s\n",
+ rctl_names[fc_hdr->fh_r_ctl],
+ type_names[fc_hdr->fh_type]);
+ return 0;
+drop:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "2539 Dropped frame rctl:%s type:%s\n",
+ rctl_names[fc_hdr->fh_r_ctl],
+ type_names[fc_hdr->fh_type]);
+ return 1;
+}
+
+/**
+ * lpfc_fc_hdr_get_vfi - Get the VFI from an FC frame
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ *
+ * This function processes the FC header to retrieve the VFI from the VF
+ * header, if one exists. This function will return the VFI if one exists
+ * or 0 if no VSAN Header exists.
+ **/
+static uint32_t
+lpfc_fc_hdr_get_vfi(struct fc_frame_header *fc_hdr)
+{
+ struct fc_vft_header *fc_vft_hdr = (struct fc_vft_header *)fc_hdr;
+
+ if (fc_hdr->fh_r_ctl != FC_RCTL_VFTH)
+ return 0;
+ return bf_get(fc_vft_hdr_vf_id, fc_vft_hdr);
+}
+
+/**
+ * lpfc_fc_frame_to_vport - Finds the vport that a frame is destined to
+ * @phba: Pointer to the HBA structure to search for the vport on
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ * @fcfi: The FC Fabric ID that the frame came from
+ *
+ * This function searches the @phba for a vport that matches the content of the
+ * @fc_hdr passed in and the @fcfi. This function uses the @fc_hdr to fetch the
+ * VFI, if the Virtual Fabric Tagging Header exists, and the DID. This function
+ * returns the matching vport pointer or NULL if unable to match frame to a
+ * vport.
+ **/
+static struct lpfc_vport *
+lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
+ uint16_t fcfi)
+{
+ struct lpfc_vport **vports;
+ struct lpfc_vport *vport = NULL;
+ int i;
+ uint32_t did = (fc_hdr->fh_d_id[0] << 16 |
+ fc_hdr->fh_d_id[1] << 8 |
+ fc_hdr->fh_d_id[2]);
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ if (phba->fcf.fcfi == fcfi &&
+ vports[i]->vfi == lpfc_fc_hdr_get_vfi(fc_hdr) &&
+ vports[i]->fc_myDID == did) {
+ vport = vports[i];
+ break;
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+ return vport;
+}
+
+/**
+ * lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
+ * @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
+ *
+ * This function searches through the existing incomplete sequences that have
+ * been sent to this @vport. If the frame matches one of the incomplete
+ * sequences then the dbuf in the @dmabuf is added to the list of frames that
+ * make up that sequence. If no sequence is found that matches this frame then
+ * the function will add the hbuf in the @dmabuf to the @vport's rcv_buffer_list
+ * This function returns a pointer to the first dmabuf in the sequence list that
+ * the frame was linked to.
+ **/
+static struct hbq_dmabuf *
+lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header *new_hdr;
+ struct fc_frame_header *temp_hdr;
+ struct lpfc_dmabuf *d_buf;
+ struct lpfc_dmabuf *h_buf;
+ struct hbq_dmabuf *seq_dmabuf = NULL;
+ struct hbq_dmabuf *temp_dmabuf = NULL;
+
+ new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ /* Use the hdr_buf to find the sequence that this frame belongs to */
+ list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
+ temp_hdr = (struct fc_frame_header *)h_buf->virt;
+ if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) ||
+ (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) ||
+ (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3)))
+ continue;
+ /* found a pending sequence that matches this frame */
+ seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ break;
+ }
+ if (!seq_dmabuf) {
+ /*
+ * This indicates first frame received for this sequence.
+ * Queue the buffer on the vport's rcv_buffer_list.
+ */
+ list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ return dmabuf;
+ }
+ temp_hdr = seq_dmabuf->hbuf.virt;
+ if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) {
+ list_add(&seq_dmabuf->dbuf.list, &dmabuf->dbuf.list);
+ return dmabuf;
+ }
+ /* find the correct place in the sequence to insert this frame */
+ list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
+ temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+ temp_hdr = (struct fc_frame_header *)temp_dmabuf->hbuf.virt;
+ /*
+ * If the frame's sequence count is greater than the frame on
+ * the list then insert the frame right after this frame
+ */
+ if (new_hdr->fh_seq_cnt > temp_hdr->fh_seq_cnt) {
+ list_add(&dmabuf->dbuf.list, &temp_dmabuf->dbuf.list);
+ return seq_dmabuf;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * lpfc_seq_complete - Indicates if a sequence is complete
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function checks the sequence, starting with the frame described by
+ * @dmabuf, to see if all the frames associated with this sequence are present.
+ * the frames associated with this sequence are linked to the @dmabuf using the
+ * dbuf list. This function looks for two major things. 1) That the first frame
+ * has a sequence count of zero. 2) There is a frame with last frame of sequence
+ * set. 3) That there are no holes in the sequence count. The function will
+ * return 1 when the sequence is complete, otherwise it will return 0.
+ **/
+static int
+lpfc_seq_complete(struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header *hdr;
+ struct lpfc_dmabuf *d_buf;
+ struct hbq_dmabuf *seq_dmabuf;
+ uint32_t fctl;
+ int seq_count = 0;
+
+ hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ /* make sure first fame of sequence has a sequence count of zero */
+ if (hdr->fh_seq_cnt != seq_count)
+ return 0;
+ fctl = (hdr->fh_f_ctl[0] << 16 |
+ hdr->fh_f_ctl[1] << 8 |
+ hdr->fh_f_ctl[2]);
+ /* If last frame of sequence we can return success. */
+ if (fctl & FC_FC_END_SEQ)
+ return 1;
+ list_for_each_entry(d_buf, &dmabuf->dbuf.list, list) {
+ seq_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+ hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+ /* If there is a hole in the sequence count then fail. */
+ if (++seq_count != hdr->fh_seq_cnt)
+ return 0;
+ fctl = (hdr->fh_f_ctl[0] << 16 |
+ hdr->fh_f_ctl[1] << 8 |
+ hdr->fh_f_ctl[2]);
+ /* If last frame of sequence we can return success. */
+ if (fctl & FC_FC_END_SEQ)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_prep_seq - Prep sequence for ULP processing
+ * @vport: Pointer to the vport on which this sequence was received
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function takes a sequence, described by a list of frames, and creates
+ * a list of iocbq structures to describe the sequence. This iocbq list will be
+ * used to issue to the generic unsolicited sequence handler. This routine
+ * returns a pointer to the first iocbq in the list. If the function is unable
+ * to allocate an iocbq then it throw out the received frames that were not
+ * able to be described and return a pointer to the first iocbq. If unable to
+ * allocate any iocbqs (including the first) this function will return NULL.
+ **/
+static struct lpfc_iocbq *
+lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
+{
+ struct lpfc_dmabuf *d_buf, *n_buf;
+ struct lpfc_iocbq *first_iocbq, *iocbq;
+ struct fc_frame_header *fc_hdr;
+ uint32_t sid;
+
+ fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+ /* remove from receive buffer list */
+ list_del_init(&seq_dmabuf->hbuf.list);
+ /* get the Remote Port's SID */
+ sid = (fc_hdr->fh_s_id[0] << 16 |
+ fc_hdr->fh_s_id[1] << 8 |
+ fc_hdr->fh_s_id[2]);
+ /* Get an iocbq struct to fill in. */
+ first_iocbq = lpfc_sli_get_iocbq(vport->phba);
+ if (first_iocbq) {
+ /* Initialize the first IOCB. */
+ first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS;
+ first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX;
+ first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id);
+ first_iocbq->iocb.unsli3.rcvsli3.vpi =
+ vport->vpi + vport->phba->vpi_base;
+ /* put the first buffer into the first IOCBq */
+ first_iocbq->context2 = &seq_dmabuf->dbuf;
+ first_iocbq->context3 = NULL;
+ first_iocbq->iocb.ulpBdeCount = 1;
+ first_iocbq->iocb.un.cont64[0].tus.f.bdeSize =
+ LPFC_DATA_BUF_SIZE;
+ first_iocbq->iocb.un.rcvels.remoteID = sid;
+ }
+ iocbq = first_iocbq;
+ /*
+ * Each IOCBq can have two Buffers assigned, so go through the list
+ * of buffers for this sequence and save two buffers in each IOCBq
+ */
+ list_for_each_entry_safe(d_buf, n_buf, &seq_dmabuf->dbuf.list, list) {
+ if (!iocbq) {
+ lpfc_in_buf_free(vport->phba, d_buf);
+ continue;
+ }
+ if (!iocbq->context3) {
+ iocbq->context3 = d_buf;
+ iocbq->iocb.ulpBdeCount++;
+ iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
+ LPFC_DATA_BUF_SIZE;
+ } else {
+ iocbq = lpfc_sli_get_iocbq(vport->phba);
+ if (!iocbq) {
+ if (first_iocbq) {
+ first_iocbq->iocb.ulpStatus =
+ IOSTAT_FCP_RSP_ERROR;
+ first_iocbq->iocb.un.ulpWord[4] =
+ IOERR_NO_RESOURCES;
+ }
+ lpfc_in_buf_free(vport->phba, d_buf);
+ continue;
+ }
+ iocbq->context2 = d_buf;
+ iocbq->context3 = NULL;
+ iocbq->iocb.ulpBdeCount = 1;
+ iocbq->iocb.un.cont64[0].tus.f.bdeSize =
+ LPFC_DATA_BUF_SIZE;
+ iocbq->iocb.un.rcvels.remoteID = sid;
+ list_add_tail(&iocbq->list, &first_iocbq->list);
+ }
+ }
+ return first_iocbq;
+}
+
+/**
+ * lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called with no lock held. This function processes all
+ * the received buffers and gives it to upper layers when a received buffer
+ * indicates that it is the final frame in the sequence. The interrupt
+ * service routine processes received buffers at interrupt contexts and adds
+ * received dma buffers to the rb_pend_list queue and signals the worker thread.
+ * Worker thread calls lpfc_sli4_handle_received_buffer, which will call the
+ * appropriate receive function when the final frame in a sequence is received.
+ **/
+int
+lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
+{
+ LIST_HEAD(cmplq);
+ struct hbq_dmabuf *dmabuf, *seq_dmabuf;
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_vport *vport;
+ uint32_t fcfi;
+ struct lpfc_iocbq *iocbq;
+
+ /* Clear hba flag and get all received buffers into the cmplq */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~HBA_RECEIVE_BUFFER;
+ list_splice_init(&phba->rb_pend_list, &cmplq);
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Process each received buffer */
+ while ((dmabuf = lpfc_sli_hbqbuf_get(&cmplq)) != NULL) {
+ fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ /* check to see if this a valid type of frame */
+ if (lpfc_fc_frame_check(phba, fc_hdr)) {
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ continue;
+ }
+ fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->rcqe);
+ vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
+ if (!vport) {
+ /* throw out the frame */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ continue;
+ }
+ /* Link this frame */
+ seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
+ if (!seq_dmabuf) {
+ /* unable to add frame to vport - throw it out */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ continue;
+ }
+ /* If not last frame in sequence continue processing frames. */
+ if (!lpfc_seq_complete(seq_dmabuf)) {
+ /*
+ * When saving off frames post a new one and mark this
+ * frame to be freed when it is finished.
+ **/
+ lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
+ dmabuf->tag = -1;
+ continue;
+ }
+ fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+ iocbq = lpfc_prep_seq(vport, seq_dmabuf);
+ if (!lpfc_complete_unsol_iocb(phba,
+ &phba->sli.ring[LPFC_ELS_RING],
+ iocbq, fc_hdr->fh_r_ctl,
+ fc_hdr->fh_type))
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2540 Ring %d handler: unexpected Rctl "
+ "x%x Type x%x received\n",
+ LPFC_ELS_RING,
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+ };
+ return 0;
+}
+
+/**
+ * lpfc_sli4_post_all_rpi_hdrs - Post the rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec. This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ *
+ * This routine does not require any locks. It's usage is expected
+ * to be driver load or reset recovery when the driver is
+ * sequential.
+ *
+ * Return codes
+ * 0 - sucessful
+ * EIO - The mailbox failed to complete successfully.
+ * When this error occurs, the driver is not guaranteed
+ * to have any rpi regions posted to the device and
+ * must either attempt to repost the regions or take a
+ * fatal error.
+ **/
+int
+lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba)
+{
+ struct lpfc_rpi_hdr *rpi_page;
+ uint32_t rc = 0;
+
+ /* Post all rpi memory regions to the port. */
+ list_for_each_entry(rpi_page, &phba->sli4_hba.lpfc_rpi_hdr_list, list) {
+ rc = lpfc_sli4_post_rpi_hdr(phba, rpi_page);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2008 Error %d posting all rpi "
+ "headers\n", rc);
+ rc = -EIO;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_post_rpi_hdr - Post an rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ * @rpi_page: pointer to the rpi memory region.
+ *
+ * This routine is invoked to post a single rpi header to the
+ * HBA consistent with the SLI-4 interface spec. This memory region
+ * maps up to 64 rpi context regions.
+ *
+ * Return codes
+ * 0 - sucessful
+ * ENOMEM - No available memory
+ * EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_post_hdr_tmpl *hdr_tmpl;
+ uint32_t rc = 0;
+ uint32_t mbox_tmo;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ /* The port is notified of the header region via a mailbox command. */
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2001 Unable to allocate memory for issuing "
+ "SLI_CONFIG_SPECIAL mailbox command\n");
+ return -ENOMEM;
+ }
+
+ /* Post all rpi memory regions to the port. */
+ hdr_tmpl = &mboxq->u.mqe.un.hdr_tmpl;
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE,
+ sizeof(struct lpfc_mbx_post_hdr_tmpl) -
+ sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
+ bf_set(lpfc_mbx_post_hdr_tmpl_page_cnt,
+ hdr_tmpl, rpi_page->page_count);
+ bf_set(lpfc_mbx_post_hdr_tmpl_rpi_offset, hdr_tmpl,
+ rpi_page->start_rpi);
+ hdr_tmpl->rpi_paddr_lo = putPaddrLow(rpi_page->dmabuf->phys);
+ hdr_tmpl->rpi_paddr_hi = putPaddrHigh(rpi_page->dmabuf->phys);
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ else
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ shdr = (union lpfc_sli4_cfg_shdr *) &hdr_tmpl->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2514 POST_RPI_HDR mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ rc = -ENXIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_sli4_alloc_rpi - Get an available rpi in the device's range
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec. This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ *
+ * Returns
+ * A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful
+ * LPFC_RPI_ALLOC_ERROR if no rpis are available.
+ **/
+int
+lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
+{
+ int rpi;
+ uint16_t max_rpi, rpi_base, rpi_limit;
+ uint16_t rpi_remaining;
+ struct lpfc_rpi_hdr *rpi_hdr;
+
+ max_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
+ rpi_base = phba->sli4_hba.max_cfg_param.rpi_base;
+ rpi_limit = phba->sli4_hba.next_rpi;
+
+ /*
+ * The valid rpi range is not guaranteed to be zero-based. Start
+ * the search at the rpi_base as reported by the port.
+ */
+ spin_lock_irq(&phba->hbalock);
+ rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, rpi_base);
+ if (rpi >= rpi_limit || rpi < rpi_base)
+ rpi = LPFC_RPI_ALLOC_ERROR;
+ else {
+ set_bit(rpi, phba->sli4_hba.rpi_bmask);
+ phba->sli4_hba.max_cfg_param.rpi_used++;
+ phba->sli4_hba.rpi_count++;
+ }
+
+ /*
+ * Don't try to allocate more rpi header regions if the device limit
+ * on available rpis max has been exhausted.
+ */
+ if ((rpi == LPFC_RPI_ALLOC_ERROR) &&
+ (phba->sli4_hba.rpi_count >= max_rpi)) {
+ spin_unlock_irq(&phba->hbalock);
+ return rpi;
+ }
+
+ /*
+ * If the driver is running low on rpi resources, allocate another
+ * page now. Note that the next_rpi value is used because
+ * it represents how many are actually in use whereas max_rpi notes
+ * how many are supported max by the device.
+ */
+ rpi_remaining = phba->sli4_hba.next_rpi - rpi_base -
+ phba->sli4_hba.rpi_count;
+ spin_unlock_irq(&phba->hbalock);
+ if (rpi_remaining < LPFC_RPI_LOW_WATER_MARK) {
+ rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
+ if (!rpi_hdr) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2002 Error Could not grow rpi "
+ "count\n");
+ } else {
+ lpfc_sli4_post_rpi_hdr(phba, rpi_hdr);
+ }
+ }
+
+ return rpi;
+}
+
+/**
+ * lpfc_sli4_free_rpi - Release an rpi for reuse.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release an rpi to the pool of
+ * available rpis maintained by the driver.
+ **/
+void
+lpfc_sli4_free_rpi(struct lpfc_hba *phba, int rpi)
+{
+ spin_lock_irq(&phba->hbalock);
+ clear_bit(rpi, phba->sli4_hba.rpi_bmask);
+ phba->sli4_hba.rpi_count--;
+ phba->sli4_hba.max_cfg_param.rpi_used--;
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_remove_rpis - Remove the rpi bitmask region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the memory region that
+ * provided rpi via a bitmask.
+ **/
+void
+lpfc_sli4_remove_rpis(struct lpfc_hba *phba)
+{
+ kfree(phba->sli4_hba.rpi_bmask);
+}
+
+/**
+ * lpfc_sli4_resume_rpi - Remove the rpi bitmask region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the memory region that
+ * provided rpi via a bitmask.
+ **/
+int
+lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_hba *phba = ndlp->phba;
+ int rc;
+
+ /* The port is notified of the header region via a mailbox command. */
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ /* Post all rpi memory regions to the port. */
+ lpfc_resume_rpi(mboxq, ndlp);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2010 Resume RPI Mailbox failed "
+ "status %d, mbxStatus x%x\n", rc,
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_sli4_init_vpi - Initialize a vpi with the port
+ * @phba: pointer to lpfc hba data structure.
+ * @vpi: vpi value to activate with the port.
+ *
+ * This routine is invoked to activate a vpi with the
+ * port when the host intends to use vports with a
+ * nonzero vpi.
+ *
+ * Returns:
+ * 0 success
+ * -Evalue otherwise
+ **/
+int
+lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
+{
+ LPFC_MBOXQ_t *mboxq;
+ int rc = 0;
+ uint32_t mbox_tmo;
+
+ if (vpi == 0)
+ return -EINVAL;
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+ lpfc_init_vpi(mboxq, vpi);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2022 INIT VPI Mailbox failed "
+ "status %d, mbxStatus x%x\n", rc,
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+ rc = -EIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_mbx_cmpl_add_fcf_record - add fcf mbox completion handler.
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This routine is invoked to manually add a single FCF record. The caller
+ * must pass a completely initialized FCF_Record. This routine takes
+ * care of the nonembedded mailbox operations.
+ **/
+static void
+lpfc_mbx_cmpl_add_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ void *virt_addr;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint32_t shdr_status, shdr_add_status;
+
+ virt_addr = mboxq->sge_array->addr[0];
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr = (union lpfc_sli4_cfg_shdr *) virt_addr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+ if ((shdr_status || shdr_add_status) &&
+ (shdr_status != STATUS_FCF_IN_USE))
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2558 ADD_FCF_RECORD mailbox failed with "
+ "status x%x add_status x%x\n",
+ shdr_status, shdr_add_status);
+
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+}
+
+/**
+ * lpfc_sli4_add_fcf_record - Manually add an FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_record: pointer to the initialized fcf record to add.
+ *
+ * This routine is invoked to manually add a single FCF record. The caller
+ * must pass a completely initialized FCF_Record. This routine takes
+ * care of the nonembedded mailbox operations.
+ **/
+int
+lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
+{
+ int rc = 0;
+ LPFC_MBOXQ_t *mboxq;
+ uint8_t *bytep;
+ void *virt_addr;
+ dma_addr_t phys_addr;
+ struct lpfc_mbx_sge sge;
+ uint32_t alloc_len, req_len;
+ uint32_t fcfindex;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2009 Failed to allocate mbox for ADD_FCF cmd\n");
+ return -ENOMEM;
+ }
+
+ req_len = sizeof(struct fcf_record) + sizeof(union lpfc_sli4_cfg_shdr) +
+ sizeof(uint32_t);
+
+ /* Allocate DMA memory and set up the non-embedded mailbox command */
+ alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_ADD_FCF,
+ req_len, LPFC_SLI4_MBX_NEMBED);
+ if (alloc_len < req_len) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2523 Allocated DMA memory size (x%x) is "
+ "less than the requested DMA memory "
+ "size (x%x)\n", alloc_len, req_len);
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return -ENOMEM;
+ }
+
+ /*
+ * Get the first SGE entry from the non-embedded DMA memory. This
+ * routine only uses a single SGE.
+ */
+ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+ phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+ if (unlikely(!mboxq->sge_array)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2526 Failed to get the non-embedded SGE "
+ "virtual address\n");
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return -ENOMEM;
+ }
+ virt_addr = mboxq->sge_array->addr[0];
+ /*
+ * Configure the FCF record for FCFI 0. This is the driver's
+ * hardcoded default and gets used in nonFIP mode.
+ */
+ fcfindex = bf_get(lpfc_fcf_record_fcf_index, fcf_record);
+ bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+ lpfc_sli_pcimem_bcopy(&fcfindex, bytep, sizeof(uint32_t));
+
+ /*
+ * Copy the fcf_index and the FCF Record Data. The data starts after
+ * the FCoE header plus word10. The data copy needs to be endian
+ * correct.
+ */
+ bytep += sizeof(uint32_t);
+ lpfc_sli_pcimem_bcopy(fcf_record, bytep, sizeof(struct fcf_record));
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_add_fcf_record;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2515 ADD_FCF_RECORD mailbox failed with "
+ "status 0x%x\n", rc);
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ rc = -EIO;
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_build_dflt_fcf_record - Build the driver's default FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_record: pointer to the fcf record to write the default data.
+ * @fcf_index: FCF table entry index.
+ *
+ * This routine is invoked to build the driver's default FCF record. The
+ * values used are hardcoded. This routine handles memory initialization.
+ *
+ **/
+void
+lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *phba,
+ struct fcf_record *fcf_record,
+ uint16_t fcf_index)
+{
+ memset(fcf_record, 0, sizeof(struct fcf_record));
+ fcf_record->max_rcv_size = LPFC_FCOE_MAX_RCV_SIZE;
+ fcf_record->fka_adv_period = LPFC_FCOE_FKA_ADV_PER;
+ fcf_record->fip_priority = LPFC_FCOE_FIP_PRIORITY;
+ bf_set(lpfc_fcf_record_mac_0, fcf_record, phba->fc_map[0]);
+ bf_set(lpfc_fcf_record_mac_1, fcf_record, phba->fc_map[1]);
+ bf_set(lpfc_fcf_record_mac_2, fcf_record, phba->fc_map[2]);
+ bf_set(lpfc_fcf_record_mac_3, fcf_record, LPFC_FCOE_FCF_MAC3);
+ bf_set(lpfc_fcf_record_mac_4, fcf_record, LPFC_FCOE_FCF_MAC4);
+ bf_set(lpfc_fcf_record_mac_5, fcf_record, LPFC_FCOE_FCF_MAC5);
+ bf_set(lpfc_fcf_record_fc_map_0, fcf_record, phba->fc_map[0]);
+ bf_set(lpfc_fcf_record_fc_map_1, fcf_record, phba->fc_map[1]);
+ bf_set(lpfc_fcf_record_fc_map_2, fcf_record, phba->fc_map[2]);
+ bf_set(lpfc_fcf_record_fcf_valid, fcf_record, 1);
+ bf_set(lpfc_fcf_record_fcf_index, fcf_record, fcf_index);
+ bf_set(lpfc_fcf_record_mac_addr_prov, fcf_record,
+ LPFC_FCF_FPMA | LPFC_FCF_SPMA);
+ /* Set the VLAN bit map */
+ if (phba->valid_vlan) {
+ fcf_record->vlan_bitmap[phba->vlan_id / 8]
+ = 1 << (phba->vlan_id % 8);
+ }
+}
+
+/**
+ * lpfc_sli4_read_fcf_record - Read the driver's default FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: FCF table entry offset.
+ *
+ * This routine is invoked to read up to @fcf_num of FCF record from the
+ * device starting with the given @fcf_index.
+ **/
+int
+lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ int rc = 0, error;
+ LPFC_MBOXQ_t *mboxq;
+ void *virt_addr;
+ dma_addr_t phys_addr;
+ uint8_t *bytep;
+ struct lpfc_mbx_sge sge;
+ uint32_t alloc_len, req_len;
+ struct lpfc_mbx_read_fcf_tbl *read_fcf;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2000 Failed to allocate mbox for "
+ "READ_FCF cmd\n");
+ return -ENOMEM;
+ }
+
+ req_len = sizeof(struct fcf_record) +
+ sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t);
+
+ /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */
+ alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len,
+ LPFC_SLI4_MBX_NEMBED);
+
+ if (alloc_len < req_len) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0291 Allocated DMA memory size (x%x) is "
+ "less than the requested DMA memory "
+ "size (x%x)\n", alloc_len, req_len);
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return -ENOMEM;
+ }
+
+ /* Get the first SGE entry from the non-embedded DMA memory. This
+ * routine only uses a single SGE.
+ */
+ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+ phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+ if (unlikely(!mboxq->sge_array)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2527 Failed to get the non-embedded SGE "
+ "virtual address\n");
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return -ENOMEM;
+ }
+ virt_addr = mboxq->sge_array->addr[0];
+ read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
+
+ /* Set up command fields */
+ bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index);
+ /* Perform necessary endian conversion */
+ bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+ lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t));
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ error = -EIO;
+ } else
+ error = 0;
+ return error;
+}
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 883938652a6a..7d37eb7459bf 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -29,13 +29,23 @@ typedef enum _lpfc_ctx_cmd {
LPFC_CTX_HOST
} lpfc_ctx_cmd;
+/* This structure is used to carry the needed response IOCB states */
+struct lpfc_sli4_rspiocb_info {
+ uint8_t hw_status;
+ uint8_t bfield;
+#define LPFC_XB 0x1
+#define LPFC_PV 0x2
+ uint8_t priority;
+ uint8_t reserved;
+};
+
/* This structure is used to handle IOCB requests / responses */
struct lpfc_iocbq {
/* lpfc_iocbqs are used in double linked lists */
struct list_head list;
struct list_head clist;
uint16_t iotag; /* pre-assigned IO tag */
- uint16_t rsvd1;
+ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */
IOCB_t iocb; /* IOCB cmd */
uint8_t retry; /* retry counter for IOCB cmd - if needed */
@@ -65,7 +75,7 @@ struct lpfc_iocbq {
struct lpfc_iocbq *);
void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
struct lpfc_iocbq *);
-
+ struct lpfc_sli4_rspiocb_info sli4_info;
};
#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */
@@ -81,14 +91,18 @@ struct lpfc_iocbq {
typedef struct lpfcMboxq {
/* MBOXQs are used in single linked lists */
struct list_head list; /* ptr to next mailbox command */
- MAILBOX_t mb; /* Mailbox cmd */
- struct lpfc_vport *vport;/* virutal port pointer */
+ union {
+ MAILBOX_t mb; /* Mailbox cmd */
+ struct lpfc_mqe mqe;
+ } u;
+ struct lpfc_vport *vport;/* virtual port pointer */
void *context1; /* caller context information */
void *context2; /* caller context information */
void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
uint8_t mbox_flag;
-
+ struct lpfc_mcqe mcqe;
+ struct lpfc_mbx_nembed_sge_virt *sge_array;
} LPFC_MBOXQ_t;
#define MBX_POLL 1 /* poll mailbox till command done, then
@@ -230,10 +244,11 @@ struct lpfc_sli {
/* 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_SLI_ACTIVE 0x200 /* SLI in firmware is active */
#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */
#define LPFC_BLOCK_MGMT_IO 0x800 /* Don't allow mgmt mbx or iocb cmds */
#define LPFC_MENLO_MAINT 0x1000 /* need for menl fw download */
+#define LPFC_SLI_ASYNC_MBX_BLK 0x2000 /* Async mailbox is blocked */
struct lpfc_sli_ring ring[LPFC_MAX_RING];
int fcp_ring; /* ring used for FCP initiator commands */
@@ -261,6 +276,8 @@ struct lpfc_sli {
#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox
command */
+#define LPFC_MBOX_SLI4_CONFIG_TMO 60 /* Sec tmo for outstanding mbox
+ command */
#define LPFC_MBOX_TMO_FLASH_CMD 300 /* Sec tmo for outstanding FLASH write
* or erase cmds. This is especially
* long because of the potential of
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
new file mode 100644
index 000000000000..5196b46608d7
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -0,0 +1,467 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2009 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+#define LPFC_ACTIVE_MBOX_WAIT_CNT 100
+#define LPFC_RELEASE_NOTIFICATION_INTERVAL 32
+#define LPFC_GET_QE_REL_INT 32
+#define LPFC_RPI_LOW_WATER_MARK 10
+/* Number of SGL entries can be posted in a 4KB nonembedded mbox command */
+#define LPFC_NEMBED_MBOX_SGL_CNT 254
+
+/* Multi-queue arrangement for fast-path FCP work queues */
+#define LPFC_FN_EQN_MAX 8
+#define LPFC_SP_EQN_DEF 1
+#define LPFC_FP_EQN_DEF 1
+#define LPFC_FP_EQN_MIN 1
+#define LPFC_FP_EQN_MAX (LPFC_FN_EQN_MAX - LPFC_SP_EQN_DEF)
+
+#define LPFC_FN_WQN_MAX 32
+#define LPFC_SP_WQN_DEF 1
+#define LPFC_FP_WQN_DEF 4
+#define LPFC_FP_WQN_MIN 1
+#define LPFC_FP_WQN_MAX (LPFC_FN_WQN_MAX - LPFC_SP_WQN_DEF)
+
+/*
+ * Provide the default FCF Record attributes used by the driver
+ * when nonFIP mode is configured and there is no other default
+ * FCF Record attributes.
+ */
+#define LPFC_FCOE_FCF_DEF_INDEX 0
+#define LPFC_FCOE_FCF_GET_FIRST 0xFFFF
+#define LPFC_FCOE_FCF_NEXT_NONE 0xFFFF
+
+/* First 3 bytes of default FCF MAC is specified by FC_MAP */
+#define LPFC_FCOE_FCF_MAC3 0xFF
+#define LPFC_FCOE_FCF_MAC4 0xFF
+#define LPFC_FCOE_FCF_MAC5 0xFE
+#define LPFC_FCOE_FCF_MAP0 0x0E
+#define LPFC_FCOE_FCF_MAP1 0xFC
+#define LPFC_FCOE_FCF_MAP2 0x00
+#define LPFC_FCOE_MAX_RCV_SIZE 0x5AC
+#define LPFC_FCOE_FKA_ADV_PER 0
+#define LPFC_FCOE_FIP_PRIORITY 0x80
+
+enum lpfc_sli4_queue_type {
+ LPFC_EQ,
+ LPFC_GCQ,
+ LPFC_MCQ,
+ LPFC_WCQ,
+ LPFC_RCQ,
+ LPFC_MQ,
+ LPFC_WQ,
+ LPFC_HRQ,
+ LPFC_DRQ
+};
+
+/* The queue sub-type defines the functional purpose of the queue */
+enum lpfc_sli4_queue_subtype {
+ LPFC_NONE,
+ LPFC_MBOX,
+ LPFC_FCP,
+ LPFC_ELS,
+ LPFC_USOL
+};
+
+union sli4_qe {
+ void *address;
+ struct lpfc_eqe *eqe;
+ struct lpfc_cqe *cqe;
+ struct lpfc_mcqe *mcqe;
+ struct lpfc_wcqe_complete *wcqe_complete;
+ struct lpfc_wcqe_release *wcqe_release;
+ struct sli4_wcqe_xri_aborted *wcqe_xri_aborted;
+ struct lpfc_rcqe_complete *rcqe_complete;
+ struct lpfc_mqe *mqe;
+ union lpfc_wqe *wqe;
+ struct lpfc_rqe *rqe;
+};
+
+struct lpfc_queue {
+ struct list_head list;
+ enum lpfc_sli4_queue_type type;
+ enum lpfc_sli4_queue_subtype subtype;
+ struct lpfc_hba *phba;
+ struct list_head child_list;
+ uint32_t entry_count; /* Number of entries to support on the queue */
+ uint32_t entry_size; /* Size of each queue entry. */
+ uint32_t queue_id; /* Queue ID assigned by the hardware */
+ struct list_head page_list;
+ uint32_t page_count; /* Number of pages allocated for this queue */
+
+ uint32_t host_index; /* The host's index for putting or getting */
+ uint32_t hba_index; /* The last known hba index for get or put */
+ union sli4_qe qe[1]; /* array to index entries (must be last) */
+};
+
+struct lpfc_cq_event {
+ struct list_head list;
+ union {
+ struct lpfc_mcqe mcqe_cmpl;
+ struct lpfc_acqe_link acqe_link;
+ struct lpfc_acqe_fcoe acqe_fcoe;
+ struct lpfc_acqe_dcbx acqe_dcbx;
+ struct lpfc_rcqe rcqe_cmpl;
+ struct sli4_wcqe_xri_aborted wcqe_axri;
+ } cqe;
+};
+
+struct lpfc_sli4_link {
+ uint8_t speed;
+ uint8_t duplex;
+ uint8_t status;
+ uint8_t physical;
+ uint8_t fault;
+};
+
+struct lpfc_fcf {
+ uint8_t fabric_name[8];
+ uint8_t mac_addr[6];
+ uint16_t fcf_indx;
+ uint16_t fcfi;
+ uint32_t fcf_flag;
+#define FCF_AVAILABLE 0x01 /* FCF available for discovery */
+#define FCF_REGISTERED 0x02 /* FCF registered with FW */
+#define FCF_DISCOVERED 0x04 /* FCF discovery started */
+#define FCF_BOOT_ENABLE 0x08 /* Boot bios use this FCF */
+#define FCF_IN_USE 0x10 /* Atleast one discovery completed */
+#define FCF_VALID_VLAN 0x20 /* Use the vlan id specified */
+ uint32_t priority;
+ uint32_t addr_mode;
+ uint16_t vlan_id;
+};
+
+#define LPFC_REGION23_SIGNATURE "RG23"
+#define LPFC_REGION23_VERSION 1
+#define LPFC_REGION23_LAST_REC 0xff
+struct lpfc_fip_param_hdr {
+ uint8_t type;
+#define FCOE_PARAM_TYPE 0xA0
+ uint8_t length;
+#define FCOE_PARAM_LENGTH 2
+ uint8_t parm_version;
+#define FIPP_VERSION 0x01
+ uint8_t parm_flags;
+#define lpfc_fip_param_hdr_fipp_mode_SHIFT 6
+#define lpfc_fip_param_hdr_fipp_mode_MASK 0x3
+#define lpfc_fip_param_hdr_fipp_mode_WORD parm_flags
+#define FIPP_MODE_ON 0x2
+#define FIPP_MODE_OFF 0x0
+#define FIPP_VLAN_VALID 0x1
+};
+
+struct lpfc_fcoe_params {
+ uint8_t fc_map[3];
+ uint8_t reserved1;
+ uint16_t vlan_tag;
+ uint8_t reserved[2];
+};
+
+struct lpfc_fcf_conn_hdr {
+ uint8_t type;
+#define FCOE_CONN_TBL_TYPE 0xA1
+ uint8_t length; /* words */
+ uint8_t reserved[2];
+};
+
+struct lpfc_fcf_conn_rec {
+ uint16_t flags;
+#define FCFCNCT_VALID 0x0001
+#define FCFCNCT_BOOT 0x0002
+#define FCFCNCT_PRIMARY 0x0004 /* if not set, Secondary */
+#define FCFCNCT_FBNM_VALID 0x0008
+#define FCFCNCT_SWNM_VALID 0x0010
+#define FCFCNCT_VLAN_VALID 0x0020
+#define FCFCNCT_AM_VALID 0x0040
+#define FCFCNCT_AM_PREFERRED 0x0080 /* if not set, AM Required */
+#define FCFCNCT_AM_SPMA 0x0100 /* if not set, FPMA */
+
+ uint16_t vlan_tag;
+ uint8_t fabric_name[8];
+ uint8_t switch_name[8];
+};
+
+struct lpfc_fcf_conn_entry {
+ struct list_head list;
+ struct lpfc_fcf_conn_rec conn_rec;
+};
+
+/*
+ * Define the host's bootstrap mailbox. This structure contains
+ * the member attributes needed to create, use, and destroy the
+ * bootstrap mailbox region.
+ *
+ * The macro definitions for the bmbx data structure are defined
+ * in lpfc_hw4.h with the register definition.
+ */
+struct lpfc_bmbx {
+ struct lpfc_dmabuf *dmabuf;
+ struct dma_address dma_address;
+ void *avirt;
+ dma_addr_t aphys;
+ uint32_t bmbx_size;
+};
+
+#define LPFC_EQE_SIZE LPFC_EQE_SIZE_4
+
+#define LPFC_EQE_SIZE_4B 4
+#define LPFC_EQE_SIZE_16B 16
+#define LPFC_CQE_SIZE 16
+#define LPFC_WQE_SIZE 64
+#define LPFC_MQE_SIZE 256
+#define LPFC_RQE_SIZE 8
+
+#define LPFC_EQE_DEF_COUNT 1024
+#define LPFC_CQE_DEF_COUNT 256
+#define LPFC_WQE_DEF_COUNT 64
+#define LPFC_MQE_DEF_COUNT 16
+#define LPFC_RQE_DEF_COUNT 512
+
+#define LPFC_QUEUE_NOARM false
+#define LPFC_QUEUE_REARM true
+
+
+/*
+ * SLI4 CT field defines
+ */
+#define SLI4_CT_RPI 0
+#define SLI4_CT_VPI 1
+#define SLI4_CT_VFI 2
+#define SLI4_CT_FCFI 3
+
+#define LPFC_SLI4_MAX_SEGMENT_SIZE 0x10000
+
+/*
+ * SLI4 specific data structures
+ */
+struct lpfc_max_cfg_param {
+ uint16_t max_xri;
+ uint16_t xri_base;
+ uint16_t xri_used;
+ uint16_t max_rpi;
+ uint16_t rpi_base;
+ uint16_t rpi_used;
+ uint16_t max_vpi;
+ uint16_t vpi_base;
+ uint16_t vpi_used;
+ uint16_t max_vfi;
+ uint16_t vfi_base;
+ uint16_t vfi_used;
+ uint16_t max_fcfi;
+ uint16_t fcfi_base;
+ uint16_t fcfi_used;
+ uint16_t max_eq;
+ uint16_t max_rq;
+ uint16_t max_cq;
+ uint16_t max_wq;
+};
+
+struct lpfc_hba;
+/* SLI4 HBA multi-fcp queue handler struct */
+struct lpfc_fcp_eq_hdl {
+ uint32_t idx;
+ struct lpfc_hba *phba;
+};
+
+/* SLI4 HBA data structure entries */
+struct lpfc_sli4_hba {
+ void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
+ PCI BAR0, config space registers */
+ void __iomem *ctrl_regs_memmap_p; /* Kernel memory mapped address for
+ PCI BAR1, control registers */
+ void __iomem *drbl_regs_memmap_p; /* Kernel memory mapped address for
+ PCI BAR2, doorbell registers */
+ /* BAR0 PCI config space register memory map */
+ void __iomem *UERRLOregaddr; /* Address to UERR_STATUS_LO register */
+ void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */
+ void __iomem *ONLINE0regaddr; /* Address to components of internal UE */
+ void __iomem *ONLINE1regaddr; /* Address to components of internal UE */
+#define LPFC_ONLINE_NERR 0xFFFFFFFF
+ void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */
+ /* BAR1 FCoE function CSR register memory map */
+ void __iomem *STAregaddr; /* Address to HST_STATE register */
+ void __iomem *ISRregaddr; /* Address to HST_ISR register */
+ void __iomem *IMRregaddr; /* Address to HST_IMR register */
+ void __iomem *ISCRregaddr; /* Address to HST_ISCR register */
+ /* BAR2 VF-0 doorbell register memory map */
+ void __iomem *RQDBregaddr; /* Address to RQ_DOORBELL register */
+ void __iomem *WQDBregaddr; /* Address to WQ_DOORBELL register */
+ void __iomem *EQCQDBregaddr; /* Address to EQCQ_DOORBELL register */
+ void __iomem *MQDBregaddr; /* Address to MQ_DOORBELL register */
+ void __iomem *BMBXregaddr; /* Address to BootStrap MBX register */
+
+ struct msix_entry *msix_entries;
+ uint32_t cfg_eqn;
+ struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
+ /* Pointers to the constructed SLI4 queues */
+ struct lpfc_queue **fp_eq; /* Fast-path event queue */
+ struct lpfc_queue *sp_eq; /* Slow-path event queue */
+ struct lpfc_queue **fcp_wq;/* Fast-path FCP work queue */
+ struct lpfc_queue *mbx_wq; /* Slow-path MBOX work queue */
+ struct lpfc_queue *els_wq; /* Slow-path ELS work queue */
+ struct lpfc_queue *hdr_rq; /* Slow-path Header Receive queue */
+ struct lpfc_queue *dat_rq; /* Slow-path Data Receive queue */
+ struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */
+ struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
+ struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
+ struct lpfc_queue *rxq_cq; /* Slow-path unsolicited complete queue */
+
+ /* Setup information for various queue parameters */
+ int eq_esize;
+ int eq_ecount;
+ int cq_esize;
+ int cq_ecount;
+ int wq_esize;
+ int wq_ecount;
+ int mq_esize;
+ int mq_ecount;
+ int rq_esize;
+ int rq_ecount;
+#define LPFC_SP_EQ_MAX_INTR_SEC 10000
+#define LPFC_FP_EQ_MAX_INTR_SEC 10000
+
+ uint32_t intr_enable;
+ struct lpfc_bmbx bmbx;
+ struct lpfc_max_cfg_param max_cfg_param;
+ uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */
+ uint16_t next_rpi;
+ uint16_t scsi_xri_max;
+ uint16_t scsi_xri_cnt;
+ struct list_head lpfc_free_sgl_list;
+ struct list_head lpfc_sgl_list;
+ struct lpfc_sglq **lpfc_els_sgl_array;
+ struct list_head lpfc_abts_els_sgl_list;
+ struct lpfc_scsi_buf **lpfc_scsi_psb_array;
+ struct list_head lpfc_abts_scsi_buf_list;
+ uint32_t total_sglq_bufs;
+ struct lpfc_sglq **lpfc_sglq_active_list;
+ struct list_head lpfc_rpi_hdr_list;
+ unsigned long *rpi_bmask;
+ uint16_t rpi_count;
+ struct lpfc_sli4_flags sli4_flags;
+ struct list_head sp_rspiocb_work_queue;
+ struct list_head sp_cqe_event_pool;
+ struct list_head sp_asynce_work_queue;
+ struct list_head sp_fcp_xri_aborted_work_queue;
+ struct list_head sp_els_xri_aborted_work_queue;
+ struct list_head sp_unsol_work_queue;
+ struct lpfc_sli4_link link_state;
+ spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
+ spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */
+};
+
+enum lpfc_sge_type {
+ GEN_BUFF_TYPE,
+ SCSI_BUFF_TYPE
+};
+
+struct lpfc_sglq {
+ /* lpfc_sglqs are used in double linked lists */
+ struct list_head list;
+ struct list_head clist;
+ enum lpfc_sge_type buff_type; /* is this a scsi sgl */
+ uint16_t iotag; /* pre-assigned IO tag */
+ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */
+ struct sli4_sge *sgl; /* pre-assigned SGL */
+ void *virt; /* virtual address. */
+ dma_addr_t phys; /* physical address */
+};
+
+struct lpfc_rpi_hdr {
+ struct list_head list;
+ uint32_t len;
+ struct lpfc_dmabuf *dmabuf;
+ uint32_t page_count;
+ uint32_t start_rpi;
+};
+
+/*
+ * SLI4 specific function prototypes
+ */
+int lpfc_pci_function_reset(struct lpfc_hba *);
+int lpfc_sli4_hba_setup(struct lpfc_hba *);
+int lpfc_sli4_hba_down(struct lpfc_hba *);
+int lpfc_sli4_config(struct lpfc_hba *, struct lpfcMboxq *, uint8_t,
+ uint8_t, uint32_t, bool);
+void lpfc_sli4_mbox_cmd_free(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_sli4_mbx_sge_set(struct lpfcMboxq *, uint32_t, dma_addr_t, uint32_t);
+void lpfc_sli4_mbx_sge_get(struct lpfcMboxq *, uint32_t,
+ struct lpfc_mbx_sge *);
+
+void lpfc_sli4_hba_reset(struct lpfc_hba *);
+struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
+ uint32_t);
+void lpfc_sli4_queue_free(struct lpfc_queue *);
+uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint16_t);
+uint32_t lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *, uint32_t, uint32_t);
+uint32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *, uint32_t);
+uint32_t lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *, uint32_t);
+uint32_t lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *, struct lpfc_queue *, uint32_t);
+uint32_t lpfc_eq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_cq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_mq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_wq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *);
+int lpfc_sli4_queue_setup(struct lpfc_hba *);
+void lpfc_sli4_queue_unset(struct lpfc_hba *);
+int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t);
+int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *);
+int lpfc_sli4_remove_all_sgl_pages(struct lpfc_hba *);
+uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *);
+int lpfc_sli4_post_async_mbox(struct lpfc_hba *);
+int lpfc_sli4_post_sgl_list(struct lpfc_hba *phba);
+int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int);
+struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
+struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
+void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
+void lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
+int lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *);
+int lpfc_sli4_post_rpi_hdr(struct lpfc_hba *, struct lpfc_rpi_hdr *);
+int lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *);
+struct lpfc_rpi_hdr *lpfc_sli4_create_rpi_hdr(struct lpfc_hba *);
+void lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *);
+int lpfc_sli4_alloc_rpi(struct lpfc_hba *);
+void lpfc_sli4_free_rpi(struct lpfc_hba *, int);
+void lpfc_sli4_remove_rpis(struct lpfc_hba *);
+void lpfc_sli4_async_event_proc(struct lpfc_hba *);
+int lpfc_sli4_resume_rpi(struct lpfc_nodelist *);
+void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
+void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
+void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
+ struct sli4_wcqe_xri_aborted *);
+void lpfc_sli4_els_xri_aborted(struct lpfc_hba *,
+ struct sli4_wcqe_xri_aborted *);
+int lpfc_sli4_brdreset(struct lpfc_hba *);
+int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *);
+void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *);
+int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *);
+int lpfc_sli4_init_vpi(struct lpfc_hba *, uint16_t);
+uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool);
+uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool);
+void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t);
+int lpfc_sli4_read_fcf_record(struct lpfc_hba *, uint16_t);
+void lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_sli4_post_status_check(struct lpfc_hba *);
+uint8_t lpfc_sli4_mbox_opcode_get(struct lpfc_hba *, struct lpfcMboxq *);
+
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 7e32e95c5392..6b8a148f0a55 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.0"
+#define LPFC_DRIVER_VERSION "8.3.2"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
@@ -26,4 +26,4 @@
#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
LPFC_DRIVER_VERSION
-#define LPFC_COPYRIGHT "Copyright(c) 2004-2008 Emulex. All rights reserved."
+#define LPFC_COPYRIGHT "Copyright(c) 2004-2009 Emulex. All rights reserved."
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 63b54c66756c..a6313ee84ac5 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -32,8 +32,10 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
@@ -89,6 +91,8 @@ lpfc_alloc_vpi(struct lpfc_hba *phba)
vpi = 0;
else
set_bit(vpi, phba->vpi_bmask);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ phba->sli4_hba.max_cfg_param.vpi_used++;
spin_unlock_irq(&phba->hbalock);
return vpi;
}
@@ -96,8 +100,12 @@ lpfc_alloc_vpi(struct lpfc_hba *phba)
static void
lpfc_free_vpi(struct lpfc_hba *phba, int vpi)
{
+ if (vpi == 0)
+ return;
spin_lock_irq(&phba->hbalock);
clear_bit(vpi, phba->vpi_bmask);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ phba->sli4_hba.max_cfg_param.vpi_used--;
spin_unlock_irq(&phba->hbalock);
}
@@ -113,7 +121,7 @@ lpfc_vport_sparm(struct lpfc_hba *phba, struct lpfc_vport *vport)
if (!pmb) {
return -ENOMEM;
}
- mb = &pmb->mb;
+ mb = &pmb->u.mb;
lpfc_read_sparam(phba, pmb, vport->vpi);
/*
@@ -206,7 +214,7 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
}
/**
- * lpfc_discovery_wait: Wait for driver discovery to quiesce.
+ * lpfc_discovery_wait - Wait for driver discovery to quiesce
* @vport: The virtual port for which this call is being executed.
*
* This driver calls this routine specifically from lpfc_vport_delete
@@ -243,23 +251,22 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
(vport->fc_flag & wait_flags) ||
((vport->port_state > LPFC_VPORT_FAILED) &&
(vport->port_state < LPFC_VPORT_READY))) {
- lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
"1833 Vport discovery quiesce Wait:"
- " vpi x%x state x%x fc_flags x%x"
+ " state x%x fc_flags x%x"
" num_nodes x%x, waiting 1000 msecs"
" total wait msecs x%x\n",
- vport->vpi, vport->port_state,
- vport->fc_flag, vport->num_disc_nodes,
+ vport->port_state, vport->fc_flag,
+ vport->num_disc_nodes,
jiffies_to_msecs(jiffies - start_time));
msleep(1000);
} else {
/* Base case. Wait variants satisfied. Break out */
- lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
"1834 Vport discovery quiesced:"
- " vpi x%x state x%x fc_flags x%x"
+ " state x%x fc_flags x%x"
" wait msecs x%x\n",
- vport->vpi, vport->port_state,
- vport->fc_flag,
+ vport->port_state, vport->fc_flag,
jiffies_to_msecs(jiffies
- start_time));
break;
@@ -267,12 +274,10 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
}
if (time_after(jiffies, wait_time_max))
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
"1835 Vport discovery quiesce failed:"
- " vpi x%x state x%x fc_flags x%x"
- " wait msecs x%x\n",
- vport->vpi, vport->port_state,
- vport->fc_flag,
+ " state x%x fc_flags x%x wait msecs x%x\n",
+ vport->port_state, vport->fc_flag,
jiffies_to_msecs(jiffies - start_time));
}
@@ -308,6 +313,21 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
+ /*
+ * In SLI4, the vpi must be activated before it can be used
+ * by the port.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ rc = lpfc_sli4_init_vpi(phba, vpi);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ "1838 Failed to INIT_VPI on vpi %d "
+ "status %d\n", vpi, rc);
+ rc = VPORT_NORESOURCES;
+ lpfc_free_vpi(phba, vpi);
+ goto error_out;
+ }
+ }
/* Assign an unused board number */
if ((instance = lpfc_get_instance()) < 0) {
@@ -535,6 +555,16 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
"physical host\n");
return VPORT_ERROR;
}
+
+ /* If the vport is a static vport fail the deletion. */
+ if ((vport->vport_flag & STATIC_VPORT) &&
+ !(phba->pport->load_flag & FC_UNLOADING)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ "1837 vport_delete failed: Cannot delete "
+ "static vport.\n");
+ return VPORT_ERROR;
+ }
+
/*
* If we are not unloading the driver then prevent the vport_delete
* from happening until after this vport's discovery is finished.
@@ -710,7 +740,7 @@ lpfc_create_vport_work_array(struct lpfc_hba *phba)
struct lpfc_vport *port_iterator;
struct lpfc_vport **vports;
int index = 0;
- vports = kzalloc((phba->max_vpi + 1) * sizeof(struct lpfc_vport *),
+ vports = kzalloc((phba->max_vports + 1) * sizeof(struct lpfc_vport *),
GFP_KERNEL);
if (vports == NULL)
return NULL;
@@ -734,14 +764,14 @@ lpfc_destroy_vport_work_array(struct lpfc_hba *phba, struct lpfc_vport **vports)
int i;
if (vports == NULL)
return;
- for (i=0; vports[i] != NULL && i <= phba->max_vpi; i++)
+ for (i = 0; vports[i] != NULL && i <= phba->max_vports; i++)
scsi_host_put(lpfc_shost_from_vport(vports[i]));
kfree(vports);
}
/**
- * lpfc_vport_reset_stat_data: Reset the statistical data for the vport.
+ * lpfc_vport_reset_stat_data - Reset the statistical data for the vport
* @vport: Pointer to vport object.
*
* This function resets the statistical data for the vport. This function
@@ -763,8 +793,7 @@ lpfc_vport_reset_stat_data(struct lpfc_vport *vport)
/**
- * lpfc_alloc_bucket: Allocate data buffer required for collecting
- * statistical data.
+ * lpfc_alloc_bucket - Allocate data buffer required for statistical data
* @vport: Pointer to vport object.
*
* This function allocates data buffer required for all the FC
@@ -797,8 +826,7 @@ lpfc_alloc_bucket(struct lpfc_vport *vport)
}
/**
- * lpfc_free_bucket: Free data buffer required for collecting
- * statistical data.
+ * lpfc_free_bucket - Free data buffer required for statistical data
* @vport: Pointer to vport object.
*
* Th function frees statistical data buffer of all the FC
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index a91f5143ceac..f3da592f7bcc 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -636,6 +636,14 @@ _base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc)
static irqreturn_t
_base_interrupt(int irq, void *bus_id)
{
+ union reply_descriptor {
+ u64 word;
+ struct {
+ u32 low;
+ u32 high;
+ } u;
+ };
+ union reply_descriptor rd;
u32 post_index, post_index_next, completed_cmds;
u8 request_desript_type;
u16 smid;
@@ -656,7 +664,8 @@ _base_interrupt(int irq, void *bus_id)
completed_cmds = 0;
do {
- if (ioc->reply_post_free[post_index].Words == ~0ULL)
+ rd.word = ioc->reply_post_free[post_index].Words;
+ if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
goto out;
reply = 0;
cb_idx = 0xFF;
@@ -721,7 +730,7 @@ _base_interrupt(int irq, void *bus_id)
for (i = 0 ; i < completed_cmds; i++) {
post_index = post_index_next;
/* poison the reply post descriptor */
- ioc->reply_post_free[post_index_next].Words = ~0ULL;
+ ioc->reply_post_free[post_index_next].Words = ULLONG_MAX;
post_index_next = (post_index ==
(ioc->reply_post_queue_depth - 1))
? 0 : post_index + 1;
@@ -1387,6 +1396,64 @@ mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
}
/**
+ * _base_display_dell_branding - Disply branding string
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_dell_branding(struct MPT2SAS_ADAPTER *ioc)
+{
+ char dell_branding[MPT2SAS_DELL_BRANDING_SIZE];
+
+ if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_DELL)
+ return;
+
+ memset(dell_branding, 0, MPT2SAS_DELL_BRANDING_SIZE);
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID:
+ strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID:
+ strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID:
+ strncpy(dell_branding,
+ MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID:
+ strncpy(dell_branding,
+ MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID:
+ strncpy(dell_branding,
+ MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_PERC_H200_SSDID:
+ strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ case MPT2SAS_DELL_6GBPS_SAS_SSDID:
+ strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_BRANDING,
+ MPT2SAS_DELL_BRANDING_SIZE - 1);
+ break;
+ default:
+ sprintf(dell_branding, "0x%4X", ioc->pdev->subsystem_device);
+ break;
+ }
+
+ printk(MPT2SAS_INFO_FMT "%s: Vendor(0x%04X), Device(0x%04X),"
+ " SSVID(0x%04X), SSDID(0x%04X)\n", ioc->name, dell_branding,
+ ioc->pdev->vendor, ioc->pdev->device, ioc->pdev->subsystem_vendor,
+ ioc->pdev->subsystem_device);
+}
+
+/**
* _base_display_ioc_capabilities - Disply IOC's capabilities.
* @ioc: per adapter object
*
@@ -1427,6 +1494,8 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
i++;
}
+ _base_display_dell_branding(ioc);
+
i = 0;
printk("), ");
printk("Capabilities=(");
@@ -3068,7 +3137,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
/* initialize Reply Post Free Queue */
for (i = 0; i < ioc->reply_post_queue_depth; i++)
- ioc->reply_post_free[i].Words = ~0ULL;
+ ioc->reply_post_free[i].Words = ULLONG_MAX;
r = _base_send_ioc_init(ioc, VF_ID, sleep_flag);
if (r)
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index 6945ff4d382e..286c185fa9e4 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -61,6 +61,7 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_sas.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
#include "mpt2sas_debug.h"
@@ -68,11 +69,11 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "00.100.11.16"
-#define MPT2SAS_MAJOR_VERSION 00
+#define MPT2SAS_DRIVER_VERSION "01.100.03.00"
+#define MPT2SAS_MAJOR_VERSION 01
#define MPT2SAS_MINOR_VERSION 100
-#define MPT2SAS_BUILD_VERSION 11
-#define MPT2SAS_RELEASE_VERSION 16
+#define MPT2SAS_BUILD_VERSION 03
+#define MPT2SAS_RELEASE_VERSION 00
/*
* Set MPT2SAS_SG_DEPTH value based on user input.
@@ -130,6 +131,30 @@
#define MPT2SAS_ERR_FMT KERN_ERR MPT2SAS_FMT
/*
+ * Dell HBA branding
+ */
+#define MPT2SAS_DELL_BRANDING_SIZE 32
+
+#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA"
+#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter"
+#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated"
+#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular"
+#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded"
+#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200"
+#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS"
+
+/*
+ * Dell HBA SSDIDs
+ */
+#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C
+#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D
+#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E
+#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F
+#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20
+#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21
+#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22
+
+/*
* per target private data
*/
#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 2d4f85c9d7a1..14e473d1fa7b 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -64,6 +64,9 @@
static struct fasync_struct *async_queue;
static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);
+static int _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type,
+ u8 *issue_reset);
+
/**
* enum block_state - blocking state
* @NON_BLOCKING: non blocking
@@ -378,10 +381,22 @@ _ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp)
void
mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
{
+ int i;
+ u8 issue_reset;
+
switch (reset_phase) {
case MPT2_IOC_PRE_RESET:
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
"MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT2_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ _ctl_send_release(ioc, i, &issue_reset);
+ }
break;
case MPT2_IOC_AFTER_RESET:
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
@@ -395,6 +410,17 @@ mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
case MPT2_IOC_DONE_RESET:
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
"MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT2_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ ioc->diag_buffer_status[i] |=
+ MPT2_DIAG_BUFFER_IS_DIAG_RESET;
+ }
break;
}
}
@@ -447,7 +473,7 @@ _ctl_poll(struct file *filep, poll_table *wait)
}
/**
- * _ctl_do_task_abort - assign an active smid to the abort_task
+ * _ctl_set_task_mid - assign an active smid to tm request
* @ioc: per adapter object
* @karg - (struct mpt2_ioctl_command)
* @tm_request - pointer to mf from user space
@@ -456,7 +482,7 @@ _ctl_poll(struct file *filep, poll_table *wait)
* during failure, the reply frame is filled.
*/
static int
-_ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
+_ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
Mpi2SCSITaskManagementRequest_t *tm_request)
{
u8 found = 0;
@@ -468,6 +494,14 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
Mpi2SCSITaskManagementReply_t *tm_reply;
u32 sz;
u32 lun;
+ char *desc = NULL;
+
+ if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+ desc = "abort_task";
+ else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ desc = "query_task";
+ else
+ return 0;
lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
@@ -491,13 +525,13 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
if (!found) {
- dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
- "DevHandle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
- tm_request->DevHandle, lun));
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
+ desc, tm_request->DevHandle, lun));
tm_reply = ioc->ctl_cmds.reply;
tm_reply->DevHandle = tm_request->DevHandle;
tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- tm_reply->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+ tm_reply->TaskType = tm_request->TaskType;
tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
tm_reply->VP_ID = tm_request->VP_ID;
tm_reply->VF_ID = tm_request->VF_ID;
@@ -509,9 +543,9 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
return 1;
}
- dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
- "DevHandle(0x%04x), lun(%d), smid(%d)\n", ioc->name,
- tm_request->DevHandle, lun, tm_request->TaskMID));
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name,
+ desc, tm_request->DevHandle, lun, tm_request->TaskMID));
return 0;
}
@@ -713,9 +747,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
(Mpi2SCSITaskManagementRequest_t *)mpi_request;
if (tm_request->TaskType ==
- MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
- if (_ctl_do_task_abort(ioc, &karg, tm_request))
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
+ tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+ if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
+ mpt2sas_base_free_smid(ioc, smid);
goto out;
+ }
}
mutex_lock(&ioc->tm_cmds.mutex);
@@ -915,9 +953,9 @@ _ctl_getiocinfo(void __user *arg)
karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
karg.firmware_version = ioc->facts.FWVersion.Word;
- strncpy(karg.driver_version, MPT2SAS_DRIVER_VERSION,
- MPT2_IOCTL_VERSION_LENGTH);
- karg.driver_version[MPT2_IOCTL_VERSION_LENGTH - 1] = '\0';
+ strcpy(karg.driver_version, MPT2SAS_DRIVER_NAME);
+ strcat(karg.driver_version, "-");
+ strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION);
karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
if (copy_to_user(arg, &karg, sizeof(karg))) {
@@ -1551,81 +1589,38 @@ _ctl_diag_query(void __user *arg)
}
/**
- * _ctl_diag_release - request to send Diag Release Message to firmware
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
+ * _ctl_send_release - Diag Release Message
+ * @ioc: per adapter object
+ * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @issue_reset - specifies whether host reset is required.
*
- * This allows ownership of the specified buffer to returned to the driver,
- * allowing an application to read the buffer without fear that firmware is
- * overwritting information in the buffer.
*/
-static long
-_ctl_diag_release(void __user *arg, enum block_state state)
+static int
+_ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset)
{
- struct mpt2_diag_release karg;
- struct MPT2SAS_ADAPTER *ioc;
- void *request_data;
- int rc;
Mpi2DiagReleaseRequest_t *mpi_request;
Mpi2DiagReleaseReply_t *mpi_reply;
- u8 buffer_type;
- unsigned long timeleft;
u16 smid;
u16 ioc_status;
- u8 issue_reset = 0;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
+ u32 ioc_state;
+ int rc;
+ unsigned long timeleft;
dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
- buffer_type = karg.unique_id & 0x000000ff;
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
- "registered\n", ioc->name, __func__, buffer_type);
- return -EINVAL;
- }
-
- if (karg.unique_id != ioc->unique_id[buffer_type]) {
- printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
- "registered\n", ioc->name, __func__, karg.unique_id);
- return -EINVAL;
- }
-
- if (ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_RELEASED) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
- "is already released\n", ioc->name, __func__,
- buffer_type);
- return 0;
- }
-
- request_data = ioc->diag_buffer[buffer_type];
+ rc = 0;
+ *issue_reset = 0;
- if (!request_data) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -ENOMEM;
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "skipping due to FAULT state\n", ioc->name,
+ __func__));
+ rc = -EAGAIN;
+ goto out;
}
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
ioc->name, __func__);
@@ -1641,7 +1636,6 @@ _ctl_diag_release(void __user *arg, enum block_state state)
goto out;
}
- rc = 0;
ioc->ctl_cmds.status = MPT2_CMD_PENDING;
memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
@@ -1660,8 +1654,9 @@ _ctl_diag_release(void __user *arg, enum block_state state)
_debug_dump_mf(mpi_request,
sizeof(Mpi2DiagReleaseRequest_t)/4);
if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
+ *issue_reset = 1;
+ rc = -EFAULT;
+ goto out;
}
/* process the completed Reply Message Frame */
@@ -1687,14 +1682,101 @@ _ctl_diag_release(void __user *arg, enum block_state state)
rc = -EFAULT;
}
- issue_host_reset:
+ out:
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * _ctl_diag_release - request to send Diag Release Message to firmware
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwritting information in the buffer.
+ */
+static long
+_ctl_diag_release(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_release karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ void *request_data;
+ int rc;
+ u8 buffer_type;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
+ "registered\n", ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
+ "registered\n", ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_RELEASED) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
+ "is already released\n", ioc->name, __func__,
+ buffer_type);
+ return 0;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+
+ if (!request_data) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ /* buffers were released by due to host reset */
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_DIAG_RESET)) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT2_DIAG_BUFFER_IS_RELEASED;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT2_DIAG_BUFFER_IS_DIAG_RESET;
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
+ "was released due to host reset\n", ioc->name, __func__,
+ buffer_type);
+ return 0;
+ }
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+ rc = _ctl_send_release(ioc, buffer_type, &issue_reset);
+
if (issue_reset)
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
FORCE_BIG_HAMMER);
- out:
-
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
mutex_unlock(&ioc->ctl_cmds.mutex);
return rc;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
index dbb6c0cf8889..4da11435533f 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
@@ -50,7 +50,7 @@
#endif
#define MPT2SAS_DEV_NAME "mpt2ctl"
-#define MPT2_MAGIC_NUMBER 'm'
+#define MPT2_MAGIC_NUMBER 'L'
#define MPT2_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */
/**
@@ -295,8 +295,9 @@ struct mpt2_ioctl_btdh_mapping {
/* status bits for ioc->diag_buffer_status */
-#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01)
-#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02)
+#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01)
+#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02)
+#define MPT2_DIAG_BUFFER_IS_DIAG_RESET (0x04)
/* application flags for mpt2_diag_register, mpt2_diag_query */
#define MPT2_APP_FLAGS_APP_OWNED (0x0001)
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 0c463c483c02..2a01a5f2a84d 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -119,7 +119,7 @@ struct sense_info {
*/
struct fw_event_work {
struct list_head list;
- struct delayed_work work;
+ struct work_struct work;
struct MPT2SAS_ADAPTER *ioc;
u8 VF_ID;
u8 host_reset_handling;
@@ -197,12 +197,12 @@ static struct pci_device_id scsih_pci_table[] = {
MODULE_DEVICE_TABLE(pci, scsih_pci_table);
/**
- * scsih_set_debug_level - global setting of ioc->logging_level.
+ * _scsih_set_debug_level - global setting of ioc->logging_level.
*
* Note: The logging levels are defined in mpt2sas_debug.h.
*/
static int
-scsih_set_debug_level(const char *val, struct kernel_param *kp)
+_scsih_set_debug_level(const char *val, struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
struct MPT2SAS_ADAPTER *ioc;
@@ -215,7 +215,7 @@ scsih_set_debug_level(const char *val, struct kernel_param *kp)
ioc->logging_level = logging_level;
return 0;
}
-module_param_call(logging_level, scsih_set_debug_level, param_get_int,
+module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
&logging_level, 0644);
/**
@@ -516,12 +516,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
handle = sas_device->handle;
parent_handle = sas_device->parent_handle;
sas_address = sas_device->sas_address;
- if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+ if (!mpt2sas_transport_port_add(ioc, handle, parent_handle))
_scsih_sas_device_remove(ioc, sas_device);
- } else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc, sas_address, parent_handle);
- _scsih_sas_device_remove(ioc, sas_device);
- }
}
/**
@@ -888,6 +884,41 @@ _scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id,
}
/**
+ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
+ * @ioc: per adapter object
+ * @id: target id
+ * @lun: lun number
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id:lun in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id,
+ unsigned int lun, int channel)
+{
+ u8 found;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ found = 0;
+ for (i = 0 ; i < ioc->request_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd &&
+ (ioc->scsi_lookup[i].scmd->device->id == id &&
+ ioc->scsi_lookup[i].scmd->device->channel == channel &&
+ ioc->scsi_lookup[i].scmd->device->lun == lun)) {
+ found = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return found;
+}
+
+/**
* _scsih_get_chain_buffer_dma - obtain block of chains (dma address)
* @ioc: per adapter object
* @smid: system request message index
@@ -1051,14 +1082,14 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * scsih_change_queue_depth - setting device queue depth
+ * _scsih_change_queue_depth - setting device queue depth
* @sdev: scsi device struct
* @qdepth: requested queue depth
*
* Returns queue depth.
*/
static int
-scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct Scsi_Host *shost = sdev->host;
int max_depth;
@@ -1083,14 +1114,14 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
}
/**
- * scsih_change_queue_depth - changing device queue tag type
+ * _scsih_change_queue_depth - changing device queue tag type
* @sdev: scsi device struct
* @tag_type: requested tag type
*
* Returns queue tag type.
*/
static int
-scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
+_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
{
if (sdev->tagged_supported) {
scsi_set_tag_type(sdev, tag_type);
@@ -1105,14 +1136,14 @@ scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
}
/**
- * scsih_target_alloc - target add routine
+ * _scsih_target_alloc - target add routine
* @starget: scsi target struct
*
* Returns 0 if ok. Any other return is assumed to be an error and
* the device is ignored.
*/
static int
-scsih_target_alloc(struct scsi_target *starget)
+_scsih_target_alloc(struct scsi_target *starget)
{
struct Scsi_Host *shost = dev_to_shost(&starget->dev);
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1167,13 +1198,13 @@ scsih_target_alloc(struct scsi_target *starget)
}
/**
- * scsih_target_destroy - target destroy routine
+ * _scsih_target_destroy - target destroy routine
* @starget: scsi target struct
*
* Returns nothing.
*/
static void
-scsih_target_destroy(struct scsi_target *starget)
+_scsih_target_destroy(struct scsi_target *starget)
{
struct Scsi_Host *shost = dev_to_shost(&starget->dev);
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1203,7 +1234,9 @@ scsih_target_destroy(struct scsi_target *starget)
rphy = dev_to_rphy(starget->dev.parent);
sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
rphy->identify.sas_address);
- if (sas_device)
+ if (sas_device && (sas_device->starget == starget) &&
+ (sas_device->id == starget->id) &&
+ (sas_device->channel == starget->channel))
sas_device->starget = NULL;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -1214,14 +1247,14 @@ scsih_target_destroy(struct scsi_target *starget)
}
/**
- * scsih_slave_alloc - device add routine
+ * _scsih_slave_alloc - device add routine
* @sdev: scsi device struct
*
* Returns 0 if ok. Any other return is assumed to be an error and
* the device is ignored.
*/
static int
-scsih_slave_alloc(struct scsi_device *sdev)
+_scsih_slave_alloc(struct scsi_device *sdev)
{
struct Scsi_Host *shost;
struct MPT2SAS_ADAPTER *ioc;
@@ -1275,13 +1308,13 @@ scsih_slave_alloc(struct scsi_device *sdev)
}
/**
- * scsih_slave_destroy - device destroy routine
+ * _scsih_slave_destroy - device destroy routine
* @sdev: scsi device struct
*
* Returns nothing.
*/
static void
-scsih_slave_destroy(struct scsi_device *sdev)
+_scsih_slave_destroy(struct scsi_device *sdev)
{
struct MPT2SAS_TARGET *sas_target_priv_data;
struct scsi_target *starget;
@@ -1297,13 +1330,13 @@ scsih_slave_destroy(struct scsi_device *sdev)
}
/**
- * scsih_display_sata_capabilities - sata capabilities
+ * _scsih_display_sata_capabilities - sata capabilities
* @ioc: per adapter object
* @sas_device: the sas_device object
* @sdev: scsi device struct
*/
static void
-scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
+_scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
struct _sas_device *sas_device, struct scsi_device *sdev)
{
Mpi2ConfigReply_t mpi_reply;
@@ -1403,14 +1436,14 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * scsih_slave_configure - device configure routine.
+ * _scsih_slave_configure - device configure routine.
* @sdev: scsi device struct
*
* Returns 0 if ok. Any other return is assumed to be an error and
* the device is ignored.
*/
static int
-scsih_slave_configure(struct scsi_device *sdev)
+_scsih_slave_configure(struct scsi_device *sdev)
{
struct Scsi_Host *shost = sdev->host;
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1491,7 +1524,7 @@ scsih_slave_configure(struct scsi_device *sdev)
r_level, raid_device->handle,
(unsigned long long)raid_device->wwid,
raid_device->num_pds, ds);
- scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth);
return 0;
}
@@ -1534,10 +1567,10 @@ scsih_slave_configure(struct scsi_device *sdev)
sas_device->slot);
if (!ssp_target)
- scsih_display_sata_capabilities(ioc, sas_device, sdev);
+ _scsih_display_sata_capabilities(ioc, sas_device, sdev);
}
- scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth);
if (ssp_target)
sas_read_port_mode_page(sdev);
@@ -1545,7 +1578,7 @@ scsih_slave_configure(struct scsi_device *sdev)
}
/**
- * scsih_bios_param - fetch head, sector, cylinder info for a disk
+ * _scsih_bios_param - fetch head, sector, cylinder info for a disk
* @sdev: scsi device struct
* @bdev: pointer to block device context
* @capacity: device size (in 512 byte sectors)
@@ -1557,7 +1590,7 @@ scsih_slave_configure(struct scsi_device *sdev)
* Return nothing.
*/
static int
-scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int params[])
{
int heads;
@@ -1638,7 +1671,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
}
/**
- * scsih_tm_done - tm completion routine
+ * _scsih_tm_done - tm completion routine
* @ioc: per adapter object
* @smid: system request message index
* @VF_ID: virtual function id
@@ -1650,7 +1683,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
* Return nothing.
*/
static void
-scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
{
MPI2DefaultReply_t *mpi_reply;
@@ -1825,13 +1858,13 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
}
/**
- * scsih_abort - eh threads main abort routine
+ * _scsih_abort - eh threads main abort routine
* @sdev: scsi device struct
*
* Returns SUCCESS if command aborted else FAILED
*/
static int
-scsih_abort(struct scsi_cmnd *scmd)
+_scsih_abort(struct scsi_cmnd *scmd)
{
struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -1891,15 +1924,86 @@ scsih_abort(struct scsi_cmnd *scmd)
return r;
}
+/**
+ * _scsih_dev_reset - eh threads main device reset routine
+ * @sdev: scsi device struct
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle;
+ int r;
+
+ printk(MPT2SAS_INFO_FMT "attempting device reset! scmd(%p)\n",
+ ioc->name, scmd);
+ scsi_print_command(scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+ ioc->name, scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc,
+ sas_device_priv_data->sas_target->handle);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_issue_tm(ioc, handle, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun,
+ 30);
+
+ /*
+ * sanity check see whether all commands to this device been
+ * completed
+ */
+ if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id,
+ scmd->device->lun, scmd->device->channel))
+ r = FAILED;
+ else
+ r = SUCCESS;
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ out:
+ printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n",
+ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
/**
- * scsih_dev_reset - eh threads main device reset routine
+ * _scsih_target_reset - eh threads main target reset routine
* @sdev: scsi device struct
*
* Returns SUCCESS if command aborted else FAILED
*/
static int
-scsih_dev_reset(struct scsi_cmnd *scmd)
+_scsih_target_reset(struct scsi_cmnd *scmd)
{
struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -1914,7 +2018,7 @@ scsih_dev_reset(struct scsi_cmnd *scmd)
sas_device_priv_data = scmd->device->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
- printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+ printk(MPT2SAS_INFO_FMT "target been deleted! scmd(%p)\n",
ioc->name, scmd);
scmd->result = DID_NO_CONNECT << 16;
scmd->scsi_done(scmd);
@@ -1964,13 +2068,13 @@ scsih_dev_reset(struct scsi_cmnd *scmd)
}
/**
- * scsih_abort - eh threads main host reset routine
+ * _scsih_abort - eh threads main host reset routine
* @sdev: scsi device struct
*
* Returns SUCCESS if command aborted else FAILED
*/
static int
-scsih_host_reset(struct scsi_cmnd *scmd)
+_scsih_host_reset(struct scsi_cmnd *scmd)
{
struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
int r, retval;
@@ -2009,8 +2113,8 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
spin_lock_irqsave(&ioc->fw_event_lock, flags);
list_add_tail(&fw_event->list, &ioc->fw_event_list);
- INIT_DELAYED_WORK(&fw_event->work, _firmware_event_work);
- queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, 1);
+ INIT_WORK(&fw_event->work, _firmware_event_work);
+ queue_work(ioc->firmware_event_thread, &fw_event->work);
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
@@ -2054,7 +2158,7 @@ _scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
return;
spin_lock_irqsave(&ioc->fw_event_lock, flags);
- queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, delay);
+ queue_work(ioc->firmware_event_thread, &fw_event->work);
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
}
@@ -2392,7 +2496,107 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
}
/**
- * scsih_qcmd - main scsi request entry point
+ * _scsih_setup_eedp - setup MPI request for EEDP transfer
+ * @scmd: pointer to scsi command object
+ * @mpi_request: pointer to the SCSI_IO reqest message frame
+ *
+ * Supporting protection 1 and 3.
+ *
+ * Returns nothing
+ */
+static void
+_scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
+{
+ u16 eedp_flags;
+ unsigned char prot_op = scsi_get_prot_op(scmd);
+ unsigned char prot_type = scsi_get_prot_type(scmd);
+
+ if (prot_type == SCSI_PROT_DIF_TYPE0 ||
+ prot_type == SCSI_PROT_DIF_TYPE2 ||
+ prot_op == SCSI_PROT_NORMAL)
+ return;
+
+ if (prot_op == SCSI_PROT_READ_STRIP)
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP;
+ else if (prot_op == SCSI_PROT_WRITE_INSERT)
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
+ else
+ return;
+
+ mpi_request->EEDPBlockSize = scmd->device->sector_size;
+
+ switch (prot_type) {
+ case SCSI_PROT_DIF_TYPE1:
+
+ /*
+ * enable ref/guard checking
+ * auto increment ref tag
+ */
+ mpi_request->EEDPFlags = eedp_flags |
+ MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+ mpi_request->CDB.EEDP32.PrimaryReferenceTag =
+ cpu_to_be32(scsi_get_lba(scmd));
+
+ break;
+
+ case SCSI_PROT_DIF_TYPE3:
+
+ /*
+ * enable guard checking
+ */
+ mpi_request->EEDPFlags = eedp_flags |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+
+ break;
+ }
+}
+
+/**
+ * _scsih_eedp_error_handling - return sense code for EEDP errors
+ * @scmd: pointer to scsi command object
+ * @ioc_status: ioc status
+ *
+ * Returns nothing
+ */
+static void
+_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
+{
+ u8 ascq;
+ u8 sk;
+ u8 host_byte;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ ascq = 0x01;
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ ascq = 0x02;
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ ascq = 0x03;
+ break;
+ default:
+ ascq = 0x00;
+ break;
+ }
+
+ if (scmd->sc_data_direction == DMA_TO_DEVICE) {
+ sk = ILLEGAL_REQUEST;
+ host_byte = DID_ABORT;
+ } else {
+ sk = ABORTED_COMMAND;
+ host_byte = DID_OK;
+ }
+
+ scsi_build_sense_buffer(0, scmd->sense_buffer, sk, 0x10, ascq);
+ scmd->result = DRIVER_SENSE << 24 | (host_byte << 16) |
+ SAM_STAT_CHECK_CONDITION;
+}
+
+/**
+ * _scsih_qcmd - main scsi request entry point
* @scmd: pointer to scsi command object
* @done: function pointer to be invoked on completion
*
@@ -2403,7 +2607,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
* SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
*/
static int
-scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
+_scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
{
struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -2472,6 +2676,7 @@ scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
}
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t));
+ _scsih_setup_eedp(scmd, mpi_request);
mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
if (sas_device_priv_data->sas_target->flags &
MPT_TARGET_FLAGS_RAID_COMPONENT)
@@ -2606,6 +2811,15 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
desc_ioc_state = "scsi ext terminated";
break;
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc_ioc_state = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc_ioc_state = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc_ioc_state = "eedp app tag error";
+ break;
default:
desc_ioc_state = "unknown";
break;
@@ -2785,7 +2999,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
}
/**
- * scsih_io_done - scsi request callback
+ * _scsih_io_done - scsi request callback
* @ioc: per adapter object
* @smid: system request message index
* @VF_ID: virtual function id
@@ -2796,7 +3010,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
* Return nothing.
*/
static void
-scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
{
Mpi2SCSIIORequest_t *mpi_request;
Mpi2SCSIIOReply_t *mpi_reply;
@@ -2863,8 +3077,9 @@ scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
struct sense_info data;
const void *sense_data = mpt2sas_base_get_sense_buffer(ioc,
smid);
- memcpy(scmd->sense_buffer, sense_data,
+ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
le32_to_cpu(mpi_reply->SenseCount));
+ memcpy(scmd->sense_buffer, sense_data, sz);
_scsih_normalize_sense(scmd->sense_buffer, &data);
/* failure prediction threshold exceeded */
if (data.asc == 0x5D)
@@ -2940,6 +3155,11 @@ scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
scmd->result = DID_RESET << 16;
break;
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ _scsih_eedp_error_handling(scmd, ioc_status);
+ break;
case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
case MPI2_IOCSTATUS_INVALID_FUNCTION:
case MPI2_IOCSTATUS_INVALID_SGL:
@@ -3923,7 +4143,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
mpt2sas_scsih_issue_tm(ioc, handle, lun,
MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
- termination_count += le32_to_cpu(mpi_reply->TerminationCount);
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
(mpi_reply->ResponseCode ==
@@ -3933,10 +4153,10 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
continue;
mpt2sas_scsih_issue_tm(ioc, handle, lun,
- MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, smid, 30);
+ MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30);
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
termination_count += le32_to_cpu(mpi_reply->TerminationCount);
}
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
ioc->broadcast_aen_busy = 0;
mutex_unlock(&ioc->tm_cmds.mutex);
@@ -4962,7 +5182,7 @@ static void
_firmware_event_work(struct work_struct *work)
{
struct fw_event_work *fw_event = container_of(work,
- struct fw_event_work, work.work);
+ struct fw_event_work, work);
unsigned long flags;
struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
@@ -5131,18 +5351,19 @@ static struct scsi_host_template scsih_driver_template = {
.module = THIS_MODULE,
.name = "Fusion MPT SAS Host",
.proc_name = MPT2SAS_DRIVER_NAME,
- .queuecommand = scsih_qcmd,
- .target_alloc = scsih_target_alloc,
- .slave_alloc = scsih_slave_alloc,
- .slave_configure = scsih_slave_configure,
- .target_destroy = scsih_target_destroy,
- .slave_destroy = scsih_slave_destroy,
- .change_queue_depth = scsih_change_queue_depth,
- .change_queue_type = scsih_change_queue_type,
- .eh_abort_handler = scsih_abort,
- .eh_device_reset_handler = scsih_dev_reset,
- .eh_host_reset_handler = scsih_host_reset,
- .bios_param = scsih_bios_param,
+ .queuecommand = _scsih_qcmd,
+ .target_alloc = _scsih_target_alloc,
+ .slave_alloc = _scsih_slave_alloc,
+ .slave_configure = _scsih_slave_configure,
+ .target_destroy = _scsih_target_destroy,
+ .slave_destroy = _scsih_slave_destroy,
+ .change_queue_depth = _scsih_change_queue_depth,
+ .change_queue_type = _scsih_change_queue_type,
+ .eh_abort_handler = _scsih_abort,
+ .eh_device_reset_handler = _scsih_dev_reset,
+ .eh_target_reset_handler = _scsih_target_reset,
+ .eh_host_reset_handler = _scsih_host_reset,
+ .bios_param = _scsih_bios_param,
.can_queue = 1,
.this_id = -1,
.sg_tablesize = MPT2SAS_SG_DEPTH,
@@ -5229,13 +5450,13 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * scsih_remove - detach and remove add host
+ * _scsih_remove - detach and remove add host
* @pdev: PCI device struct
*
* Return nothing.
*/
static void __devexit
-scsih_remove(struct pci_dev *pdev)
+_scsih_remove(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5443,14 +5664,14 @@ _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
}
/**
- * scsih_probe - attach and add scsi host
+ * _scsih_probe - attach and add scsi host
* @pdev: PCI device struct
* @id: pci device id
*
* Returns 0 success, anything else error.
*/
static int
-scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct MPT2SAS_ADAPTER *ioc;
struct Scsi_Host *shost;
@@ -5504,6 +5725,9 @@ scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_add_shost_fail;
}
+ scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION);
+
/* event thread */
snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
"fw_event%d", ioc->id);
@@ -5537,14 +5761,14 @@ scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
#ifdef CONFIG_PM
/**
- * scsih_suspend - power management suspend main entry point
+ * _scsih_suspend - power management suspend main entry point
* @pdev: PCI device struct
* @state: PM state change to (usually PCI_D3)
*
* Returns 0 success, anything else error.
*/
static int
-scsih_suspend(struct pci_dev *pdev, pm_message_t state)
+_scsih_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5565,13 +5789,13 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state)
}
/**
- * scsih_resume - power management resume main entry point
+ * _scsih_resume - power management resume main entry point
* @pdev: PCI device struct
*
* Returns 0 success, anything else error.
*/
static int
-scsih_resume(struct pci_dev *pdev)
+_scsih_resume(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5600,22 +5824,22 @@ scsih_resume(struct pci_dev *pdev)
static struct pci_driver scsih_driver = {
.name = MPT2SAS_DRIVER_NAME,
.id_table = scsih_pci_table,
- .probe = scsih_probe,
- .remove = __devexit_p(scsih_remove),
+ .probe = _scsih_probe,
+ .remove = __devexit_p(_scsih_remove),
#ifdef CONFIG_PM
- .suspend = scsih_suspend,
- .resume = scsih_resume,
+ .suspend = _scsih_suspend,
+ .resume = _scsih_resume,
#endif
};
/**
- * scsih_init - main entry point for this driver.
+ * _scsih_init - main entry point for this driver.
*
* Returns 0 success, anything else error.
*/
static int __init
-scsih_init(void)
+_scsih_init(void)
{
int error;
@@ -5631,10 +5855,10 @@ scsih_init(void)
mpt2sas_base_initialize_callback_handler();
/* queuecommand callback hander */
- scsi_io_cb_idx = mpt2sas_base_register_callback_handler(scsih_io_done);
+ scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done);
/* task managment callback handler */
- tm_cb_idx = mpt2sas_base_register_callback_handler(scsih_tm_done);
+ tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done);
/* base internal commands callback handler */
base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done);
@@ -5660,12 +5884,12 @@ scsih_init(void)
}
/**
- * scsih_exit - exit point for this driver (when it is a module).
+ * _scsih_exit - exit point for this driver (when it is a module).
*
* Returns 0 success, anything else error.
*/
static void __exit
-scsih_exit(void)
+_scsih_exit(void)
{
printk(KERN_INFO "mpt2sas version %s unloading\n",
MPT2SAS_DRIVER_VERSION);
@@ -5683,5 +5907,5 @@ scsih_exit(void)
mpt2sas_ctl_exit();
}
-module_init(scsih_init);
-module_exit(scsih_exit);
+module_init(_scsih_init);
+module_exit(_scsih_exit);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index af95a449930e..686695b155c7 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -264,7 +264,7 @@ struct rep_manu_reply{
};
/**
- * transport_expander_report_manufacture - obtain SMP report_manufacture
+ * _transport_expander_report_manufacture - obtain SMP report_manufacture
* @ioc: per adapter object
* @sas_address: expander sas address
* @edev: the sas_expander_device object
@@ -274,7 +274,7 @@ struct rep_manu_reply{
* Returns 0 for success, non-zero for failure.
*/
static int
-transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
+_transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
u64 sas_address, struct sas_expander_device *edev)
{
Mpi2SmpPassthroughRequest_t *mpi_request;
@@ -578,7 +578,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
mpt2sas_port->remote_identify.device_type ==
MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
- transport_expander_report_manufacture(ioc,
+ _transport_expander_report_manufacture(ioc,
mpt2sas_port->remote_identify.sas_address,
rphy_to_expander_device(rphy));
@@ -852,7 +852,7 @@ rphy_to_ioc(struct sas_rphy *rphy)
}
/**
- * transport_get_linkerrors -
+ * _transport_get_linkerrors -
* @phy: The sas phy object
*
* Only support sas_host direct attached phys.
@@ -860,7 +860,7 @@ rphy_to_ioc(struct sas_rphy *rphy)
*
*/
static int
-transport_get_linkerrors(struct sas_phy *phy)
+_transport_get_linkerrors(struct sas_phy *phy)
{
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
struct _sas_phy *mpt2sas_phy;
@@ -903,14 +903,14 @@ transport_get_linkerrors(struct sas_phy *phy)
}
/**
- * transport_get_enclosure_identifier -
+ * _transport_get_enclosure_identifier -
* @phy: The sas phy object
*
* Obtain the enclosure logical id for an expander.
* Returns 0 for success, non-zero for failure.
*/
static int
-transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
{
struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
struct _sas_node *sas_expander;
@@ -929,13 +929,13 @@ transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
}
/**
- * transport_get_bay_identifier -
+ * _transport_get_bay_identifier -
* @phy: The sas phy object
*
* Returns the slot id for a device that resides inside an enclosure.
*/
static int
-transport_get_bay_identifier(struct sas_rphy *rphy)
+_transport_get_bay_identifier(struct sas_rphy *rphy)
{
struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
struct _sas_device *sas_device;
@@ -953,7 +953,7 @@ transport_get_bay_identifier(struct sas_rphy *rphy)
}
/**
- * transport_phy_reset -
+ * _transport_phy_reset -
* @phy: The sas phy object
* @hard_reset:
*
@@ -961,7 +961,7 @@ transport_get_bay_identifier(struct sas_rphy *rphy)
* Returns 0 for success, non-zero for failure.
*/
static int
-transport_phy_reset(struct sas_phy *phy, int hard_reset)
+_transport_phy_reset(struct sas_phy *phy, int hard_reset)
{
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
struct _sas_phy *mpt2sas_phy;
@@ -1002,7 +1002,7 @@ transport_phy_reset(struct sas_phy *phy, int hard_reset)
}
/**
- * transport_smp_handler - transport portal for smp passthru
+ * _transport_smp_handler - transport portal for smp passthru
* @shost: shost object
* @rphy: sas transport rphy object
* @req:
@@ -1012,7 +1012,7 @@ transport_phy_reset(struct sas_phy *phy, int hard_reset)
* smp_rep_general /sys/class/bsg/expander-5:0
*/
static int
-transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
+_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
struct request *req)
{
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1170,8 +1170,8 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
memcpy(req->sense, mpi_reply, sizeof(*mpi_reply));
req->sense_len = sizeof(*mpi_reply);
- rsp->resid_len = blk_rq_bytes(rsp) -
- mpi_reply->ResponseDataLength;
+ req->resid_len = 0;
+ rsp->resid_len -= mpi_reply->ResponseDataLength;
} else {
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT
"%s - no reply\n", ioc->name, __func__));
@@ -1200,11 +1200,11 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
}
struct sas_function_template mpt2sas_transport_functions = {
- .get_linkerrors = transport_get_linkerrors,
- .get_enclosure_identifier = transport_get_enclosure_identifier,
- .get_bay_identifier = transport_get_bay_identifier,
- .phy_reset = transport_phy_reset,
- .smp_handler = transport_smp_handler,
+ .get_linkerrors = _transport_get_linkerrors,
+ .get_enclosure_identifier = _transport_get_enclosure_identifier,
+ .get_bay_identifier = _transport_get_bay_identifier,
+ .phy_reset = _transport_phy_reset,
+ .smp_handler = _transport_smp_handler,
};
struct scsi_transport_template *mpt2sas_transport_template;
diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c
deleted file mode 100644
index e4acebd10d1b..000000000000
--- a/drivers/scsi/mvsas.c
+++ /dev/null
@@ -1,3222 +0,0 @@
-/*
- mvsas.c - Marvell 88SE6440 SAS/SATA support
-
- Copyright 2007 Red Hat, Inc.
- Copyright 2008 Marvell. <kewei@marvell.com>
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2,
- or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public
- License along with this program; see the file COPYING. If not,
- write to the Free Software Foundation, 675 Mass Ave, Cambridge,
- MA 02139, USA.
-
- ---------------------------------------------------------------
-
- Random notes:
- * hardware supports controlling the endian-ness of data
- structures. this permits elimination of all the le32_to_cpu()
- and cpu_to_le32() conversions.
-
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/ctype.h>
-#include <scsi/libsas.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/sas_ata.h>
-#include <asm/io.h>
-
-#define DRV_NAME "mvsas"
-#define DRV_VERSION "0.5.2"
-#define _MV_DUMP 0
-#define MVS_DISABLE_NVRAM
-#define MVS_DISABLE_MSI
-
-#define mr32(reg) readl(regs + MVS_##reg)
-#define mw32(reg,val) writel((val), regs + MVS_##reg)
-#define mw32_f(reg,val) do { \
- writel((val), regs + MVS_##reg); \
- readl(regs + MVS_##reg); \
- } while (0)
-
-#define MVS_ID_NOT_MAPPED 0x7f
-#define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width)
-
-/* offset for D2H FIS in the Received FIS List Structure */
-#define SATA_RECEIVED_D2H_FIS(reg_set) \
- ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x40)
-#define SATA_RECEIVED_PIO_FIS(reg_set) \
- ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x20)
-#define UNASSOC_D2H_FIS(id) \
- ((void *) mvi->rx_fis + 0x100 * id)
-
-#define for_each_phy(__lseq_mask, __mc, __lseq, __rest) \
- for ((__mc) = (__lseq_mask), (__lseq) = 0; \
- (__mc) != 0 && __rest; \
- (++__lseq), (__mc) >>= 1)
-
-/* driver compile-time configuration */
-enum driver_configuration {
- MVS_TX_RING_SZ = 1024, /* TX ring size (12-bit) */
- MVS_RX_RING_SZ = 1024, /* RX ring size (12-bit) */
- /* software requires power-of-2
- ring size */
-
- MVS_SLOTS = 512, /* command slots */
- MVS_SLOT_BUF_SZ = 8192, /* cmd tbl + IU + status + PRD */
- MVS_SSP_CMD_SZ = 64, /* SSP command table buffer size */
- MVS_ATA_CMD_SZ = 96, /* SATA command table buffer size */
- MVS_OAF_SZ = 64, /* Open address frame buffer size */
-
- MVS_RX_FIS_COUNT = 17, /* Optional rx'd FISs (max 17) */
-
- MVS_QUEUE_SIZE = 30, /* Support Queue depth */
- MVS_CAN_QUEUE = MVS_SLOTS - 1, /* SCSI Queue depth */
-};
-
-/* unchangeable hardware details */
-enum hardware_details {
- MVS_MAX_PHYS = 8, /* max. possible phys */
- MVS_MAX_PORTS = 8, /* max. possible ports */
- MVS_RX_FISL_SZ = 0x400 + (MVS_RX_FIS_COUNT * 0x100),
-};
-
-/* peripheral registers (BAR2) */
-enum peripheral_registers {
- SPI_CTL = 0x10, /* EEPROM control */
- SPI_CMD = 0x14, /* EEPROM command */
- SPI_DATA = 0x18, /* EEPROM data */
-};
-
-enum peripheral_register_bits {
- TWSI_RDY = (1U << 7), /* EEPROM interface ready */
- TWSI_RD = (1U << 4), /* EEPROM read access */
-
- SPI_ADDR_MASK = 0x3ffff, /* bits 17:0 */
-};
-
-/* enhanced mode registers (BAR4) */
-enum hw_registers {
- MVS_GBL_CTL = 0x04, /* global control */
- MVS_GBL_INT_STAT = 0x08, /* global irq status */
- MVS_GBL_PI = 0x0C, /* ports implemented bitmask */
- MVS_GBL_PORT_TYPE = 0xa0, /* port type */
-
- MVS_CTL = 0x100, /* SAS/SATA port configuration */
- MVS_PCS = 0x104, /* SAS/SATA port control/status */
- MVS_CMD_LIST_LO = 0x108, /* cmd list addr */
- MVS_CMD_LIST_HI = 0x10C,
- MVS_RX_FIS_LO = 0x110, /* RX FIS list addr */
- MVS_RX_FIS_HI = 0x114,
-
- MVS_TX_CFG = 0x120, /* TX configuration */
- MVS_TX_LO = 0x124, /* TX (delivery) ring addr */
- MVS_TX_HI = 0x128,
-
- MVS_TX_PROD_IDX = 0x12C, /* TX producer pointer */
- MVS_TX_CONS_IDX = 0x130, /* TX consumer pointer (RO) */
- MVS_RX_CFG = 0x134, /* RX configuration */
- MVS_RX_LO = 0x138, /* RX (completion) ring addr */
- MVS_RX_HI = 0x13C,
- MVS_RX_CONS_IDX = 0x140, /* RX consumer pointer (RO) */
-
- MVS_INT_COAL = 0x148, /* Int coalescing config */
- MVS_INT_COAL_TMOUT = 0x14C, /* Int coalescing timeout */
- MVS_INT_STAT = 0x150, /* Central int status */
- MVS_INT_MASK = 0x154, /* Central int enable */
- MVS_INT_STAT_SRS = 0x158, /* SATA register set status */
- MVS_INT_MASK_SRS = 0x15C,
-
- /* ports 1-3 follow after this */
- MVS_P0_INT_STAT = 0x160, /* port0 interrupt status */
- MVS_P0_INT_MASK = 0x164, /* port0 interrupt mask */
- MVS_P4_INT_STAT = 0x200, /* Port 4 interrupt status */
- MVS_P4_INT_MASK = 0x204, /* Port 4 interrupt enable mask */
-
- /* ports 1-3 follow after this */
- MVS_P0_SER_CTLSTAT = 0x180, /* port0 serial control/status */
- MVS_P4_SER_CTLSTAT = 0x220, /* port4 serial control/status */
-
- MVS_CMD_ADDR = 0x1B8, /* Command register port (addr) */
- MVS_CMD_DATA = 0x1BC, /* Command register port (data) */
-
- /* ports 1-3 follow after this */
- MVS_P0_CFG_ADDR = 0x1C0, /* port0 phy register address */
- MVS_P0_CFG_DATA = 0x1C4, /* port0 phy register data */
- MVS_P4_CFG_ADDR = 0x230, /* Port 4 config address */
- MVS_P4_CFG_DATA = 0x234, /* Port 4 config data */
-
- /* ports 1-3 follow after this */
- MVS_P0_VSR_ADDR = 0x1E0, /* port0 VSR address */
- MVS_P0_VSR_DATA = 0x1E4, /* port0 VSR data */
- MVS_P4_VSR_ADDR = 0x250, /* port 4 VSR addr */
- MVS_P4_VSR_DATA = 0x254, /* port 4 VSR data */
-};
-
-enum hw_register_bits {
- /* MVS_GBL_CTL */
- INT_EN = (1U << 1), /* Global int enable */
- HBA_RST = (1U << 0), /* HBA reset */
-
- /* MVS_GBL_INT_STAT */
- INT_XOR = (1U << 4), /* XOR engine event */
- INT_SAS_SATA = (1U << 0), /* SAS/SATA event */
-
- /* MVS_GBL_PORT_TYPE */ /* shl for ports 1-3 */
- SATA_TARGET = (1U << 16), /* port0 SATA target enable */
- MODE_AUTO_DET_PORT7 = (1U << 15), /* port0 SAS/SATA autodetect */
- MODE_AUTO_DET_PORT6 = (1U << 14),
- MODE_AUTO_DET_PORT5 = (1U << 13),
- MODE_AUTO_DET_PORT4 = (1U << 12),
- MODE_AUTO_DET_PORT3 = (1U << 11),
- MODE_AUTO_DET_PORT2 = (1U << 10),
- MODE_AUTO_DET_PORT1 = (1U << 9),
- MODE_AUTO_DET_PORT0 = (1U << 8),
- MODE_AUTO_DET_EN = MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 |
- MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 |
- MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 |
- MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7,
- MODE_SAS_PORT7_MASK = (1U << 7), /* port0 SAS(1), SATA(0) mode */
- MODE_SAS_PORT6_MASK = (1U << 6),
- MODE_SAS_PORT5_MASK = (1U << 5),
- MODE_SAS_PORT4_MASK = (1U << 4),
- MODE_SAS_PORT3_MASK = (1U << 3),
- MODE_SAS_PORT2_MASK = (1U << 2),
- MODE_SAS_PORT1_MASK = (1U << 1),
- MODE_SAS_PORT0_MASK = (1U << 0),
- MODE_SAS_SATA = MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK |
- MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK |
- MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK |
- MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK,
-
- /* SAS_MODE value may be
- * dictated (in hw) by values
- * of SATA_TARGET & AUTO_DET
- */
-
- /* MVS_TX_CFG */
- TX_EN = (1U << 16), /* Enable TX */
- TX_RING_SZ_MASK = 0xfff, /* TX ring size, bits 11:0 */
-
- /* MVS_RX_CFG */
- RX_EN = (1U << 16), /* Enable RX */
- RX_RING_SZ_MASK = 0xfff, /* RX ring size, bits 11:0 */
-
- /* MVS_INT_COAL */
- COAL_EN = (1U << 16), /* Enable int coalescing */
-
- /* MVS_INT_STAT, MVS_INT_MASK */
- CINT_I2C = (1U << 31), /* I2C event */
- CINT_SW0 = (1U << 30), /* software event 0 */
- CINT_SW1 = (1U << 29), /* software event 1 */
- CINT_PRD_BC = (1U << 28), /* PRD BC err for read cmd */
- CINT_DMA_PCIE = (1U << 27), /* DMA to PCIE timeout */
- CINT_MEM = (1U << 26), /* int mem parity err */
- CINT_I2C_SLAVE = (1U << 25), /* slave I2C event */
- CINT_SRS = (1U << 3), /* SRS event */
- CINT_CI_STOP = (1U << 1), /* cmd issue stopped */
- CINT_DONE = (1U << 0), /* cmd completion */
-
- /* shl for ports 1-3 */
- CINT_PORT_STOPPED = (1U << 16), /* port0 stopped */
- CINT_PORT = (1U << 8), /* port0 event */
- CINT_PORT_MASK_OFFSET = 8,
- CINT_PORT_MASK = (0xFF << CINT_PORT_MASK_OFFSET),
-
- /* TX (delivery) ring bits */
- TXQ_CMD_SHIFT = 29,
- TXQ_CMD_SSP = 1, /* SSP protocol */
- TXQ_CMD_SMP = 2, /* SMP protocol */
- TXQ_CMD_STP = 3, /* STP/SATA protocol */
- TXQ_CMD_SSP_FREE_LIST = 4, /* add to SSP targ free list */
- TXQ_CMD_SLOT_RESET = 7, /* reset command slot */
- TXQ_MODE_I = (1U << 28), /* mode: 0=target,1=initiator */
- TXQ_PRIO_HI = (1U << 27), /* priority: 0=normal, 1=high */
- TXQ_SRS_SHIFT = 20, /* SATA register set */
- TXQ_SRS_MASK = 0x7f,
- TXQ_PHY_SHIFT = 12, /* PHY bitmap */
- TXQ_PHY_MASK = 0xff,
- TXQ_SLOT_MASK = 0xfff, /* slot number */
-
- /* RX (completion) ring bits */
- RXQ_GOOD = (1U << 23), /* Response good */
- RXQ_SLOT_RESET = (1U << 21), /* Slot reset complete */
- RXQ_CMD_RX = (1U << 20), /* target cmd received */
- RXQ_ATTN = (1U << 19), /* attention */
- RXQ_RSP = (1U << 18), /* response frame xfer'd */
- RXQ_ERR = (1U << 17), /* err info rec xfer'd */
- RXQ_DONE = (1U << 16), /* cmd complete */
- RXQ_SLOT_MASK = 0xfff, /* slot number */
-
- /* mvs_cmd_hdr bits */
- MCH_PRD_LEN_SHIFT = 16, /* 16-bit PRD table len */
- MCH_SSP_FR_TYPE_SHIFT = 13, /* SSP frame type */
-
- /* SSP initiator only */
- MCH_SSP_FR_CMD = 0x0, /* COMMAND frame */
-
- /* SSP initiator or target */
- MCH_SSP_FR_TASK = 0x1, /* TASK frame */
-
- /* SSP target only */
- MCH_SSP_FR_XFER_RDY = 0x4, /* XFER_RDY frame */
- MCH_SSP_FR_RESP = 0x5, /* RESPONSE frame */
- MCH_SSP_FR_READ = 0x6, /* Read DATA frame(s) */
- MCH_SSP_FR_READ_RESP = 0x7, /* ditto, plus RESPONSE */
-
- MCH_PASSTHRU = (1U << 12), /* pass-through (SSP) */
- MCH_FBURST = (1U << 11), /* first burst (SSP) */
- MCH_CHK_LEN = (1U << 10), /* chk xfer len (SSP) */
- MCH_RETRY = (1U << 9), /* tport layer retry (SSP) */
- MCH_PROTECTION = (1U << 8), /* protection info rec (SSP) */
- MCH_RESET = (1U << 7), /* Reset (STP/SATA) */
- MCH_FPDMA = (1U << 6), /* First party DMA (STP/SATA) */
- MCH_ATAPI = (1U << 5), /* ATAPI (STP/SATA) */
- MCH_BIST = (1U << 4), /* BIST activate (STP/SATA) */
- MCH_PMP_MASK = 0xf, /* PMP from cmd FIS (STP/SATA)*/
-
- CCTL_RST = (1U << 5), /* port logic reset */
-
- /* 0(LSB first), 1(MSB first) */
- CCTL_ENDIAN_DATA = (1U << 3), /* PRD data */
- CCTL_ENDIAN_RSP = (1U << 2), /* response frame */
- CCTL_ENDIAN_OPEN = (1U << 1), /* open address frame */
- CCTL_ENDIAN_CMD = (1U << 0), /* command table */
-
- /* MVS_Px_SER_CTLSTAT (per-phy control) */
- PHY_SSP_RST = (1U << 3), /* reset SSP link layer */
- PHY_BCAST_CHG = (1U << 2), /* broadcast(change) notif */
- PHY_RST_HARD = (1U << 1), /* hard reset + phy reset */
- PHY_RST = (1U << 0), /* phy reset */
- PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0xF << 8),
- PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0xF << 12),
- PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
- PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
- (0xF << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
- PHY_READY_MASK = (1U << 20),
-
- /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
- PHYEV_DEC_ERR = (1U << 24), /* Phy Decoding Error */
- PHYEV_UNASSOC_FIS = (1U << 19), /* unassociated FIS rx'd */
- PHYEV_AN = (1U << 18), /* SATA async notification */
- PHYEV_BIST_ACT = (1U << 17), /* BIST activate FIS */
- PHYEV_SIG_FIS = (1U << 16), /* signature FIS */
- PHYEV_POOF = (1U << 12), /* phy ready from 1 -> 0 */
- PHYEV_IU_BIG = (1U << 11), /* IU too long err */
- PHYEV_IU_SMALL = (1U << 10), /* IU too short err */
- PHYEV_UNK_TAG = (1U << 9), /* unknown tag */
- PHYEV_BROAD_CH = (1U << 8), /* broadcast(CHANGE) */
- PHYEV_COMWAKE = (1U << 7), /* COMWAKE rx'd */
- PHYEV_PORT_SEL = (1U << 6), /* port selector present */
- PHYEV_HARD_RST = (1U << 5), /* hard reset rx'd */
- PHYEV_ID_TMOUT = (1U << 4), /* identify timeout */
- PHYEV_ID_FAIL = (1U << 3), /* identify failed */
- PHYEV_ID_DONE = (1U << 2), /* identify done */
- PHYEV_HARD_RST_DONE = (1U << 1), /* hard reset done */
- PHYEV_RDY_CH = (1U << 0), /* phy ready changed state */
-
- /* MVS_PCS */
- PCS_EN_SATA_REG_SHIFT = (16), /* Enable SATA Register Set */
- PCS_EN_PORT_XMT_SHIFT = (12), /* Enable Port Transmit */
- PCS_EN_PORT_XMT_SHIFT2 = (8), /* For 6480 */
- PCS_SATA_RETRY = (1U << 8), /* retry ctl FIS on R_ERR */
- PCS_RSP_RX_EN = (1U << 7), /* raw response rx */
- PCS_SELF_CLEAR = (1U << 5), /* self-clearing int mode */
- PCS_FIS_RX_EN = (1U << 4), /* FIS rx enable */
- PCS_CMD_STOP_ERR = (1U << 3), /* cmd stop-on-err enable */
- PCS_CMD_RST = (1U << 1), /* reset cmd issue */
- PCS_CMD_EN = (1U << 0), /* enable cmd issue */
-
- /* Port n Attached Device Info */
- PORT_DEV_SSP_TRGT = (1U << 19),
- PORT_DEV_SMP_TRGT = (1U << 18),
- PORT_DEV_STP_TRGT = (1U << 17),
- PORT_DEV_SSP_INIT = (1U << 11),
- PORT_DEV_SMP_INIT = (1U << 10),
- PORT_DEV_STP_INIT = (1U << 9),
- PORT_PHY_ID_MASK = (0xFFU << 24),
- PORT_DEV_TRGT_MASK = (0x7U << 17),
- PORT_DEV_INIT_MASK = (0x7U << 9),
- PORT_DEV_TYPE_MASK = (0x7U << 0),
-
- /* Port n PHY Status */
- PHY_RDY = (1U << 2),
- PHY_DW_SYNC = (1U << 1),
- PHY_OOB_DTCTD = (1U << 0),
-
- /* VSR */
- /* PHYMODE 6 (CDB) */
- PHY_MODE6_LATECLK = (1U << 29), /* Lock Clock */
- PHY_MODE6_DTL_SPEED = (1U << 27), /* Digital Loop Speed */
- PHY_MODE6_FC_ORDER = (1U << 26), /* Fibre Channel Mode Order*/
- PHY_MODE6_MUCNT_EN = (1U << 24), /* u Count Enable */
- PHY_MODE6_SEL_MUCNT_LEN = (1U << 22), /* Training Length Select */
- PHY_MODE6_SELMUPI = (1U << 20), /* Phase Multi Select (init) */
- PHY_MODE6_SELMUPF = (1U << 18), /* Phase Multi Select (final) */
- PHY_MODE6_SELMUFF = (1U << 16), /* Freq Loop Multi Sel(final) */
- PHY_MODE6_SELMUFI = (1U << 14), /* Freq Loop Multi Sel(init) */
- PHY_MODE6_FREEZE_LOOP = (1U << 12), /* Freeze Rx CDR Loop */
- PHY_MODE6_INT_RXFOFFS = (1U << 3), /* Rx CDR Freq Loop Enable */
- PHY_MODE6_FRC_RXFOFFS = (1U << 2), /* Initial Rx CDR Offset */
- PHY_MODE6_STAU_0D8 = (1U << 1), /* Rx CDR Freq Loop Saturate */
- PHY_MODE6_RXSAT_DIS = (1U << 0), /* Saturate Ctl */
-};
-
-enum mvs_info_flags {
- MVF_MSI = (1U << 0), /* MSI is enabled */
- MVF_PHY_PWR_FIX = (1U << 1), /* bug workaround */
-};
-
-enum sas_cmd_port_registers {
- CMD_CMRST_OOB_DET = 0x100, /* COMRESET OOB detect register */
- CMD_CMWK_OOB_DET = 0x104, /* COMWAKE OOB detect register */
- CMD_CMSAS_OOB_DET = 0x108, /* COMSAS OOB detect register */
- CMD_BRST_OOB_DET = 0x10c, /* burst OOB detect register */
- CMD_OOB_SPACE = 0x110, /* OOB space control register */
- CMD_OOB_BURST = 0x114, /* OOB burst control register */
- CMD_PHY_TIMER = 0x118, /* PHY timer control register */
- CMD_PHY_CONFIG0 = 0x11c, /* PHY config register 0 */
- CMD_PHY_CONFIG1 = 0x120, /* PHY config register 1 */
- CMD_SAS_CTL0 = 0x124, /* SAS control register 0 */
- CMD_SAS_CTL1 = 0x128, /* SAS control register 1 */
- CMD_SAS_CTL2 = 0x12c, /* SAS control register 2 */
- CMD_SAS_CTL3 = 0x130, /* SAS control register 3 */
- CMD_ID_TEST = 0x134, /* ID test register */
- CMD_PL_TIMER = 0x138, /* PL timer register */
- CMD_WD_TIMER = 0x13c, /* WD timer register */
- CMD_PORT_SEL_COUNT = 0x140, /* port selector count register */
- CMD_APP_MEM_CTL = 0x144, /* Application Memory Control */
- CMD_XOR_MEM_CTL = 0x148, /* XOR Block Memory Control */
- CMD_DMA_MEM_CTL = 0x14c, /* DMA Block Memory Control */
- CMD_PORT_MEM_CTL0 = 0x150, /* Port Memory Control 0 */
- CMD_PORT_MEM_CTL1 = 0x154, /* Port Memory Control 1 */
- CMD_SATA_PORT_MEM_CTL0 = 0x158, /* SATA Port Memory Control 0 */
- CMD_SATA_PORT_MEM_CTL1 = 0x15c, /* SATA Port Memory Control 1 */
- CMD_XOR_MEM_BIST_CTL = 0x160, /* XOR Memory BIST Control */
- CMD_XOR_MEM_BIST_STAT = 0x164, /* XOR Memroy BIST Status */
- CMD_DMA_MEM_BIST_CTL = 0x168, /* DMA Memory BIST Control */
- CMD_DMA_MEM_BIST_STAT = 0x16c, /* DMA Memory BIST Status */
- CMD_PORT_MEM_BIST_CTL = 0x170, /* Port Memory BIST Control */
- CMD_PORT_MEM_BIST_STAT0 = 0x174, /* Port Memory BIST Status 0 */
- CMD_PORT_MEM_BIST_STAT1 = 0x178, /* Port Memory BIST Status 1 */
- CMD_STP_MEM_BIST_CTL = 0x17c, /* STP Memory BIST Control */
- CMD_STP_MEM_BIST_STAT0 = 0x180, /* STP Memory BIST Status 0 */
- CMD_STP_MEM_BIST_STAT1 = 0x184, /* STP Memory BIST Status 1 */
- CMD_RESET_COUNT = 0x188, /* Reset Count */
- CMD_MONTR_DATA_SEL = 0x18C, /* Monitor Data/Select */
- CMD_PLL_PHY_CONFIG = 0x190, /* PLL/PHY Configuration */
- CMD_PHY_CTL = 0x194, /* PHY Control and Status */
- CMD_PHY_TEST_COUNT0 = 0x198, /* Phy Test Count 0 */
- CMD_PHY_TEST_COUNT1 = 0x19C, /* Phy Test Count 1 */
- CMD_PHY_TEST_COUNT2 = 0x1A0, /* Phy Test Count 2 */
- CMD_APP_ERR_CONFIG = 0x1A4, /* Application Error Configuration */
- CMD_PND_FIFO_CTL0 = 0x1A8, /* Pending FIFO Control 0 */
- CMD_HOST_CTL = 0x1AC, /* Host Control Status */
- CMD_HOST_WR_DATA = 0x1B0, /* Host Write Data */
- CMD_HOST_RD_DATA = 0x1B4, /* Host Read Data */
- CMD_PHY_MODE_21 = 0x1B8, /* Phy Mode 21 */
- CMD_SL_MODE0 = 0x1BC, /* SL Mode 0 */
- CMD_SL_MODE1 = 0x1C0, /* SL Mode 1 */
- CMD_PND_FIFO_CTL1 = 0x1C4, /* Pending FIFO Control 1 */
-};
-
-/* SAS/SATA configuration port registers, aka phy registers */
-enum sas_sata_config_port_regs {
- PHYR_IDENTIFY = 0x00, /* info for IDENTIFY frame */
- PHYR_ADDR_LO = 0x04, /* my SAS address (low) */
- PHYR_ADDR_HI = 0x08, /* my SAS address (high) */
- PHYR_ATT_DEV_INFO = 0x0C, /* attached device info */
- PHYR_ATT_ADDR_LO = 0x10, /* attached dev SAS addr (low) */
- PHYR_ATT_ADDR_HI = 0x14, /* attached dev SAS addr (high) */
- PHYR_SATA_CTL = 0x18, /* SATA control */
- PHYR_PHY_STAT = 0x1C, /* PHY status */
- PHYR_SATA_SIG0 = 0x20, /*port SATA signature FIS(Byte 0-3) */
- PHYR_SATA_SIG1 = 0x24, /*port SATA signature FIS(Byte 4-7) */
- PHYR_SATA_SIG2 = 0x28, /*port SATA signature FIS(Byte 8-11) */
- PHYR_SATA_SIG3 = 0x2c, /*port SATA signature FIS(Byte 12-15) */
- PHYR_R_ERR_COUNT = 0x30, /* port R_ERR count register */
- PHYR_CRC_ERR_COUNT = 0x34, /* port CRC error count register */
- PHYR_WIDE_PORT = 0x38, /* wide port participating */
- PHYR_CURRENT0 = 0x80, /* current connection info 0 */
- PHYR_CURRENT1 = 0x84, /* current connection info 1 */
- PHYR_CURRENT2 = 0x88, /* current connection info 2 */
-};
-
-/* SAS/SATA Vendor Specific Port Registers */
-enum sas_sata_vsp_regs {
- VSR_PHY_STAT = 0x00, /* Phy Status */
- VSR_PHY_MODE1 = 0x01, /* phy tx */
- VSR_PHY_MODE2 = 0x02, /* tx scc */
- VSR_PHY_MODE3 = 0x03, /* pll */
- VSR_PHY_MODE4 = 0x04, /* VCO */
- VSR_PHY_MODE5 = 0x05, /* Rx */
- VSR_PHY_MODE6 = 0x06, /* CDR */
- VSR_PHY_MODE7 = 0x07, /* Impedance */
- VSR_PHY_MODE8 = 0x08, /* Voltage */
- VSR_PHY_MODE9 = 0x09, /* Test */
- VSR_PHY_MODE10 = 0x0A, /* Power */
- VSR_PHY_MODE11 = 0x0B, /* Phy Mode */
- VSR_PHY_VS0 = 0x0C, /* Vednor Specific 0 */
- VSR_PHY_VS1 = 0x0D, /* Vednor Specific 1 */
-};
-
-enum pci_cfg_registers {
- PCR_PHY_CTL = 0x40,
- PCR_PHY_CTL2 = 0x90,
- PCR_DEV_CTRL = 0xE8,
-};
-
-enum pci_cfg_register_bits {
- PCTL_PWR_ON = (0xFU << 24),
- PCTL_OFF = (0xFU << 12),
- PRD_REQ_SIZE = (0x4000),
- PRD_REQ_MASK = (0x00007000),
-};
-
-enum nvram_layout_offsets {
- NVR_SIG = 0x00, /* 0xAA, 0x55 */
- NVR_SAS_ADDR = 0x02, /* 8-byte SAS address */
-};
-
-enum chip_flavors {
- chip_6320,
- chip_6440,
- chip_6480,
-};
-
-enum port_type {
- PORT_TYPE_SAS = (1L << 1),
- PORT_TYPE_SATA = (1L << 0),
-};
-
-/* Command Table Format */
-enum ct_format {
- /* SSP */
- SSP_F_H = 0x00,
- SSP_F_IU = 0x18,
- SSP_F_MAX = 0x4D,
- /* STP */
- STP_CMD_FIS = 0x00,
- STP_ATAPI_CMD = 0x40,
- STP_F_MAX = 0x10,
- /* SMP */
- SMP_F_T = 0x00,
- SMP_F_DEP = 0x01,
- SMP_F_MAX = 0x101,
-};
-
-enum status_buffer {
- SB_EIR_OFF = 0x00, /* Error Information Record */
- SB_RFB_OFF = 0x08, /* Response Frame Buffer */
- SB_RFB_MAX = 0x400, /* RFB size*/
-};
-
-enum error_info_rec {
- CMD_ISS_STPD = (1U << 31), /* Cmd Issue Stopped */
- CMD_PI_ERR = (1U << 30), /* Protection info error. see flags2 */
- RSP_OVER = (1U << 29), /* rsp buffer overflow */
- RETRY_LIM = (1U << 28), /* FIS/frame retry limit exceeded */
- UNK_FIS = (1U << 27), /* unknown FIS */
- DMA_TERM = (1U << 26), /* DMA terminate primitive rx'd */
- SYNC_ERR = (1U << 25), /* SYNC rx'd during frame xmit */
- TFILE_ERR = (1U << 24), /* SATA taskfile Error bit set */
- R_ERR = (1U << 23), /* SATA returned R_ERR prim */
- RD_OFS = (1U << 20), /* Read DATA frame invalid offset */
- XFER_RDY_OFS = (1U << 19), /* XFER_RDY offset error */
- UNEXP_XFER_RDY = (1U << 18), /* unexpected XFER_RDY error */
- DATA_OVER_UNDER = (1U << 16), /* data overflow/underflow */
- INTERLOCK = (1U << 15), /* interlock error */
- NAK = (1U << 14), /* NAK rx'd */
- ACK_NAK_TO = (1U << 13), /* ACK/NAK timeout */
- CXN_CLOSED = (1U << 12), /* cxn closed w/out ack/nak */
- OPEN_TO = (1U << 11), /* I_T nexus lost, open cxn timeout */
- PATH_BLOCKED = (1U << 10), /* I_T nexus lost, pathway blocked */
- NO_DEST = (1U << 9), /* I_T nexus lost, no destination */
- STP_RES_BSY = (1U << 8), /* STP resources busy */
- BREAK = (1U << 7), /* break received */
- BAD_DEST = (1U << 6), /* bad destination */
- BAD_PROTO = (1U << 5), /* protocol not supported */
- BAD_RATE = (1U << 4), /* cxn rate not supported */
- WRONG_DEST = (1U << 3), /* wrong destination error */
- CREDIT_TO = (1U << 2), /* credit timeout */
- WDOG_TO = (1U << 1), /* watchdog timeout */
- BUF_PAR = (1U << 0), /* buffer parity error */
-};
-
-enum error_info_rec_2 {
- SLOT_BSY_ERR = (1U << 31), /* Slot Busy Error */
- GRD_CHK_ERR = (1U << 14), /* Guard Check Error */
- APP_CHK_ERR = (1U << 13), /* Application Check error */
- REF_CHK_ERR = (1U << 12), /* Reference Check Error */
- USR_BLK_NM = (1U << 0), /* User Block Number */
-};
-
-struct mvs_chip_info {
- u32 n_phy;
- u32 srs_sz;
- u32 slot_width;
-};
-
-struct mvs_err_info {
- __le32 flags;
- __le32 flags2;
-};
-
-struct mvs_prd {
- __le64 addr; /* 64-bit buffer address */
- __le32 reserved;
- __le32 len; /* 16-bit length */
-};
-
-struct mvs_cmd_hdr {
- __le32 flags; /* PRD tbl len; SAS, SATA ctl */
- __le32 lens; /* cmd, max resp frame len */
- __le32 tags; /* targ port xfer tag; tag */
- __le32 data_len; /* data xfer len */
- __le64 cmd_tbl; /* command table address */
- __le64 open_frame; /* open addr frame address */
- __le64 status_buf; /* status buffer address */
- __le64 prd_tbl; /* PRD tbl address */
- __le32 reserved[4];
-};
-
-struct mvs_port {
- struct asd_sas_port sas_port;
- u8 port_attached;
- u8 taskfileset;
- u8 wide_port_phymap;
- struct list_head list;
-};
-
-struct mvs_phy {
- struct mvs_port *port;
- struct asd_sas_phy sas_phy;
- struct sas_identify identify;
- struct scsi_device *sdev;
- u64 dev_sas_addr;
- u64 att_dev_sas_addr;
- u32 att_dev_info;
- u32 dev_info;
- u32 phy_type;
- u32 phy_status;
- u32 irq_status;
- u32 frame_rcvd_size;
- u8 frame_rcvd[32];
- u8 phy_attached;
- enum sas_linkrate minimum_linkrate;
- enum sas_linkrate maximum_linkrate;
-};
-
-struct mvs_slot_info {
- struct list_head list;
- struct sas_task *task;
- u32 n_elem;
- u32 tx;
-
- /* DMA buffer for storing cmd tbl, open addr frame, status buffer,
- * and PRD table
- */
- void *buf;
- dma_addr_t buf_dma;
-#if _MV_DUMP
- u32 cmd_size;
-#endif
-
- void *response;
- struct mvs_port *port;
-};
-
-struct mvs_info {
- unsigned long flags;
-
- spinlock_t lock; /* host-wide lock */
- struct pci_dev *pdev; /* our device */
- void __iomem *regs; /* enhanced mode registers */
- void __iomem *peri_regs; /* peripheral registers */
-
- u8 sas_addr[SAS_ADDR_SIZE];
- struct sas_ha_struct sas; /* SCSI/SAS glue */
- struct Scsi_Host *shost;
-
- __le32 *tx; /* TX (delivery) DMA ring */
- dma_addr_t tx_dma;
- u32 tx_prod; /* cached next-producer idx */
-
- __le32 *rx; /* RX (completion) DMA ring */
- dma_addr_t rx_dma;
- u32 rx_cons; /* RX consumer idx */
-
- __le32 *rx_fis; /* RX'd FIS area */
- dma_addr_t rx_fis_dma;
-
- struct mvs_cmd_hdr *slot; /* DMA command header slots */
- dma_addr_t slot_dma;
-
- const struct mvs_chip_info *chip;
-
- u8 tags[MVS_SLOTS];
- struct mvs_slot_info slot_info[MVS_SLOTS];
- /* further per-slot information */
- struct mvs_phy phy[MVS_MAX_PHYS];
- struct mvs_port port[MVS_MAX_PHYS];
-#ifdef MVS_USE_TASKLET
- struct tasklet_struct tasklet;
-#endif
-};
-
-static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
- void *funcdata);
-static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port);
-static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val);
-static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port);
-static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val);
-static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val);
-static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port);
-
-static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i);
-static void mvs_detect_porttype(struct mvs_info *mvi, int i);
-static void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
-static void mvs_release_task(struct mvs_info *mvi, int phy_no);
-
-static int mvs_scan_finished(struct Scsi_Host *, unsigned long);
-static void mvs_scan_start(struct Scsi_Host *);
-static int mvs_slave_configure(struct scsi_device *sdev);
-
-static struct scsi_transport_template *mvs_stt;
-
-static const struct mvs_chip_info mvs_chips[] = {
- [chip_6320] = { 2, 16, 9 },
- [chip_6440] = { 4, 16, 9 },
- [chip_6480] = { 8, 32, 10 },
-};
-
-static struct scsi_host_template mvs_sht = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .queuecommand = sas_queuecommand,
- .target_alloc = sas_target_alloc,
- .slave_configure = mvs_slave_configure,
- .slave_destroy = sas_slave_destroy,
- .scan_finished = mvs_scan_finished,
- .scan_start = mvs_scan_start,
- .change_queue_depth = sas_change_queue_depth,
- .change_queue_type = sas_change_queue_type,
- .bios_param = sas_bios_param,
- .can_queue = 1,
- .cmd_per_lun = 1,
- .this_id = -1,
- .sg_tablesize = SG_ALL,
- .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
- .target_destroy = sas_target_destroy,
- .ioctl = sas_ioctl,
-};
-
-static void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
-{
- u32 i;
- u32 run;
- u32 offset;
-
- offset = 0;
- while (size) {
- printk("%08X : ", baseaddr + offset);
- if (size >= 16)
- run = 16;
- else
- run = size;
- size -= run;
- for (i = 0; i < 16; i++) {
- if (i < run)
- printk("%02X ", (u32)data[i]);
- else
- printk(" ");
- }
- printk(": ");
- for (i = 0; i < run; i++)
- printk("%c", isalnum(data[i]) ? data[i] : '.');
- printk("\n");
- data = &data[16];
- offset += run;
- }
- printk("\n");
-}
-
-#if _MV_DUMP
-static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
- enum sas_protocol proto)
-{
- u32 offset;
- struct pci_dev *pdev = mvi->pdev;
- struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
- offset = slot->cmd_size + MVS_OAF_SZ +
- sizeof(struct mvs_prd) * slot->n_elem;
- dev_printk(KERN_DEBUG, &pdev->dev, "+---->Status buffer[%d] :\n",
- tag);
- mvs_hexdump(32, (u8 *) slot->response,
- (u32) slot->buf_dma + offset);
-}
-#endif
-
-static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
- enum sas_protocol proto)
-{
-#if _MV_DUMP
- u32 sz, w_ptr;
- u64 addr;
- void __iomem *regs = mvi->regs;
- struct pci_dev *pdev = mvi->pdev;
- struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
- /*Delivery Queue */
- sz = mr32(TX_CFG) & TX_RING_SZ_MASK;
- w_ptr = slot->tx;
- addr = mr32(TX_HI) << 16 << 16 | mr32(TX_LO);
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Delivery Queue Base Address=0x%llX (PA)"
- "(tx_dma=0x%llX), Entry=%04d\n",
- addr, mvi->tx_dma, w_ptr);
- mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
- (u32) mvi->tx_dma + sizeof(u32) * w_ptr);
- /*Command List */
- addr = mvi->slot_dma;
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Command List Base Address=0x%llX (PA)"
- "(slot_dma=0x%llX), Header=%03d\n",
- addr, slot->buf_dma, tag);
- dev_printk(KERN_DEBUG, &pdev->dev, "Command Header[%03d]:\n", tag);
- /*mvs_cmd_hdr */
- mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
- (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
- /*1.command table area */
- dev_printk(KERN_DEBUG, &pdev->dev, "+---->Command Table :\n");
- mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
- /*2.open address frame area */
- dev_printk(KERN_DEBUG, &pdev->dev, "+---->Open Address Frame :\n");
- mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
- (u32) slot->buf_dma + slot->cmd_size);
- /*3.status buffer */
- mvs_hba_sb_dump(mvi, tag, proto);
- /*4.PRD table */
- dev_printk(KERN_DEBUG, &pdev->dev, "+---->PRD table :\n");
- mvs_hexdump(sizeof(struct mvs_prd) * slot->n_elem,
- (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
- (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
-#endif
-}
-
-static void mvs_hba_cq_dump(struct mvs_info *mvi)
-{
-#if (_MV_DUMP > 2)
- u64 addr;
- void __iomem *regs = mvi->regs;
- struct pci_dev *pdev = mvi->pdev;
- u32 entry = mvi->rx_cons + 1;
- u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
-
- /*Completion Queue */
- addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
- dev_printk(KERN_DEBUG, &pdev->dev, "Completion Task = 0x%p\n",
- mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Completion List Base Address=0x%llX (PA), "
- "CQ_Entry=%04d, CQ_WP=0x%08X\n",
- addr, entry - 1, mvi->rx[0]);
- mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
- mvi->rx_dma + sizeof(u32) * entry);
-#endif
-}
-
-static void mvs_hba_interrupt_enable(struct mvs_info *mvi)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp;
-
- tmp = mr32(GBL_CTL);
-
- mw32(GBL_CTL, tmp | INT_EN);
-}
-
-static void mvs_hba_interrupt_disable(struct mvs_info *mvi)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp;
-
- tmp = mr32(GBL_CTL);
-
- mw32(GBL_CTL, tmp & ~INT_EN);
-}
-
-static int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
-
-/* move to PCI layer or libata core? */
-static int pci_go_64(struct pci_dev *pdev)
-{
- int rc;
-
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
- if (rc) {
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
- return rc;
- }
- }
- } else {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit DMA enable failed\n");
- return rc;
- }
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
- return rc;
- }
- }
-
- return rc;
-}
-
-static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
-{
- if (task->lldd_task) {
- struct mvs_slot_info *slot;
- slot = (struct mvs_slot_info *) task->lldd_task;
- *tag = slot - mvi->slot_info;
- return 1;
- }
- return 0;
-}
-
-static void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
-{
- void *bitmap = (void *) &mvi->tags;
- clear_bit(tag, bitmap);
-}
-
-static void mvs_tag_free(struct mvs_info *mvi, u32 tag)
-{
- mvs_tag_clear(mvi, tag);
-}
-
-static void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
-{
- void *bitmap = (void *) &mvi->tags;
- set_bit(tag, bitmap);
-}
-
-static int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
-{
- unsigned int index, tag;
- void *bitmap = (void *) &mvi->tags;
-
- index = find_first_zero_bit(bitmap, MVS_SLOTS);
- tag = index;
- if (tag >= MVS_SLOTS)
- return -SAS_QUEUE_FULL;
- mvs_tag_set(mvi, tag);
- *tag_out = tag;
- return 0;
-}
-
-static void mvs_tag_init(struct mvs_info *mvi)
-{
- int i;
- for (i = 0; i < MVS_SLOTS; ++i)
- mvs_tag_clear(mvi, i);
-}
-
-#ifndef MVS_DISABLE_NVRAM
-static int mvs_eep_read(void __iomem *regs, u32 addr, u32 *data)
-{
- int timeout = 1000;
-
- if (addr & ~SPI_ADDR_MASK)
- return -EINVAL;
-
- writel(addr, regs + SPI_CMD);
- writel(TWSI_RD, regs + SPI_CTL);
-
- while (timeout-- > 0) {
- if (readl(regs + SPI_CTL) & TWSI_RDY) {
- *data = readl(regs + SPI_DATA);
- return 0;
- }
-
- udelay(10);
- }
-
- return -EBUSY;
-}
-
-static int mvs_eep_read_buf(void __iomem *regs, u32 addr,
- void *buf, u32 buflen)
-{
- u32 addr_end, tmp_addr, i, j;
- u32 tmp = 0;
- int rc;
- u8 *tmp8, *buf8 = buf;
-
- addr_end = addr + buflen;
- tmp_addr = ALIGN(addr, 4);
- if (addr > 0xff)
- return -EINVAL;
-
- j = addr & 0x3;
- if (j) {
- rc = mvs_eep_read(regs, tmp_addr, &tmp);
- if (rc)
- return rc;
-
- tmp8 = (u8 *)&tmp;
- for (i = j; i < 4; i++)
- *buf8++ = tmp8[i];
-
- tmp_addr += 4;
- }
-
- for (j = ALIGN(addr_end, 4); tmp_addr < j; tmp_addr += 4) {
- rc = mvs_eep_read(regs, tmp_addr, &tmp);
- if (rc)
- return rc;
-
- memcpy(buf8, &tmp, 4);
- buf8 += 4;
- }
-
- if (tmp_addr < addr_end) {
- rc = mvs_eep_read(regs, tmp_addr, &tmp);
- if (rc)
- return rc;
-
- tmp8 = (u8 *)&tmp;
- j = addr_end - tmp_addr;
- for (i = 0; i < j; i++)
- *buf8++ = tmp8[i];
-
- tmp_addr += 4;
- }
-
- return 0;
-}
-#endif
-
-static int mvs_nvram_read(struct mvs_info *mvi, u32 addr,
- void *buf, u32 buflen)
-{
-#ifndef MVS_DISABLE_NVRAM
- void __iomem *regs = mvi->regs;
- int rc, i;
- u32 sum;
- u8 hdr[2], *tmp;
- const char *msg;
-
- rc = mvs_eep_read_buf(regs, addr, &hdr, 2);
- if (rc) {
- msg = "nvram hdr read failed";
- goto err_out;
- }
- rc = mvs_eep_read_buf(regs, addr + 2, buf, buflen);
- if (rc) {
- msg = "nvram read failed";
- goto err_out;
- }
-
- if (hdr[0] != 0x5A) {
- /* entry id */
- msg = "invalid nvram entry id";
- rc = -ENOENT;
- goto err_out;
- }
-
- tmp = buf;
- sum = ((u32)hdr[0]) + ((u32)hdr[1]);
- for (i = 0; i < buflen; i++)
- sum += ((u32)tmp[i]);
-
- if (sum) {
- msg = "nvram checksum failure";
- rc = -EILSEQ;
- goto err_out;
- }
-
- return 0;
-
-err_out:
- dev_printk(KERN_ERR, &mvi->pdev->dev, "%s", msg);
- return rc;
-#else
- /* FIXME , For SAS target mode */
- memcpy(buf, "\x50\x05\x04\x30\x11\xab\x00\x00", 8);
- return 0;
-#endif
-}
-
-static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
-{
- struct mvs_phy *phy = &mvi->phy[i];
- struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i];
-
- if (!phy->phy_attached)
- return;
-
- if (sas_phy->phy) {
- struct sas_phy *sphy = sas_phy->phy;
-
- sphy->negotiated_linkrate = sas_phy->linkrate;
- sphy->minimum_linkrate = phy->minimum_linkrate;
- sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
- sphy->maximum_linkrate = phy->maximum_linkrate;
- sphy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
- }
-
- if (phy->phy_type & PORT_TYPE_SAS) {
- struct sas_identify_frame *id;
-
- id = (struct sas_identify_frame *)phy->frame_rcvd;
- id->dev_type = phy->identify.device_type;
- id->initiator_bits = SAS_PROTOCOL_ALL;
- id->target_bits = phy->identify.target_port_protocols;
- } else if (phy->phy_type & PORT_TYPE_SATA) {
- /* TODO */
- }
- mvi->sas.sas_phy[i]->frame_rcvd_size = phy->frame_rcvd_size;
- mvi->sas.notify_port_event(mvi->sas.sas_phy[i],
- PORTE_BYTES_DMAED);
-}
-
-static int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
-{
- /* give the phy enabling interrupt event time to come in (1s
- * is empirically about all it takes) */
- if (time < HZ)
- return 0;
- /* Wait for discovery to finish */
- scsi_flush_work(shost);
- return 1;
-}
-
-static void mvs_scan_start(struct Scsi_Host *shost)
-{
- int i;
- struct mvs_info *mvi = SHOST_TO_SAS_HA(shost)->lldd_ha;
-
- for (i = 0; i < mvi->chip->n_phy; ++i) {
- mvs_bytes_dmaed(mvi, i);
- }
-}
-
-static int mvs_slave_configure(struct scsi_device *sdev)
-{
- struct domain_device *dev = sdev_to_domain_dev(sdev);
- int ret = sas_slave_configure(sdev);
-
- if (ret)
- return ret;
-
- if (dev_is_sata(dev)) {
- /* struct ata_port *ap = dev->sata_dev.ap; */
- /* struct ata_device *adev = ap->link.device; */
-
- /* clamp at no NCQ for the time being */
- /* adev->flags |= ATA_DFLAG_NCQ_OFF; */
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
- }
- return 0;
-}
-
-static void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
-{
- struct pci_dev *pdev = mvi->pdev;
- struct sas_ha_struct *sas_ha = &mvi->sas;
- struct mvs_phy *phy = &mvi->phy[phy_no];
- struct asd_sas_phy *sas_phy = &phy->sas_phy;
-
- phy->irq_status = mvs_read_port_irq_stat(mvi, phy_no);
- /*
- * events is port event now ,
- * we need check the interrupt status which belongs to per port.
- */
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Port %d Event = %X\n",
- phy_no, phy->irq_status);
-
- if (phy->irq_status & (PHYEV_POOF | PHYEV_DEC_ERR)) {
- mvs_release_task(mvi, phy_no);
- if (!mvs_is_phy_ready(mvi, phy_no)) {
- sas_phy_disconnected(sas_phy);
- sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL);
- dev_printk(KERN_INFO, &pdev->dev,
- "Port %d Unplug Notice\n", phy_no);
-
- } else
- mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, NULL);
- }
- if (!(phy->irq_status & PHYEV_DEC_ERR)) {
- if (phy->irq_status & PHYEV_COMWAKE) {
- u32 tmp = mvs_read_port_irq_mask(mvi, phy_no);
- mvs_write_port_irq_mask(mvi, phy_no,
- tmp | PHYEV_SIG_FIS);
- }
- if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
- phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
- if (phy->phy_status) {
- mvs_detect_porttype(mvi, phy_no);
-
- if (phy->phy_type & PORT_TYPE_SATA) {
- u32 tmp = mvs_read_port_irq_mask(mvi,
- phy_no);
- tmp &= ~PHYEV_SIG_FIS;
- mvs_write_port_irq_mask(mvi,
- phy_no, tmp);
- }
-
- mvs_update_phyinfo(mvi, phy_no, 0);
- sas_ha->notify_phy_event(sas_phy,
- PHYE_OOB_DONE);
- mvs_bytes_dmaed(mvi, phy_no);
- } else {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "plugin interrupt but phy is gone\n");
- mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET,
- NULL);
- }
- } else if (phy->irq_status & PHYEV_BROAD_CH) {
- mvs_release_task(mvi, phy_no);
- sas_ha->notify_port_event(sas_phy,
- PORTE_BROADCAST_RCVD);
- }
- }
- mvs_write_port_irq_stat(mvi, phy_no, phy->irq_status);
-}
-
-static void mvs_int_sata(struct mvs_info *mvi)
-{
- u32 tmp;
- void __iomem *regs = mvi->regs;
- tmp = mr32(INT_STAT_SRS);
- mw32(INT_STAT_SRS, tmp & 0xFFFF);
-}
-
-static void mvs_slot_reset(struct mvs_info *mvi, struct sas_task *task,
- u32 slot_idx)
-{
- void __iomem *regs = mvi->regs;
- struct domain_device *dev = task->dev;
- struct asd_sas_port *sas_port = dev->port;
- struct mvs_port *port = mvi->slot_info[slot_idx].port;
- u32 reg_set, phy_mask;
-
- if (!sas_protocol_ata(task->task_proto)) {
- reg_set = 0;
- phy_mask = (port->wide_port_phymap) ? port->wide_port_phymap :
- sas_port->phy_mask;
- } else {
- reg_set = port->taskfileset;
- phy_mask = sas_port->phy_mask;
- }
- mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | slot_idx |
- (TXQ_CMD_SLOT_RESET << TXQ_CMD_SHIFT) |
- (phy_mask << TXQ_PHY_SHIFT) |
- (reg_set << TXQ_SRS_SHIFT));
-
- mw32(TX_PROD_IDX, mvi->tx_prod);
- mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
-}
-
-static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
- u32 slot_idx, int err)
-{
- struct mvs_port *port = mvi->slot_info[slot_idx].port;
- struct task_status_struct *tstat = &task->task_status;
- struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
- int stat = SAM_GOOD;
-
- resp->frame_len = sizeof(struct dev_to_host_fis);
- memcpy(&resp->ending_fis[0],
- SATA_RECEIVED_D2H_FIS(port->taskfileset),
- sizeof(struct dev_to_host_fis));
- tstat->buf_valid_size = sizeof(*resp);
- if (unlikely(err))
- stat = SAS_PROTO_RESPONSE;
- return stat;
-}
-
-static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
-{
- u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
- mvs_tag_clear(mvi, slot_idx);
-}
-
-static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
- struct mvs_slot_info *slot, u32 slot_idx)
-{
- if (!sas_protocol_ata(task->task_proto))
- if (slot->n_elem)
- pci_unmap_sg(mvi->pdev, task->scatter,
- slot->n_elem, task->data_dir);
-
- switch (task->task_proto) {
- case SAS_PROTOCOL_SMP:
- pci_unmap_sg(mvi->pdev, &task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
- pci_unmap_sg(mvi->pdev, &task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
- break;
-
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SSP:
- default:
- /* do nothing */
- break;
- }
- list_del(&slot->list);
- task->lldd_task = NULL;
- slot->task = NULL;
- slot->port = NULL;
-}
-
-static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
- u32 slot_idx)
-{
- struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
- u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
- u32 err_dw1 = le32_to_cpu(*(u32 *) (slot->response + 4));
- int stat = SAM_CHECK_COND;
-
- if (err_dw1 & SLOT_BSY_ERR) {
- stat = SAS_QUEUE_FULL;
- mvs_slot_reset(mvi, task, slot_idx);
- }
- switch (task->task_proto) {
- case SAS_PROTOCOL_SSP:
- break;
- case SAS_PROTOCOL_SMP:
- break;
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
- if (err_dw0 & TFILE_ERR)
- stat = mvs_sata_done(mvi, task, slot_idx, 1);
- break;
- default:
- break;
- }
-
- mvs_hexdump(16, (u8 *) slot->response, 0);
- return stat;
-}
-
-static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
-{
- u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
- struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
- struct sas_task *task = slot->task;
- struct task_status_struct *tstat;
- struct mvs_port *port;
- bool aborted;
- void *to;
-
- if (unlikely(!task || !task->lldd_task))
- return -1;
-
- mvs_hba_cq_dump(mvi);
-
- spin_lock(&task->task_state_lock);
- aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
- if (!aborted) {
- task->task_state_flags &=
- ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
- task->task_state_flags |= SAS_TASK_STATE_DONE;
- }
- spin_unlock(&task->task_state_lock);
-
- if (aborted) {
- mvs_slot_task_free(mvi, task, slot, slot_idx);
- mvs_slot_free(mvi, rx_desc);
- return -1;
- }
-
- port = slot->port;
- tstat = &task->task_status;
- memset(tstat, 0, sizeof(*tstat));
- tstat->resp = SAS_TASK_COMPLETE;
-
- if (unlikely(!port->port_attached || flags)) {
- mvs_slot_err(mvi, task, slot_idx);
- if (!sas_protocol_ata(task->task_proto))
- tstat->stat = SAS_PHY_DOWN;
- goto out;
- }
-
- /* error info record present */
- if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
- tstat->stat = mvs_slot_err(mvi, task, slot_idx);
- goto out;
- }
-
- switch (task->task_proto) {
- case SAS_PROTOCOL_SSP:
- /* hw says status == 0, datapres == 0 */
- if (rx_desc & RXQ_GOOD) {
- tstat->stat = SAM_GOOD;
- tstat->resp = SAS_TASK_COMPLETE;
- }
- /* response frame present */
- else if (rx_desc & RXQ_RSP) {
- struct ssp_response_iu *iu =
- slot->response + sizeof(struct mvs_err_info);
- sas_ssp_task_response(&mvi->pdev->dev, task, iu);
- }
-
- /* should never happen? */
- else
- tstat->stat = SAM_CHECK_COND;
- break;
-
- case SAS_PROTOCOL_SMP: {
- struct scatterlist *sg_resp = &task->smp_task.smp_resp;
- tstat->stat = SAM_GOOD;
- to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
- memcpy(to + sg_resp->offset,
- slot->response + sizeof(struct mvs_err_info),
- sg_dma_len(sg_resp));
- kunmap_atomic(to, KM_IRQ0);
- break;
- }
-
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
- tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
- break;
- }
-
- default:
- tstat->stat = SAM_CHECK_COND;
- break;
- }
-
-out:
- mvs_slot_task_free(mvi, task, slot, slot_idx);
- if (unlikely(tstat->stat != SAS_QUEUE_FULL))
- mvs_slot_free(mvi, rx_desc);
-
- spin_unlock(&mvi->lock);
- task->task_done(task);
- spin_lock(&mvi->lock);
- return tstat->stat;
-}
-
-static void mvs_release_task(struct mvs_info *mvi, int phy_no)
-{
- struct list_head *pos, *n;
- struct mvs_slot_info *slot;
- struct mvs_phy *phy = &mvi->phy[phy_no];
- struct mvs_port *port = phy->port;
- u32 rx_desc;
-
- if (!port)
- return;
-
- list_for_each_safe(pos, n, &port->list) {
- slot = container_of(pos, struct mvs_slot_info, list);
- rx_desc = (u32) (slot - mvi->slot_info);
- mvs_slot_complete(mvi, rx_desc, 1);
- }
-}
-
-static void mvs_int_full(struct mvs_info *mvi)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp, stat;
- int i;
-
- stat = mr32(INT_STAT);
-
- mvs_int_rx(mvi, false);
-
- for (i = 0; i < MVS_MAX_PORTS; i++) {
- tmp = (stat >> i) & (CINT_PORT | CINT_PORT_STOPPED);
- if (tmp)
- mvs_int_port(mvi, i, tmp);
- }
-
- if (stat & CINT_SRS)
- mvs_int_sata(mvi);
-
- mw32(INT_STAT, stat);
-}
-
-static int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
-{
- void __iomem *regs = mvi->regs;
- u32 rx_prod_idx, rx_desc;
- bool attn = false;
- struct pci_dev *pdev = mvi->pdev;
-
- /* the first dword in the RX ring is special: it contains
- * a mirror of the hardware's RX producer index, so that
- * we don't have to stall the CPU reading that register.
- * The actual RX ring is offset by one dword, due to this.
- */
- rx_prod_idx = mvi->rx_cons;
- mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
- if (mvi->rx_cons == 0xfff) /* h/w hasn't touched RX ring yet */
- return 0;
-
- /* The CMPL_Q may come late, read from register and try again
- * note: if coalescing is enabled,
- * it will need to read from register every time for sure
- */
- if (mvi->rx_cons == rx_prod_idx)
- mvi->rx_cons = mr32(RX_CONS_IDX) & RX_RING_SZ_MASK;
-
- if (mvi->rx_cons == rx_prod_idx)
- return 0;
-
- while (mvi->rx_cons != rx_prod_idx) {
-
- /* increment our internal RX consumer pointer */
- rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
-
- rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
-
- if (likely(rx_desc & RXQ_DONE))
- mvs_slot_complete(mvi, rx_desc, 0);
- if (rx_desc & RXQ_ATTN) {
- attn = true;
- dev_printk(KERN_DEBUG, &pdev->dev, "ATTN %X\n",
- rx_desc);
- } else if (rx_desc & RXQ_ERR) {
- if (!(rx_desc & RXQ_DONE))
- mvs_slot_complete(mvi, rx_desc, 0);
- dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR %X\n",
- rx_desc);
- } else if (rx_desc & RXQ_SLOT_RESET) {
- dev_printk(KERN_DEBUG, &pdev->dev, "Slot reset[%X]\n",
- rx_desc);
- mvs_slot_free(mvi, rx_desc);
- }
- }
-
- if (attn && self_clear)
- mvs_int_full(mvi);
-
- return 0;
-}
-
-#ifdef MVS_USE_TASKLET
-static void mvs_tasklet(unsigned long data)
-{
- struct mvs_info *mvi = (struct mvs_info *) data;
- unsigned long flags;
-
- spin_lock_irqsave(&mvi->lock, flags);
-
-#ifdef MVS_DISABLE_MSI
- mvs_int_full(mvi);
-#else
- mvs_int_rx(mvi, true);
-#endif
- spin_unlock_irqrestore(&mvi->lock, flags);
-}
-#endif
-
-static irqreturn_t mvs_interrupt(int irq, void *opaque)
-{
- struct mvs_info *mvi = opaque;
- void __iomem *regs = mvi->regs;
- u32 stat;
-
- stat = mr32(GBL_INT_STAT);
-
- if (stat == 0 || stat == 0xffffffff)
- return IRQ_NONE;
-
- /* clear CMD_CMPLT ASAP */
- mw32_f(INT_STAT, CINT_DONE);
-
-#ifndef MVS_USE_TASKLET
- spin_lock(&mvi->lock);
-
- mvs_int_full(mvi);
-
- spin_unlock(&mvi->lock);
-#else
- tasklet_schedule(&mvi->tasklet);
-#endif
- return IRQ_HANDLED;
-}
-
-#ifndef MVS_DISABLE_MSI
-static irqreturn_t mvs_msi_interrupt(int irq, void *opaque)
-{
- struct mvs_info *mvi = opaque;
-
-#ifndef MVS_USE_TASKLET
- spin_lock(&mvi->lock);
-
- mvs_int_rx(mvi, true);
-
- spin_unlock(&mvi->lock);
-#else
- tasklet_schedule(&mvi->tasklet);
-#endif
- return IRQ_HANDLED;
-}
-#endif
-
-struct mvs_task_exec_info {
- struct sas_task *task;
- struct mvs_cmd_hdr *hdr;
- struct mvs_port *port;
- u32 tag;
- int n_elem;
-};
-
-static int mvs_task_prep_smp(struct mvs_info *mvi,
- struct mvs_task_exec_info *tei)
-{
- int elem, rc, i;
- struct sas_task *task = tei->task;
- struct mvs_cmd_hdr *hdr = tei->hdr;
- struct scatterlist *sg_req, *sg_resp;
- u32 req_len, resp_len, tag = tei->tag;
- void *buf_tmp;
- u8 *buf_oaf;
- dma_addr_t buf_tmp_dma;
- struct mvs_prd *buf_prd;
- struct scatterlist *sg;
- struct mvs_slot_info *slot = &mvi->slot_info[tag];
- struct asd_sas_port *sas_port = task->dev->port;
- u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#if _MV_DUMP
- u8 *buf_cmd;
- void *from;
-#endif
- /*
- * DMA-map SMP request, response buffers
- */
- sg_req = &task->smp_task.smp_req;
- elem = pci_map_sg(mvi->pdev, sg_req, 1, PCI_DMA_TODEVICE);
- if (!elem)
- return -ENOMEM;
- req_len = sg_dma_len(sg_req);
-
- sg_resp = &task->smp_task.smp_resp;
- elem = pci_map_sg(mvi->pdev, sg_resp, 1, PCI_DMA_FROMDEVICE);
- if (!elem) {
- rc = -ENOMEM;
- goto err_out;
- }
- resp_len = sg_dma_len(sg_resp);
-
- /* must be in dwords */
- if ((req_len & 0x3) || (resp_len & 0x3)) {
- rc = -EINVAL;
- goto err_out_2;
- }
-
- /*
- * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
- */
-
- /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
- buf_tmp = slot->buf;
- buf_tmp_dma = slot->buf_dma;
-
-#if _MV_DUMP
- buf_cmd = buf_tmp;
- hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
- buf_tmp += req_len;
- buf_tmp_dma += req_len;
- slot->cmd_size = req_len;
-#else
- hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
-#endif
-
- /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
- buf_oaf = buf_tmp;
- hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
- buf_tmp += MVS_OAF_SZ;
- buf_tmp_dma += MVS_OAF_SZ;
-
- /* region 3: PRD table ********************************************* */
- buf_prd = buf_tmp;
- if (tei->n_elem)
- hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
- else
- hdr->prd_tbl = 0;
-
- i = sizeof(struct mvs_prd) * tei->n_elem;
- buf_tmp += i;
- buf_tmp_dma += i;
-
- /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
- slot->response = buf_tmp;
- hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
- /*
- * Fill in TX ring and command slot header
- */
- slot->tx = mvi->tx_prod;
- mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
- TXQ_MODE_I | tag |
- (sas_port->phy_mask << TXQ_PHY_SHIFT));
-
- hdr->flags |= flags;
- hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
- hdr->tags = cpu_to_le32(tag);
- hdr->data_len = 0;
-
- /* generate open address frame hdr (first 12 bytes) */
- buf_oaf[0] = (1 << 7) | (0 << 4) | 0x01; /* initiator, SMP, ftype 1h */
- buf_oaf[1] = task->dev->linkrate & 0xf;
- *(u16 *)(buf_oaf + 2) = 0xFFFF; /* SAS SPEC */
- memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
- /* fill in PRD (scatter/gather) table, if any */
- for_each_sg(task->scatter, sg, tei->n_elem, i) {
- buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
- buf_prd->len = cpu_to_le32(sg_dma_len(sg));
- buf_prd++;
- }
-
-#if _MV_DUMP
- /* copy cmd table */
- from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
- memcpy(buf_cmd, from + sg_req->offset, req_len);
- kunmap_atomic(from, KM_IRQ0);
-#endif
- return 0;
-
-err_out_2:
- pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
-err_out:
- pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
- return rc;
-}
-
-static void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp, offs;
- u8 *tfs = &port->taskfileset;
-
- if (*tfs == MVS_ID_NOT_MAPPED)
- return;
-
- offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT);
- if (*tfs < 16) {
- tmp = mr32(PCS);
- mw32(PCS, tmp & ~offs);
- } else {
- tmp = mr32(CTL);
- mw32(CTL, tmp & ~offs);
- }
-
- tmp = mr32(INT_STAT_SRS) & (1U << *tfs);
- if (tmp)
- mw32(INT_STAT_SRS, tmp);
-
- *tfs = MVS_ID_NOT_MAPPED;
-}
-
-static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port)
-{
- int i;
- u32 tmp, offs;
- void __iomem *regs = mvi->regs;
-
- if (port->taskfileset != MVS_ID_NOT_MAPPED)
- return 0;
-
- tmp = mr32(PCS);
-
- for (i = 0; i < mvi->chip->srs_sz; i++) {
- if (i == 16)
- tmp = mr32(CTL);
- offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT);
- if (!(tmp & offs)) {
- port->taskfileset = i;
-
- if (i < 16)
- mw32(PCS, tmp | offs);
- else
- mw32(CTL, tmp | offs);
- tmp = mr32(INT_STAT_SRS) & (1U << i);
- if (tmp)
- mw32(INT_STAT_SRS, tmp);
- return 0;
- }
- }
- return MVS_ID_NOT_MAPPED;
-}
-
-static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
-{
- struct ata_queued_cmd *qc = task->uldd_task;
-
- if (qc) {
- if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
- qc->tf.command == ATA_CMD_FPDMA_READ) {
- *tag = qc->tag;
- return 1;
- }
- }
-
- return 0;
-}
-
-static int mvs_task_prep_ata(struct mvs_info *mvi,
- struct mvs_task_exec_info *tei)
-{
- struct sas_task *task = tei->task;
- struct domain_device *dev = task->dev;
- struct mvs_cmd_hdr *hdr = tei->hdr;
- struct asd_sas_port *sas_port = dev->port;
- struct mvs_slot_info *slot;
- struct scatterlist *sg;
- struct mvs_prd *buf_prd;
- struct mvs_port *port = tei->port;
- u32 tag = tei->tag;
- u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
- void *buf_tmp;
- u8 *buf_cmd, *buf_oaf;
- dma_addr_t buf_tmp_dma;
- u32 i, req_len, resp_len;
- const u32 max_resp_len = SB_RFB_MAX;
-
- if (mvs_assign_reg_set(mvi, port) == MVS_ID_NOT_MAPPED)
- return -EBUSY;
-
- slot = &mvi->slot_info[tag];
- slot->tx = mvi->tx_prod;
- mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
- (TXQ_CMD_STP << TXQ_CMD_SHIFT) |
- (sas_port->phy_mask << TXQ_PHY_SHIFT) |
- (port->taskfileset << TXQ_SRS_SHIFT));
-
- if (task->ata_task.use_ncq)
- flags |= MCH_FPDMA;
- if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
- if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
- flags |= MCH_ATAPI;
- }
-
- /* FIXME: fill in port multiplier number */
-
- hdr->flags = cpu_to_le32(flags);
-
- /* FIXME: the low order order 5 bits for the TAG if enable NCQ */
- if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr->tags))
- task->ata_task.fis.sector_count |= hdr->tags << 3;
- else
- hdr->tags = cpu_to_le32(tag);
- hdr->data_len = cpu_to_le32(task->total_xfer_len);
-
- /*
- * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
- */
-
- /* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
- buf_cmd = buf_tmp = slot->buf;
- buf_tmp_dma = slot->buf_dma;
-
- hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-
- buf_tmp += MVS_ATA_CMD_SZ;
- buf_tmp_dma += MVS_ATA_CMD_SZ;
-#if _MV_DUMP
- slot->cmd_size = MVS_ATA_CMD_SZ;
-#endif
-
- /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
- /* used for STP. unused for SATA? */
- buf_oaf = buf_tmp;
- hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
- buf_tmp += MVS_OAF_SZ;
- buf_tmp_dma += MVS_OAF_SZ;
-
- /* region 3: PRD table ********************************************* */
- buf_prd = buf_tmp;
- if (tei->n_elem)
- hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
- else
- hdr->prd_tbl = 0;
-
- i = sizeof(struct mvs_prd) * tei->n_elem;
- buf_tmp += i;
- buf_tmp_dma += i;
-
- /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
- /* FIXME: probably unused, for SATA. kept here just in case
- * we get a STP/SATA error information record
- */
- slot->response = buf_tmp;
- hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
- req_len = sizeof(struct host_to_dev_fis);
- resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
- sizeof(struct mvs_err_info) - i;
-
- /* request, response lengths */
- resp_len = min(resp_len, max_resp_len);
- hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
-
- task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
- /* fill in command FIS and ATAPI CDB */
- memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
- if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
- memcpy(buf_cmd + STP_ATAPI_CMD,
- task->ata_task.atapi_packet, 16);
-
- /* generate open address frame hdr (first 12 bytes) */
- buf_oaf[0] = (1 << 7) | (2 << 4) | 0x1; /* initiator, STP, ftype 1h */
- buf_oaf[1] = task->dev->linkrate & 0xf;
- *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag);
- memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
- /* fill in PRD (scatter/gather) table, if any */
- for_each_sg(task->scatter, sg, tei->n_elem, i) {
- buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
- buf_prd->len = cpu_to_le32(sg_dma_len(sg));
- buf_prd++;
- }
-
- return 0;
-}
-
-static int mvs_task_prep_ssp(struct mvs_info *mvi,
- struct mvs_task_exec_info *tei)
-{
- struct sas_task *task = tei->task;
- struct mvs_cmd_hdr *hdr = tei->hdr;
- struct mvs_port *port = tei->port;
- struct mvs_slot_info *slot;
- struct scatterlist *sg;
- struct mvs_prd *buf_prd;
- struct ssp_frame_hdr *ssp_hdr;
- void *buf_tmp;
- u8 *buf_cmd, *buf_oaf, fburst = 0;
- dma_addr_t buf_tmp_dma;
- u32 flags;
- u32 resp_len, req_len, i, tag = tei->tag;
- const u32 max_resp_len = SB_RFB_MAX;
- u8 phy_mask;
-
- slot = &mvi->slot_info[tag];
-
- phy_mask = (port->wide_port_phymap) ? port->wide_port_phymap :
- task->dev->port->phy_mask;
- slot->tx = mvi->tx_prod;
- mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
- (TXQ_CMD_SSP << TXQ_CMD_SHIFT) |
- (phy_mask << TXQ_PHY_SHIFT));
-
- flags = MCH_RETRY;
- if (task->ssp_task.enable_first_burst) {
- flags |= MCH_FBURST;
- fburst = (1 << 7);
- }
- hdr->flags = cpu_to_le32(flags |
- (tei->n_elem << MCH_PRD_LEN_SHIFT) |
- (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT));
-
- hdr->tags = cpu_to_le32(tag);
- hdr->data_len = cpu_to_le32(task->total_xfer_len);
-
- /*
- * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
- */
-
- /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
- buf_cmd = buf_tmp = slot->buf;
- buf_tmp_dma = slot->buf_dma;
-
- hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-
- buf_tmp += MVS_SSP_CMD_SZ;
- buf_tmp_dma += MVS_SSP_CMD_SZ;
-#if _MV_DUMP
- slot->cmd_size = MVS_SSP_CMD_SZ;
-#endif
-
- /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
- buf_oaf = buf_tmp;
- hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
- buf_tmp += MVS_OAF_SZ;
- buf_tmp_dma += MVS_OAF_SZ;
-
- /* region 3: PRD table ********************************************* */
- buf_prd = buf_tmp;
- if (tei->n_elem)
- hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
- else
- hdr->prd_tbl = 0;
-
- i = sizeof(struct mvs_prd) * tei->n_elem;
- buf_tmp += i;
- buf_tmp_dma += i;
-
- /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
- slot->response = buf_tmp;
- hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
- resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
- sizeof(struct mvs_err_info) - i;
- resp_len = min(resp_len, max_resp_len);
-
- req_len = sizeof(struct ssp_frame_hdr) + 28;
-
- /* request, response lengths */
- hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
-
- /* generate open address frame hdr (first 12 bytes) */
- buf_oaf[0] = (1 << 7) | (1 << 4) | 0x1; /* initiator, SSP, ftype 1h */
- buf_oaf[1] = task->dev->linkrate & 0xf;
- *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag);
- memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
- /* fill in SSP frame header (Command Table.SSP frame header) */
- ssp_hdr = (struct ssp_frame_hdr *)buf_cmd;
- ssp_hdr->frame_type = SSP_COMMAND;
- memcpy(ssp_hdr->hashed_dest_addr, task->dev->hashed_sas_addr,
- HASHED_SAS_ADDR_SIZE);
- memcpy(ssp_hdr->hashed_src_addr,
- task->dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
- ssp_hdr->tag = cpu_to_be16(tag);
-
- /* fill in command frame IU */
- buf_cmd += sizeof(*ssp_hdr);
- memcpy(buf_cmd, &task->ssp_task.LUN, 8);
- buf_cmd[9] = fburst | task->ssp_task.task_attr |
- (task->ssp_task.task_prio << 3);
- memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
-
- /* fill in PRD (scatter/gather) table, if any */
- for_each_sg(task->scatter, sg, tei->n_elem, i) {
- buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
- buf_prd->len = cpu_to_le32(sg_dma_len(sg));
- buf_prd++;
- }
-
- return 0;
-}
-
-static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags)
-{
- struct domain_device *dev = task->dev;
- struct mvs_info *mvi = dev->port->ha->lldd_ha;
- struct pci_dev *pdev = mvi->pdev;
- void __iomem *regs = mvi->regs;
- struct mvs_task_exec_info tei;
- struct sas_task *t = task;
- struct mvs_slot_info *slot;
- u32 tag = 0xdeadbeef, rc, n_elem = 0;
- unsigned long flags;
- u32 n = num, pass = 0;
-
- spin_lock_irqsave(&mvi->lock, flags);
- do {
- dev = t->dev;
- tei.port = &mvi->port[dev->port->id];
-
- if (!tei.port->port_attached) {
- if (sas_protocol_ata(t->task_proto)) {
- rc = SAS_PHY_DOWN;
- goto out_done;
- } else {
- struct task_status_struct *ts = &t->task_status;
- ts->resp = SAS_TASK_UNDELIVERED;
- ts->stat = SAS_PHY_DOWN;
- t->task_done(t);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
- continue;
- }
- }
-
- if (!sas_protocol_ata(t->task_proto)) {
- if (t->num_scatter) {
- n_elem = pci_map_sg(mvi->pdev, t->scatter,
- t->num_scatter,
- t->data_dir);
- if (!n_elem) {
- rc = -ENOMEM;
- goto err_out;
- }
- }
- } else {
- n_elem = t->num_scatter;
- }
-
- rc = mvs_tag_alloc(mvi, &tag);
- if (rc)
- goto err_out;
-
- slot = &mvi->slot_info[tag];
- t->lldd_task = NULL;
- slot->n_elem = n_elem;
- memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
- tei.task = t;
- tei.hdr = &mvi->slot[tag];
- tei.tag = tag;
- tei.n_elem = n_elem;
-
- switch (t->task_proto) {
- case SAS_PROTOCOL_SMP:
- rc = mvs_task_prep_smp(mvi, &tei);
- break;
- case SAS_PROTOCOL_SSP:
- rc = mvs_task_prep_ssp(mvi, &tei);
- break;
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
- rc = mvs_task_prep_ata(mvi, &tei);
- break;
- default:
- dev_printk(KERN_ERR, &pdev->dev,
- "unknown sas_task proto: 0x%x\n",
- t->task_proto);
- rc = -EINVAL;
- break;
- }
-
- if (rc)
- goto err_out_tag;
-
- slot->task = t;
- slot->port = tei.port;
- t->lldd_task = (void *) slot;
- list_add_tail(&slot->list, &slot->port->list);
- /* TODO: select normal or high priority */
-
- spin_lock(&t->task_state_lock);
- t->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock(&t->task_state_lock);
-
- mvs_hba_memory_dump(mvi, tag, t->task_proto);
-
- ++pass;
- mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
- if (n > 1)
- t = list_entry(t->list.next, struct sas_task, list);
- } while (--n);
-
- rc = 0;
- goto out_done;
-
-err_out_tag:
- mvs_tag_free(mvi, tag);
-err_out:
- dev_printk(KERN_ERR, &pdev->dev, "mvsas exec failed[%d]!\n", rc);
- if (!sas_protocol_ata(t->task_proto))
- if (n_elem)
- pci_unmap_sg(mvi->pdev, t->scatter, n_elem,
- t->data_dir);
-out_done:
- if (pass)
- mw32(TX_PROD_IDX, (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
- spin_unlock_irqrestore(&mvi->lock, flags);
- return rc;
-}
-
-static int mvs_task_abort(struct sas_task *task)
-{
- int rc;
- unsigned long flags;
- struct mvs_info *mvi = task->dev->port->ha->lldd_ha;
- struct pci_dev *pdev = mvi->pdev;
- int tag;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (task->task_state_flags & SAS_TASK_STATE_DONE) {
- rc = TMF_RESP_FUNC_COMPLETE;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- goto out_done;
- }
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- switch (task->task_proto) {
- case SAS_PROTOCOL_SMP:
- dev_printk(KERN_DEBUG, &pdev->dev, "SMP Abort! \n");
- break;
- case SAS_PROTOCOL_SSP:
- dev_printk(KERN_DEBUG, &pdev->dev, "SSP Abort! \n");
- break;
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:{
- dev_printk(KERN_DEBUG, &pdev->dev, "STP Abort! \n");
-#if _MV_DUMP
- dev_printk(KERN_DEBUG, &pdev->dev, "Dump D2H FIS: \n");
- mvs_hexdump(sizeof(struct host_to_dev_fis),
- (void *)&task->ata_task.fis, 0);
- dev_printk(KERN_DEBUG, &pdev->dev, "Dump ATAPI Cmd : \n");
- mvs_hexdump(16, task->ata_task.atapi_packet, 0);
-#endif
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) {
- /* TODO */
- ;
- }
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- break;
- }
- default:
- break;
- }
-
- if (mvs_find_tag(mvi, task, &tag)) {
- spin_lock_irqsave(&mvi->lock, flags);
- mvs_slot_task_free(mvi, task, &mvi->slot_info[tag], tag);
- spin_unlock_irqrestore(&mvi->lock, flags);
- }
- if (!mvs_task_exec(task, 1, GFP_ATOMIC))
- rc = TMF_RESP_FUNC_COMPLETE;
- else
- rc = TMF_RESP_FUNC_FAILED;
-out_done:
- return rc;
-}
-
-static void mvs_free(struct mvs_info *mvi)
-{
- int i;
-
- if (!mvi)
- return;
-
- for (i = 0; i < MVS_SLOTS; i++) {
- struct mvs_slot_info *slot = &mvi->slot_info[i];
-
- if (slot->buf)
- dma_free_coherent(&mvi->pdev->dev, MVS_SLOT_BUF_SZ,
- slot->buf, slot->buf_dma);
- }
-
- if (mvi->tx)
- dma_free_coherent(&mvi->pdev->dev,
- sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
- mvi->tx, mvi->tx_dma);
- if (mvi->rx_fis)
- dma_free_coherent(&mvi->pdev->dev, MVS_RX_FISL_SZ,
- mvi->rx_fis, mvi->rx_fis_dma);
- if (mvi->rx)
- dma_free_coherent(&mvi->pdev->dev,
- sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
- mvi->rx, mvi->rx_dma);
- if (mvi->slot)
- dma_free_coherent(&mvi->pdev->dev,
- sizeof(*mvi->slot) * MVS_SLOTS,
- mvi->slot, mvi->slot_dma);
-#ifdef MVS_ENABLE_PERI
- if (mvi->peri_regs)
- iounmap(mvi->peri_regs);
-#endif
- if (mvi->regs)
- iounmap(mvi->regs);
- if (mvi->shost)
- scsi_host_put(mvi->shost);
- kfree(mvi->sas.sas_port);
- kfree(mvi->sas.sas_phy);
- kfree(mvi);
-}
-
-/* FIXME: locking? */
-static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
- void *funcdata)
-{
- struct mvs_info *mvi = sas_phy->ha->lldd_ha;
- int rc = 0, phy_id = sas_phy->id;
- u32 tmp;
-
- tmp = mvs_read_phy_ctl(mvi, phy_id);
-
- switch (func) {
- case PHY_FUNC_SET_LINK_RATE:{
- struct sas_phy_linkrates *rates = funcdata;
- u32 lrmin = 0, lrmax = 0;
-
- lrmin = (rates->minimum_linkrate << 8);
- lrmax = (rates->maximum_linkrate << 12);
-
- if (lrmin) {
- tmp &= ~(0xf << 8);
- tmp |= lrmin;
- }
- if (lrmax) {
- tmp &= ~(0xf << 12);
- tmp |= lrmax;
- }
- mvs_write_phy_ctl(mvi, phy_id, tmp);
- break;
- }
-
- case PHY_FUNC_HARD_RESET:
- if (tmp & PHY_RST_HARD)
- break;
- mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST_HARD);
- break;
-
- case PHY_FUNC_LINK_RESET:
- mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST);
- break;
-
- case PHY_FUNC_DISABLE:
- case PHY_FUNC_RELEASE_SPINUP_HOLD:
- default:
- rc = -EOPNOTSUPP;
- }
-
- return rc;
-}
-
-static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
-{
- struct mvs_phy *phy = &mvi->phy[phy_id];
- struct asd_sas_phy *sas_phy = &phy->sas_phy;
-
- sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
- sas_phy->class = SAS;
- sas_phy->iproto = SAS_PROTOCOL_ALL;
- sas_phy->tproto = 0;
- sas_phy->type = PHY_TYPE_PHYSICAL;
- sas_phy->role = PHY_ROLE_INITIATOR;
- sas_phy->oob_mode = OOB_NOT_CONNECTED;
- sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
-
- sas_phy->id = phy_id;
- sas_phy->sas_addr = &mvi->sas_addr[0];
- sas_phy->frame_rcvd = &phy->frame_rcvd[0];
- sas_phy->ha = &mvi->sas;
- sas_phy->lldd_phy = phy;
-}
-
-static struct mvs_info *__devinit mvs_alloc(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct mvs_info *mvi;
- unsigned long res_start, res_len, res_flag;
- struct asd_sas_phy **arr_phy;
- struct asd_sas_port **arr_port;
- const struct mvs_chip_info *chip = &mvs_chips[ent->driver_data];
- int i;
-
- /*
- * alloc and init our per-HBA mvs_info struct
- */
-
- mvi = kzalloc(sizeof(*mvi), GFP_KERNEL);
- if (!mvi)
- return NULL;
-
- spin_lock_init(&mvi->lock);
-#ifdef MVS_USE_TASKLET
- tasklet_init(&mvi->tasklet, mvs_tasklet, (unsigned long)mvi);
-#endif
- mvi->pdev = pdev;
- mvi->chip = chip;
-
- if (pdev->device == 0x6440 && pdev->revision == 0)
- mvi->flags |= MVF_PHY_PWR_FIX;
-
- /*
- * alloc and init SCSI, SAS glue
- */
-
- mvi->shost = scsi_host_alloc(&mvs_sht, sizeof(void *));
- if (!mvi->shost)
- goto err_out;
-
- arr_phy = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL);
- arr_port = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL);
- if (!arr_phy || !arr_port)
- goto err_out;
-
- for (i = 0; i < MVS_MAX_PHYS; i++) {
- mvs_phy_init(mvi, i);
- arr_phy[i] = &mvi->phy[i].sas_phy;
- arr_port[i] = &mvi->port[i].sas_port;
- mvi->port[i].taskfileset = MVS_ID_NOT_MAPPED;
- mvi->port[i].wide_port_phymap = 0;
- mvi->port[i].port_attached = 0;
- INIT_LIST_HEAD(&mvi->port[i].list);
- }
-
- SHOST_TO_SAS_HA(mvi->shost) = &mvi->sas;
- mvi->shost->transportt = mvs_stt;
- mvi->shost->max_id = 21;
- mvi->shost->max_lun = ~0;
- mvi->shost->max_channel = 0;
- mvi->shost->max_cmd_len = 16;
-
- mvi->sas.sas_ha_name = DRV_NAME;
- mvi->sas.dev = &pdev->dev;
- mvi->sas.lldd_module = THIS_MODULE;
- mvi->sas.sas_addr = &mvi->sas_addr[0];
- mvi->sas.sas_phy = arr_phy;
- mvi->sas.sas_port = arr_port;
- mvi->sas.num_phys = chip->n_phy;
- mvi->sas.lldd_max_execute_num = 1;
- mvi->sas.lldd_queue_size = MVS_QUEUE_SIZE;
- mvi->shost->can_queue = MVS_CAN_QUEUE;
- mvi->shost->cmd_per_lun = MVS_SLOTS / mvi->sas.num_phys;
- mvi->sas.lldd_ha = mvi;
- mvi->sas.core.shost = mvi->shost;
-
- mvs_tag_init(mvi);
-
- /*
- * ioremap main and peripheral registers
- */
-
-#ifdef MVS_ENABLE_PERI
- res_start = pci_resource_start(pdev, 2);
- res_len = pci_resource_len(pdev, 2);
- if (!res_start || !res_len)
- goto err_out;
-
- mvi->peri_regs = ioremap_nocache(res_start, res_len);
- if (!mvi->peri_regs)
- goto err_out;
-#endif
-
- res_start = pci_resource_start(pdev, 4);
- res_len = pci_resource_len(pdev, 4);
- if (!res_start || !res_len)
- goto err_out;
-
- res_flag = pci_resource_flags(pdev, 4);
- if (res_flag & IORESOURCE_CACHEABLE)
- mvi->regs = ioremap(res_start, res_len);
- else
- mvi->regs = ioremap_nocache(res_start, res_len);
-
- if (!mvi->regs)
- goto err_out;
-
- /*
- * alloc and init our DMA areas
- */
-
- mvi->tx = dma_alloc_coherent(&pdev->dev,
- sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
- &mvi->tx_dma, GFP_KERNEL);
- if (!mvi->tx)
- goto err_out;
- memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ);
-
- mvi->rx_fis = dma_alloc_coherent(&pdev->dev, MVS_RX_FISL_SZ,
- &mvi->rx_fis_dma, GFP_KERNEL);
- if (!mvi->rx_fis)
- goto err_out;
- memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ);
-
- mvi->rx = dma_alloc_coherent(&pdev->dev,
- sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
- &mvi->rx_dma, GFP_KERNEL);
- if (!mvi->rx)
- goto err_out;
- memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1));
-
- mvi->rx[0] = cpu_to_le32(0xfff);
- mvi->rx_cons = 0xfff;
-
- mvi->slot = dma_alloc_coherent(&pdev->dev,
- sizeof(*mvi->slot) * MVS_SLOTS,
- &mvi->slot_dma, GFP_KERNEL);
- if (!mvi->slot)
- goto err_out;
- memset(mvi->slot, 0, sizeof(*mvi->slot) * MVS_SLOTS);
-
- for (i = 0; i < MVS_SLOTS; i++) {
- struct mvs_slot_info *slot = &mvi->slot_info[i];
-
- slot->buf = dma_alloc_coherent(&pdev->dev, MVS_SLOT_BUF_SZ,
- &slot->buf_dma, GFP_KERNEL);
- if (!slot->buf)
- goto err_out;
- memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
- }
-
- /* finally, read NVRAM to get our SAS address */
- if (mvs_nvram_read(mvi, NVR_SAS_ADDR, &mvi->sas_addr, 8))
- goto err_out;
- return mvi;
-
-err_out:
- mvs_free(mvi);
- return NULL;
-}
-
-static u32 mvs_cr32(void __iomem *regs, u32 addr)
-{
- mw32(CMD_ADDR, addr);
- return mr32(CMD_DATA);
-}
-
-static void mvs_cw32(void __iomem *regs, u32 addr, u32 val)
-{
- mw32(CMD_ADDR, addr);
- mw32(CMD_DATA, val);
-}
-
-static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port)
-{
- void __iomem *regs = mvi->regs;
- return (port < 4)?mr32(P0_SER_CTLSTAT + port * 4):
- mr32(P4_SER_CTLSTAT + (port - 4) * 4);
-}
-
-static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val)
-{
- void __iomem *regs = mvi->regs;
- if (port < 4)
- mw32(P0_SER_CTLSTAT + port * 4, val);
- else
- mw32(P4_SER_CTLSTAT + (port - 4) * 4, val);
-}
-
-static u32 mvs_read_port(struct mvs_info *mvi, u32 off, u32 off2, u32 port)
-{
- void __iomem *regs = mvi->regs + off;
- void __iomem *regs2 = mvi->regs + off2;
- return (port < 4)?readl(regs + port * 8):
- readl(regs2 + (port - 4) * 8);
-}
-
-static void mvs_write_port(struct mvs_info *mvi, u32 off, u32 off2,
- u32 port, u32 val)
-{
- void __iomem *regs = mvi->regs + off;
- void __iomem *regs2 = mvi->regs + off2;
- if (port < 4)
- writel(val, regs + port * 8);
- else
- writel(val, regs2 + (port - 4) * 8);
-}
-
-static u32 mvs_read_port_cfg_data(struct mvs_info *mvi, u32 port)
-{
- return mvs_read_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port);
-}
-
-static void mvs_write_port_cfg_data(struct mvs_info *mvi, u32 port, u32 val)
-{
- mvs_write_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port, val);
-}
-
-static void mvs_write_port_cfg_addr(struct mvs_info *mvi, u32 port, u32 addr)
-{
- mvs_write_port(mvi, MVS_P0_CFG_ADDR, MVS_P4_CFG_ADDR, port, addr);
-}
-
-static u32 mvs_read_port_vsr_data(struct mvs_info *mvi, u32 port)
-{
- return mvs_read_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port);
-}
-
-static void mvs_write_port_vsr_data(struct mvs_info *mvi, u32 port, u32 val)
-{
- mvs_write_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port, val);
-}
-
-static void mvs_write_port_vsr_addr(struct mvs_info *mvi, u32 port, u32 addr)
-{
- mvs_write_port(mvi, MVS_P0_VSR_ADDR, MVS_P4_VSR_ADDR, port, addr);
-}
-
-static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port)
-{
- return mvs_read_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port);
-}
-
-static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val)
-{
- mvs_write_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port, val);
-}
-
-static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port)
-{
- return mvs_read_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port);
-}
-
-static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val)
-{
- mvs_write_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port, val);
-}
-
-static void __devinit mvs_phy_hacks(struct mvs_info *mvi)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp;
-
- /* workaround for SATA R-ERR, to ignore phy glitch */
- tmp = mvs_cr32(regs, CMD_PHY_TIMER);
- tmp &= ~(1 << 9);
- tmp |= (1 << 10);
- mvs_cw32(regs, CMD_PHY_TIMER, tmp);
-
- /* enable retry 127 times */
- mvs_cw32(regs, CMD_SAS_CTL1, 0x7f7f);
-
- /* extend open frame timeout to max */
- tmp = mvs_cr32(regs, CMD_SAS_CTL0);
- tmp &= ~0xffff;
- tmp |= 0x3fff;
- mvs_cw32(regs, CMD_SAS_CTL0, tmp);
-
- /* workaround for WDTIMEOUT , set to 550 ms */
- mvs_cw32(regs, CMD_WD_TIMER, 0x86470);
-
- /* not to halt for different port op during wideport link change */
- mvs_cw32(regs, CMD_APP_ERR_CONFIG, 0xffefbf7d);
-
- /* workaround for Seagate disk not-found OOB sequence, recv
- * COMINIT before sending out COMWAKE */
- tmp = mvs_cr32(regs, CMD_PHY_MODE_21);
- tmp &= 0x0000ffff;
- tmp |= 0x00fa0000;
- mvs_cw32(regs, CMD_PHY_MODE_21, tmp);
-
- tmp = mvs_cr32(regs, CMD_PHY_TIMER);
- tmp &= 0x1fffffff;
- tmp |= (2U << 29); /* 8 ms retry */
- mvs_cw32(regs, CMD_PHY_TIMER, tmp);
-
- /* TEST - for phy decoding error, adjust voltage levels */
- mw32(P0_VSR_ADDR + 0, 0x8);
- mw32(P0_VSR_DATA + 0, 0x2F0);
-
- mw32(P0_VSR_ADDR + 8, 0x8);
- mw32(P0_VSR_DATA + 8, 0x2F0);
-
- mw32(P0_VSR_ADDR + 16, 0x8);
- mw32(P0_VSR_DATA + 16, 0x2F0);
-
- mw32(P0_VSR_ADDR + 24, 0x8);
- mw32(P0_VSR_DATA + 24, 0x2F0);
-
-}
-
-static void mvs_enable_xmt(struct mvs_info *mvi, int PhyId)
-{
- void __iomem *regs = mvi->regs;
- u32 tmp;
-
- tmp = mr32(PCS);
- if (mvi->chip->n_phy <= 4)
- tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT);
- else
- tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT2);
- mw32(PCS, tmp);
-}
-
-static void mvs_detect_porttype(struct mvs_info *mvi, int i)
-{
- void __iomem *regs = mvi->regs;
- u32 reg;
- struct mvs_phy *phy = &mvi->phy[i];
-
- /* TODO check & save device type */
- reg = mr32(GBL_PORT_TYPE);
-
- if (reg & MODE_SAS_SATA & (1 << i))
- phy->phy_type |= PORT_TYPE_SAS;
- else
- phy->phy_type |= PORT_TYPE_SATA;
-}
-
-static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
-{
- u32 *s = (u32 *) buf;
-
- if (!s)
- return NULL;
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
- s[3] = mvs_read_port_cfg_data(mvi, i);
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
- s[2] = mvs_read_port_cfg_data(mvi, i);
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
- s[1] = mvs_read_port_cfg_data(mvi, i);
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
- s[0] = mvs_read_port_cfg_data(mvi, i);
-
- return (void *)s;
-}
-
-static u32 mvs_is_sig_fis_received(u32 irq_status)
-{
- return irq_status & PHYEV_SIG_FIS;
-}
-
-static void mvs_update_wideport(struct mvs_info *mvi, int i)
-{
- struct mvs_phy *phy = &mvi->phy[i];
- struct mvs_port *port = phy->port;
- int j, no;
-
- for_each_phy(port->wide_port_phymap, no, j, mvi->chip->n_phy)
- if (no & 1) {
- mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT);
- mvs_write_port_cfg_data(mvi, no,
- port->wide_port_phymap);
- } else {
- mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT);
- mvs_write_port_cfg_data(mvi, no, 0);
- }
-}
-
-static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i)
-{
- u32 tmp;
- struct mvs_phy *phy = &mvi->phy[i];
- struct mvs_port *port = phy->port;;
-
- tmp = mvs_read_phy_ctl(mvi, i);
-
- if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) {
- if (!port)
- phy->phy_attached = 1;
- return tmp;
- }
-
- if (port) {
- if (phy->phy_type & PORT_TYPE_SAS) {
- port->wide_port_phymap &= ~(1U << i);
- if (!port->wide_port_phymap)
- port->port_attached = 0;
- mvs_update_wideport(mvi, i);
- } else if (phy->phy_type & PORT_TYPE_SATA)
- port->port_attached = 0;
- mvs_free_reg_set(mvi, phy->port);
- phy->port = NULL;
- phy->phy_attached = 0;
- phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
- }
- return 0;
-}
-
-static void mvs_update_phyinfo(struct mvs_info *mvi, int i,
- int get_st)
-{
- struct mvs_phy *phy = &mvi->phy[i];
- struct pci_dev *pdev = mvi->pdev;
- u32 tmp;
- u64 tmp64;
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY);
- phy->dev_info = mvs_read_port_cfg_data(mvi, i);
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI);
- phy->dev_sas_addr = (u64) mvs_read_port_cfg_data(mvi, i) << 32;
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO);
- phy->dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
-
- if (get_st) {
- phy->irq_status = mvs_read_port_irq_stat(mvi, i);
- phy->phy_status = mvs_is_phy_ready(mvi, i);
- }
-
- if (phy->phy_status) {
- u32 phy_st;
- struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i];
-
- mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
- phy_st = mvs_read_port_cfg_data(mvi, i);
-
- sas_phy->linkrate =
- (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
- PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
- phy->minimum_linkrate =
- (phy->phy_status &
- PHY_MIN_SPP_PHYS_LINK_RATE_MASK) >> 8;
- phy->maximum_linkrate =
- (phy->phy_status &
- PHY_MAX_SPP_PHYS_LINK_RATE_MASK) >> 12;
-
- if (phy->phy_type & PORT_TYPE_SAS) {
- /* Updated attached_sas_addr */
- mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI);
- phy->att_dev_sas_addr =
- (u64) mvs_read_port_cfg_data(mvi, i) << 32;
- mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO);
- phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
- mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO);
- phy->att_dev_info = mvs_read_port_cfg_data(mvi, i);
- phy->identify.device_type =
- phy->att_dev_info & PORT_DEV_TYPE_MASK;
-
- if (phy->identify.device_type == SAS_END_DEV)
- phy->identify.target_port_protocols =
- SAS_PROTOCOL_SSP;
- else if (phy->identify.device_type != NO_DEVICE)
- phy->identify.target_port_protocols =
- SAS_PROTOCOL_SMP;
- if (phy_st & PHY_OOB_DTCTD)
- sas_phy->oob_mode = SAS_OOB_MODE;
- phy->frame_rcvd_size =
- sizeof(struct sas_identify_frame);
- } else if (phy->phy_type & PORT_TYPE_SATA) {
- phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
- if (mvs_is_sig_fis_received(phy->irq_status)) {
- phy->att_dev_sas_addr = i; /* temp */
- if (phy_st & PHY_OOB_DTCTD)
- sas_phy->oob_mode = SATA_OOB_MODE;
- phy->frame_rcvd_size =
- sizeof(struct dev_to_host_fis);
- mvs_get_d2h_reg(mvi, i,
- (void *)sas_phy->frame_rcvd);
- } else {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "No sig fis\n");
- phy->phy_type &= ~(PORT_TYPE_SATA);
- goto out_done;
- }
- }
- tmp64 = cpu_to_be64(phy->att_dev_sas_addr);
- memcpy(sas_phy->attached_sas_addr, &tmp64, SAS_ADDR_SIZE);
-
- dev_printk(KERN_DEBUG, &pdev->dev,
- "phy[%d] Get Attached Address 0x%llX ,"
- " SAS Address 0x%llX\n",
- i,
- (unsigned long long)phy->att_dev_sas_addr,
- (unsigned long long)phy->dev_sas_addr);
- dev_printk(KERN_DEBUG, &pdev->dev,
- "Rate = %x , type = %d\n",
- sas_phy->linkrate, phy->phy_type);
-
- /* workaround for HW phy decoding error on 1.5g disk drive */
- mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
- tmp = mvs_read_port_vsr_data(mvi, i);
- if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
- PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) ==
- SAS_LINK_RATE_1_5_GBPS)
- tmp &= ~PHY_MODE6_LATECLK;
- else
- tmp |= PHY_MODE6_LATECLK;
- mvs_write_port_vsr_data(mvi, i, tmp);
-
- }
-out_done:
- if (get_st)
- mvs_write_port_irq_stat(mvi, i, phy->irq_status);
-}
-
-static void mvs_port_formed(struct asd_sas_phy *sas_phy)
-{
- struct sas_ha_struct *sas_ha = sas_phy->ha;
- struct mvs_info *mvi = sas_ha->lldd_ha;
- struct asd_sas_port *sas_port = sas_phy->port;
- struct mvs_phy *phy = sas_phy->lldd_phy;
- struct mvs_port *port = &mvi->port[sas_port->id];
- unsigned long flags;
-
- spin_lock_irqsave(&mvi->lock, flags);
- port->port_attached = 1;
- phy->port = port;
- port->taskfileset = MVS_ID_NOT_MAPPED;
- if (phy->phy_type & PORT_TYPE_SAS) {
- port->wide_port_phymap = sas_port->phy_mask;
- mvs_update_wideport(mvi, sas_phy->id);
- }
- spin_unlock_irqrestore(&mvi->lock, flags);
-}
-
-static int mvs_I_T_nexus_reset(struct domain_device *dev)
-{
- return TMF_RESP_FUNC_FAILED;
-}
-
-static int __devinit mvs_hw_init(struct mvs_info *mvi)
-{
- void __iomem *regs = mvi->regs;
- int i;
- u32 tmp, cctl;
-
- /* make sure interrupts are masked immediately (paranoia) */
- mw32(GBL_CTL, 0);
- tmp = mr32(GBL_CTL);
-
- /* Reset Controller */
- if (!(tmp & HBA_RST)) {
- if (mvi->flags & MVF_PHY_PWR_FIX) {
- pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
- tmp &= ~PCTL_PWR_ON;
- tmp |= PCTL_OFF;
- pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
-
- pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
- tmp &= ~PCTL_PWR_ON;
- tmp |= PCTL_OFF;
- pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
- }
-
- /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */
- mw32_f(GBL_CTL, HBA_RST);
- }
-
- /* wait for reset to finish; timeout is just a guess */
- i = 1000;
- while (i-- > 0) {
- msleep(10);
-
- if (!(mr32(GBL_CTL) & HBA_RST))
- break;
- }
- if (mr32(GBL_CTL) & HBA_RST) {
- dev_printk(KERN_ERR, &mvi->pdev->dev, "HBA reset failed\n");
- return -EBUSY;
- }
-
- /* Init Chip */
- /* make sure RST is set; HBA_RST /should/ have done that for us */
- cctl = mr32(CTL);
- if (cctl & CCTL_RST)
- cctl &= ~CCTL_RST;
- else
- mw32_f(CTL, cctl | CCTL_RST);
-
- /* write to device control _AND_ device status register? - A.C. */
- pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp);
- tmp &= ~PRD_REQ_MASK;
- tmp |= PRD_REQ_SIZE;
- pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp);
-
- pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
- tmp |= PCTL_PWR_ON;
- tmp &= ~PCTL_OFF;
- pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
-
- pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
- tmp |= PCTL_PWR_ON;
- tmp &= ~PCTL_OFF;
- pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
-
- mw32_f(CTL, cctl);
-
- /* reset control */
- mw32(PCS, 0); /*MVS_PCS */
-
- mvs_phy_hacks(mvi);
-
- mw32(CMD_LIST_LO, mvi->slot_dma);
- mw32(CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
-
- mw32(RX_FIS_LO, mvi->rx_fis_dma);
- mw32(RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
-
- mw32(TX_CFG, MVS_CHIP_SLOT_SZ);
- mw32(TX_LO, mvi->tx_dma);
- mw32(TX_HI, (mvi->tx_dma >> 16) >> 16);
-
- mw32(RX_CFG, MVS_RX_RING_SZ);
- mw32(RX_LO, mvi->rx_dma);
- mw32(RX_HI, (mvi->rx_dma >> 16) >> 16);
-
- /* enable auto port detection */
- mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN);
- msleep(1100);
- /* init and reset phys */
- for (i = 0; i < mvi->chip->n_phy; i++) {
- u32 lo = be32_to_cpu(*(u32 *)&mvi->sas_addr[4]);
- u32 hi = be32_to_cpu(*(u32 *)&mvi->sas_addr[0]);
-
- mvs_detect_porttype(mvi, i);
-
- /* set phy local SAS address */
- mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO);
- mvs_write_port_cfg_data(mvi, i, lo);
- mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI);
- mvs_write_port_cfg_data(mvi, i, hi);
-
- /* reset phy */
- tmp = mvs_read_phy_ctl(mvi, i);
- tmp |= PHY_RST;
- mvs_write_phy_ctl(mvi, i, tmp);
- }
-
- msleep(100);
-
- for (i = 0; i < mvi->chip->n_phy; i++) {
- /* clear phy int status */
- tmp = mvs_read_port_irq_stat(mvi, i);
- tmp &= ~PHYEV_SIG_FIS;
- mvs_write_port_irq_stat(mvi, i, tmp);
-
- /* set phy int mask */
- tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS |
- PHYEV_ID_DONE | PHYEV_DEC_ERR;
- mvs_write_port_irq_mask(mvi, i, tmp);
-
- msleep(100);
- mvs_update_phyinfo(mvi, i, 1);
- mvs_enable_xmt(mvi, i);
- }
-
- /* FIXME: update wide port bitmaps */
-
- /* little endian for open address and command table, etc. */
- /* A.C.
- * it seems that ( from the spec ) turning on big-endian won't
- * do us any good on big-endian machines, need further confirmation
- */
- cctl = mr32(CTL);
- cctl |= CCTL_ENDIAN_CMD;
- cctl |= CCTL_ENDIAN_DATA;
- cctl &= ~CCTL_ENDIAN_OPEN;
- cctl |= CCTL_ENDIAN_RSP;
- mw32_f(CTL, cctl);
-
- /* reset CMD queue */
- tmp = mr32(PCS);
- tmp |= PCS_CMD_RST;
- mw32(PCS, tmp);
- /* interrupt coalescing may cause missing HW interrput in some case,
- * and the max count is 0x1ff, while our max slot is 0x200,
- * it will make count 0.
- */
- tmp = 0;
- mw32(INT_COAL, tmp);
-
- tmp = 0x100;
- mw32(INT_COAL_TMOUT, tmp);
-
- /* ladies and gentlemen, start your engines */
- mw32(TX_CFG, 0);
- mw32(TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
- mw32(RX_CFG, MVS_RX_RING_SZ | RX_EN);
- /* enable CMD/CMPL_Q/RESP mode */
- mw32(PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN);
-
- /* enable completion queue interrupt */
- tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS);
- mw32(INT_MASK, tmp);
-
- /* Enable SRS interrupt */
- mw32(INT_MASK_SRS, 0xFF);
- return 0;
-}
-
-static void __devinit mvs_print_info(struct mvs_info *mvi)
-{
- struct pci_dev *pdev = mvi->pdev;
- static int printed_version;
-
- if (!printed_version++)
- dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
-
- dev_printk(KERN_INFO, &pdev->dev, "%u phys, addr %llx\n",
- mvi->chip->n_phy, SAS_ADDR(mvi->sas_addr));
-}
-
-static int __devinit mvs_pci_init(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- int rc;
- struct mvs_info *mvi;
- irq_handler_t irq_handler = mvs_interrupt;
-
- rc = pci_enable_device(pdev);
- if (rc)
- return rc;
-
- pci_set_master(pdev);
-
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc)
- goto err_out_disable;
-
- rc = pci_go_64(pdev);
- if (rc)
- goto err_out_regions;
-
- mvi = mvs_alloc(pdev, ent);
- if (!mvi) {
- rc = -ENOMEM;
- goto err_out_regions;
- }
-
- rc = mvs_hw_init(mvi);
- if (rc)
- goto err_out_mvi;
-
-#ifndef MVS_DISABLE_MSI
- if (!pci_enable_msi(pdev)) {
- u32 tmp;
- void __iomem *regs = mvi->regs;
- mvi->flags |= MVF_MSI;
- irq_handler = mvs_msi_interrupt;
- tmp = mr32(PCS);
- mw32(PCS, tmp | PCS_SELF_CLEAR);
- }
-#endif
-
- rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, mvi);
- if (rc)
- goto err_out_msi;
-
- rc = scsi_add_host(mvi->shost, &pdev->dev);
- if (rc)
- goto err_out_irq;
-
- rc = sas_register_ha(&mvi->sas);
- if (rc)
- goto err_out_shost;
-
- pci_set_drvdata(pdev, mvi);
-
- mvs_print_info(mvi);
-
- mvs_hba_interrupt_enable(mvi);
-
- scsi_scan_host(mvi->shost);
-
- return 0;
-
-err_out_shost:
- scsi_remove_host(mvi->shost);
-err_out_irq:
- free_irq(pdev->irq, mvi);
-err_out_msi:
- if (mvi->flags |= MVF_MSI)
- pci_disable_msi(pdev);
-err_out_mvi:
- mvs_free(mvi);
-err_out_regions:
- pci_release_regions(pdev);
-err_out_disable:
- pci_disable_device(pdev);
- return rc;
-}
-
-static void __devexit mvs_pci_remove(struct pci_dev *pdev)
-{
- struct mvs_info *mvi = pci_get_drvdata(pdev);
-
- pci_set_drvdata(pdev, NULL);
-
- if (mvi) {
- sas_unregister_ha(&mvi->sas);
- mvs_hba_interrupt_disable(mvi);
- sas_remove_host(mvi->shost);
- scsi_remove_host(mvi->shost);
-
- free_irq(pdev->irq, mvi);
- if (mvi->flags & MVF_MSI)
- pci_disable_msi(pdev);
- mvs_free(mvi);
- pci_release_regions(pdev);
- }
- pci_disable_device(pdev);
-}
-
-static struct sas_domain_function_template mvs_transport_ops = {
- .lldd_execute_task = mvs_task_exec,
- .lldd_control_phy = mvs_phy_control,
- .lldd_abort_task = mvs_task_abort,
- .lldd_port_formed = mvs_port_formed,
- .lldd_I_T_nexus_reset = mvs_I_T_nexus_reset,
-};
-
-static struct pci_device_id __devinitdata mvs_pci_table[] = {
- { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
- { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
- {
- .vendor = PCI_VENDOR_ID_MARVELL,
- .device = 0x6440,
- .subvendor = PCI_ANY_ID,
- .subdevice = 0x6480,
- .class = 0,
- .class_mask = 0,
- .driver_data = chip_6480,
- },
- { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 },
- { PCI_VDEVICE(MARVELL, 0x6480), chip_6480 },
-
- { } /* terminate list */
-};
-
-static struct pci_driver mvs_pci_driver = {
- .name = DRV_NAME,
- .id_table = mvs_pci_table,
- .probe = mvs_pci_init,
- .remove = __devexit_p(mvs_pci_remove),
-};
-
-static int __init mvs_init(void)
-{
- int rc;
-
- mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);
- if (!mvs_stt)
- return -ENOMEM;
-
- rc = pci_register_driver(&mvs_pci_driver);
- if (rc)
- goto err_out;
-
- return 0;
-
-err_out:
- sas_release_transport(mvs_stt);
- return rc;
-}
-
-static void __exit mvs_exit(void)
-{
- pci_unregister_driver(&mvs_pci_driver);
- sas_release_transport(mvs_stt);
-}
-
-module_init(mvs_init);
-module_exit(mvs_exit);
-
-MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
-MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver");
-MODULE_VERSION(DRV_VERSION);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, mvs_pci_table);
diff --git a/drivers/scsi/mvsas/Kconfig b/drivers/scsi/mvsas/Kconfig
new file mode 100644
index 000000000000..6de7af27e507
--- /dev/null
+++ b/drivers/scsi/mvsas/Kconfig
@@ -0,0 +1,42 @@
+#
+# Kernel configuration file for 88SE64XX/88SE94XX SAS/SATA driver.
+#
+# Copyright 2007 Red Hat, Inc.
+# Copyright 2008 Marvell. <kewei@marvell.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the 88SE64XX/88SE94XX driver.
+#
+# The 88SE64XX/88SE94XX driver is free software; you can redistribute
+# it and/or modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; version 2 of the
+# License.
+#
+# The 88SE64XX/88SE94XX driver is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with 88SE64XX/88SE94XX Driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#
+
+config SCSI_MVSAS
+ tristate "Marvell 88SE64XX/88SE94XX SAS/SATA support"
+ depends on PCI
+ select SCSI_SAS_LIBSAS
+ select FW_LOADER
+ help
+ This driver supports Marvell's SAS/SATA 3Gb/s PCI-E 88SE64XX and 6Gb/s
+ PCI-E 88SE94XX chip based host adapters.
+
+config SCSI_MVSAS_DEBUG
+ bool "Compile in debug mode"
+ default y
+ depends on SCSI_MVSAS
+ help
+ Compiles the 88SE64XX/88SE94XX driver in debug mode. In debug mode,
+ the driver prints some messages to the console.
diff --git a/drivers/scsi/mvsas/Makefile b/drivers/scsi/mvsas/Makefile
new file mode 100644
index 000000000000..52ac4264677d
--- /dev/null
+++ b/drivers/scsi/mvsas/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for Marvell 88SE64xx/88SE84xx SAS/SATA driver.
+#
+# Copyright 2007 Red Hat, Inc.
+# Copyright 2008 Marvell. <kewei@marvell.com>
+#
+# This file is licensed under GPLv2.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+ifeq ($(CONFIG_SCSI_MVSAS_DEBUG),y)
+ EXTRA_CFLAGS += -DMV_DEBUG
+endif
+
+obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
+mvsas-y += mv_init.o \
+ mv_sas.o \
+ mv_64xx.o \
+ mv_94xx.o
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c
new file mode 100644
index 000000000000..10a5077b6aed
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_64xx.c
@@ -0,0 +1,793 @@
+/*
+ * Marvell 88SE64xx hardware specific
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#include "mv_sas.h"
+#include "mv_64xx.h"
+#include "mv_chips.h"
+
+static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i)
+{
+ void __iomem *regs = mvi->regs;
+ u32 reg;
+ struct mvs_phy *phy = &mvi->phy[i];
+
+ /* TODO check & save device type */
+ reg = mr32(MVS_GBL_PORT_TYPE);
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ if (reg & MODE_SAS_SATA & (1 << i))
+ phy->phy_type |= PORT_TYPE_SAS;
+ else
+ phy->phy_type |= PORT_TYPE_SATA;
+}
+
+static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ tmp = mr32(MVS_PCS);
+ if (mvi->chip->n_phy <= 4)
+ tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
+ else
+ tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
+ mw32(MVS_PCS, tmp);
+}
+
+static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+
+ mvs_phy_hacks(mvi);
+
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ /* TEST - for phy decoding error, adjust voltage levels */
+ mw32(MVS_P0_VSR_ADDR + 0, 0x8);
+ mw32(MVS_P0_VSR_DATA + 0, 0x2F0);
+
+ mw32(MVS_P0_VSR_ADDR + 8, 0x8);
+ mw32(MVS_P0_VSR_DATA + 8, 0x2F0);
+
+ mw32(MVS_P0_VSR_ADDR + 16, 0x8);
+ mw32(MVS_P0_VSR_DATA + 16, 0x2F0);
+
+ mw32(MVS_P0_VSR_ADDR + 24, 0x8);
+ mw32(MVS_P0_VSR_DATA + 24, 0x2F0);
+ } else {
+ int i;
+ /* disable auto port detection */
+ mw32(MVS_GBL_PORT_TYPE, 0);
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE7);
+ mvs_write_port_vsr_data(mvi, i, 0x90000000);
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE9);
+ mvs_write_port_vsr_data(mvi, i, 0x50f2);
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE11);
+ mvs_write_port_vsr_data(mvi, i, 0x0e);
+ }
+ }
+}
+
+static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
+{
+ void __iomem *regs = mvi->regs;
+ u32 reg, tmp;
+
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ if (phy_id < 4)
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &reg);
+ else
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &reg);
+
+ } else
+ reg = mr32(MVS_PHY_CTL);
+
+ tmp = reg;
+ if (phy_id < 4)
+ tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
+ else
+ tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS;
+
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ if (phy_id < 4) {
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+ mdelay(10);
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
+ } else {
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+ mdelay(10);
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, reg);
+ }
+ } else {
+ mw32(MVS_PHY_CTL, tmp);
+ mdelay(10);
+ mw32(MVS_PHY_CTL, reg);
+ }
+}
+
+static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
+{
+ u32 tmp;
+ tmp = mvs_read_port_irq_stat(mvi, phy_id);
+ tmp &= ~PHYEV_RDY_CH;
+ mvs_write_port_irq_stat(mvi, phy_id, tmp);
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ if (hard)
+ tmp |= PHY_RST_HARD;
+ else
+ tmp |= PHY_RST;
+ mvs_write_phy_ctl(mvi, phy_id, tmp);
+ if (hard) {
+ do {
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ } while (tmp & PHY_RST_HARD);
+ }
+}
+
+static int __devinit mvs_64xx_chip_reset(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+ int i;
+
+ /* make sure interrupts are masked immediately (paranoia) */
+ mw32(MVS_GBL_CTL, 0);
+ tmp = mr32(MVS_GBL_CTL);
+
+ /* Reset Controller */
+ if (!(tmp & HBA_RST)) {
+ if (mvi->flags & MVF_PHY_PWR_FIX) {
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_PHY_DSBL;
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_PHY_DSBL;
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+ }
+ }
+
+ /* make sure interrupts are masked immediately (paranoia) */
+ mw32(MVS_GBL_CTL, 0);
+ tmp = mr32(MVS_GBL_CTL);
+
+ /* Reset Controller */
+ if (!(tmp & HBA_RST)) {
+ /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */
+ mw32_f(MVS_GBL_CTL, HBA_RST);
+ }
+
+ /* wait for reset to finish; timeout is just a guess */
+ i = 1000;
+ while (i-- > 0) {
+ msleep(10);
+
+ if (!(mr32(MVS_GBL_CTL) & HBA_RST))
+ break;
+ }
+ if (mr32(MVS_GBL_CTL) & HBA_RST) {
+ dev_printk(KERN_ERR, mvi->dev, "HBA reset failed\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void mvs_64xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ u32 offs;
+ if (phy_id < 4)
+ offs = PCR_PHY_CTL;
+ else {
+ offs = PCR_PHY_CTL2;
+ phy_id -= 4;
+ }
+ pci_read_config_dword(mvi->pdev, offs, &tmp);
+ tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id);
+ pci_write_config_dword(mvi->pdev, offs, tmp);
+ } else {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id);
+ mw32(MVS_PHY_CTL, tmp);
+ }
+}
+
+static void mvs_64xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ u32 offs;
+ if (phy_id < 4)
+ offs = PCR_PHY_CTL;
+ else {
+ offs = PCR_PHY_CTL2;
+ phy_id -= 4;
+ }
+ pci_read_config_dword(mvi->pdev, offs, &tmp);
+ tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id));
+ pci_write_config_dword(mvi->pdev, offs, tmp);
+ } else {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id));
+ mw32(MVS_PHY_CTL, tmp);
+ }
+}
+
+static int __devinit mvs_64xx_init(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ int i;
+ u32 tmp, cctl;
+
+ if (mvi->pdev && mvi->pdev->revision == 0)
+ mvi->flags |= MVF_PHY_PWR_FIX;
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ mvs_show_pcie_usage(mvi);
+ tmp = mvs_64xx_chip_reset(mvi);
+ if (tmp)
+ return tmp;
+ } else {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_PHY_DSBL;
+ mw32(MVS_PHY_CTL, tmp);
+ }
+
+ /* Init Chip */
+ /* make sure RST is set; HBA_RST /should/ have done that for us */
+ cctl = mr32(MVS_CTL) & 0xFFFF;
+ if (cctl & CCTL_RST)
+ cctl &= ~CCTL_RST;
+ else
+ mw32_f(MVS_CTL, cctl | CCTL_RST);
+
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ /* write to device control _AND_ device status register */
+ pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp);
+ tmp &= ~PRD_REQ_MASK;
+ tmp |= PRD_REQ_SIZE;
+ pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp);
+
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp &= ~PCTL_PHY_DSBL;
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+
+ pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
+ tmp &= PCTL_PWR_OFF;
+ tmp &= ~PCTL_PHY_DSBL;
+ pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+ } else {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_COM_ON;
+ tmp &= ~PCTL_PHY_DSBL;
+ tmp |= PCTL_LINK_RST;
+ mw32(MVS_PHY_CTL, tmp);
+ msleep(100);
+ tmp &= ~PCTL_LINK_RST;
+ mw32(MVS_PHY_CTL, tmp);
+ msleep(100);
+ }
+
+ /* reset control */
+ mw32(MVS_PCS, 0); /* MVS_PCS */
+ /* init phys */
+ mvs_64xx_phy_hacks(mvi);
+
+ /* enable auto port detection */
+ mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN);
+
+ mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
+ mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
+
+ mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
+ mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
+
+ mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
+ mw32(MVS_TX_LO, mvi->tx_dma);
+ mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
+
+ mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
+ mw32(MVS_RX_LO, mvi->rx_dma);
+ mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
+
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ /* set phy local SAS address */
+ /* should set little endian SAS address to 64xx chip */
+ mvs_set_sas_addr(mvi, i, PHYR_ADDR_LO, PHYR_ADDR_HI,
+ cpu_to_be64(mvi->phy[i].dev_sas_addr));
+
+ mvs_64xx_enable_xmt(mvi, i);
+
+ mvs_64xx_phy_reset(mvi, i, 1);
+ msleep(500);
+ mvs_64xx_detect_porttype(mvi, i);
+ }
+ if (mvi->flags & MVF_FLAG_SOC) {
+ /* set select registers */
+ writel(0x0E008000, regs + 0x000);
+ writel(0x59000008, regs + 0x004);
+ writel(0x20, regs + 0x008);
+ writel(0x20, regs + 0x00c);
+ writel(0x20, regs + 0x010);
+ writel(0x20, regs + 0x014);
+ writel(0x20, regs + 0x018);
+ writel(0x20, regs + 0x01c);
+ }
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ /* clear phy int status */
+ tmp = mvs_read_port_irq_stat(mvi, i);
+ tmp &= ~PHYEV_SIG_FIS;
+ mvs_write_port_irq_stat(mvi, i, tmp);
+
+ /* set phy int mask */
+ tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS |
+ PHYEV_ID_DONE | PHYEV_DCDR_ERR | PHYEV_CRC_ERR |
+ PHYEV_DEC_ERR;
+ mvs_write_port_irq_mask(mvi, i, tmp);
+
+ msleep(100);
+ mvs_update_phyinfo(mvi, i, 1);
+ }
+
+ /* FIXME: update wide port bitmaps */
+
+ /* little endian for open address and command table, etc. */
+ /*
+ * it seems that ( from the spec ) turning on big-endian won't
+ * do us any good on big-endian machines, need further confirmation
+ */
+ cctl = mr32(MVS_CTL);
+ cctl |= CCTL_ENDIAN_CMD;
+ cctl |= CCTL_ENDIAN_DATA;
+ cctl &= ~CCTL_ENDIAN_OPEN;
+ cctl |= CCTL_ENDIAN_RSP;
+ mw32_f(MVS_CTL, cctl);
+
+ /* reset CMD queue */
+ tmp = mr32(MVS_PCS);
+ tmp |= PCS_CMD_RST;
+ mw32(MVS_PCS, tmp);
+ /* interrupt coalescing may cause missing HW interrput in some case,
+ * and the max count is 0x1ff, while our max slot is 0x200,
+ * it will make count 0.
+ */
+ tmp = 0;
+ mw32(MVS_INT_COAL, tmp);
+
+ tmp = 0x100;
+ mw32(MVS_INT_COAL_TMOUT, tmp);
+
+ /* ladies and gentlemen, start your engines */
+ mw32(MVS_TX_CFG, 0);
+ mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
+ mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
+ /* enable CMD/CMPL_Q/RESP mode */
+ mw32(MVS_PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN |
+ PCS_CMD_EN | PCS_CMD_STOP_ERR);
+
+ /* enable completion queue interrupt */
+ tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
+ CINT_DMA_PCIE);
+
+ mw32(MVS_INT_MASK, tmp);
+
+ /* Enable SRS interrupt */
+ mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+
+ return 0;
+}
+
+static int mvs_64xx_ioremap(struct mvs_info *mvi)
+{
+ if (!mvs_ioremap(mvi, 4, 2))
+ return 0;
+ return -1;
+}
+
+static void mvs_64xx_iounmap(struct mvs_info *mvi)
+{
+ mvs_iounmap(mvi->regs);
+ mvs_iounmap(mvi->regs_ex);
+}
+
+static void mvs_64xx_interrupt_enable(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ tmp = mr32(MVS_GBL_CTL);
+ mw32(MVS_GBL_CTL, tmp | INT_EN);
+}
+
+static void mvs_64xx_interrupt_disable(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ tmp = mr32(MVS_GBL_CTL);
+ mw32(MVS_GBL_CTL, tmp & ~INT_EN);
+}
+
+static u32 mvs_64xx_isr_status(struct mvs_info *mvi, int irq)
+{
+ void __iomem *regs = mvi->regs;
+ u32 stat;
+
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ stat = mr32(MVS_GBL_INT_STAT);
+
+ if (stat == 0 || stat == 0xffffffff)
+ return 0;
+ } else
+ stat = 1;
+ return stat;
+}
+
+static irqreturn_t mvs_64xx_isr(struct mvs_info *mvi, int irq, u32 stat)
+{
+ void __iomem *regs = mvi->regs;
+
+ /* clear CMD_CMPLT ASAP */
+ mw32_f(MVS_INT_STAT, CINT_DONE);
+#ifndef MVS_USE_TASKLET
+ spin_lock(&mvi->lock);
+#endif
+ mvs_int_full(mvi);
+#ifndef MVS_USE_TASKLET
+ spin_unlock(&mvi->lock);
+#endif
+ return IRQ_HANDLED;
+}
+
+static void mvs_64xx_command_active(struct mvs_info *mvi, u32 slot_idx)
+{
+ u32 tmp;
+ mvs_cw32(mvi, 0x40 + (slot_idx >> 3), 1 << (slot_idx % 32));
+ mvs_cw32(mvi, 0x00 + (slot_idx >> 3), 1 << (slot_idx % 32));
+ do {
+ tmp = mvs_cr32(mvi, 0x00 + (slot_idx >> 3));
+ } while (tmp & 1 << (slot_idx % 32));
+ do {
+ tmp = mvs_cr32(mvi, 0x40 + (slot_idx >> 3));
+ } while (tmp & 1 << (slot_idx % 32));
+}
+
+static void mvs_64xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
+ u32 tfs)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ if (type == PORT_TYPE_SATA) {
+ tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ }
+ mw32(MVS_INT_STAT, CINT_CI_STOP);
+ tmp = mr32(MVS_PCS) | 0xFF00;
+ mw32(MVS_PCS, tmp);
+}
+
+static void mvs_64xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp, offs;
+
+ if (*tfs == MVS_ID_NOT_MAPPED)
+ return;
+
+ offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT);
+ if (*tfs < 16) {
+ tmp = mr32(MVS_PCS);
+ mw32(MVS_PCS, tmp & ~offs);
+ } else {
+ tmp = mr32(MVS_CTL);
+ mw32(MVS_CTL, tmp & ~offs);
+ }
+
+ tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << *tfs);
+ if (tmp)
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+
+ *tfs = MVS_ID_NOT_MAPPED;
+ return;
+}
+
+static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+ int i;
+ u32 tmp, offs;
+ void __iomem *regs = mvi->regs;
+
+ if (*tfs != MVS_ID_NOT_MAPPED)
+ return 0;
+
+ tmp = mr32(MVS_PCS);
+
+ for (i = 0; i < mvi->chip->srs_sz; i++) {
+ if (i == 16)
+ tmp = mr32(MVS_CTL);
+ offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT);
+ if (!(tmp & offs)) {
+ *tfs = i;
+
+ if (i < 16)
+ mw32(MVS_PCS, tmp | offs);
+ else
+ mw32(MVS_CTL, tmp | offs);
+ tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << i);
+ if (tmp)
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ return 0;
+ }
+ }
+ return MVS_ID_NOT_MAPPED;
+}
+
+void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
+{
+ int i;
+ struct scatterlist *sg;
+ struct mvs_prd *buf_prd = prd;
+ for_each_sg(scatter, sg, nr, i) {
+ buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+ buf_prd->len = cpu_to_le32(sg_dma_len(sg));
+ buf_prd++;
+ }
+}
+
+static int mvs_64xx_oob_done(struct mvs_info *mvi, int i)
+{
+ u32 phy_st;
+ mvs_write_port_cfg_addr(mvi, i,
+ PHYR_PHY_STAT);
+ phy_st = mvs_read_port_cfg_data(mvi, i);
+ if (phy_st & PHY_OOB_DTCTD)
+ return 1;
+ return 0;
+}
+
+static void mvs_64xx_fix_phy_info(struct mvs_info *mvi, int i,
+ struct sas_identify_frame *id)
+
+{
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ sas_phy->linkrate =
+ (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
+
+ phy->minimum_linkrate =
+ (phy->phy_status &
+ PHY_MIN_SPP_PHYS_LINK_RATE_MASK) >> 8;
+ phy->maximum_linkrate =
+ (phy->phy_status &
+ PHY_MAX_SPP_PHYS_LINK_RATE_MASK) >> 12;
+
+ mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY);
+ phy->dev_info = mvs_read_port_cfg_data(mvi, i);
+
+ mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO);
+ phy->att_dev_info = mvs_read_port_cfg_data(mvi, i);
+
+ mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI);
+ phy->att_dev_sas_addr =
+ (u64) mvs_read_port_cfg_data(mvi, i) << 32;
+ mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO);
+ phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
+ phy->att_dev_sas_addr = SAS_ADDR(&phy->att_dev_sas_addr);
+}
+
+static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i)
+{
+ u32 tmp;
+ struct mvs_phy *phy = &mvi->phy[i];
+ /* workaround for HW phy decoding error on 1.5g disk drive */
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
+ tmp = mvs_read_port_vsr_data(mvi, i);
+ if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) ==
+ SAS_LINK_RATE_1_5_GBPS)
+ tmp &= ~PHY_MODE6_LATECLK;
+ else
+ tmp |= PHY_MODE6_LATECLK;
+ mvs_write_port_vsr_data(mvi, i, tmp);
+}
+
+void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+ struct sas_phy_linkrates *rates)
+{
+ u32 lrmin = 0, lrmax = 0;
+ u32 tmp;
+
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ lrmin = (rates->minimum_linkrate << 8);
+ lrmax = (rates->maximum_linkrate << 12);
+
+ if (lrmin) {
+ tmp &= ~(0xf << 8);
+ tmp |= lrmin;
+ }
+ if (lrmax) {
+ tmp &= ~(0xf << 12);
+ tmp |= lrmax;
+ }
+ mvs_write_phy_ctl(mvi, phy_id, tmp);
+ mvs_64xx_phy_reset(mvi, phy_id, 1);
+}
+
+static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
+{
+ u32 tmp;
+ void __iomem *regs = mvi->regs;
+ tmp = mr32(MVS_PCS);
+ mw32(MVS_PCS, tmp & 0xFFFF);
+ mw32(MVS_PCS, tmp);
+ tmp = mr32(MVS_CTL);
+ mw32(MVS_CTL, tmp & 0xFFFF);
+ mw32(MVS_CTL, tmp);
+}
+
+
+u32 mvs_64xx_spi_read_data(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex;
+ return ior32(SPI_DATA_REG_64XX);
+}
+
+void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data)
+{
+ void __iomem *regs = mvi->regs_ex;
+ iow32(SPI_DATA_REG_64XX, data);
+}
+
+
+int mvs_64xx_spi_buildcmd(struct mvs_info *mvi,
+ u32 *dwCmd,
+ u8 cmd,
+ u8 read,
+ u8 length,
+ u32 addr
+ )
+{
+ u32 dwTmp;
+
+ dwTmp = ((u32)cmd << 24) | ((u32)length << 19);
+ if (read)
+ dwTmp |= 1U<<23;
+
+ if (addr != MV_MAX_U32) {
+ dwTmp |= 1U<<22;
+ dwTmp |= (addr & 0x0003FFFF);
+ }
+
+ *dwCmd = dwTmp;
+ return 0;
+}
+
+
+int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+{
+ void __iomem *regs = mvi->regs_ex;
+ int retry;
+
+ for (retry = 0; retry < 1; retry++) {
+ iow32(SPI_CTRL_REG_64XX, SPI_CTRL_VENDOR_ENABLE);
+ iow32(SPI_CMD_REG_64XX, cmd);
+ iow32(SPI_CTRL_REG_64XX,
+ SPI_CTRL_VENDOR_ENABLE | SPI_CTRL_SPISTART);
+ }
+
+ return 0;
+}
+
+int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+{
+ void __iomem *regs = mvi->regs_ex;
+ u32 i, dwTmp;
+
+ for (i = 0; i < timeout; i++) {
+ dwTmp = ior32(SPI_CTRL_REG_64XX);
+ if (!(dwTmp & SPI_CTRL_SPISTART))
+ return 0;
+ msleep(10);
+ }
+
+ return -1;
+}
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+{
+ int i;
+ struct mvs_prd *buf_prd = prd;
+ buf_prd += from;
+ for (i = 0; i < MAX_SG_ENTRY - from; i++) {
+ buf_prd->addr = cpu_to_le64(buf_dma);
+ buf_prd->len = cpu_to_le32(buf_len);
+ ++buf_prd;
+ }
+}
+#endif
+
+const struct mvs_dispatch mvs_64xx_dispatch = {
+ "mv64xx",
+ mvs_64xx_init,
+ NULL,
+ mvs_64xx_ioremap,
+ mvs_64xx_iounmap,
+ mvs_64xx_isr,
+ mvs_64xx_isr_status,
+ mvs_64xx_interrupt_enable,
+ mvs_64xx_interrupt_disable,
+ mvs_read_phy_ctl,
+ mvs_write_phy_ctl,
+ mvs_read_port_cfg_data,
+ mvs_write_port_cfg_data,
+ mvs_write_port_cfg_addr,
+ mvs_read_port_vsr_data,
+ mvs_write_port_vsr_data,
+ mvs_write_port_vsr_addr,
+ mvs_read_port_irq_stat,
+ mvs_write_port_irq_stat,
+ mvs_read_port_irq_mask,
+ mvs_write_port_irq_mask,
+ mvs_get_sas_addr,
+ mvs_64xx_command_active,
+ mvs_64xx_issue_stop,
+ mvs_start_delivery,
+ mvs_rx_update,
+ mvs_int_full,
+ mvs_64xx_assign_reg_set,
+ mvs_64xx_free_reg_set,
+ mvs_get_prd_size,
+ mvs_get_prd_count,
+ mvs_64xx_make_prd,
+ mvs_64xx_detect_porttype,
+ mvs_64xx_oob_done,
+ mvs_64xx_fix_phy_info,
+ mvs_64xx_phy_work_around,
+ mvs_64xx_phy_set_link_rate,
+ mvs_hw_max_link_rate,
+ mvs_64xx_phy_disable,
+ mvs_64xx_phy_enable,
+ mvs_64xx_phy_reset,
+ mvs_64xx_stp_reset,
+ mvs_64xx_clear_active_cmds,
+ mvs_64xx_spi_read_data,
+ mvs_64xx_spi_write_data,
+ mvs_64xx_spi_buildcmd,
+ mvs_64xx_spi_issuecmd,
+ mvs_64xx_spi_waitdataready,
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ mvs_64xx_fix_dma,
+#endif
+};
+
diff --git a/drivers/scsi/mvsas/mv_64xx.h b/drivers/scsi/mvsas/mv_64xx.h
new file mode 100644
index 000000000000..42e947d9795e
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_64xx.h
@@ -0,0 +1,151 @@
+/*
+ * Marvell 88SE64xx hardware specific head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#ifndef _MVS64XX_REG_H_
+#define _MVS64XX_REG_H_
+
+#include <linux/types.h>
+
+#define MAX_LINK_RATE SAS_LINK_RATE_3_0_GBPS
+
+/* enhanced mode registers (BAR4) */
+enum hw_registers {
+ MVS_GBL_CTL = 0x04, /* global control */
+ MVS_GBL_INT_STAT = 0x08, /* global irq status */
+ MVS_GBL_PI = 0x0C, /* ports implemented bitmask */
+
+ MVS_PHY_CTL = 0x40, /* SOC PHY Control */
+ MVS_PORTS_IMP = 0x9C, /* SOC Port Implemented */
+
+ MVS_GBL_PORT_TYPE = 0xa0, /* port type */
+
+ MVS_CTL = 0x100, /* SAS/SATA port configuration */
+ MVS_PCS = 0x104, /* SAS/SATA port control/status */
+ MVS_CMD_LIST_LO = 0x108, /* cmd list addr */
+ MVS_CMD_LIST_HI = 0x10C,
+ MVS_RX_FIS_LO = 0x110, /* RX FIS list addr */
+ MVS_RX_FIS_HI = 0x114,
+
+ MVS_TX_CFG = 0x120, /* TX configuration */
+ MVS_TX_LO = 0x124, /* TX (delivery) ring addr */
+ MVS_TX_HI = 0x128,
+
+ MVS_TX_PROD_IDX = 0x12C, /* TX producer pointer */
+ MVS_TX_CONS_IDX = 0x130, /* TX consumer pointer (RO) */
+ MVS_RX_CFG = 0x134, /* RX configuration */
+ MVS_RX_LO = 0x138, /* RX (completion) ring addr */
+ MVS_RX_HI = 0x13C,
+ MVS_RX_CONS_IDX = 0x140, /* RX consumer pointer (RO) */
+
+ MVS_INT_COAL = 0x148, /* Int coalescing config */
+ MVS_INT_COAL_TMOUT = 0x14C, /* Int coalescing timeout */
+ MVS_INT_STAT = 0x150, /* Central int status */
+ MVS_INT_MASK = 0x154, /* Central int enable */
+ MVS_INT_STAT_SRS_0 = 0x158, /* SATA register set status */
+ MVS_INT_MASK_SRS_0 = 0x15C,
+
+ /* ports 1-3 follow after this */
+ MVS_P0_INT_STAT = 0x160, /* port0 interrupt status */
+ MVS_P0_INT_MASK = 0x164, /* port0 interrupt mask */
+ /* ports 5-7 follow after this */
+ MVS_P4_INT_STAT = 0x200, /* Port4 interrupt status */
+ MVS_P4_INT_MASK = 0x204, /* Port4 interrupt enable mask */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_SER_CTLSTAT = 0x180, /* port0 serial control/status */
+ /* ports 5-7 follow after this */
+ MVS_P4_SER_CTLSTAT = 0x220, /* port4 serial control/status */
+
+ MVS_CMD_ADDR = 0x1B8, /* Command register port (addr) */
+ MVS_CMD_DATA = 0x1BC, /* Command register port (data) */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_CFG_ADDR = 0x1C0, /* port0 phy register address */
+ MVS_P0_CFG_DATA = 0x1C4, /* port0 phy register data */
+ /* ports 5-7 follow after this */
+ MVS_P4_CFG_ADDR = 0x230, /* Port4 config address */
+ MVS_P4_CFG_DATA = 0x234, /* Port4 config data */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_VSR_ADDR = 0x1E0, /* port0 VSR address */
+ MVS_P0_VSR_DATA = 0x1E4, /* port0 VSR data */
+ /* ports 5-7 follow after this */
+ MVS_P4_VSR_ADDR = 0x250, /* port4 VSR addr */
+ MVS_P4_VSR_DATA = 0x254, /* port4 VSR data */
+};
+
+enum pci_cfg_registers {
+ PCR_PHY_CTL = 0x40,
+ PCR_PHY_CTL2 = 0x90,
+ PCR_DEV_CTRL = 0xE8,
+ PCR_LINK_STAT = 0xF2,
+};
+
+/* SAS/SATA Vendor Specific Port Registers */
+enum sas_sata_vsp_regs {
+ VSR_PHY_STAT = 0x00, /* Phy Status */
+ VSR_PHY_MODE1 = 0x01, /* phy tx */
+ VSR_PHY_MODE2 = 0x02, /* tx scc */
+ VSR_PHY_MODE3 = 0x03, /* pll */
+ VSR_PHY_MODE4 = 0x04, /* VCO */
+ VSR_PHY_MODE5 = 0x05, /* Rx */
+ VSR_PHY_MODE6 = 0x06, /* CDR */
+ VSR_PHY_MODE7 = 0x07, /* Impedance */
+ VSR_PHY_MODE8 = 0x08, /* Voltage */
+ VSR_PHY_MODE9 = 0x09, /* Test */
+ VSR_PHY_MODE10 = 0x0A, /* Power */
+ VSR_PHY_MODE11 = 0x0B, /* Phy Mode */
+ VSR_PHY_VS0 = 0x0C, /* Vednor Specific 0 */
+ VSR_PHY_VS1 = 0x0D, /* Vednor Specific 1 */
+};
+
+enum chip_register_bits {
+ PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0xF << 8),
+ PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0xF << 12),
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
+ (0xF << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
+};
+
+#define MAX_SG_ENTRY 64
+
+struct mvs_prd {
+ __le64 addr; /* 64-bit buffer address */
+ __le32 reserved;
+ __le32 len; /* 16-bit length */
+};
+
+#define SPI_CTRL_REG 0xc0
+#define SPI_CTRL_VENDOR_ENABLE (1U<<29)
+#define SPI_CTRL_SPIRDY (1U<<22)
+#define SPI_CTRL_SPISTART (1U<<20)
+
+#define SPI_CMD_REG 0xc4
+#define SPI_DATA_REG 0xc8
+
+#define SPI_CTRL_REG_64XX 0x10
+#define SPI_CMD_REG_64XX 0x14
+#define SPI_DATA_REG_64XX 0x18
+
+#endif
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
new file mode 100644
index 000000000000..0940fae19d20
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -0,0 +1,672 @@
+/*
+ * Marvell 88SE94xx hardware specific
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#include "mv_sas.h"
+#include "mv_94xx.h"
+#include "mv_chips.h"
+
+static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
+{
+ u32 reg;
+ struct mvs_phy *phy = &mvi->phy[i];
+ u32 phy_status;
+
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE3);
+ reg = mvs_read_port_vsr_data(mvi, i);
+ phy_status = ((reg & 0x3f0000) >> 16) & 0xff;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ switch (phy_status) {
+ case 0x10:
+ phy->phy_type |= PORT_TYPE_SAS;
+ break;
+ case 0x1d:
+ default:
+ phy->phy_type |= PORT_TYPE_SATA;
+ break;
+ }
+}
+
+static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ tmp = mr32(MVS_PCS);
+ tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
+ mw32(MVS_PCS, tmp);
+}
+
+static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
+{
+ u32 tmp;
+
+ tmp = mvs_read_port_irq_stat(mvi, phy_id);
+ tmp &= ~PHYEV_RDY_CH;
+ mvs_write_port_irq_stat(mvi, phy_id, tmp);
+ if (hard) {
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ tmp |= PHY_RST_HARD;
+ mvs_write_phy_ctl(mvi, phy_id, tmp);
+ do {
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ } while (tmp & PHY_RST_HARD);
+ } else {
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_STAT);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp |= PHY_RST;
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+ }
+}
+
+static void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
+{
+ u32 tmp;
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp | 0x00800000);
+}
+
+static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
+{
+ mvs_write_port_vsr_addr(mvi, phy_id, 0x1B4);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
+ mvs_write_port_vsr_addr(mvi, phy_id, 0x104);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x00018080);
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x00207fff);
+}
+
+static int __devinit mvs_94xx_init(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ int i;
+ u32 tmp, cctl;
+
+ mvs_show_pcie_usage(mvi);
+ if (mvi->flags & MVF_FLAG_SOC) {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_PHY_DSBL;
+ mw32(MVS_PHY_CTL, tmp);
+ }
+
+ /* Init Chip */
+ /* make sure RST is set; HBA_RST /should/ have done that for us */
+ cctl = mr32(MVS_CTL) & 0xFFFF;
+ if (cctl & CCTL_RST)
+ cctl &= ~CCTL_RST;
+ else
+ mw32_f(MVS_CTL, cctl | CCTL_RST);
+
+ if (mvi->flags & MVF_FLAG_SOC) {
+ tmp = mr32(MVS_PHY_CTL);
+ tmp &= ~PCTL_PWR_OFF;
+ tmp |= PCTL_COM_ON;
+ tmp &= ~PCTL_PHY_DSBL;
+ tmp |= PCTL_LINK_RST;
+ mw32(MVS_PHY_CTL, tmp);
+ msleep(100);
+ tmp &= ~PCTL_LINK_RST;
+ mw32(MVS_PHY_CTL, tmp);
+ msleep(100);
+ }
+
+ /* reset control */
+ mw32(MVS_PCS, 0); /* MVS_PCS */
+ mw32(MVS_STP_REG_SET_0, 0);
+ mw32(MVS_STP_REG_SET_1, 0);
+
+ /* init phys */
+ mvs_phy_hacks(mvi);
+
+ /* disable Multiplexing, enable phy implemented */
+ mw32(MVS_PORTS_IMP, 0xFF);
+
+
+ mw32(MVS_PA_VSR_ADDR, 0x00000104);
+ mw32(MVS_PA_VSR_PORT, 0x00018080);
+ mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE8);
+ mw32(MVS_PA_VSR_PORT, 0x0084ffff);
+
+ /* set LED blink when IO*/
+ mw32(MVS_PA_VSR_ADDR, 0x00000030);
+ tmp = mr32(MVS_PA_VSR_PORT);
+ tmp &= 0xFFFF00FF;
+ tmp |= 0x00003300;
+ mw32(MVS_PA_VSR_PORT, tmp);
+
+ mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
+ mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
+
+ mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
+ mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
+
+ mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
+ mw32(MVS_TX_LO, mvi->tx_dma);
+ mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
+
+ mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
+ mw32(MVS_RX_LO, mvi->rx_dma);
+ mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
+
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ mvs_94xx_phy_disable(mvi, i);
+ /* set phy local SAS address */
+ mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
+ (mvi->phy[i].dev_sas_addr));
+
+ mvs_94xx_enable_xmt(mvi, i);
+ mvs_94xx_phy_enable(mvi, i);
+
+ mvs_94xx_phy_reset(mvi, i, 1);
+ msleep(500);
+ mvs_94xx_detect_porttype(mvi, i);
+ }
+
+ if (mvi->flags & MVF_FLAG_SOC) {
+ /* set select registers */
+ writel(0x0E008000, regs + 0x000);
+ writel(0x59000008, regs + 0x004);
+ writel(0x20, regs + 0x008);
+ writel(0x20, regs + 0x00c);
+ writel(0x20, regs + 0x010);
+ writel(0x20, regs + 0x014);
+ writel(0x20, regs + 0x018);
+ writel(0x20, regs + 0x01c);
+ }
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ /* clear phy int status */
+ tmp = mvs_read_port_irq_stat(mvi, i);
+ tmp &= ~PHYEV_SIG_FIS;
+ mvs_write_port_irq_stat(mvi, i, tmp);
+
+ /* set phy int mask */
+ tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH |
+ PHYEV_ID_DONE | PHYEV_DCDR_ERR | PHYEV_CRC_ERR ;
+ mvs_write_port_irq_mask(mvi, i, tmp);
+
+ msleep(100);
+ mvs_update_phyinfo(mvi, i, 1);
+ }
+
+ /* FIXME: update wide port bitmaps */
+
+ /* little endian for open address and command table, etc. */
+ /*
+ * it seems that ( from the spec ) turning on big-endian won't
+ * do us any good on big-endian machines, need further confirmation
+ */
+ cctl = mr32(MVS_CTL);
+ cctl |= CCTL_ENDIAN_CMD;
+ cctl |= CCTL_ENDIAN_DATA;
+ cctl &= ~CCTL_ENDIAN_OPEN;
+ cctl |= CCTL_ENDIAN_RSP;
+ mw32_f(MVS_CTL, cctl);
+
+ /* reset CMD queue */
+ tmp = mr32(MVS_PCS);
+ tmp |= PCS_CMD_RST;
+ mw32(MVS_PCS, tmp);
+ /* interrupt coalescing may cause missing HW interrput in some case,
+ * and the max count is 0x1ff, while our max slot is 0x200,
+ * it will make count 0.
+ */
+ tmp = 0;
+ mw32(MVS_INT_COAL, tmp);
+
+ tmp = 0x100;
+ mw32(MVS_INT_COAL_TMOUT, tmp);
+
+ /* ladies and gentlemen, start your engines */
+ mw32(MVS_TX_CFG, 0);
+ mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
+ mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
+ /* enable CMD/CMPL_Q/RESP mode */
+ mw32(MVS_PCS, PCS_SATA_RETRY_2 | PCS_FIS_RX_EN |
+ PCS_CMD_EN | PCS_CMD_STOP_ERR);
+
+ /* enable completion queue interrupt */
+ tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
+ CINT_DMA_PCIE);
+ tmp |= CINT_PHY_MASK;
+ mw32(MVS_INT_MASK, tmp);
+
+ /* Enable SRS interrupt */
+ mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+
+ return 0;
+}
+
+static int mvs_94xx_ioremap(struct mvs_info *mvi)
+{
+ if (!mvs_ioremap(mvi, 2, -1)) {
+ mvi->regs_ex = mvi->regs + 0x10200;
+ mvi->regs += 0x20000;
+ if (mvi->id == 1)
+ mvi->regs += 0x4000;
+ return 0;
+ }
+ return -1;
+}
+
+static void mvs_94xx_iounmap(struct mvs_info *mvi)
+{
+ if (mvi->regs) {
+ mvi->regs -= 0x20000;
+ if (mvi->id == 1)
+ mvi->regs -= 0x4000;
+ mvs_iounmap(mvi->regs);
+ }
+}
+
+static void mvs_94xx_interrupt_enable(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex;
+ u32 tmp;
+
+ tmp = mr32(MVS_GBL_CTL);
+ tmp |= (IRQ_SAS_A | IRQ_SAS_B);
+ mw32(MVS_GBL_INT_STAT, tmp);
+ writel(tmp, regs + 0x0C);
+ writel(tmp, regs + 0x10);
+ writel(tmp, regs + 0x14);
+ writel(tmp, regs + 0x18);
+ mw32(MVS_GBL_CTL, tmp);
+}
+
+static void mvs_94xx_interrupt_disable(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex;
+ u32 tmp;
+
+ tmp = mr32(MVS_GBL_CTL);
+
+ tmp &= ~(IRQ_SAS_A | IRQ_SAS_B);
+ mw32(MVS_GBL_INT_STAT, tmp);
+ writel(tmp, regs + 0x0C);
+ writel(tmp, regs + 0x10);
+ writel(tmp, regs + 0x14);
+ writel(tmp, regs + 0x18);
+ mw32(MVS_GBL_CTL, tmp);
+}
+
+static u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq)
+{
+ void __iomem *regs = mvi->regs_ex;
+ u32 stat = 0;
+ if (!(mvi->flags & MVF_FLAG_SOC)) {
+ stat = mr32(MVS_GBL_INT_STAT);
+
+ if (!(stat & (IRQ_SAS_A | IRQ_SAS_B)))
+ return 0;
+ }
+ return stat;
+}
+
+static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
+{
+ void __iomem *regs = mvi->regs;
+
+ if (((stat & IRQ_SAS_A) && mvi->id == 0) ||
+ ((stat & IRQ_SAS_B) && mvi->id == 1)) {
+ mw32_f(MVS_INT_STAT, CINT_DONE);
+ #ifndef MVS_USE_TASKLET
+ spin_lock(&mvi->lock);
+ #endif
+ mvs_int_full(mvi);
+ #ifndef MVS_USE_TASKLET
+ spin_unlock(&mvi->lock);
+ #endif
+ }
+ return IRQ_HANDLED;
+}
+
+static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
+{
+ u32 tmp;
+ mvs_cw32(mvi, 0x300 + (slot_idx >> 3), 1 << (slot_idx % 32));
+ do {
+ tmp = mvs_cr32(mvi, 0x300 + (slot_idx >> 3));
+ } while (tmp & 1 << (slot_idx % 32));
+}
+
+static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
+ u32 tfs)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ if (type == PORT_TYPE_SATA) {
+ tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ }
+ mw32(MVS_INT_STAT, CINT_CI_STOP);
+ tmp = mr32(MVS_PCS) | 0xFF00;
+ mw32(MVS_PCS, tmp);
+}
+
+static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+ u8 reg_set = *tfs;
+
+ if (*tfs == MVS_ID_NOT_MAPPED)
+ return;
+
+ mvi->sata_reg_set &= ~bit(reg_set);
+ if (reg_set < 32) {
+ w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
+ tmp = mr32(MVS_INT_STAT_SRS_0) & (u32)mvi->sata_reg_set;
+ if (tmp)
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ } else {
+ w_reg_set_enable(reg_set, mvi->sata_reg_set);
+ tmp = mr32(MVS_INT_STAT_SRS_1) & mvi->sata_reg_set;
+ if (tmp)
+ mw32(MVS_INT_STAT_SRS_1, tmp);
+ }
+
+ *tfs = MVS_ID_NOT_MAPPED;
+
+ return;
+}
+
+static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+ int i;
+ void __iomem *regs = mvi->regs;
+
+ if (*tfs != MVS_ID_NOT_MAPPED)
+ return 0;
+
+ i = mv_ffc64(mvi->sata_reg_set);
+ if (i > 32) {
+ mvi->sata_reg_set |= bit(i);
+ w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
+ *tfs = i;
+ return 0;
+ } else if (i >= 0) {
+ mvi->sata_reg_set |= bit(i);
+ w_reg_set_enable(i, (u32)mvi->sata_reg_set);
+ *tfs = i;
+ return 0;
+ }
+ return MVS_ID_NOT_MAPPED;
+}
+
+static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
+{
+ int i;
+ struct scatterlist *sg;
+ struct mvs_prd *buf_prd = prd;
+ for_each_sg(scatter, sg, nr, i) {
+ buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+ buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+ buf_prd++;
+ }
+}
+
+static int mvs_94xx_oob_done(struct mvs_info *mvi, int i)
+{
+ u32 phy_st;
+ phy_st = mvs_read_phy_ctl(mvi, i);
+ if (phy_st & PHY_READY_MASK) /* phy ready */
+ return 1;
+ return 0;
+}
+
+static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
+ struct sas_identify_frame *id)
+{
+ int i;
+ u32 id_frame[7];
+
+ for (i = 0; i < 7; i++) {
+ mvs_write_port_cfg_addr(mvi, port_id,
+ CONFIG_ID_FRAME0 + i * 4);
+ id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+ }
+ memcpy(id, id_frame, 28);
+}
+
+static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
+ struct sas_identify_frame *id)
+{
+ int i;
+ u32 id_frame[7];
+
+ /* mvs_hexdump(28, (u8 *)id_frame, 0); */
+ for (i = 0; i < 7; i++) {
+ mvs_write_port_cfg_addr(mvi, port_id,
+ CONFIG_ATT_ID_FRAME0 + i * 4);
+ id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+ mv_dprintk("94xx phy %d atta frame %d %x.\n",
+ port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
+ }
+ /* mvs_hexdump(28, (u8 *)id_frame, 0); */
+ memcpy(id, id_frame, 28);
+}
+
+static u32 mvs_94xx_make_dev_info(struct sas_identify_frame *id)
+{
+ u32 att_dev_info = 0;
+
+ att_dev_info |= id->dev_type;
+ if (id->stp_iport)
+ att_dev_info |= PORT_DEV_STP_INIT;
+ if (id->smp_iport)
+ att_dev_info |= PORT_DEV_SMP_INIT;
+ if (id->ssp_iport)
+ att_dev_info |= PORT_DEV_SSP_INIT;
+ if (id->stp_tport)
+ att_dev_info |= PORT_DEV_STP_TRGT;
+ if (id->smp_tport)
+ att_dev_info |= PORT_DEV_SMP_TRGT;
+ if (id->ssp_tport)
+ att_dev_info |= PORT_DEV_SSP_TRGT;
+
+ att_dev_info |= (u32)id->phy_id<<24;
+ return att_dev_info;
+}
+
+static u32 mvs_94xx_make_att_info(struct sas_identify_frame *id)
+{
+ return mvs_94xx_make_dev_info(id);
+}
+
+static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
+ struct sas_identify_frame *id)
+{
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ mv_dprintk("get all reg link rate is 0x%x\n", phy->phy_status);
+ sas_phy->linkrate =
+ (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
+ sas_phy->linkrate += 0x8;
+ mv_dprintk("get link rate is %d\n", sas_phy->linkrate);
+ phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
+ mvs_94xx_get_dev_identify_frame(mvi, i, id);
+ phy->dev_info = mvs_94xx_make_dev_info(id);
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ mvs_94xx_get_att_identify_frame(mvi, i, id);
+ phy->att_dev_info = mvs_94xx_make_att_info(id);
+ phy->att_dev_sas_addr = *(u64 *)id->sas_addr;
+ } else {
+ phy->att_dev_info = PORT_DEV_STP_TRGT | 1;
+ }
+
+}
+
+void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+ struct sas_phy_linkrates *rates)
+{
+ /* TODO */
+}
+
+static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
+{
+ u32 tmp;
+ void __iomem *regs = mvi->regs;
+ tmp = mr32(MVS_STP_REG_SET_0);
+ mw32(MVS_STP_REG_SET_0, 0);
+ mw32(MVS_STP_REG_SET_0, tmp);
+ tmp = mr32(MVS_STP_REG_SET_1);
+ mw32(MVS_STP_REG_SET_1, 0);
+ mw32(MVS_STP_REG_SET_1, tmp);
+}
+
+
+u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ return mr32(SPI_RD_DATA_REG_94XX);
+}
+
+void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ mw32(SPI_RD_DATA_REG_94XX, data);
+}
+
+
+int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
+ u32 *dwCmd,
+ u8 cmd,
+ u8 read,
+ u8 length,
+ u32 addr
+ )
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ u32 dwTmp;
+
+ dwTmp = ((u32)cmd << 8) | ((u32)length << 4);
+ if (read)
+ dwTmp |= SPI_CTRL_READ_94XX;
+
+ if (addr != MV_MAX_U32) {
+ mw32(SPI_ADDR_REG_94XX, (addr & 0x0003FFFFL));
+ dwTmp |= SPI_ADDR_VLD_94XX;
+ }
+
+ *dwCmd = dwTmp;
+ return 0;
+}
+
+
+int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX);
+
+ return 0;
+}
+
+int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ u32 i, dwTmp;
+
+ for (i = 0; i < timeout; i++) {
+ dwTmp = mr32(SPI_CTRL_REG_94XX);
+ if (!(dwTmp & SPI_CTRL_SpiStart_94XX))
+ return 0;
+ msleep(10);
+ }
+
+ return -1;
+}
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+void mvs_94xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+{
+ int i;
+ struct mvs_prd *buf_prd = prd;
+ buf_prd += from;
+ for (i = 0; i < MAX_SG_ENTRY - from; i++) {
+ buf_prd->addr = cpu_to_le64(buf_dma);
+ buf_prd->im_len.len = cpu_to_le32(buf_len);
+ ++buf_prd;
+ }
+}
+#endif
+
+const struct mvs_dispatch mvs_94xx_dispatch = {
+ "mv94xx",
+ mvs_94xx_init,
+ NULL,
+ mvs_94xx_ioremap,
+ mvs_94xx_iounmap,
+ mvs_94xx_isr,
+ mvs_94xx_isr_status,
+ mvs_94xx_interrupt_enable,
+ mvs_94xx_interrupt_disable,
+ mvs_read_phy_ctl,
+ mvs_write_phy_ctl,
+ mvs_read_port_cfg_data,
+ mvs_write_port_cfg_data,
+ mvs_write_port_cfg_addr,
+ mvs_read_port_vsr_data,
+ mvs_write_port_vsr_data,
+ mvs_write_port_vsr_addr,
+ mvs_read_port_irq_stat,
+ mvs_write_port_irq_stat,
+ mvs_read_port_irq_mask,
+ mvs_write_port_irq_mask,
+ mvs_get_sas_addr,
+ mvs_94xx_command_active,
+ mvs_94xx_issue_stop,
+ mvs_start_delivery,
+ mvs_rx_update,
+ mvs_int_full,
+ mvs_94xx_assign_reg_set,
+ mvs_94xx_free_reg_set,
+ mvs_get_prd_size,
+ mvs_get_prd_count,
+ mvs_94xx_make_prd,
+ mvs_94xx_detect_porttype,
+ mvs_94xx_oob_done,
+ mvs_94xx_fix_phy_info,
+ NULL,
+ mvs_94xx_phy_set_link_rate,
+ mvs_hw_max_link_rate,
+ mvs_94xx_phy_disable,
+ mvs_94xx_phy_enable,
+ mvs_94xx_phy_reset,
+ NULL,
+ mvs_94xx_clear_active_cmds,
+ mvs_94xx_spi_read_data,
+ mvs_94xx_spi_write_data,
+ mvs_94xx_spi_buildcmd,
+ mvs_94xx_spi_issuecmd,
+ mvs_94xx_spi_waitdataready,
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ mvs_94xx_fix_dma,
+#endif
+};
+
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
new file mode 100644
index 000000000000..23ed9b164669
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -0,0 +1,222 @@
+/*
+ * Marvell 88SE94xx hardware specific head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#ifndef _MVS94XX_REG_H_
+#define _MVS94XX_REG_H_
+
+#include <linux/types.h>
+
+#define MAX_LINK_RATE SAS_LINK_RATE_6_0_GBPS
+
+enum hw_registers {
+ MVS_GBL_CTL = 0x04, /* global control */
+ MVS_GBL_INT_STAT = 0x00, /* global irq status */
+ MVS_GBL_PI = 0x0C, /* ports implemented bitmask */
+
+ MVS_PHY_CTL = 0x40, /* SOC PHY Control */
+ MVS_PORTS_IMP = 0x9C, /* SOC Port Implemented */
+
+ MVS_GBL_PORT_TYPE = 0xa0, /* port type */
+
+ MVS_CTL = 0x100, /* SAS/SATA port configuration */
+ MVS_PCS = 0x104, /* SAS/SATA port control/status */
+ MVS_CMD_LIST_LO = 0x108, /* cmd list addr */
+ MVS_CMD_LIST_HI = 0x10C,
+ MVS_RX_FIS_LO = 0x110, /* RX FIS list addr */
+ MVS_RX_FIS_HI = 0x114,
+ MVS_STP_REG_SET_0 = 0x118, /* STP/SATA Register Set Enable */
+ MVS_STP_REG_SET_1 = 0x11C,
+ MVS_TX_CFG = 0x120, /* TX configuration */
+ MVS_TX_LO = 0x124, /* TX (delivery) ring addr */
+ MVS_TX_HI = 0x128,
+
+ MVS_TX_PROD_IDX = 0x12C, /* TX producer pointer */
+ MVS_TX_CONS_IDX = 0x130, /* TX consumer pointer (RO) */
+ MVS_RX_CFG = 0x134, /* RX configuration */
+ MVS_RX_LO = 0x138, /* RX (completion) ring addr */
+ MVS_RX_HI = 0x13C,
+ MVS_RX_CONS_IDX = 0x140, /* RX consumer pointer (RO) */
+
+ MVS_INT_COAL = 0x148, /* Int coalescing config */
+ MVS_INT_COAL_TMOUT = 0x14C, /* Int coalescing timeout */
+ MVS_INT_STAT = 0x150, /* Central int status */
+ MVS_INT_MASK = 0x154, /* Central int enable */
+ MVS_INT_STAT_SRS_0 = 0x158, /* SATA register set status */
+ MVS_INT_MASK_SRS_0 = 0x15C,
+ MVS_INT_STAT_SRS_1 = 0x160,
+ MVS_INT_MASK_SRS_1 = 0x164,
+ MVS_NON_NCQ_ERR_0 = 0x168, /* SRS Non-specific NCQ Error */
+ MVS_NON_NCQ_ERR_1 = 0x16C,
+ MVS_CMD_ADDR = 0x170, /* Command register port (addr) */
+ MVS_CMD_DATA = 0x174, /* Command register port (data) */
+ MVS_MEM_PARITY_ERR = 0x178, /* Memory parity error */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_INT_STAT = 0x180, /* port0 interrupt status */
+ MVS_P0_INT_MASK = 0x184, /* port0 interrupt mask */
+ /* ports 5-7 follow after this */
+ MVS_P4_INT_STAT = 0x1A0, /* Port4 interrupt status */
+ MVS_P4_INT_MASK = 0x1A4, /* Port4 interrupt enable mask */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_SER_CTLSTAT = 0x1D0, /* port0 serial control/status */
+ /* ports 5-7 follow after this */
+ MVS_P4_SER_CTLSTAT = 0x1E0, /* port4 serial control/status */
+
+ /* ports 1-3 follow after this */
+ MVS_P0_CFG_ADDR = 0x200, /* port0 phy register address */
+ MVS_P0_CFG_DATA = 0x204, /* port0 phy register data */
+ /* ports 5-7 follow after this */
+ MVS_P4_CFG_ADDR = 0x220, /* Port4 config address */
+ MVS_P4_CFG_DATA = 0x224, /* Port4 config data */
+
+ /* phys 1-3 follow after this */
+ MVS_P0_VSR_ADDR = 0x250, /* phy0 VSR address */
+ MVS_P0_VSR_DATA = 0x254, /* phy0 VSR data */
+ /* phys 1-3 follow after this */
+ /* multiplexing */
+ MVS_P4_VSR_ADDR = 0x250, /* phy4 VSR address */
+ MVS_P4_VSR_DATA = 0x254, /* phy4 VSR data */
+ MVS_PA_VSR_ADDR = 0x290, /* All port VSR addr */
+ MVS_PA_VSR_PORT = 0x294, /* All port VSR data */
+};
+
+enum pci_cfg_registers {
+ PCR_PHY_CTL = 0x40,
+ PCR_PHY_CTL2 = 0x90,
+ PCR_DEV_CTRL = 0x78,
+ PCR_LINK_STAT = 0x82,
+};
+
+/* SAS/SATA Vendor Specific Port Registers */
+enum sas_sata_vsp_regs {
+ VSR_PHY_STAT = 0x00 * 4, /* Phy Status */
+ VSR_PHY_MODE1 = 0x01 * 4, /* phy tx */
+ VSR_PHY_MODE2 = 0x02 * 4, /* tx scc */
+ VSR_PHY_MODE3 = 0x03 * 4, /* pll */
+ VSR_PHY_MODE4 = 0x04 * 4, /* VCO */
+ VSR_PHY_MODE5 = 0x05 * 4, /* Rx */
+ VSR_PHY_MODE6 = 0x06 * 4, /* CDR */
+ VSR_PHY_MODE7 = 0x07 * 4, /* Impedance */
+ VSR_PHY_MODE8 = 0x08 * 4, /* Voltage */
+ VSR_PHY_MODE9 = 0x09 * 4, /* Test */
+ VSR_PHY_MODE10 = 0x0A * 4, /* Power */
+ VSR_PHY_MODE11 = 0x0B * 4, /* Phy Mode */
+ VSR_PHY_VS0 = 0x0C * 4, /* Vednor Specific 0 */
+ VSR_PHY_VS1 = 0x0D * 4, /* Vednor Specific 1 */
+};
+
+enum chip_register_bits {
+ PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
+ PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (12),
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
+ (0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
+};
+
+enum pci_interrupt_cause {
+ /* MAIN_IRQ_CAUSE (R10200) Bits*/
+ IRQ_COM_IN_I2O_IOP0 = (1 << 0),
+ IRQ_COM_IN_I2O_IOP1 = (1 << 1),
+ IRQ_COM_IN_I2O_IOP2 = (1 << 2),
+ IRQ_COM_IN_I2O_IOP3 = (1 << 3),
+ IRQ_COM_OUT_I2O_HOS0 = (1 << 4),
+ IRQ_COM_OUT_I2O_HOS1 = (1 << 5),
+ IRQ_COM_OUT_I2O_HOS2 = (1 << 6),
+ IRQ_COM_OUT_I2O_HOS3 = (1 << 7),
+ IRQ_PCIF_TO_CPU_DRBL0 = (1 << 8),
+ IRQ_PCIF_TO_CPU_DRBL1 = (1 << 9),
+ IRQ_PCIF_TO_CPU_DRBL2 = (1 << 10),
+ IRQ_PCIF_TO_CPU_DRBL3 = (1 << 11),
+ IRQ_PCIF_DRBL0 = (1 << 12),
+ IRQ_PCIF_DRBL1 = (1 << 13),
+ IRQ_PCIF_DRBL2 = (1 << 14),
+ IRQ_PCIF_DRBL3 = (1 << 15),
+ IRQ_XOR_A = (1 << 16),
+ IRQ_XOR_B = (1 << 17),
+ IRQ_SAS_A = (1 << 18),
+ IRQ_SAS_B = (1 << 19),
+ IRQ_CPU_CNTRL = (1 << 20),
+ IRQ_GPIO = (1 << 21),
+ IRQ_UART = (1 << 22),
+ IRQ_SPI = (1 << 23),
+ IRQ_I2C = (1 << 24),
+ IRQ_SGPIO = (1 << 25),
+ IRQ_COM_ERR = (1 << 29),
+ IRQ_I2O_ERR = (1 << 30),
+ IRQ_PCIE_ERR = (1 << 31),
+};
+
+#define MAX_SG_ENTRY 255
+
+struct mvs_prd_imt {
+ __le32 len:22;
+ u8 _r_a:2;
+ u8 misc_ctl:4;
+ u8 inter_sel:4;
+};
+
+struct mvs_prd {
+ /* 64-bit buffer address */
+ __le64 addr;
+ /* 22-bit length */
+ struct mvs_prd_imt im_len;
+} __attribute__ ((packed));
+
+#define SPI_CTRL_REG_94XX 0xc800
+#define SPI_ADDR_REG_94XX 0xc804
+#define SPI_WR_DATA_REG_94XX 0xc808
+#define SPI_RD_DATA_REG_94XX 0xc80c
+#define SPI_CTRL_READ_94XX (1U << 2)
+#define SPI_ADDR_VLD_94XX (1U << 1)
+#define SPI_CTRL_SpiStart_94XX (1U << 0)
+
+#define mv_ffc(x) ffz(x)
+
+static inline int
+mv_ffc64(u64 v)
+{
+ int i;
+ i = mv_ffc((u32)v);
+ if (i >= 0)
+ return i;
+ i = mv_ffc((u32)(v>>32));
+
+ if (i != 0)
+ return 32 + i;
+
+ return -1;
+}
+
+#define r_reg_set_enable(i) \
+ (((i) > 31) ? mr32(MVS_STP_REG_SET_1) : \
+ mr32(MVS_STP_REG_SET_0))
+
+#define w_reg_set_enable(i, tmp) \
+ (((i) > 31) ? mw32(MVS_STP_REG_SET_1, tmp) : \
+ mw32(MVS_STP_REG_SET_0, tmp))
+
+extern const struct mvs_dispatch mvs_94xx_dispatch;
+#endif
+
diff --git a/drivers/scsi/mvsas/mv_chips.h b/drivers/scsi/mvsas/mv_chips.h
new file mode 100644
index 000000000000..a67e1c4172f9
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_chips.h
@@ -0,0 +1,280 @@
+/*
+ * Marvell 88SE64xx/88SE94xx register IO interface
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+
+#ifndef _MV_CHIPS_H_
+#define _MV_CHIPS_H_
+
+#define mr32(reg) readl(regs + reg)
+#define mw32(reg, val) writel((val), regs + reg)
+#define mw32_f(reg, val) do { \
+ mw32(reg, val); \
+ mr32(reg); \
+ } while (0)
+
+#define iow32(reg, val) outl(val, (unsigned long)(regs + reg))
+#define ior32(reg) inl((unsigned long)(regs + reg))
+#define iow16(reg, val) outw((unsigned long)(val, regs + reg))
+#define ior16(reg) inw((unsigned long)(regs + reg))
+#define iow8(reg, val) outb((unsigned long)(val, regs + reg))
+#define ior8(reg) inb((unsigned long)(regs + reg))
+
+static inline u32 mvs_cr32(struct mvs_info *mvi, u32 addr)
+{
+ void __iomem *regs = mvi->regs;
+ mw32(MVS_CMD_ADDR, addr);
+ return mr32(MVS_CMD_DATA);
+}
+
+static inline void mvs_cw32(struct mvs_info *mvi, u32 addr, u32 val)
+{
+ void __iomem *regs = mvi->regs;
+ mw32(MVS_CMD_ADDR, addr);
+ mw32(MVS_CMD_DATA, val);
+}
+
+static inline u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port)
+{
+ void __iomem *regs = mvi->regs;
+ return (port < 4) ? mr32(MVS_P0_SER_CTLSTAT + port * 4) :
+ mr32(MVS_P4_SER_CTLSTAT + (port - 4) * 4);
+}
+
+static inline void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val)
+{
+ void __iomem *regs = mvi->regs;
+ if (port < 4)
+ mw32(MVS_P0_SER_CTLSTAT + port * 4, val);
+ else
+ mw32(MVS_P4_SER_CTLSTAT + (port - 4) * 4, val);
+}
+
+static inline u32 mvs_read_port(struct mvs_info *mvi, u32 off,
+ u32 off2, u32 port)
+{
+ void __iomem *regs = mvi->regs + off;
+ void __iomem *regs2 = mvi->regs + off2;
+ return (port < 4) ? readl(regs + port * 8) :
+ readl(regs2 + (port - 4) * 8);
+}
+
+static inline void mvs_write_port(struct mvs_info *mvi, u32 off, u32 off2,
+ u32 port, u32 val)
+{
+ void __iomem *regs = mvi->regs + off;
+ void __iomem *regs2 = mvi->regs + off2;
+ if (port < 4)
+ writel(val, regs + port * 8);
+ else
+ writel(val, regs2 + (port - 4) * 8);
+}
+
+static inline u32 mvs_read_port_cfg_data(struct mvs_info *mvi, u32 port)
+{
+ return mvs_read_port(mvi, MVS_P0_CFG_DATA,
+ MVS_P4_CFG_DATA, port);
+}
+
+static inline void mvs_write_port_cfg_data(struct mvs_info *mvi,
+ u32 port, u32 val)
+{
+ mvs_write_port(mvi, MVS_P0_CFG_DATA,
+ MVS_P4_CFG_DATA, port, val);
+}
+
+static inline void mvs_write_port_cfg_addr(struct mvs_info *mvi,
+ u32 port, u32 addr)
+{
+ mvs_write_port(mvi, MVS_P0_CFG_ADDR,
+ MVS_P4_CFG_ADDR, port, addr);
+ mdelay(10);
+}
+
+static inline u32 mvs_read_port_vsr_data(struct mvs_info *mvi, u32 port)
+{
+ return mvs_read_port(mvi, MVS_P0_VSR_DATA,
+ MVS_P4_VSR_DATA, port);
+}
+
+static inline void mvs_write_port_vsr_data(struct mvs_info *mvi,
+ u32 port, u32 val)
+{
+ mvs_write_port(mvi, MVS_P0_VSR_DATA,
+ MVS_P4_VSR_DATA, port, val);
+}
+
+static inline void mvs_write_port_vsr_addr(struct mvs_info *mvi,
+ u32 port, u32 addr)
+{
+ mvs_write_port(mvi, MVS_P0_VSR_ADDR,
+ MVS_P4_VSR_ADDR, port, addr);
+ mdelay(10);
+}
+
+static inline u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port)
+{
+ return mvs_read_port(mvi, MVS_P0_INT_STAT,
+ MVS_P4_INT_STAT, port);
+}
+
+static inline void mvs_write_port_irq_stat(struct mvs_info *mvi,
+ u32 port, u32 val)
+{
+ mvs_write_port(mvi, MVS_P0_INT_STAT,
+ MVS_P4_INT_STAT, port, val);
+}
+
+static inline u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port)
+{
+ return mvs_read_port(mvi, MVS_P0_INT_MASK,
+ MVS_P4_INT_MASK, port);
+
+}
+
+static inline void mvs_write_port_irq_mask(struct mvs_info *mvi,
+ u32 port, u32 val)
+{
+ mvs_write_port(mvi, MVS_P0_INT_MASK,
+ MVS_P4_INT_MASK, port, val);
+}
+
+static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi)
+{
+ u32 tmp;
+
+ /* workaround for SATA R-ERR, to ignore phy glitch */
+ tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
+ tmp &= ~(1 << 9);
+ tmp |= (1 << 10);
+ mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
+
+ /* enable retry 127 times */
+ mvs_cw32(mvi, CMD_SAS_CTL1, 0x7f7f);
+
+ /* extend open frame timeout to max */
+ tmp = mvs_cr32(mvi, CMD_SAS_CTL0);
+ tmp &= ~0xffff;
+ tmp |= 0x3fff;
+ mvs_cw32(mvi, CMD_SAS_CTL0, tmp);
+
+ /* workaround for WDTIMEOUT , set to 550 ms */
+ mvs_cw32(mvi, CMD_WD_TIMER, 0x7a0000);
+
+ /* not to halt for different port op during wideport link change */
+ mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d);
+
+ /* workaround for Seagate disk not-found OOB sequence, recv
+ * COMINIT before sending out COMWAKE */
+ tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
+ tmp &= 0x0000ffff;
+ tmp |= 0x00fa0000;
+ mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
+
+ tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
+ tmp &= 0x1fffffff;
+ tmp |= (2U << 29); /* 8 ms retry */
+ mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
+}
+
+static inline void mvs_int_sata(struct mvs_info *mvi)
+{
+ u32 tmp;
+ void __iomem *regs = mvi->regs;
+ tmp = mr32(MVS_INT_STAT_SRS_0);
+ if (tmp)
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ MVS_CHIP_DISP->clear_active_cmds(mvi);
+}
+
+static inline void mvs_int_full(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp, stat;
+ int i;
+
+ stat = mr32(MVS_INT_STAT);
+ mvs_int_rx(mvi, false);
+
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ tmp = (stat >> i) & (CINT_PORT | CINT_PORT_STOPPED);
+ if (tmp)
+ mvs_int_port(mvi, i, tmp);
+ }
+
+ if (stat & CINT_SRS)
+ mvs_int_sata(mvi);
+
+ mw32(MVS_INT_STAT, stat);
+}
+
+static inline void mvs_start_delivery(struct mvs_info *mvi, u32 tx)
+{
+ void __iomem *regs = mvi->regs;
+ mw32(MVS_TX_PROD_IDX, tx);
+}
+
+static inline u32 mvs_rx_update(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ return mr32(MVS_RX_CONS_IDX);
+}
+
+static inline u32 mvs_get_prd_size(void)
+{
+ return sizeof(struct mvs_prd);
+}
+
+static inline u32 mvs_get_prd_count(void)
+{
+ return MAX_SG_ENTRY;
+}
+
+static inline void mvs_show_pcie_usage(struct mvs_info *mvi)
+{
+ u16 link_stat, link_spd;
+ const char *spd[] = {
+ "UnKnown",
+ "2.5",
+ "5.0",
+ };
+ if (mvi->flags & MVF_FLAG_SOC || mvi->id > 0)
+ return;
+
+ pci_read_config_word(mvi->pdev, PCR_LINK_STAT, &link_stat);
+ link_spd = (link_stat & PLS_LINK_SPD) >> PLS_LINK_SPD_OFFS;
+ if (link_spd >= 3)
+ link_spd = 0;
+ dev_printk(KERN_INFO, mvi->dev,
+ "mvsas: PCI-E x%u, Bandwidth Usage: %s Gbps\n",
+ (link_stat & PLS_NEG_LINK_WD) >> PLS_NEG_LINK_WD_OFFS,
+ spd[link_spd]);
+}
+
+static inline u32 mvs_hw_max_link_rate(void)
+{
+ return MAX_LINK_RATE;
+}
+
+#endif /* _MV_CHIPS_H_ */
+
diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h
new file mode 100644
index 000000000000..f8cb9defb961
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_defs.h
@@ -0,0 +1,502 @@
+/*
+ * Marvell 88SE64xx/88SE94xx const head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#ifndef _MV_DEFS_H_
+#define _MV_DEFS_H_
+
+
+enum chip_flavors {
+ chip_6320,
+ chip_6440,
+ chip_6485,
+ chip_9480,
+ chip_9180,
+};
+
+/* driver compile-time configuration */
+enum driver_configuration {
+ MVS_SLOTS = 512, /* command slots */
+ MVS_TX_RING_SZ = 1024, /* TX ring size (12-bit) */
+ MVS_RX_RING_SZ = 1024, /* RX ring size (12-bit) */
+ /* software requires power-of-2
+ ring size */
+ MVS_SOC_SLOTS = 64,
+ MVS_SOC_TX_RING_SZ = MVS_SOC_SLOTS * 2,
+ MVS_SOC_RX_RING_SZ = MVS_SOC_SLOTS * 2,
+
+ MVS_SLOT_BUF_SZ = 8192, /* cmd tbl + IU + status + PRD */
+ MVS_SSP_CMD_SZ = 64, /* SSP command table buffer size */
+ MVS_ATA_CMD_SZ = 96, /* SATA command table buffer size */
+ MVS_OAF_SZ = 64, /* Open address frame buffer size */
+ MVS_QUEUE_SIZE = 32, /* Support Queue depth */
+ MVS_CAN_QUEUE = MVS_SLOTS - 2, /* SCSI Queue depth */
+ MVS_SOC_CAN_QUEUE = MVS_SOC_SLOTS - 2,
+};
+
+/* unchangeable hardware details */
+enum hardware_details {
+ MVS_MAX_PHYS = 8, /* max. possible phys */
+ MVS_MAX_PORTS = 8, /* max. possible ports */
+ MVS_SOC_PHYS = 4, /* soc phys */
+ MVS_SOC_PORTS = 4, /* soc phys */
+ MVS_MAX_DEVICES = 1024, /* max supported device */
+};
+
+/* peripheral registers (BAR2) */
+enum peripheral_registers {
+ SPI_CTL = 0x10, /* EEPROM control */
+ SPI_CMD = 0x14, /* EEPROM command */
+ SPI_DATA = 0x18, /* EEPROM data */
+};
+
+enum peripheral_register_bits {
+ TWSI_RDY = (1U << 7), /* EEPROM interface ready */
+ TWSI_RD = (1U << 4), /* EEPROM read access */
+
+ SPI_ADDR_MASK = 0x3ffff, /* bits 17:0 */
+};
+
+enum hw_register_bits {
+ /* MVS_GBL_CTL */
+ INT_EN = (1U << 1), /* Global int enable */
+ HBA_RST = (1U << 0), /* HBA reset */
+
+ /* MVS_GBL_INT_STAT */
+ INT_XOR = (1U << 4), /* XOR engine event */
+ INT_SAS_SATA = (1U << 0), /* SAS/SATA event */
+
+ /* MVS_GBL_PORT_TYPE */ /* shl for ports 1-3 */
+ SATA_TARGET = (1U << 16), /* port0 SATA target enable */
+ MODE_AUTO_DET_PORT7 = (1U << 15), /* port0 SAS/SATA autodetect */
+ MODE_AUTO_DET_PORT6 = (1U << 14),
+ MODE_AUTO_DET_PORT5 = (1U << 13),
+ MODE_AUTO_DET_PORT4 = (1U << 12),
+ MODE_AUTO_DET_PORT3 = (1U << 11),
+ MODE_AUTO_DET_PORT2 = (1U << 10),
+ MODE_AUTO_DET_PORT1 = (1U << 9),
+ MODE_AUTO_DET_PORT0 = (1U << 8),
+ MODE_AUTO_DET_EN = MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 |
+ MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 |
+ MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 |
+ MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7,
+ MODE_SAS_PORT7_MASK = (1U << 7), /* port0 SAS(1), SATA(0) mode */
+ MODE_SAS_PORT6_MASK = (1U << 6),
+ MODE_SAS_PORT5_MASK = (1U << 5),
+ MODE_SAS_PORT4_MASK = (1U << 4),
+ MODE_SAS_PORT3_MASK = (1U << 3),
+ MODE_SAS_PORT2_MASK = (1U << 2),
+ MODE_SAS_PORT1_MASK = (1U << 1),
+ MODE_SAS_PORT0_MASK = (1U << 0),
+ MODE_SAS_SATA = MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK |
+ MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK |
+ MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK |
+ MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK,
+
+ /* SAS_MODE value may be
+ * dictated (in hw) by values
+ * of SATA_TARGET & AUTO_DET
+ */
+
+ /* MVS_TX_CFG */
+ TX_EN = (1U << 16), /* Enable TX */
+ TX_RING_SZ_MASK = 0xfff, /* TX ring size, bits 11:0 */
+
+ /* MVS_RX_CFG */
+ RX_EN = (1U << 16), /* Enable RX */
+ RX_RING_SZ_MASK = 0xfff, /* RX ring size, bits 11:0 */
+
+ /* MVS_INT_COAL */
+ COAL_EN = (1U << 16), /* Enable int coalescing */
+
+ /* MVS_INT_STAT, MVS_INT_MASK */
+ CINT_I2C = (1U << 31), /* I2C event */
+ CINT_SW0 = (1U << 30), /* software event 0 */
+ CINT_SW1 = (1U << 29), /* software event 1 */
+ CINT_PRD_BC = (1U << 28), /* PRD BC err for read cmd */
+ CINT_DMA_PCIE = (1U << 27), /* DMA to PCIE timeout */
+ CINT_MEM = (1U << 26), /* int mem parity err */
+ CINT_I2C_SLAVE = (1U << 25), /* slave I2C event */
+ CINT_SRS = (1U << 3), /* SRS event */
+ CINT_CI_STOP = (1U << 1), /* cmd issue stopped */
+ CINT_DONE = (1U << 0), /* cmd completion */
+
+ /* shl for ports 1-3 */
+ CINT_PORT_STOPPED = (1U << 16), /* port0 stopped */
+ CINT_PORT = (1U << 8), /* port0 event */
+ CINT_PORT_MASK_OFFSET = 8,
+ CINT_PORT_MASK = (0xFF << CINT_PORT_MASK_OFFSET),
+ CINT_PHY_MASK_OFFSET = 4,
+ CINT_PHY_MASK = (0x0F << CINT_PHY_MASK_OFFSET),
+
+ /* TX (delivery) ring bits */
+ TXQ_CMD_SHIFT = 29,
+ TXQ_CMD_SSP = 1, /* SSP protocol */
+ TXQ_CMD_SMP = 2, /* SMP protocol */
+ TXQ_CMD_STP = 3, /* STP/SATA protocol */
+ TXQ_CMD_SSP_FREE_LIST = 4, /* add to SSP targ free list */
+ TXQ_CMD_SLOT_RESET = 7, /* reset command slot */
+ TXQ_MODE_I = (1U << 28), /* mode: 0=target,1=initiator */
+ TXQ_MODE_TARGET = 0,
+ TXQ_MODE_INITIATOR = 1,
+ TXQ_PRIO_HI = (1U << 27), /* priority: 0=normal, 1=high */
+ TXQ_PRI_NORMAL = 0,
+ TXQ_PRI_HIGH = 1,
+ TXQ_SRS_SHIFT = 20, /* SATA register set */
+ TXQ_SRS_MASK = 0x7f,
+ TXQ_PHY_SHIFT = 12, /* PHY bitmap */
+ TXQ_PHY_MASK = 0xff,
+ TXQ_SLOT_MASK = 0xfff, /* slot number */
+
+ /* RX (completion) ring bits */
+ RXQ_GOOD = (1U << 23), /* Response good */
+ RXQ_SLOT_RESET = (1U << 21), /* Slot reset complete */
+ RXQ_CMD_RX = (1U << 20), /* target cmd received */
+ RXQ_ATTN = (1U << 19), /* attention */
+ RXQ_RSP = (1U << 18), /* response frame xfer'd */
+ RXQ_ERR = (1U << 17), /* err info rec xfer'd */
+ RXQ_DONE = (1U << 16), /* cmd complete */
+ RXQ_SLOT_MASK = 0xfff, /* slot number */
+
+ /* mvs_cmd_hdr bits */
+ MCH_PRD_LEN_SHIFT = 16, /* 16-bit PRD table len */
+ MCH_SSP_FR_TYPE_SHIFT = 13, /* SSP frame type */
+
+ /* SSP initiator only */
+ MCH_SSP_FR_CMD = 0x0, /* COMMAND frame */
+
+ /* SSP initiator or target */
+ MCH_SSP_FR_TASK = 0x1, /* TASK frame */
+
+ /* SSP target only */
+ MCH_SSP_FR_XFER_RDY = 0x4, /* XFER_RDY frame */
+ MCH_SSP_FR_RESP = 0x5, /* RESPONSE frame */
+ MCH_SSP_FR_READ = 0x6, /* Read DATA frame(s) */
+ MCH_SSP_FR_READ_RESP = 0x7, /* ditto, plus RESPONSE */
+
+ MCH_SSP_MODE_PASSTHRU = 1,
+ MCH_SSP_MODE_NORMAL = 0,
+ MCH_PASSTHRU = (1U << 12), /* pass-through (SSP) */
+ MCH_FBURST = (1U << 11), /* first burst (SSP) */
+ MCH_CHK_LEN = (1U << 10), /* chk xfer len (SSP) */
+ MCH_RETRY = (1U << 9), /* tport layer retry (SSP) */
+ MCH_PROTECTION = (1U << 8), /* protection info rec (SSP) */
+ MCH_RESET = (1U << 7), /* Reset (STP/SATA) */
+ MCH_FPDMA = (1U << 6), /* First party DMA (STP/SATA) */
+ MCH_ATAPI = (1U << 5), /* ATAPI (STP/SATA) */
+ MCH_BIST = (1U << 4), /* BIST activate (STP/SATA) */
+ MCH_PMP_MASK = 0xf, /* PMP from cmd FIS (STP/SATA)*/
+
+ CCTL_RST = (1U << 5), /* port logic reset */
+
+ /* 0(LSB first), 1(MSB first) */
+ CCTL_ENDIAN_DATA = (1U << 3), /* PRD data */
+ CCTL_ENDIAN_RSP = (1U << 2), /* response frame */
+ CCTL_ENDIAN_OPEN = (1U << 1), /* open address frame */
+ CCTL_ENDIAN_CMD = (1U << 0), /* command table */
+
+ /* MVS_Px_SER_CTLSTAT (per-phy control) */
+ PHY_SSP_RST = (1U << 3), /* reset SSP link layer */
+ PHY_BCAST_CHG = (1U << 2), /* broadcast(change) notif */
+ PHY_RST_HARD = (1U << 1), /* hard reset + phy reset */
+ PHY_RST = (1U << 0), /* phy reset */
+ PHY_READY_MASK = (1U << 20),
+
+ /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
+ PHYEV_DEC_ERR = (1U << 24), /* Phy Decoding Error */
+ PHYEV_DCDR_ERR = (1U << 23), /* STP Deocder Error */
+ PHYEV_CRC_ERR = (1U << 22), /* STP CRC Error */
+ PHYEV_UNASSOC_FIS = (1U << 19), /* unassociated FIS rx'd */
+ PHYEV_AN = (1U << 18), /* SATA async notification */
+ PHYEV_BIST_ACT = (1U << 17), /* BIST activate FIS */
+ PHYEV_SIG_FIS = (1U << 16), /* signature FIS */
+ PHYEV_POOF = (1U << 12), /* phy ready from 1 -> 0 */
+ PHYEV_IU_BIG = (1U << 11), /* IU too long err */
+ PHYEV_IU_SMALL = (1U << 10), /* IU too short err */
+ PHYEV_UNK_TAG = (1U << 9), /* unknown tag */
+ PHYEV_BROAD_CH = (1U << 8), /* broadcast(CHANGE) */
+ PHYEV_COMWAKE = (1U << 7), /* COMWAKE rx'd */
+ PHYEV_PORT_SEL = (1U << 6), /* port selector present */
+ PHYEV_HARD_RST = (1U << 5), /* hard reset rx'd */
+ PHYEV_ID_TMOUT = (1U << 4), /* identify timeout */
+ PHYEV_ID_FAIL = (1U << 3), /* identify failed */
+ PHYEV_ID_DONE = (1U << 2), /* identify done */
+ PHYEV_HARD_RST_DONE = (1U << 1), /* hard reset done */
+ PHYEV_RDY_CH = (1U << 0), /* phy ready changed state */
+
+ /* MVS_PCS */
+ PCS_EN_SATA_REG_SHIFT = (16), /* Enable SATA Register Set */
+ PCS_EN_PORT_XMT_SHIFT = (12), /* Enable Port Transmit */
+ PCS_EN_PORT_XMT_SHIFT2 = (8), /* For 6485 */
+ PCS_SATA_RETRY = (1U << 8), /* retry ctl FIS on R_ERR */
+ PCS_RSP_RX_EN = (1U << 7), /* raw response rx */
+ PCS_SATA_RETRY_2 = (1U << 6), /* For 9180 */
+ PCS_SELF_CLEAR = (1U << 5), /* self-clearing int mode */
+ PCS_FIS_RX_EN = (1U << 4), /* FIS rx enable */
+ PCS_CMD_STOP_ERR = (1U << 3), /* cmd stop-on-err enable */
+ PCS_CMD_RST = (1U << 1), /* reset cmd issue */
+ PCS_CMD_EN = (1U << 0), /* enable cmd issue */
+
+ /* Port n Attached Device Info */
+ PORT_DEV_SSP_TRGT = (1U << 19),
+ PORT_DEV_SMP_TRGT = (1U << 18),
+ PORT_DEV_STP_TRGT = (1U << 17),
+ PORT_DEV_SSP_INIT = (1U << 11),
+ PORT_DEV_SMP_INIT = (1U << 10),
+ PORT_DEV_STP_INIT = (1U << 9),
+ PORT_PHY_ID_MASK = (0xFFU << 24),
+ PORT_SSP_TRGT_MASK = (0x1U << 19),
+ PORT_SSP_INIT_MASK = (0x1U << 11),
+ PORT_DEV_TRGT_MASK = (0x7U << 17),
+ PORT_DEV_INIT_MASK = (0x7U << 9),
+ PORT_DEV_TYPE_MASK = (0x7U << 0),
+
+ /* Port n PHY Status */
+ PHY_RDY = (1U << 2),
+ PHY_DW_SYNC = (1U << 1),
+ PHY_OOB_DTCTD = (1U << 0),
+
+ /* VSR */
+ /* PHYMODE 6 (CDB) */
+ PHY_MODE6_LATECLK = (1U << 29), /* Lock Clock */
+ PHY_MODE6_DTL_SPEED = (1U << 27), /* Digital Loop Speed */
+ PHY_MODE6_FC_ORDER = (1U << 26), /* Fibre Channel Mode Order*/
+ PHY_MODE6_MUCNT_EN = (1U << 24), /* u Count Enable */
+ PHY_MODE6_SEL_MUCNT_LEN = (1U << 22), /* Training Length Select */
+ PHY_MODE6_SELMUPI = (1U << 20), /* Phase Multi Select (init) */
+ PHY_MODE6_SELMUPF = (1U << 18), /* Phase Multi Select (final) */
+ PHY_MODE6_SELMUFF = (1U << 16), /* Freq Loop Multi Sel(final) */
+ PHY_MODE6_SELMUFI = (1U << 14), /* Freq Loop Multi Sel(init) */
+ PHY_MODE6_FREEZE_LOOP = (1U << 12), /* Freeze Rx CDR Loop */
+ PHY_MODE6_INT_RXFOFFS = (1U << 3), /* Rx CDR Freq Loop Enable */
+ PHY_MODE6_FRC_RXFOFFS = (1U << 2), /* Initial Rx CDR Offset */
+ PHY_MODE6_STAU_0D8 = (1U << 1), /* Rx CDR Freq Loop Saturate */
+ PHY_MODE6_RXSAT_DIS = (1U << 0), /* Saturate Ctl */
+};
+
+/* SAS/SATA configuration port registers, aka phy registers */
+enum sas_sata_config_port_regs {
+ PHYR_IDENTIFY = 0x00, /* info for IDENTIFY frame */
+ PHYR_ADDR_LO = 0x04, /* my SAS address (low) */
+ PHYR_ADDR_HI = 0x08, /* my SAS address (high) */
+ PHYR_ATT_DEV_INFO = 0x0C, /* attached device info */
+ PHYR_ATT_ADDR_LO = 0x10, /* attached dev SAS addr (low) */
+ PHYR_ATT_ADDR_HI = 0x14, /* attached dev SAS addr (high) */
+ PHYR_SATA_CTL = 0x18, /* SATA control */
+ PHYR_PHY_STAT = 0x1C, /* PHY status */
+ PHYR_SATA_SIG0 = 0x20, /*port SATA signature FIS(Byte 0-3) */
+ PHYR_SATA_SIG1 = 0x24, /*port SATA signature FIS(Byte 4-7) */
+ PHYR_SATA_SIG2 = 0x28, /*port SATA signature FIS(Byte 8-11) */
+ PHYR_SATA_SIG3 = 0x2c, /*port SATA signature FIS(Byte 12-15) */
+ PHYR_R_ERR_COUNT = 0x30, /* port R_ERR count register */
+ PHYR_CRC_ERR_COUNT = 0x34, /* port CRC error count register */
+ PHYR_WIDE_PORT = 0x38, /* wide port participating */
+ PHYR_CURRENT0 = 0x80, /* current connection info 0 */
+ PHYR_CURRENT1 = 0x84, /* current connection info 1 */
+ PHYR_CURRENT2 = 0x88, /* current connection info 2 */
+ CONFIG_ID_FRAME0 = 0x100, /* Port device ID frame register 0 */
+ CONFIG_ID_FRAME1 = 0x104, /* Port device ID frame register 1 */
+ CONFIG_ID_FRAME2 = 0x108, /* Port device ID frame register 2 */
+ CONFIG_ID_FRAME3 = 0x10c, /* Port device ID frame register 3 */
+ CONFIG_ID_FRAME4 = 0x110, /* Port device ID frame register 4 */
+ CONFIG_ID_FRAME5 = 0x114, /* Port device ID frame register 5 */
+ CONFIG_ID_FRAME6 = 0x118, /* Port device ID frame register 6 */
+ CONFIG_ATT_ID_FRAME0 = 0x11c, /* attached ID frame register 0 */
+ CONFIG_ATT_ID_FRAME1 = 0x120, /* attached ID frame register 1 */
+ CONFIG_ATT_ID_FRAME2 = 0x124, /* attached ID frame register 2 */
+ CONFIG_ATT_ID_FRAME3 = 0x128, /* attached ID frame register 3 */
+ CONFIG_ATT_ID_FRAME4 = 0x12c, /* attached ID frame register 4 */
+ CONFIG_ATT_ID_FRAME5 = 0x130, /* attached ID frame register 5 */
+ CONFIG_ATT_ID_FRAME6 = 0x134, /* attached ID frame register 6 */
+};
+
+enum sas_cmd_port_registers {
+ CMD_CMRST_OOB_DET = 0x100, /* COMRESET OOB detect register */
+ CMD_CMWK_OOB_DET = 0x104, /* COMWAKE OOB detect register */
+ CMD_CMSAS_OOB_DET = 0x108, /* COMSAS OOB detect register */
+ CMD_BRST_OOB_DET = 0x10c, /* burst OOB detect register */
+ CMD_OOB_SPACE = 0x110, /* OOB space control register */
+ CMD_OOB_BURST = 0x114, /* OOB burst control register */
+ CMD_PHY_TIMER = 0x118, /* PHY timer control register */
+ CMD_PHY_CONFIG0 = 0x11c, /* PHY config register 0 */
+ CMD_PHY_CONFIG1 = 0x120, /* PHY config register 1 */
+ CMD_SAS_CTL0 = 0x124, /* SAS control register 0 */
+ CMD_SAS_CTL1 = 0x128, /* SAS control register 1 */
+ CMD_SAS_CTL2 = 0x12c, /* SAS control register 2 */
+ CMD_SAS_CTL3 = 0x130, /* SAS control register 3 */
+ CMD_ID_TEST = 0x134, /* ID test register */
+ CMD_PL_TIMER = 0x138, /* PL timer register */
+ CMD_WD_TIMER = 0x13c, /* WD timer register */
+ CMD_PORT_SEL_COUNT = 0x140, /* port selector count register */
+ CMD_APP_MEM_CTL = 0x144, /* Application Memory Control */
+ CMD_XOR_MEM_CTL = 0x148, /* XOR Block Memory Control */
+ CMD_DMA_MEM_CTL = 0x14c, /* DMA Block Memory Control */
+ CMD_PORT_MEM_CTL0 = 0x150, /* Port Memory Control 0 */
+ CMD_PORT_MEM_CTL1 = 0x154, /* Port Memory Control 1 */
+ CMD_SATA_PORT_MEM_CTL0 = 0x158, /* SATA Port Memory Control 0 */
+ CMD_SATA_PORT_MEM_CTL1 = 0x15c, /* SATA Port Memory Control 1 */
+ CMD_XOR_MEM_BIST_CTL = 0x160, /* XOR Memory BIST Control */
+ CMD_XOR_MEM_BIST_STAT = 0x164, /* XOR Memroy BIST Status */
+ CMD_DMA_MEM_BIST_CTL = 0x168, /* DMA Memory BIST Control */
+ CMD_DMA_MEM_BIST_STAT = 0x16c, /* DMA Memory BIST Status */
+ CMD_PORT_MEM_BIST_CTL = 0x170, /* Port Memory BIST Control */
+ CMD_PORT_MEM_BIST_STAT0 = 0x174, /* Port Memory BIST Status 0 */
+ CMD_PORT_MEM_BIST_STAT1 = 0x178, /* Port Memory BIST Status 1 */
+ CMD_STP_MEM_BIST_CTL = 0x17c, /* STP Memory BIST Control */
+ CMD_STP_MEM_BIST_STAT0 = 0x180, /* STP Memory BIST Status 0 */
+ CMD_STP_MEM_BIST_STAT1 = 0x184, /* STP Memory BIST Status 1 */
+ CMD_RESET_COUNT = 0x188, /* Reset Count */
+ CMD_MONTR_DATA_SEL = 0x18C, /* Monitor Data/Select */
+ CMD_PLL_PHY_CONFIG = 0x190, /* PLL/PHY Configuration */
+ CMD_PHY_CTL = 0x194, /* PHY Control and Status */
+ CMD_PHY_TEST_COUNT0 = 0x198, /* Phy Test Count 0 */
+ CMD_PHY_TEST_COUNT1 = 0x19C, /* Phy Test Count 1 */
+ CMD_PHY_TEST_COUNT2 = 0x1A0, /* Phy Test Count 2 */
+ CMD_APP_ERR_CONFIG = 0x1A4, /* Application Error Configuration */
+ CMD_PND_FIFO_CTL0 = 0x1A8, /* Pending FIFO Control 0 */
+ CMD_HOST_CTL = 0x1AC, /* Host Control Status */
+ CMD_HOST_WR_DATA = 0x1B0, /* Host Write Data */
+ CMD_HOST_RD_DATA = 0x1B4, /* Host Read Data */
+ CMD_PHY_MODE_21 = 0x1B8, /* Phy Mode 21 */
+ CMD_SL_MODE0 = 0x1BC, /* SL Mode 0 */
+ CMD_SL_MODE1 = 0x1C0, /* SL Mode 1 */
+ CMD_PND_FIFO_CTL1 = 0x1C4, /* Pending FIFO Control 1 */
+};
+
+enum mvs_info_flags {
+ MVF_MSI = (1U << 0), /* MSI is enabled */
+ MVF_PHY_PWR_FIX = (1U << 1), /* bug workaround */
+ MVF_FLAG_SOC = (1U << 2), /* SoC integrated controllers */
+};
+
+enum mvs_event_flags {
+ PHY_PLUG_EVENT = (3U),
+ PHY_PLUG_IN = (1U << 0), /* phy plug in */
+ PHY_PLUG_OUT = (1U << 1), /* phy plug out */
+};
+
+enum mvs_port_type {
+ PORT_TGT_MASK = (1U << 5),
+ PORT_INIT_PORT = (1U << 4),
+ PORT_TGT_PORT = (1U << 3),
+ PORT_INIT_TGT_PORT = (PORT_INIT_PORT | PORT_TGT_PORT),
+ PORT_TYPE_SAS = (1U << 1),
+ PORT_TYPE_SATA = (1U << 0),
+};
+
+/* Command Table Format */
+enum ct_format {
+ /* SSP */
+ SSP_F_H = 0x00,
+ SSP_F_IU = 0x18,
+ SSP_F_MAX = 0x4D,
+ /* STP */
+ STP_CMD_FIS = 0x00,
+ STP_ATAPI_CMD = 0x40,
+ STP_F_MAX = 0x10,
+ /* SMP */
+ SMP_F_T = 0x00,
+ SMP_F_DEP = 0x01,
+ SMP_F_MAX = 0x101,
+};
+
+enum status_buffer {
+ SB_EIR_OFF = 0x00, /* Error Information Record */
+ SB_RFB_OFF = 0x08, /* Response Frame Buffer */
+ SB_RFB_MAX = 0x400, /* RFB size*/
+};
+
+enum error_info_rec {
+ CMD_ISS_STPD = (1U << 31), /* Cmd Issue Stopped */
+ CMD_PI_ERR = (1U << 30), /* Protection info error. see flags2 */
+ RSP_OVER = (1U << 29), /* rsp buffer overflow */
+ RETRY_LIM = (1U << 28), /* FIS/frame retry limit exceeded */
+ UNK_FIS = (1U << 27), /* unknown FIS */
+ DMA_TERM = (1U << 26), /* DMA terminate primitive rx'd */
+ SYNC_ERR = (1U << 25), /* SYNC rx'd during frame xmit */
+ TFILE_ERR = (1U << 24), /* SATA taskfile Error bit set */
+ R_ERR = (1U << 23), /* SATA returned R_ERR prim */
+ RD_OFS = (1U << 20), /* Read DATA frame invalid offset */
+ XFER_RDY_OFS = (1U << 19), /* XFER_RDY offset error */
+ UNEXP_XFER_RDY = (1U << 18), /* unexpected XFER_RDY error */
+ DATA_OVER_UNDER = (1U << 16), /* data overflow/underflow */
+ INTERLOCK = (1U << 15), /* interlock error */
+ NAK = (1U << 14), /* NAK rx'd */
+ ACK_NAK_TO = (1U << 13), /* ACK/NAK timeout */
+ CXN_CLOSED = (1U << 12), /* cxn closed w/out ack/nak */
+ OPEN_TO = (1U << 11), /* I_T nexus lost, open cxn timeout */
+ PATH_BLOCKED = (1U << 10), /* I_T nexus lost, pathway blocked */
+ NO_DEST = (1U << 9), /* I_T nexus lost, no destination */
+ STP_RES_BSY = (1U << 8), /* STP resources busy */
+ BREAK = (1U << 7), /* break received */
+ BAD_DEST = (1U << 6), /* bad destination */
+ BAD_PROTO = (1U << 5), /* protocol not supported */
+ BAD_RATE = (1U << 4), /* cxn rate not supported */
+ WRONG_DEST = (1U << 3), /* wrong destination error */
+ CREDIT_TO = (1U << 2), /* credit timeout */
+ WDOG_TO = (1U << 1), /* watchdog timeout */
+ BUF_PAR = (1U << 0), /* buffer parity error */
+};
+
+enum error_info_rec_2 {
+ SLOT_BSY_ERR = (1U << 31), /* Slot Busy Error */
+ GRD_CHK_ERR = (1U << 14), /* Guard Check Error */
+ APP_CHK_ERR = (1U << 13), /* Application Check error */
+ REF_CHK_ERR = (1U << 12), /* Reference Check Error */
+ USR_BLK_NM = (1U << 0), /* User Block Number */
+};
+
+enum pci_cfg_register_bits {
+ PCTL_PWR_OFF = (0xFU << 24),
+ PCTL_COM_ON = (0xFU << 20),
+ PCTL_LINK_RST = (0xFU << 16),
+ PCTL_LINK_OFFS = (16),
+ PCTL_PHY_DSBL = (0xFU << 12),
+ PCTL_PHY_DSBL_OFFS = (12),
+ PRD_REQ_SIZE = (0x4000),
+ PRD_REQ_MASK = (0x00007000),
+ PLS_NEG_LINK_WD = (0x3FU << 4),
+ PLS_NEG_LINK_WD_OFFS = 4,
+ PLS_LINK_SPD = (0x0FU << 0),
+ PLS_LINK_SPD_OFFS = 0,
+};
+
+enum open_frame_protocol {
+ PROTOCOL_SMP = 0x0,
+ PROTOCOL_SSP = 0x1,
+ PROTOCOL_STP = 0x2,
+};
+
+/* define for response frame datapres field */
+enum datapres_field {
+ NO_DATA = 0,
+ RESPONSE_DATA = 1,
+ SENSE_DATA = 2,
+};
+
+/* define task management IU */
+struct mvs_tmf_task{
+ u8 tmf;
+ u16 tag_of_task_to_be_managed;
+};
+#endif
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
new file mode 100644
index 000000000000..8646a19f999d
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -0,0 +1,703 @@
+/*
+ * Marvell 88SE64xx/88SE94xx pci init
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+
+#include "mv_sas.h"
+
+static struct scsi_transport_template *mvs_stt;
+static const struct mvs_chip_info mvs_chips[] = {
+ [chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
+ [chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
+ [chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
+ [chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
+ [chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
+};
+
+#define SOC_SAS_NUM 2
+
+static struct scsi_host_template mvs_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = sas_queuecommand,
+ .target_alloc = sas_target_alloc,
+ .slave_configure = mvs_slave_configure,
+ .slave_destroy = sas_slave_destroy,
+ .scan_finished = mvs_scan_finished,
+ .scan_start = mvs_scan_start,
+ .change_queue_depth = sas_change_queue_depth,
+ .change_queue_type = sas_change_queue_type,
+ .bios_param = sas_bios_param,
+ .can_queue = 1,
+ .cmd_per_lun = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_device_reset_handler = sas_eh_device_reset_handler,
+ .eh_bus_reset_handler = sas_eh_bus_reset_handler,
+ .slave_alloc = mvs_slave_alloc,
+ .target_destroy = sas_target_destroy,
+ .ioctl = sas_ioctl,
+};
+
+static struct sas_domain_function_template mvs_transport_ops = {
+ .lldd_dev_found = mvs_dev_found,
+ .lldd_dev_gone = mvs_dev_gone,
+
+ .lldd_execute_task = mvs_queue_command,
+ .lldd_control_phy = mvs_phy_control,
+
+ .lldd_abort_task = mvs_abort_task,
+ .lldd_abort_task_set = mvs_abort_task_set,
+ .lldd_clear_aca = mvs_clear_aca,
+ .lldd_clear_task_set = mvs_clear_task_set,
+ .lldd_I_T_nexus_reset = mvs_I_T_nexus_reset,
+ .lldd_lu_reset = mvs_lu_reset,
+ .lldd_query_task = mvs_query_task,
+
+ .lldd_port_formed = mvs_port_formed,
+ .lldd_port_deformed = mvs_port_deformed,
+
+};
+
+static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
+{
+ struct mvs_phy *phy = &mvi->phy[phy_id];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ phy->mvi = mvi;
+ init_timer(&phy->timer);
+ sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
+ sas_phy->class = SAS;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
+ sas_phy->tproto = 0;
+ sas_phy->type = PHY_TYPE_PHYSICAL;
+ sas_phy->role = PHY_ROLE_INITIATOR;
+ sas_phy->oob_mode = OOB_NOT_CONNECTED;
+ sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+
+ sas_phy->id = phy_id;
+ sas_phy->sas_addr = &mvi->sas_addr[0];
+ sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+ sas_phy->ha = (struct sas_ha_struct *)mvi->shost->hostdata;
+ sas_phy->lldd_phy = phy;
+}
+
+static void mvs_free(struct mvs_info *mvi)
+{
+ int i;
+ struct mvs_wq *mwq;
+ int slot_nr;
+
+ if (!mvi)
+ return;
+
+ if (mvi->flags & MVF_FLAG_SOC)
+ slot_nr = MVS_SOC_SLOTS;
+ else
+ slot_nr = MVS_SLOTS;
+
+ for (i = 0; i < mvi->tags_num; i++) {
+ struct mvs_slot_info *slot = &mvi->slot_info[i];
+ if (slot->buf)
+ dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
+ slot->buf, slot->buf_dma);
+ }
+
+ if (mvi->tx)
+ dma_free_coherent(mvi->dev,
+ sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
+ mvi->tx, mvi->tx_dma);
+ if (mvi->rx_fis)
+ dma_free_coherent(mvi->dev, MVS_RX_FISL_SZ,
+ mvi->rx_fis, mvi->rx_fis_dma);
+ if (mvi->rx)
+ dma_free_coherent(mvi->dev,
+ sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
+ mvi->rx, mvi->rx_dma);
+ if (mvi->slot)
+ dma_free_coherent(mvi->dev,
+ sizeof(*mvi->slot) * slot_nr,
+ mvi->slot, mvi->slot_dma);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ if (mvi->bulk_buffer)
+ dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
+ mvi->bulk_buffer, mvi->bulk_buffer_dma);
+#endif
+
+ MVS_CHIP_DISP->chip_iounmap(mvi);
+ if (mvi->shost)
+ scsi_host_put(mvi->shost);
+ list_for_each_entry(mwq, &mvi->wq_list, entry)
+ cancel_delayed_work(&mwq->work_q);
+ kfree(mvi);
+}
+
+#ifdef MVS_USE_TASKLET
+struct tasklet_struct mv_tasklet;
+static void mvs_tasklet(unsigned long opaque)
+{
+ unsigned long flags;
+ u32 stat;
+ u16 core_nr, i = 0;
+
+ struct mvs_info *mvi;
+ struct sas_ha_struct *sha = (struct sas_ha_struct *)opaque;
+
+ core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+ if (unlikely(!mvi))
+ BUG_ON(1);
+
+ for (i = 0; i < core_nr; i++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+ stat = MVS_CHIP_DISP->isr_status(mvi, mvi->irq);
+ if (stat)
+ MVS_CHIP_DISP->isr(mvi, mvi->irq, stat);
+ }
+
+}
+#endif
+
+static irqreturn_t mvs_interrupt(int irq, void *opaque)
+{
+ u32 core_nr, i = 0;
+ u32 stat;
+ struct mvs_info *mvi;
+ struct sas_ha_struct *sha = opaque;
+
+ core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+ if (unlikely(!mvi))
+ return IRQ_NONE;
+
+ stat = MVS_CHIP_DISP->isr_status(mvi, irq);
+ if (!stat)
+ return IRQ_NONE;
+
+#ifdef MVS_USE_TASKLET
+ tasklet_schedule(&mv_tasklet);
+#else
+ for (i = 0; i < core_nr; i++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+ MVS_CHIP_DISP->isr(mvi, irq, stat);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
+{
+ int i, slot_nr;
+
+ if (mvi->flags & MVF_FLAG_SOC)
+ slot_nr = MVS_SOC_SLOTS;
+ else
+ slot_nr = MVS_SLOTS;
+
+ spin_lock_init(&mvi->lock);
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ mvs_phy_init(mvi, i);
+ mvi->port[i].wide_port_phymap = 0;
+ mvi->port[i].port_attached = 0;
+ INIT_LIST_HEAD(&mvi->port[i].list);
+ }
+ for (i = 0; i < MVS_MAX_DEVICES; i++) {
+ mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED;
+ mvi->devices[i].dev_type = NO_DEVICE;
+ mvi->devices[i].device_id = i;
+ mvi->devices[i].dev_status = MVS_DEV_NORMAL;
+ }
+
+ /*
+ * alloc and init our DMA areas
+ */
+ mvi->tx = dma_alloc_coherent(mvi->dev,
+ sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
+ &mvi->tx_dma, GFP_KERNEL);
+ if (!mvi->tx)
+ goto err_out;
+ memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ);
+ mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ,
+ &mvi->rx_fis_dma, GFP_KERNEL);
+ if (!mvi->rx_fis)
+ goto err_out;
+ memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ);
+
+ mvi->rx = dma_alloc_coherent(mvi->dev,
+ sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
+ &mvi->rx_dma, GFP_KERNEL);
+ if (!mvi->rx)
+ goto err_out;
+ memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1));
+ mvi->rx[0] = cpu_to_le32(0xfff);
+ mvi->rx_cons = 0xfff;
+
+ mvi->slot = dma_alloc_coherent(mvi->dev,
+ sizeof(*mvi->slot) * slot_nr,
+ &mvi->slot_dma, GFP_KERNEL);
+ if (!mvi->slot)
+ goto err_out;
+ memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr);
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ mvi->bulk_buffer = dma_alloc_coherent(mvi->dev,
+ TRASH_BUCKET_SIZE,
+ &mvi->bulk_buffer_dma, GFP_KERNEL);
+ if (!mvi->bulk_buffer)
+ goto err_out;
+#endif
+ for (i = 0; i < slot_nr; i++) {
+ struct mvs_slot_info *slot = &mvi->slot_info[i];
+
+ slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
+ &slot->buf_dma, GFP_KERNEL);
+ if (!slot->buf) {
+ printk(KERN_DEBUG"failed to allocate slot->buf.\n");
+ goto err_out;
+ }
+ memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+ ++mvi->tags_num;
+ }
+ /* Initialize tags */
+ mvs_tag_init(mvi);
+ return 0;
+err_out:
+ return 1;
+}
+
+
+int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
+{
+ unsigned long res_start, res_len, res_flag, res_flag_ex = 0;
+ struct pci_dev *pdev = mvi->pdev;
+ if (bar_ex != -1) {
+ /*
+ * ioremap main and peripheral registers
+ */
+ res_start = pci_resource_start(pdev, bar_ex);
+ res_len = pci_resource_len(pdev, bar_ex);
+ if (!res_start || !res_len)
+ goto err_out;
+
+ res_flag_ex = pci_resource_flags(pdev, bar_ex);
+ if (res_flag_ex & IORESOURCE_MEM) {
+ if (res_flag_ex & IORESOURCE_CACHEABLE)
+ mvi->regs_ex = ioremap(res_start, res_len);
+ else
+ mvi->regs_ex = ioremap_nocache(res_start,
+ res_len);
+ } else
+ mvi->regs_ex = (void *)res_start;
+ if (!mvi->regs_ex)
+ goto err_out;
+ }
+
+ res_start = pci_resource_start(pdev, bar);
+ res_len = pci_resource_len(pdev, bar);
+ if (!res_start || !res_len)
+ goto err_out;
+
+ res_flag = pci_resource_flags(pdev, bar);
+ if (res_flag & IORESOURCE_CACHEABLE)
+ mvi->regs = ioremap(res_start, res_len);
+ else
+ mvi->regs = ioremap_nocache(res_start, res_len);
+
+ if (!mvi->regs) {
+ if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM))
+ iounmap(mvi->regs_ex);
+ mvi->regs_ex = NULL;
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ return -1;
+}
+
+void mvs_iounmap(void __iomem *regs)
+{
+ iounmap(regs);
+}
+
+static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
+ const struct pci_device_id *ent,
+ struct Scsi_Host *shost, unsigned int id)
+{
+ struct mvs_info *mvi;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ mvi = kzalloc(sizeof(*mvi) + MVS_SLOTS * sizeof(struct mvs_slot_info),
+ GFP_KERNEL);
+ if (!mvi)
+ return NULL;
+
+ mvi->pdev = pdev;
+ mvi->dev = &pdev->dev;
+ mvi->chip_id = ent->driver_data;
+ mvi->chip = &mvs_chips[mvi->chip_id];
+ INIT_LIST_HEAD(&mvi->wq_list);
+ mvi->irq = pdev->irq;
+
+ ((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi;
+ ((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy;
+
+ mvi->id = id;
+ mvi->sas = sha;
+ mvi->shost = shost;
+#ifdef MVS_USE_TASKLET
+ tasklet_init(&mv_tasklet, mvs_tasklet, (unsigned long)sha);
+#endif
+
+ if (MVS_CHIP_DISP->chip_ioremap(mvi))
+ goto err_out;
+ if (!mvs_alloc(mvi, shost))
+ return mvi;
+err_out:
+ mvs_free(mvi);
+ return NULL;
+}
+
+/* move to PCI layer or libata core? */
+static int pci_go_64(struct pci_dev *pdev)
+{
+ int rc;
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "64-bit DMA enable failed\n");
+ return rc;
+ }
+ }
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit DMA enable failed\n");
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
+ const struct mvs_chip_info *chip_info)
+{
+ int phy_nr, port_nr; unsigned short core_nr;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ core_nr = chip_info->n_host;
+ phy_nr = core_nr * chip_info->n_phy;
+ port_nr = phy_nr;
+
+ memset(sha, 0x00, sizeof(struct sas_ha_struct));
+ arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
+ arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy || !arr_port)
+ goto exit_free;
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+
+ sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL);
+ if (!sha->lldd_ha)
+ goto exit_free;
+
+ ((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
+
+ shost->transportt = mvs_stt;
+ shost->max_id = 128;
+ shost->max_lun = ~0;
+ shost->max_channel = 1;
+ shost->max_cmd_len = 16;
+
+ return 0;
+exit_free:
+ kfree(arr_phy);
+ kfree(arr_port);
+ return -1;
+
+}
+
+static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
+ const struct mvs_chip_info *chip_info)
+{
+ int can_queue, i = 0, j = 0;
+ struct mvs_info *mvi = NULL;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ unsigned short nr_core = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+
+ for (j = 0; j < nr_core; j++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
+ for (i = 0; i < chip_info->n_phy; i++) {
+ sha->sas_phy[j * chip_info->n_phy + i] =
+ &mvi->phy[i].sas_phy;
+ sha->sas_port[j * chip_info->n_phy + i] =
+ &mvi->port[i].sas_port;
+ }
+ }
+
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = mvi->dev;
+ sha->lldd_module = THIS_MODULE;
+ sha->sas_addr = &mvi->sas_addr[0];
+
+ sha->num_phys = nr_core * chip_info->n_phy;
+
+ sha->lldd_max_execute_num = 1;
+
+ if (mvi->flags & MVF_FLAG_SOC)
+ can_queue = MVS_SOC_CAN_QUEUE;
+ else
+ can_queue = MVS_CAN_QUEUE;
+
+ sha->lldd_queue_size = can_queue;
+ shost->can_queue = can_queue;
+ mvi->shost->cmd_per_lun = MVS_SLOTS/sha->num_phys;
+ sha->core.shost = mvi->shost;
+}
+
+static void mvs_init_sas_add(struct mvs_info *mvi)
+{
+ u8 i;
+ for (i = 0; i < mvi->chip->n_phy; i++) {
+ mvi->phy[i].dev_sas_addr = 0x5005043011ab0000ULL;
+ mvi->phy[i].dev_sas_addr =
+ cpu_to_be64((u64)(*(u64 *)&mvi->phy[i].dev_sas_addr));
+ }
+
+ memcpy(mvi->sas_addr, &mvi->phy[0].dev_sas_addr, SAS_ADDR_SIZE);
+}
+
+static int __devinit mvs_pci_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned int rc, nhost = 0;
+ struct mvs_info *mvi;
+ irq_handler_t irq_handler = mvs_interrupt;
+ struct Scsi_Host *shost = NULL;
+ const struct mvs_chip_info *chip;
+
+ dev_printk(KERN_INFO, &pdev->dev,
+ "mvsas: driver version %s\n", DRV_VERSION);
+ rc = pci_enable_device(pdev);
+ if (rc)
+ goto err_out_enable;
+
+ pci_set_master(pdev);
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc)
+ goto err_out_disable;
+
+ rc = pci_go_64(pdev);
+ if (rc)
+ goto err_out_regions;
+
+ shost = scsi_host_alloc(&mvs_sht, sizeof(void *));
+ if (!shost) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ chip = &mvs_chips[ent->driver_data];
+ SHOST_TO_SAS_HA(shost) =
+ kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+ if (!SHOST_TO_SAS_HA(shost)) {
+ kfree(shost);
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ rc = mvs_prep_sas_ha_init(shost, chip);
+ if (rc) {
+ kfree(shost);
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
+
+ do {
+ mvi = mvs_pci_alloc(pdev, ent, shost, nhost);
+ if (!mvi) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ mvs_init_sas_add(mvi);
+
+ mvi->instance = nhost;
+ rc = MVS_CHIP_DISP->chip_init(mvi);
+ if (rc) {
+ mvs_free(mvi);
+ goto err_out_regions;
+ }
+ nhost++;
+ } while (nhost < chip->n_host);
+
+ mvs_post_sas_ha_init(shost, chip);
+
+ rc = scsi_add_host(shost, &pdev->dev);
+ if (rc)
+ goto err_out_shost;
+
+ rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
+ if (rc)
+ goto err_out_shost;
+ rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED,
+ DRV_NAME, SHOST_TO_SAS_HA(shost));
+ if (rc)
+ goto err_not_sas;
+
+ MVS_CHIP_DISP->interrupt_enable(mvi);
+
+ scsi_scan_host(mvi->shost);
+
+ return 0;
+
+err_not_sas:
+ sas_unregister_ha(SHOST_TO_SAS_HA(shost));
+err_out_shost:
+ scsi_remove_host(mvi->shost);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out_disable:
+ pci_disable_device(pdev);
+err_out_enable:
+ return rc;
+}
+
+static void __devexit mvs_pci_remove(struct pci_dev *pdev)
+{
+ unsigned short core_nr, i = 0;
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct mvs_info *mvi = NULL;
+
+ core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+#ifdef MVS_USE_TASKLET
+ tasklet_kill(&mv_tasklet);
+#endif
+
+ pci_set_drvdata(pdev, NULL);
+ sas_unregister_ha(sha);
+ sas_remove_host(mvi->shost);
+ scsi_remove_host(mvi->shost);
+
+ MVS_CHIP_DISP->interrupt_disable(mvi);
+ free_irq(mvi->irq, sha);
+ for (i = 0; i < core_nr; i++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+ mvs_free(mvi);
+ }
+ kfree(sha->sas_phy);
+ kfree(sha->sas_port);
+ kfree(sha);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ return;
+}
+
+static struct pci_device_id __devinitdata mvs_pci_table[] = {
+ { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
+ { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
+ {
+ .vendor = PCI_VENDOR_ID_MARVELL,
+ .device = 0x6440,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = 0x6480,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = chip_6485,
+ },
+ { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 },
+ { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 },
+ { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 },
+ { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 },
+
+ { } /* terminate list */
+};
+
+static struct pci_driver mvs_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = mvs_pci_table,
+ .probe = mvs_pci_init,
+ .remove = __devexit_p(mvs_pci_remove),
+};
+
+/* task handler */
+struct task_struct *mvs_th;
+static int __init mvs_init(void)
+{
+ int rc;
+ mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);
+ if (!mvs_stt)
+ return -ENOMEM;
+
+ rc = pci_register_driver(&mvs_pci_driver);
+
+ if (rc)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ sas_release_transport(mvs_stt);
+ return rc;
+}
+
+static void __exit mvs_exit(void)
+{
+ pci_unregister_driver(&mvs_pci_driver);
+ sas_release_transport(mvs_stt);
+}
+
+module_init(mvs_init);
+module_exit(mvs_exit);
+
+MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
+MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+#ifdef CONFIG_PCI
+MODULE_DEVICE_TABLE(pci, mvs_pci_table);
+#endif
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
new file mode 100644
index 000000000000..0d2138641214
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -0,0 +1,2154 @@
+/*
+ * Marvell 88SE64xx/88SE94xx main function
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#include "mv_sas.h"
+
+static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
+{
+ if (task->lldd_task) {
+ struct mvs_slot_info *slot;
+ slot = task->lldd_task;
+ *tag = slot->slot_tag;
+ return 1;
+ }
+ return 0;
+}
+
+void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
+{
+ void *bitmap = &mvi->tags;
+ clear_bit(tag, bitmap);
+}
+
+void mvs_tag_free(struct mvs_info *mvi, u32 tag)
+{
+ mvs_tag_clear(mvi, tag);
+}
+
+void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
+{
+ void *bitmap = &mvi->tags;
+ set_bit(tag, bitmap);
+}
+
+inline int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
+{
+ unsigned int index, tag;
+ void *bitmap = &mvi->tags;
+
+ index = find_first_zero_bit(bitmap, mvi->tags_num);
+ tag = index;
+ if (tag >= mvi->tags_num)
+ return -SAS_QUEUE_FULL;
+ mvs_tag_set(mvi, tag);
+ *tag_out = tag;
+ return 0;
+}
+
+void mvs_tag_init(struct mvs_info *mvi)
+{
+ int i;
+ for (i = 0; i < mvi->tags_num; ++i)
+ mvs_tag_clear(mvi, i);
+}
+
+void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
+{
+ u32 i;
+ u32 run;
+ u32 offset;
+
+ offset = 0;
+ while (size) {
+ printk(KERN_DEBUG"%08X : ", baseaddr + offset);
+ if (size >= 16)
+ run = 16;
+ else
+ run = size;
+ size -= run;
+ for (i = 0; i < 16; i++) {
+ if (i < run)
+ printk(KERN_DEBUG"%02X ", (u32)data[i]);
+ else
+ printk(KERN_DEBUG" ");
+ }
+ printk(KERN_DEBUG": ");
+ for (i = 0; i < run; i++)
+ printk(KERN_DEBUG"%c",
+ isalnum(data[i]) ? data[i] : '.');
+ printk(KERN_DEBUG"\n");
+ data = &data[16];
+ offset += run;
+ }
+ printk(KERN_DEBUG"\n");
+}
+
+#if (_MV_DUMP > 1)
+static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
+ enum sas_protocol proto)
+{
+ u32 offset;
+ struct mvs_slot_info *slot = &mvi->slot_info[tag];
+
+ offset = slot->cmd_size + MVS_OAF_SZ +
+ MVS_CHIP_DISP->prd_size() * slot->n_elem;
+ dev_printk(KERN_DEBUG, mvi->dev, "+---->Status buffer[%d] :\n",
+ tag);
+ mvs_hexdump(32, (u8 *) slot->response,
+ (u32) slot->buf_dma + offset);
+}
+#endif
+
+static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
+ enum sas_protocol proto)
+{
+#if (_MV_DUMP > 1)
+ u32 sz, w_ptr;
+ u64 addr;
+ struct mvs_slot_info *slot = &mvi->slot_info[tag];
+
+ /*Delivery Queue */
+ sz = MVS_CHIP_SLOT_SZ;
+ w_ptr = slot->tx;
+ addr = mvi->tx_dma;
+ dev_printk(KERN_DEBUG, mvi->dev,
+ "Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
+ dev_printk(KERN_DEBUG, mvi->dev,
+ "Delivery Queue Base Address=0x%llX (PA)"
+ "(tx_dma=0x%llX), Entry=%04d\n",
+ addr, (unsigned long long)mvi->tx_dma, w_ptr);
+ mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
+ (u32) mvi->tx_dma + sizeof(u32) * w_ptr);
+ /*Command List */
+ addr = mvi->slot_dma;
+ dev_printk(KERN_DEBUG, mvi->dev,
+ "Command List Base Address=0x%llX (PA)"
+ "(slot_dma=0x%llX), Header=%03d\n",
+ addr, (unsigned long long)slot->buf_dma, tag);
+ dev_printk(KERN_DEBUG, mvi->dev, "Command Header[%03d]:\n", tag);
+ /*mvs_cmd_hdr */
+ mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
+ (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
+ /*1.command table area */
+ dev_printk(KERN_DEBUG, mvi->dev, "+---->Command Table :\n");
+ mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
+ /*2.open address frame area */
+ dev_printk(KERN_DEBUG, mvi->dev, "+---->Open Address Frame :\n");
+ mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
+ (u32) slot->buf_dma + slot->cmd_size);
+ /*3.status buffer */
+ mvs_hba_sb_dump(mvi, tag, proto);
+ /*4.PRD table */
+ dev_printk(KERN_DEBUG, mvi->dev, "+---->PRD table :\n");
+ mvs_hexdump(MVS_CHIP_DISP->prd_size() * slot->n_elem,
+ (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
+ (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
+#endif
+}
+
+static void mvs_hba_cq_dump(struct mvs_info *mvi)
+{
+#if (_MV_DUMP > 2)
+ u64 addr;
+ void __iomem *regs = mvi->regs;
+ u32 entry = mvi->rx_cons + 1;
+ u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
+
+ /*Completion Queue */
+ addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
+ dev_printk(KERN_DEBUG, mvi->dev, "Completion Task = 0x%p\n",
+ mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
+ dev_printk(KERN_DEBUG, mvi->dev,
+ "Completion List Base Address=0x%llX (PA), "
+ "CQ_Entry=%04d, CQ_WP=0x%08X\n",
+ addr, entry - 1, mvi->rx[0]);
+ mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
+ mvi->rx_dma + sizeof(u32) * entry);
+#endif
+}
+
+void mvs_get_sas_addr(void *buf, u32 buflen)
+{
+ /*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
+}
+
+struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
+{
+ unsigned long i = 0, j = 0, hi = 0;
+ struct sas_ha_struct *sha = dev->port->ha;
+ struct mvs_info *mvi = NULL;
+ struct asd_sas_phy *phy;
+
+ while (sha->sas_port[i]) {
+ if (sha->sas_port[i] == dev->port) {
+ phy = container_of(sha->sas_port[i]->phy_list.next,
+ struct asd_sas_phy, port_phy_el);
+ j = 0;
+ while (sha->sas_phy[j]) {
+ if (sha->sas_phy[j] == phy)
+ break;
+ j++;
+ }
+ break;
+ }
+ i++;
+ }
+ hi = j/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
+
+ return mvi;
+
+}
+
+/* FIXME */
+int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
+{
+ unsigned long i = 0, j = 0, n = 0, num = 0;
+ struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+ struct sas_ha_struct *sha = dev->port->ha;
+
+ while (sha->sas_port[i]) {
+ if (sha->sas_port[i] == dev->port) {
+ struct asd_sas_phy *phy;
+ list_for_each_entry(phy,
+ &sha->sas_port[i]->phy_list, port_phy_el) {
+ j = 0;
+ while (sha->sas_phy[j]) {
+ if (sha->sas_phy[j] == phy)
+ break;
+ j++;
+ }
+ phyno[n] = (j >= mvi->chip->n_phy) ?
+ (j - mvi->chip->n_phy) : j;
+ num++;
+ n++;
+ }
+ break;
+ }
+ i++;
+ }
+ return num;
+}
+
+static inline void mvs_free_reg_set(struct mvs_info *mvi,
+ struct mvs_device *dev)
+{
+ if (!dev) {
+ mv_printk("device has been free.\n");
+ return;
+ }
+ if (dev->runing_req != 0)
+ return;
+ if (dev->taskfileset == MVS_ID_NOT_MAPPED)
+ return;
+ MVS_CHIP_DISP->free_reg_set(mvi, &dev->taskfileset);
+}
+
+static inline u8 mvs_assign_reg_set(struct mvs_info *mvi,
+ struct mvs_device *dev)
+{
+ if (dev->taskfileset != MVS_ID_NOT_MAPPED)
+ return 0;
+ return MVS_CHIP_DISP->assign_reg_set(mvi, &dev->taskfileset);
+}
+
+void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard)
+{
+ u32 no;
+ for_each_phy(phy_mask, phy_mask, no) {
+ if (!(phy_mask & 1))
+ continue;
+ MVS_CHIP_DISP->phy_reset(mvi, no, hard);
+ }
+}
+
+/* FIXME: locking? */
+int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata)
+{
+ int rc = 0, phy_id = sas_phy->id;
+ u32 tmp, i = 0, hi;
+ struct sas_ha_struct *sha = sas_phy->ha;
+ struct mvs_info *mvi = NULL;
+
+ while (sha->sas_phy[i]) {
+ if (sha->sas_phy[i] == sas_phy)
+ break;
+ i++;
+ }
+ hi = i/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
+
+ switch (func) {
+ case PHY_FUNC_SET_LINK_RATE:
+ MVS_CHIP_DISP->phy_set_link_rate(mvi, phy_id, funcdata);
+ break;
+
+ case PHY_FUNC_HARD_RESET:
+ tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
+ if (tmp & PHY_RST_HARD)
+ break;
+ MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
+ break;
+
+ case PHY_FUNC_LINK_RESET:
+ MVS_CHIP_DISP->phy_enable(mvi, phy_id);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
+ break;
+
+ case PHY_FUNC_DISABLE:
+ MVS_CHIP_DISP->phy_disable(mvi, phy_id);
+ break;
+ case PHY_FUNC_RELEASE_SPINUP_HOLD:
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ msleep(200);
+ return rc;
+}
+
+void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
+ u32 off_lo, u32 off_hi, u64 sas_addr)
+{
+ u32 lo = (u32)sas_addr;
+ u32 hi = (u32)(sas_addr>>32);
+
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_lo);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, lo);
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_hi);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, hi);
+}
+
+static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
+{
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sas_ha;
+ if (!phy->phy_attached)
+ return;
+
+ if (!(phy->att_dev_info & PORT_DEV_TRGT_MASK)
+ && phy->phy_type & PORT_TYPE_SAS) {
+ return;
+ }
+
+ sas_ha = mvi->sas;
+ sas_ha->notify_phy_event(sas_phy, PHYE_OOB_DONE);
+
+ if (sas_phy->phy) {
+ struct sas_phy *sphy = sas_phy->phy;
+
+ sphy->negotiated_linkrate = sas_phy->linkrate;
+ sphy->minimum_linkrate = phy->minimum_linkrate;
+ sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sphy->maximum_linkrate = phy->maximum_linkrate;
+ sphy->maximum_linkrate_hw = MVS_CHIP_DISP->phy_max_link_rate();
+ }
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ struct sas_identify_frame *id;
+
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ id->dev_type = phy->identify.device_type;
+ id->initiator_bits = SAS_PROTOCOL_ALL;
+ id->target_bits = phy->identify.target_port_protocols;
+ } else if (phy->phy_type & PORT_TYPE_SATA) {
+ /*Nothing*/
+ }
+ mv_dprintk("phy %d byte dmaded.\n", i + mvi->id * mvi->chip->n_phy);
+
+ sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+
+ mvi->sas->notify_port_event(sas_phy,
+ PORTE_BYTES_DMAED);
+}
+
+int mvs_slave_alloc(struct scsi_device *scsi_dev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
+ if (dev_is_sata(dev)) {
+ /* We don't need to rescan targets
+ * if REPORT_LUNS request is failed
+ */
+ if (scsi_dev->lun > 0)
+ return -ENXIO;
+ scsi_dev->tagged_supported = 1;
+ }
+
+ return sas_slave_alloc(scsi_dev);
+}
+
+int mvs_slave_configure(struct scsi_device *sdev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(sdev);
+ int ret = sas_slave_configure(sdev);
+
+ if (ret)
+ return ret;
+ if (dev_is_sata(dev)) {
+ /* may set PIO mode */
+ #if MV_DISABLE_NCQ
+ struct ata_port *ap = dev->sata_dev.ap;
+ struct ata_device *adev = ap->link.device;
+ adev->flags |= ATA_DFLAG_NCQ_OFF;
+ scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
+ #endif
+ }
+ return 0;
+}
+
+void mvs_scan_start(struct Scsi_Host *shost)
+{
+ int i, j;
+ unsigned short core_nr;
+ struct mvs_info *mvi;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+
+ for (j = 0; j < core_nr; j++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
+ for (i = 0; i < mvi->chip->n_phy; ++i)
+ mvs_bytes_dmaed(mvi, i);
+ }
+}
+
+int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ /* give the phy enabling interrupt event time to come in (1s
+ * is empirically about all it takes) */
+ if (time < HZ)
+ return 0;
+ /* Wait for discovery to finish */
+ scsi_flush_work(shost);
+ return 1;
+}
+
+static int mvs_task_prep_smp(struct mvs_info *mvi,
+ struct mvs_task_exec_info *tei)
+{
+ int elem, rc, i;
+ struct sas_task *task = tei->task;
+ struct mvs_cmd_hdr *hdr = tei->hdr;
+ struct domain_device *dev = task->dev;
+ struct asd_sas_port *sas_port = dev->port;
+ struct scatterlist *sg_req, *sg_resp;
+ u32 req_len, resp_len, tag = tei->tag;
+ void *buf_tmp;
+ u8 *buf_oaf;
+ dma_addr_t buf_tmp_dma;
+ void *buf_prd;
+ struct mvs_slot_info *slot = &mvi->slot_info[tag];
+ u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#if _MV_DUMP
+ u8 *buf_cmd;
+ void *from;
+#endif
+ /*
+ * DMA-map SMP request, response buffers
+ */
+ sg_req = &task->smp_task.smp_req;
+ elem = dma_map_sg(mvi->dev, sg_req, 1, PCI_DMA_TODEVICE);
+ if (!elem)
+ return -ENOMEM;
+ req_len = sg_dma_len(sg_req);
+
+ sg_resp = &task->smp_task.smp_resp;
+ elem = dma_map_sg(mvi->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+ if (!elem) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ resp_len = SB_RFB_MAX;
+
+ /* must be in dwords */
+ if ((req_len & 0x3) || (resp_len & 0x3)) {
+ rc = -EINVAL;
+ goto err_out_2;
+ }
+
+ /*
+ * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+ */
+
+ /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ***** */
+ buf_tmp = slot->buf;
+ buf_tmp_dma = slot->buf_dma;
+
+#if _MV_DUMP
+ buf_cmd = buf_tmp;
+ hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+ buf_tmp += req_len;
+ buf_tmp_dma += req_len;
+ slot->cmd_size = req_len;
+#else
+ hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
+#endif
+
+ /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+ buf_oaf = buf_tmp;
+ hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+ buf_tmp += MVS_OAF_SZ;
+ buf_tmp_dma += MVS_OAF_SZ;
+
+ /* region 3: PRD table *********************************** */
+ buf_prd = buf_tmp;
+ if (tei->n_elem)
+ hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+ else
+ hdr->prd_tbl = 0;
+
+ i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
+ buf_tmp += i;
+ buf_tmp_dma += i;
+
+ /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+ slot->response = buf_tmp;
+ hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+ if (mvi->flags & MVF_FLAG_SOC)
+ hdr->reserved[0] = 0;
+
+ /*
+ * Fill in TX ring and command slot header
+ */
+ slot->tx = mvi->tx_prod;
+ mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
+ TXQ_MODE_I | tag |
+ (sas_port->phy_mask << TXQ_PHY_SHIFT));
+
+ hdr->flags |= flags;
+ hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
+ hdr->tags = cpu_to_le32(tag);
+ hdr->data_len = 0;
+
+ /* generate open address frame hdr (first 12 bytes) */
+ /* initiator, SMP, ftype 1h */
+ buf_oaf[0] = (1 << 7) | (PROTOCOL_SMP << 4) | 0x01;
+ buf_oaf[1] = dev->linkrate & 0xf;
+ *(u16 *)(buf_oaf + 2) = 0xFFFF; /* SAS SPEC */
+ memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+ /* fill in PRD (scatter/gather) table, if any */
+ MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+
+#if _MV_DUMP
+ /* copy cmd table */
+ from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
+ memcpy(buf_cmd, from + sg_req->offset, req_len);
+ kunmap_atomic(from, KM_IRQ0);
+#endif
+ return 0;
+
+err_out_2:
+ dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+err_out:
+ dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ return rc;
+}
+
+static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
+{
+ struct ata_queued_cmd *qc = task->uldd_task;
+
+ if (qc) {
+ if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+ qc->tf.command == ATA_CMD_FPDMA_READ) {
+ *tag = qc->tag;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int mvs_task_prep_ata(struct mvs_info *mvi,
+ struct mvs_task_exec_info *tei)
+{
+ struct sas_task *task = tei->task;
+ struct domain_device *dev = task->dev;
+ struct mvs_device *mvi_dev = dev->lldd_dev;
+ struct mvs_cmd_hdr *hdr = tei->hdr;
+ struct asd_sas_port *sas_port = dev->port;
+ struct mvs_slot_info *slot;
+ void *buf_prd;
+ u32 tag = tei->tag, hdr_tag;
+ u32 flags, del_q;
+ void *buf_tmp;
+ u8 *buf_cmd, *buf_oaf;
+ dma_addr_t buf_tmp_dma;
+ u32 i, req_len, resp_len;
+ const u32 max_resp_len = SB_RFB_MAX;
+
+ if (mvs_assign_reg_set(mvi, mvi_dev) == MVS_ID_NOT_MAPPED) {
+ mv_dprintk("Have not enough regiset for dev %d.\n",
+ mvi_dev->device_id);
+ return -EBUSY;
+ }
+ slot = &mvi->slot_info[tag];
+ slot->tx = mvi->tx_prod;
+ del_q = TXQ_MODE_I | tag |
+ (TXQ_CMD_STP << TXQ_CMD_SHIFT) |
+ (sas_port->phy_mask << TXQ_PHY_SHIFT) |
+ (mvi_dev->taskfileset << TXQ_SRS_SHIFT);
+ mvi->tx[mvi->tx_prod] = cpu_to_le32(del_q);
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ if (task->data_dir == DMA_FROM_DEVICE)
+ flags = (MVS_CHIP_DISP->prd_count() << MCH_PRD_LEN_SHIFT);
+ else
+ flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#else
+ flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#endif
+ if (task->ata_task.use_ncq)
+ flags |= MCH_FPDMA;
+ if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
+ if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
+ flags |= MCH_ATAPI;
+ }
+
+ /* FIXME: fill in port multiplier number */
+
+ hdr->flags = cpu_to_le32(flags);
+
+ /* FIXME: the low order order 5 bits for the TAG if enable NCQ */
+ if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag))
+ task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
+ else
+ hdr_tag = tag;
+
+ hdr->tags = cpu_to_le32(hdr_tag);
+
+ hdr->data_len = cpu_to_le32(task->total_xfer_len);
+
+ /*
+ * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+ */
+
+ /* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
+ buf_cmd = buf_tmp = slot->buf;
+ buf_tmp_dma = slot->buf_dma;
+
+ hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+
+ buf_tmp += MVS_ATA_CMD_SZ;
+ buf_tmp_dma += MVS_ATA_CMD_SZ;
+#if _MV_DUMP
+ slot->cmd_size = MVS_ATA_CMD_SZ;
+#endif
+
+ /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+ /* used for STP. unused for SATA? */
+ buf_oaf = buf_tmp;
+ hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+ buf_tmp += MVS_OAF_SZ;
+ buf_tmp_dma += MVS_OAF_SZ;
+
+ /* region 3: PRD table ********************************************* */
+ buf_prd = buf_tmp;
+
+ if (tei->n_elem)
+ hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+ else
+ hdr->prd_tbl = 0;
+ i = MVS_CHIP_DISP->prd_size() * MVS_CHIP_DISP->prd_count();
+
+ buf_tmp += i;
+ buf_tmp_dma += i;
+
+ /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+ /* FIXME: probably unused, for SATA. kept here just in case
+ * we get a STP/SATA error information record
+ */
+ slot->response = buf_tmp;
+ hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+ if (mvi->flags & MVF_FLAG_SOC)
+ hdr->reserved[0] = 0;
+
+ req_len = sizeof(struct host_to_dev_fis);
+ resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
+ sizeof(struct mvs_err_info) - i;
+
+ /* request, response lengths */
+ resp_len = min(resp_len, max_resp_len);
+ hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
+
+ if (likely(!task->ata_task.device_control_reg_update))
+ task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
+ /* fill in command FIS and ATAPI CDB */
+ memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
+ if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
+ memcpy(buf_cmd + STP_ATAPI_CMD,
+ task->ata_task.atapi_packet, 16);
+
+ /* generate open address frame hdr (first 12 bytes) */
+ /* initiator, STP, ftype 1h */
+ buf_oaf[0] = (1 << 7) | (PROTOCOL_STP << 4) | 0x1;
+ buf_oaf[1] = dev->linkrate & 0xf;
+ *(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
+ memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+ /* fill in PRD (scatter/gather) table, if any */
+ MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ if (task->data_dir == DMA_FROM_DEVICE)
+ MVS_CHIP_DISP->dma_fix(mvi->bulk_buffer_dma,
+ TRASH_BUCKET_SIZE, tei->n_elem, buf_prd);
+#endif
+ return 0;
+}
+
+static int mvs_task_prep_ssp(struct mvs_info *mvi,
+ struct mvs_task_exec_info *tei, int is_tmf,
+ struct mvs_tmf_task *tmf)
+{
+ struct sas_task *task = tei->task;
+ struct mvs_cmd_hdr *hdr = tei->hdr;
+ struct mvs_port *port = tei->port;
+ struct domain_device *dev = task->dev;
+ struct mvs_device *mvi_dev = dev->lldd_dev;
+ struct asd_sas_port *sas_port = dev->port;
+ struct mvs_slot_info *slot;
+ void *buf_prd;
+ struct ssp_frame_hdr *ssp_hdr;
+ void *buf_tmp;
+ u8 *buf_cmd, *buf_oaf, fburst = 0;
+ dma_addr_t buf_tmp_dma;
+ u32 flags;
+ u32 resp_len, req_len, i, tag = tei->tag;
+ const u32 max_resp_len = SB_RFB_MAX;
+ u32 phy_mask;
+
+ slot = &mvi->slot_info[tag];
+
+ phy_mask = ((port->wide_port_phymap) ? port->wide_port_phymap :
+ sas_port->phy_mask) & TXQ_PHY_MASK;
+
+ slot->tx = mvi->tx_prod;
+ mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
+ (TXQ_CMD_SSP << TXQ_CMD_SHIFT) |
+ (phy_mask << TXQ_PHY_SHIFT));
+
+ flags = MCH_RETRY;
+ if (task->ssp_task.enable_first_burst) {
+ flags |= MCH_FBURST;
+ fburst = (1 << 7);
+ }
+ if (is_tmf)
+ flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
+ else
+ flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT);
+ hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
+ hdr->tags = cpu_to_le32(tag);
+ hdr->data_len = cpu_to_le32(task->total_xfer_len);
+
+ /*
+ * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+ */
+
+ /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
+ buf_cmd = buf_tmp = slot->buf;
+ buf_tmp_dma = slot->buf_dma;
+
+ hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+
+ buf_tmp += MVS_SSP_CMD_SZ;
+ buf_tmp_dma += MVS_SSP_CMD_SZ;
+#if _MV_DUMP
+ slot->cmd_size = MVS_SSP_CMD_SZ;
+#endif
+
+ /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+ buf_oaf = buf_tmp;
+ hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+ buf_tmp += MVS_OAF_SZ;
+ buf_tmp_dma += MVS_OAF_SZ;
+
+ /* region 3: PRD table ********************************************* */
+ buf_prd = buf_tmp;
+ if (tei->n_elem)
+ hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+ else
+ hdr->prd_tbl = 0;
+
+ i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
+ buf_tmp += i;
+ buf_tmp_dma += i;
+
+ /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+ slot->response = buf_tmp;
+ hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+ if (mvi->flags & MVF_FLAG_SOC)
+ hdr->reserved[0] = 0;
+
+ resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
+ sizeof(struct mvs_err_info) - i;
+ resp_len = min(resp_len, max_resp_len);
+
+ req_len = sizeof(struct ssp_frame_hdr) + 28;
+
+ /* request, response lengths */
+ hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
+
+ /* generate open address frame hdr (first 12 bytes) */
+ /* initiator, SSP, ftype 1h */
+ buf_oaf[0] = (1 << 7) | (PROTOCOL_SSP << 4) | 0x1;
+ buf_oaf[1] = dev->linkrate & 0xf;
+ *(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
+ memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+ /* fill in SSP frame header (Command Table.SSP frame header) */
+ ssp_hdr = (struct ssp_frame_hdr *)buf_cmd;
+
+ if (is_tmf)
+ ssp_hdr->frame_type = SSP_TASK;
+ else
+ ssp_hdr->frame_type = SSP_COMMAND;
+
+ memcpy(ssp_hdr->hashed_dest_addr, dev->hashed_sas_addr,
+ HASHED_SAS_ADDR_SIZE);
+ memcpy(ssp_hdr->hashed_src_addr,
+ dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+ ssp_hdr->tag = cpu_to_be16(tag);
+
+ /* fill in IU for TASK and Command Frame */
+ buf_cmd += sizeof(*ssp_hdr);
+ memcpy(buf_cmd, &task->ssp_task.LUN, 8);
+
+ if (ssp_hdr->frame_type != SSP_TASK) {
+ buf_cmd[9] = fburst | task->ssp_task.task_attr |
+ (task->ssp_task.task_prio << 3);
+ memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
+ } else{
+ buf_cmd[10] = tmf->tmf;
+ switch (tmf->tmf) {
+ case TMF_ABORT_TASK:
+ case TMF_QUERY_TASK:
+ buf_cmd[12] =
+ (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+ buf_cmd[13] =
+ tmf->tag_of_task_to_be_managed & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+ /* fill in PRD (scatter/gather) table, if any */
+ MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+ return 0;
+}
+
+#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
+static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
+ struct completion *completion,int is_tmf,
+ struct mvs_tmf_task *tmf)
+{
+ struct domain_device *dev = task->dev;
+ struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+ struct mvs_task_exec_info tei;
+ struct sas_task *t = task;
+ struct mvs_slot_info *slot;
+ u32 tag = 0xdeadbeef, rc, n_elem = 0;
+ u32 n = num, pass = 0;
+ unsigned long flags = 0;
+
+ if (!dev->port) {
+ struct task_status_struct *tsm = &t->task_status;
+
+ tsm->resp = SAS_TASK_UNDELIVERED;
+ tsm->stat = SAS_PHY_DOWN;
+ t->task_done(t);
+ return 0;
+ }
+
+ spin_lock_irqsave(&mvi->lock, flags);
+ do {
+ dev = t->dev;
+ mvi_dev = dev->lldd_dev;
+ if (DEV_IS_GONE(mvi_dev)) {
+ if (mvi_dev)
+ mv_dprintk("device %d not ready.\n",
+ mvi_dev->device_id);
+ else
+ mv_dprintk("device %016llx not ready.\n",
+ SAS_ADDR(dev->sas_addr));
+
+ rc = SAS_PHY_DOWN;
+ goto out_done;
+ }
+
+ if (dev->port->id >= mvi->chip->n_phy)
+ tei.port = &mvi->port[dev->port->id - mvi->chip->n_phy];
+ else
+ tei.port = &mvi->port[dev->port->id];
+
+ if (!tei.port->port_attached) {
+ if (sas_protocol_ata(t->task_proto)) {
+ mv_dprintk("port %d does not"
+ "attached device.\n", dev->port->id);
+ rc = SAS_PHY_DOWN;
+ goto out_done;
+ } else {
+ struct task_status_struct *ts = &t->task_status;
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ t->task_done(t);
+ if (n > 1)
+ t = list_entry(t->list.next,
+ struct sas_task, list);
+ continue;
+ }
+ }
+
+ if (!sas_protocol_ata(t->task_proto)) {
+ if (t->num_scatter) {
+ n_elem = dma_map_sg(mvi->dev,
+ t->scatter,
+ t->num_scatter,
+ t->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ }
+ } else {
+ n_elem = t->num_scatter;
+ }
+
+ rc = mvs_tag_alloc(mvi, &tag);
+ if (rc)
+ goto err_out;
+
+ slot = &mvi->slot_info[tag];
+
+
+ t->lldd_task = NULL;
+ slot->n_elem = n_elem;
+ slot->slot_tag = tag;
+ memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+
+ tei.task = t;
+ tei.hdr = &mvi->slot[tag];
+ tei.tag = tag;
+ tei.n_elem = n_elem;
+ switch (t->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ rc = mvs_task_prep_smp(mvi, &tei);
+ break;
+ case SAS_PROTOCOL_SSP:
+ rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ rc = mvs_task_prep_ata(mvi, &tei);
+ break;
+ default:
+ dev_printk(KERN_ERR, mvi->dev,
+ "unknown sas_task proto: 0x%x\n",
+ t->task_proto);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ mv_dprintk("rc is %x\n", rc);
+ goto err_out_tag;
+ }
+ slot->task = t;
+ slot->port = tei.port;
+ t->lldd_task = slot;
+ list_add_tail(&slot->entry, &tei.port->list);
+ /* TODO: select normal or high priority */
+ spin_lock(&t->task_state_lock);
+ t->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&t->task_state_lock);
+
+ mvs_hba_memory_dump(mvi, tag, t->task_proto);
+ mvi_dev->runing_req++;
+ ++pass;
+ mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
+ if (n > 1)
+ t = list_entry(t->list.next, struct sas_task, list);
+ } while (--n);
+ rc = 0;
+ goto out_done;
+
+err_out_tag:
+ mvs_tag_free(mvi, tag);
+err_out:
+
+ dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
+ if (!sas_protocol_ata(t->task_proto))
+ if (n_elem)
+ dma_unmap_sg(mvi->dev, t->scatter, n_elem,
+ t->data_dir);
+out_done:
+ if (likely(pass)) {
+ MVS_CHIP_DISP->start_delivery(mvi,
+ (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
+ }
+ spin_unlock_irqrestore(&mvi->lock, flags);
+ return rc;
+}
+
+int mvs_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags)
+{
+ return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
+}
+
+static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
+{
+ u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
+ mvs_tag_clear(mvi, slot_idx);
+}
+
+static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
+ struct mvs_slot_info *slot, u32 slot_idx)
+{
+ if (!slot->task)
+ return;
+ if (!sas_protocol_ata(task->task_proto))
+ if (slot->n_elem)
+ dma_unmap_sg(mvi->dev, task->scatter,
+ slot->n_elem, task->data_dir);
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SSP:
+ default:
+ /* do nothing */
+ break;
+ }
+ list_del_init(&slot->entry);
+ task->lldd_task = NULL;
+ slot->task = NULL;
+ slot->port = NULL;
+ slot->slot_tag = 0xFFFFFFFF;
+ mvs_slot_free(mvi, slot_idx);
+}
+
+static void mvs_update_wideport(struct mvs_info *mvi, int i)
+{
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct mvs_port *port = phy->port;
+ int j, no;
+
+ for_each_phy(port->wide_port_phymap, j, no) {
+ if (j & 1) {
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
+ PHYR_WIDE_PORT);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
+ port->wide_port_phymap);
+ } else {
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
+ PHYR_WIDE_PORT);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
+ 0);
+ }
+ }
+}
+
+static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i)
+{
+ u32 tmp;
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct mvs_port *port = phy->port;
+
+ tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, i);
+ if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) {
+ if (!port)
+ phy->phy_attached = 1;
+ return tmp;
+ }
+
+ if (port) {
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ port->wide_port_phymap &= ~(1U << i);
+ if (!port->wide_port_phymap)
+ port->port_attached = 0;
+ mvs_update_wideport(mvi, i);
+ } else if (phy->phy_type & PORT_TYPE_SATA)
+ port->port_attached = 0;
+ phy->port = NULL;
+ phy->phy_attached = 0;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ }
+ return 0;
+}
+
+static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
+{
+ u32 *s = (u32 *) buf;
+
+ if (!s)
+ return NULL;
+
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
+ s[3] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
+ s[2] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
+ s[1] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
+ s[0] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+ /* Workaround: take some ATAPI devices for ATA */
+ if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
+ s[1] = 0x00EB1401 | (*((u8 *)&s[1] + 3) & 0x10);
+
+ return s;
+}
+
+static u32 mvs_is_sig_fis_received(u32 irq_status)
+{
+ return irq_status & PHYEV_SIG_FIS;
+}
+
+void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
+{
+ struct mvs_phy *phy = &mvi->phy[i];
+ struct sas_identify_frame *id;
+
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+
+ if (get_st) {
+ phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, i);
+ phy->phy_status = mvs_is_phy_ready(mvi, i);
+ }
+
+ if (phy->phy_status) {
+ int oob_done = 0;
+ struct asd_sas_phy *sas_phy = &mvi->phy[i].sas_phy;
+
+ oob_done = MVS_CHIP_DISP->oob_done(mvi, i);
+
+ MVS_CHIP_DISP->fix_phy_info(mvi, i, id);
+ if (phy->phy_type & PORT_TYPE_SATA) {
+ phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
+ if (mvs_is_sig_fis_received(phy->irq_status)) {
+ phy->phy_attached = 1;
+ phy->att_dev_sas_addr =
+ i + mvi->id * mvi->chip->n_phy;
+ if (oob_done)
+ sas_phy->oob_mode = SATA_OOB_MODE;
+ phy->frame_rcvd_size =
+ sizeof(struct dev_to_host_fis);
+ mvs_get_d2h_reg(mvi, i, id);
+ } else {
+ u32 tmp;
+ dev_printk(KERN_DEBUG, mvi->dev,
+ "Phy%d : No sig fis\n", i);
+ tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, i);
+ MVS_CHIP_DISP->write_port_irq_mask(mvi, i,
+ tmp | PHYEV_SIG_FIS);
+ phy->phy_attached = 0;
+ phy->phy_type &= ~PORT_TYPE_SATA;
+ MVS_CHIP_DISP->phy_reset(mvi, i, 0);
+ goto out_done;
+ }
+ } else if (phy->phy_type & PORT_TYPE_SAS
+ || phy->att_dev_info & PORT_SSP_INIT_MASK) {
+ phy->phy_attached = 1;
+ phy->identify.device_type =
+ phy->att_dev_info & PORT_DEV_TYPE_MASK;
+
+ if (phy->identify.device_type == SAS_END_DEV)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != NO_DEVICE)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SMP;
+ if (oob_done)
+ sas_phy->oob_mode = SAS_OOB_MODE;
+ phy->frame_rcvd_size =
+ sizeof(struct sas_identify_frame);
+ }
+ memcpy(sas_phy->attached_sas_addr,
+ &phy->att_dev_sas_addr, SAS_ADDR_SIZE);
+
+ if (MVS_CHIP_DISP->phy_work_around)
+ MVS_CHIP_DISP->phy_work_around(mvi, i);
+ }
+ mv_dprintk("port %d attach dev info is %x\n",
+ i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
+ mv_dprintk("port %d attach sas addr is %llx\n",
+ i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
+out_done:
+ if (get_st)
+ MVS_CHIP_DISP->write_port_irq_stat(mvi, i, phy->irq_status);
+}
+
+static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
+{
+ struct sas_ha_struct *sas_ha = sas_phy->ha;
+ struct mvs_info *mvi = NULL; int i = 0, hi;
+ struct mvs_phy *phy = sas_phy->lldd_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ struct mvs_port *port;
+ unsigned long flags = 0;
+ if (!sas_port)
+ return;
+
+ while (sas_ha->sas_phy[i]) {
+ if (sas_ha->sas_phy[i] == sas_phy)
+ break;
+ i++;
+ }
+ hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
+ mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
+ if (sas_port->id >= mvi->chip->n_phy)
+ port = &mvi->port[sas_port->id - mvi->chip->n_phy];
+ else
+ port = &mvi->port[sas_port->id];
+ if (lock)
+ spin_lock_irqsave(&mvi->lock, flags);
+ port->port_attached = 1;
+ phy->port = port;
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ port->wide_port_phymap = sas_port->phy_mask;
+ mv_printk("set wide port phy map %x\n", sas_port->phy_mask);
+ mvs_update_wideport(mvi, sas_phy->id);
+ }
+ if (lock)
+ spin_unlock_irqrestore(&mvi->lock, flags);
+}
+
+static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock)
+{
+ /*Nothing*/
+}
+
+
+void mvs_port_formed(struct asd_sas_phy *sas_phy)
+{
+ mvs_port_notify_formed(sas_phy, 1);
+}
+
+void mvs_port_deformed(struct asd_sas_phy *sas_phy)
+{
+ mvs_port_notify_deformed(sas_phy, 1);
+}
+
+struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
+{
+ u32 dev;
+ for (dev = 0; dev < MVS_MAX_DEVICES; dev++) {
+ if (mvi->devices[dev].dev_type == NO_DEVICE) {
+ mvi->devices[dev].device_id = dev;
+ return &mvi->devices[dev];
+ }
+ }
+
+ if (dev == MVS_MAX_DEVICES)
+ mv_printk("max support %d devices, ignore ..\n",
+ MVS_MAX_DEVICES);
+
+ return NULL;
+}
+
+void mvs_free_dev(struct mvs_device *mvi_dev)
+{
+ u32 id = mvi_dev->device_id;
+ memset(mvi_dev, 0, sizeof(*mvi_dev));
+ mvi_dev->device_id = id;
+ mvi_dev->dev_type = NO_DEVICE;
+ mvi_dev->dev_status = MVS_DEV_NORMAL;
+ mvi_dev->taskfileset = MVS_ID_NOT_MAPPED;
+}
+
+int mvs_dev_found_notify(struct domain_device *dev, int lock)
+{
+ unsigned long flags = 0;
+ int res = 0;
+ struct mvs_info *mvi = NULL;
+ struct domain_device *parent_dev = dev->parent;
+ struct mvs_device *mvi_device;
+
+ mvi = mvs_find_dev_mvi(dev);
+
+ if (lock)
+ spin_lock_irqsave(&mvi->lock, flags);
+
+ mvi_device = mvs_alloc_dev(mvi);
+ if (!mvi_device) {
+ res = -1;
+ goto found_out;
+ }
+ dev->lldd_dev = mvi_device;
+ mvi_device->dev_type = dev->dev_type;
+ mvi_device->mvi_info = mvi;
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ int phy_id;
+ u8 phy_num = parent_dev->ex_dev.num_phys;
+ struct ex_phy *phy;
+ for (phy_id = 0; phy_id < phy_num; phy_id++) {
+ phy = &parent_dev->ex_dev.ex_phy[phy_id];
+ if (SAS_ADDR(phy->attached_sas_addr) ==
+ SAS_ADDR(dev->sas_addr)) {
+ mvi_device->attached_phy = phy_id;
+ break;
+ }
+ }
+
+ if (phy_id == phy_num) {
+ mv_printk("Error: no attached dev:%016llx"
+ "at ex:%016llx.\n",
+ SAS_ADDR(dev->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr));
+ res = -1;
+ }
+ }
+
+found_out:
+ if (lock)
+ spin_unlock_irqrestore(&mvi->lock, flags);
+ return res;
+}
+
+int mvs_dev_found(struct domain_device *dev)
+{
+ return mvs_dev_found_notify(dev, 1);
+}
+
+void mvs_dev_gone_notify(struct domain_device *dev, int lock)
+{
+ unsigned long flags = 0;
+ struct mvs_device *mvi_dev = dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+
+ if (lock)
+ spin_lock_irqsave(&mvi->lock, flags);
+
+ if (mvi_dev) {
+ mv_dprintk("found dev[%d:%x] is gone.\n",
+ mvi_dev->device_id, mvi_dev->dev_type);
+ mvs_free_reg_set(mvi, mvi_dev);
+ mvs_free_dev(mvi_dev);
+ } else {
+ mv_dprintk("found dev has gone.\n");
+ }
+ dev->lldd_dev = NULL;
+
+ if (lock)
+ spin_unlock_irqrestore(&mvi->lock, flags);
+}
+
+
+void mvs_dev_gone(struct domain_device *dev)
+{
+ mvs_dev_gone_notify(dev, 1);
+}
+
+static struct sas_task *mvs_alloc_task(void)
+{
+ struct sas_task *task = kzalloc(sizeof(struct sas_task), GFP_KERNEL);
+
+ if (task) {
+ INIT_LIST_HEAD(&task->list);
+ spin_lock_init(&task->task_state_lock);
+ task->task_state_flags = SAS_TASK_STATE_PENDING;
+ init_timer(&task->timer);
+ init_completion(&task->completion);
+ }
+ return task;
+}
+
+static void mvs_free_task(struct sas_task *task)
+{
+ if (task) {
+ BUG_ON(!list_empty(&task->list));
+ kfree(task);
+ }
+}
+
+static void mvs_task_done(struct sas_task *task)
+{
+ if (!del_timer(&task->timer))
+ return;
+ complete(&task->completion);
+}
+
+static void mvs_tmf_timedout(unsigned long data)
+{
+ struct sas_task *task = (struct sas_task *)data;
+
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ complete(&task->completion);
+}
+
+/* XXX */
+#define MVS_TASK_TIMEOUT 20
+static int mvs_exec_internal_tmf_task(struct domain_device *dev,
+ void *parameter, u32 para_len, struct mvs_tmf_task *tmf)
+{
+ int res, retry;
+ struct sas_task *task = NULL;
+
+ for (retry = 0; retry < 3; retry++) {
+ task = mvs_alloc_task();
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = dev;
+ task->task_proto = dev->tproto;
+
+ memcpy(&task->ssp_task, parameter, para_len);
+ task->task_done = mvs_task_done;
+
+ task->timer.data = (unsigned long) task;
+ task->timer.function = mvs_tmf_timedout;
+ task->timer.expires = jiffies + MVS_TASK_TIMEOUT*HZ;
+ add_timer(&task->timer);
+
+ res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf);
+
+ if (res) {
+ del_timer(&task->timer);
+ mv_printk("executing internel task failed:%d\n", res);
+ goto ex_err;
+ }
+
+ wait_for_completion(&task->completion);
+ res = -TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ mv_printk("TMF task[%x] timeout.\n", tmf->tmf);
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
+ /* no error, but return the number of bytes of
+ * underrun */
+ res = task->task_status.residual;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
+ mv_dprintk("blocked task error.\n");
+ res = -EMSGSIZE;
+ break;
+ } else {
+ mv_dprintk(" task to dev %016llx response: 0x%x "
+ "status 0x%x\n",
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat);
+ mvs_free_task(task);
+ task = NULL;
+
+ }
+ }
+ex_err:
+ BUG_ON(retry == 3 && task != NULL);
+ if (task != NULL)
+ mvs_free_task(task);
+ return res;
+}
+
+static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
+ u8 *lun, struct mvs_tmf_task *tmf)
+{
+ struct sas_ssp_task ssp_task;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ if (!(dev->tproto & SAS_PROTOCOL_SSP))
+ return TMF_RESP_FUNC_ESUPP;
+
+ strncpy((u8 *)&ssp_task.LUN, lun, 8);
+
+ return mvs_exec_internal_tmf_task(dev, &ssp_task,
+ sizeof(ssp_task), tmf);
+}
+
+
+/* Standard mandates link reset for ATA (type 0)
+ and hard reset for SSP (type 1) , only for RECOVERY */
+static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
+{
+ int rc;
+ struct sas_phy *phy = sas_find_local_phy(dev);
+ int reset_type = (dev->dev_type == SATA_DEV ||
+ (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+ rc = sas_phy_reset(phy, reset_type);
+ msleep(2000);
+ return rc;
+}
+
+/* mandatory SAM-3 */
+int mvs_lu_reset(struct domain_device *dev, u8 *lun)
+{
+ unsigned long flags;
+ int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
+ struct mvs_tmf_task tmf_task;
+ struct mvs_device * mvi_dev = dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+
+ tmf_task.tmf = TMF_LU_RESET;
+ mvi_dev->dev_status = MVS_DEV_EH;
+ rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ num = mvs_find_dev_phyno(dev, phyno);
+ spin_lock_irqsave(&mvi->lock, flags);
+ for (i = 0; i < num; i++)
+ mvs_release_task(mvi, phyno[i], dev);
+ spin_unlock_irqrestore(&mvi->lock, flags);
+ }
+ /* If failed, fall-through I_T_Nexus reset */
+ mv_printk("%s for device[%x]:rc= %d\n", __func__,
+ mvi_dev->device_id, rc);
+ return rc;
+}
+
+int mvs_I_T_nexus_reset(struct domain_device *dev)
+{
+ unsigned long flags;
+ int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
+ struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+
+ if (mvi_dev->dev_status != MVS_DEV_EH)
+ return TMF_RESP_FUNC_COMPLETE;
+ rc = mvs_debug_I_T_nexus_reset(dev);
+ mv_printk("%s for device[%x]:rc= %d\n",
+ __func__, mvi_dev->device_id, rc);
+
+ /* housekeeper */
+ num = mvs_find_dev_phyno(dev, phyno);
+ spin_lock_irqsave(&mvi->lock, flags);
+ for (i = 0; i < num; i++)
+ mvs_release_task(mvi, phyno[i], dev);
+ spin_unlock_irqrestore(&mvi->lock, flags);
+
+ return rc;
+}
+/* optional SAM-3 */
+int mvs_query_task(struct sas_task *task)
+{
+ u32 tag;
+ struct scsi_lun lun;
+ struct mvs_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = mvs_find_tag(mvi, task, &tag);
+ if (rc == 0) {
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+
+ tmf_task.tmf = TMF_QUERY_TASK;
+ tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+ rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ switch (rc) {
+ /* The task is still in Lun, release it then */
+ case TMF_RESP_FUNC_SUCC:
+ /* The task is not in Lun or failed, reset the phy */
+ case TMF_RESP_FUNC_FAILED:
+ case TMF_RESP_FUNC_COMPLETE:
+ break;
+ }
+ }
+ mv_printk("%s:rc= %d\n", __func__, rc);
+ return rc;
+}
+
+/* mandatory SAM-3, still need free task/slot info */
+int mvs_abort_task(struct sas_task *task)
+{
+ struct scsi_lun lun;
+ struct mvs_tmf_task tmf_task;
+ struct domain_device *dev = task->dev;
+ struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_info *mvi = mvi_dev->mvi_info;
+ int rc = TMF_RESP_FUNC_FAILED;
+ unsigned long flags;
+ u32 tag;
+
+ if (mvi->exp_req)
+ mvi->exp_req--;
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = mvs_find_tag(mvi, task, &tag);
+ if (rc == 0) {
+ mv_printk("No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+
+ tmf_task.tmf = TMF_ABORT_TASK;
+ tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+ rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+
+ /* if successful, clear the task and callback forwards.*/
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ u32 slot_no;
+ struct mvs_slot_info *slot;
+
+ if (task->lldd_task) {
+ slot = task->lldd_task;
+ slot_no = (u32) (slot - mvi->slot_info);
+ mvs_slot_complete(mvi, slot_no, 1);
+ }
+ }
+ } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+ task->task_proto & SAS_PROTOCOL_STP) {
+ /* to do free register_set */
+ } else {
+ /* SMP */
+
+ }
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ mv_printk("%s:rc= %d\n", __func__, rc);
+ return rc;
+}
+
+int mvs_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct mvs_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_ABORT_TASK_SET;
+ rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+ return rc;
+}
+
+int mvs_clear_aca(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct mvs_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_CLEAR_ACA;
+ rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+ return rc;
+}
+
+int mvs_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct mvs_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_CLEAR_TASK_SET;
+ rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+ return rc;
+}
+
+static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
+ u32 slot_idx, int err)
+{
+ struct mvs_device *mvi_dev = task->dev->lldd_dev;
+ struct task_status_struct *tstat = &task->task_status;
+ struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
+ int stat = SAM_GOOD;
+
+
+ resp->frame_len = sizeof(struct dev_to_host_fis);
+ memcpy(&resp->ending_fis[0],
+ SATA_RECEIVED_D2H_FIS(mvi_dev->taskfileset),
+ sizeof(struct dev_to_host_fis));
+ tstat->buf_valid_size = sizeof(*resp);
+ if (unlikely(err))
+ stat = SAS_PROTO_RESPONSE;
+ return stat;
+}
+
+static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
+ u32 slot_idx)
+{
+ struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
+ int stat;
+ u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
+ u32 tfs = 0;
+ enum mvs_port_type type = PORT_TYPE_SAS;
+
+ if (err_dw0 & CMD_ISS_STPD)
+ MVS_CHIP_DISP->issue_stop(mvi, type, tfs);
+
+ MVS_CHIP_DISP->command_active(mvi, slot_idx);
+
+ stat = SAM_CHECK_COND;
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ stat = SAS_ABORTED_TASK;
+ break;
+ case SAS_PROTOCOL_SMP:
+ stat = SAM_CHECK_COND;
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ {
+ if (err_dw0 == 0x80400002)
+ mv_printk("find reserved error, why?\n");
+
+ task->ata_task.use_ncq = 0;
+ stat = SAS_PROTO_RESPONSE;
+ mvs_sata_done(mvi, task, slot_idx, 1);
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ return stat;
+}
+
+int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
+{
+ u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
+ struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
+ struct sas_task *task = slot->task;
+ struct mvs_device *mvi_dev = NULL;
+ struct task_status_struct *tstat;
+
+ bool aborted;
+ void *to;
+ enum exec_status sts;
+
+ if (mvi->exp_req)
+ mvi->exp_req--;
+ if (unlikely(!task || !task->lldd_task))
+ return -1;
+
+ tstat = &task->task_status;
+ mvi_dev = task->dev->lldd_dev;
+
+ mvs_hba_cq_dump(mvi);
+
+ spin_lock(&task->task_state_lock);
+ task->task_state_flags &=
+ ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ /* race condition*/
+ aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
+ spin_unlock(&task->task_state_lock);
+
+ memset(tstat, 0, sizeof(*tstat));
+ tstat->resp = SAS_TASK_COMPLETE;
+
+ if (unlikely(aborted)) {
+ tstat->stat = SAS_ABORTED_TASK;
+ if (mvi_dev)
+ mvi_dev->runing_req--;
+ if (sas_protocol_ata(task->task_proto))
+ mvs_free_reg_set(mvi, mvi_dev);
+
+ mvs_slot_task_free(mvi, task, slot, slot_idx);
+ return -1;
+ }
+
+ if (unlikely(!mvi_dev || !slot->port->port_attached || flags)) {
+ mv_dprintk("port has not device.\n");
+ tstat->stat = SAS_PHY_DOWN;
+ goto out;
+ }
+
+ /*
+ if (unlikely((rx_desc & RXQ_ERR) || (*(u64 *) slot->response))) {
+ mv_dprintk("Find device[%016llx] RXQ_ERR %X,
+ err info:%016llx\n",
+ SAS_ADDR(task->dev->sas_addr),
+ rx_desc, (u64)(*(u64 *) slot->response));
+ }
+ */
+
+ /* error info record present */
+ if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
+ tstat->stat = mvs_slot_err(mvi, task, slot_idx);
+ goto out;
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ /* hw says status == 0, datapres == 0 */
+ if (rx_desc & RXQ_GOOD) {
+ tstat->stat = SAM_GOOD;
+ tstat->resp = SAS_TASK_COMPLETE;
+ }
+ /* response frame present */
+ else if (rx_desc & RXQ_RSP) {
+ struct ssp_response_iu *iu = slot->response +
+ sizeof(struct mvs_err_info);
+ sas_ssp_task_response(mvi->dev, task, iu);
+ } else
+ tstat->stat = SAM_CHECK_COND;
+ break;
+
+ case SAS_PROTOCOL_SMP: {
+ struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+ tstat->stat = SAM_GOOD;
+ to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
+ memcpy(to + sg_resp->offset,
+ slot->response + sizeof(struct mvs_err_info),
+ sg_dma_len(sg_resp));
+ kunmap_atomic(to, KM_IRQ0);
+ break;
+ }
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
+ tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
+ break;
+ }
+
+ default:
+ tstat->stat = SAM_CHECK_COND;
+ break;
+ }
+
+out:
+ if (mvi_dev) {
+ mvi_dev->runing_req--;
+ if (sas_protocol_ata(task->task_proto))
+ mvs_free_reg_set(mvi, mvi_dev);
+ }
+ mvs_slot_task_free(mvi, task, slot, slot_idx);
+ sts = tstat->stat;
+
+ spin_unlock(&mvi->lock);
+ if (task->task_done)
+ task->task_done(task);
+ else
+ mv_dprintk("why has not task_done.\n");
+ spin_lock(&mvi->lock);
+
+ return sts;
+}
+
+void mvs_release_task(struct mvs_info *mvi,
+ int phy_no, struct domain_device *dev)
+{
+ int i = 0; u32 slot_idx;
+ struct mvs_phy *phy;
+ struct mvs_port *port;
+ struct mvs_slot_info *slot, *slot2;
+
+ phy = &mvi->phy[phy_no];
+ port = phy->port;
+ if (!port)
+ return;
+
+ list_for_each_entry_safe(slot, slot2, &port->list, entry) {
+ struct sas_task *task;
+ slot_idx = (u32) (slot - mvi->slot_info);
+ task = slot->task;
+
+ if (dev && task->dev != dev)
+ continue;
+
+ mv_printk("Release slot [%x] tag[%x], task [%p]:\n",
+ slot_idx, slot->slot_tag, task);
+
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ mv_printk("attached with SSP task CDB[");
+ for (i = 0; i < 16; i++)
+ mv_printk(" %02x", task->ssp_task.cdb[i]);
+ mv_printk(" ]\n");
+ }
+
+ mvs_slot_complete(mvi, slot_idx, 1);
+ }
+}
+
+static void mvs_phy_disconnected(struct mvs_phy *phy)
+{
+ phy->phy_attached = 0;
+ phy->att_dev_info = 0;
+ phy->att_dev_sas_addr = 0;
+}
+
+static void mvs_work_queue(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
+ struct mvs_info *mvi = mwq->mvi;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mvi->lock, flags);
+ if (mwq->handler & PHY_PLUG_EVENT) {
+ u32 phy_no = (unsigned long) mwq->data;
+ struct sas_ha_struct *sas_ha = mvi->sas;
+ struct mvs_phy *phy = &mvi->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (phy->phy_event & PHY_PLUG_OUT) {
+ u32 tmp;
+ struct sas_identify_frame *id;
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no);
+ phy->phy_event &= ~PHY_PLUG_OUT;
+ if (!(tmp & PHY_READY_MASK)) {
+ sas_phy_disconnected(sas_phy);
+ mvs_phy_disconnected(phy);
+ sas_ha->notify_phy_event(sas_phy,
+ PHYE_LOSS_OF_SIGNAL);
+ mv_dprintk("phy%d Removed Device\n", phy_no);
+ } else {
+ MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
+ mvs_update_phyinfo(mvi, phy_no, 1);
+ mvs_bytes_dmaed(mvi, phy_no);
+ mvs_port_notify_formed(sas_phy, 0);
+ mv_dprintk("phy%d Attached Device\n", phy_no);
+ }
+ }
+ }
+ list_del(&mwq->entry);
+ spin_unlock_irqrestore(&mvi->lock, flags);
+ kfree(mwq);
+}
+
+static int mvs_handle_event(struct mvs_info *mvi, void *data, int handler)
+{
+ struct mvs_wq *mwq;
+ int ret = 0;
+
+ mwq = kmalloc(sizeof(struct mvs_wq), GFP_ATOMIC);
+ if (mwq) {
+ mwq->mvi = mvi;
+ mwq->data = data;
+ mwq->handler = handler;
+ MV_INIT_DELAYED_WORK(&mwq->work_q, mvs_work_queue, mwq);
+ list_add_tail(&mwq->entry, &mvi->wq_list);
+ schedule_delayed_work(&mwq->work_q, HZ * 2);
+ } else
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+static void mvs_sig_time_out(unsigned long tphy)
+{
+ struct mvs_phy *phy = (struct mvs_phy *)tphy;
+ struct mvs_info *mvi = phy->mvi;
+ u8 phy_no;
+
+ for (phy_no = 0; phy_no < mvi->chip->n_phy; phy_no++) {
+ if (&mvi->phy[phy_no] == phy) {
+ mv_dprintk("Get signature time out, reset phy %d\n",
+ phy_no+mvi->id*mvi->chip->n_phy);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
+ }
+ }
+}
+
+static void mvs_sig_remove_timer(struct mvs_phy *phy)
+{
+ if (phy->timer.function)
+ del_timer(&phy->timer);
+ phy->timer.function = NULL;
+}
+
+void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
+{
+ u32 tmp;
+ struct sas_ha_struct *sas_ha = mvi->sas;
+ struct mvs_phy *phy = &mvi->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
+ mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
+ MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
+ mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy,
+ phy->irq_status);
+
+ /*
+ * events is port event now ,
+ * we need check the interrupt status which belongs to per port.
+ */
+
+ if (phy->irq_status & PHYEV_DCDR_ERR)
+ mv_dprintk("port %d STP decoding error.\n",
+ phy_no+mvi->id*mvi->chip->n_phy);
+
+ if (phy->irq_status & PHYEV_POOF) {
+ if (!(phy->phy_event & PHY_PLUG_OUT)) {
+ int dev_sata = phy->phy_type & PORT_TYPE_SATA;
+ int ready;
+ mvs_release_task(mvi, phy_no, NULL);
+ phy->phy_event |= PHY_PLUG_OUT;
+ mvs_handle_event(mvi,
+ (void *)(unsigned long)phy_no,
+ PHY_PLUG_EVENT);
+ ready = mvs_is_phy_ready(mvi, phy_no);
+ if (!ready)
+ mv_dprintk("phy%d Unplug Notice\n",
+ phy_no +
+ mvi->id * mvi->chip->n_phy);
+ if (ready || dev_sata) {
+ if (MVS_CHIP_DISP->stp_reset)
+ MVS_CHIP_DISP->stp_reset(mvi,
+ phy_no);
+ else
+ MVS_CHIP_DISP->phy_reset(mvi,
+ phy_no, 0);
+ return;
+ }
+ }
+ }
+
+ if (phy->irq_status & PHYEV_COMWAKE) {
+ tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, phy_no);
+ MVS_CHIP_DISP->write_port_irq_mask(mvi, phy_no,
+ tmp | PHYEV_SIG_FIS);
+ if (phy->timer.function == NULL) {
+ phy->timer.data = (unsigned long)phy;
+ phy->timer.function = mvs_sig_time_out;
+ phy->timer.expires = jiffies + 10*HZ;
+ add_timer(&phy->timer);
+ }
+ }
+ if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
+ phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
+ mvs_sig_remove_timer(phy);
+ mv_dprintk("notify plug in on phy[%d]\n", phy_no);
+ if (phy->phy_status) {
+ mdelay(10);
+ MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
+ if (phy->phy_type & PORT_TYPE_SATA) {
+ tmp = MVS_CHIP_DISP->read_port_irq_mask(
+ mvi, phy_no);
+ tmp &= ~PHYEV_SIG_FIS;
+ MVS_CHIP_DISP->write_port_irq_mask(mvi,
+ phy_no, tmp);
+ }
+ mvs_update_phyinfo(mvi, phy_no, 0);
+ mvs_bytes_dmaed(mvi, phy_no);
+ /* whether driver is going to handle hot plug */
+ if (phy->phy_event & PHY_PLUG_OUT) {
+ mvs_port_notify_formed(sas_phy, 0);
+ phy->phy_event &= ~PHY_PLUG_OUT;
+ }
+ } else {
+ mv_dprintk("plugin interrupt but phy%d is gone\n",
+ phy_no + mvi->id*mvi->chip->n_phy);
+ }
+ } else if (phy->irq_status & PHYEV_BROAD_CH) {
+ mv_dprintk("port %d broadcast change.\n",
+ phy_no + mvi->id*mvi->chip->n_phy);
+ /* exception for Samsung disk drive*/
+ mdelay(1000);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ }
+ MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
+}
+
+int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
+{
+ u32 rx_prod_idx, rx_desc;
+ bool attn = false;
+
+ /* the first dword in the RX ring is special: it contains
+ * a mirror of the hardware's RX producer index, so that
+ * we don't have to stall the CPU reading that register.
+ * The actual RX ring is offset by one dword, due to this.
+ */
+ rx_prod_idx = mvi->rx_cons;
+ mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
+ if (mvi->rx_cons == 0xfff) /* h/w hasn't touched RX ring yet */
+ return 0;
+
+ /* The CMPL_Q may come late, read from register and try again
+ * note: if coalescing is enabled,
+ * it will need to read from register every time for sure
+ */
+ if (unlikely(mvi->rx_cons == rx_prod_idx))
+ mvi->rx_cons = MVS_CHIP_DISP->rx_update(mvi) & RX_RING_SZ_MASK;
+
+ if (mvi->rx_cons == rx_prod_idx)
+ return 0;
+
+ while (mvi->rx_cons != rx_prod_idx) {
+ /* increment our internal RX consumer pointer */
+ rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
+ rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
+
+ if (likely(rx_desc & RXQ_DONE))
+ mvs_slot_complete(mvi, rx_desc, 0);
+ if (rx_desc & RXQ_ATTN) {
+ attn = true;
+ } else if (rx_desc & RXQ_ERR) {
+ if (!(rx_desc & RXQ_DONE))
+ mvs_slot_complete(mvi, rx_desc, 0);
+ } else if (rx_desc & RXQ_SLOT_RESET) {
+ mvs_slot_free(mvi, rx_desc);
+ }
+ }
+
+ if (attn && self_clear)
+ MVS_CHIP_DISP->int_full(mvi);
+ return 0;
+}
+
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
new file mode 100644
index 000000000000..aa2270af1bac
--- /dev/null
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -0,0 +1,406 @@
+/*
+ * Marvell 88SE64xx/88SE94xx main function head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+*/
+
+#ifndef _MV_SAS_H_
+#define _MV_SAS_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sas_ata.h>
+#include <linux/version.h>
+#include "mv_defs.h"
+
+#define DRV_NAME "mvsas"
+#define DRV_VERSION "0.8.2"
+#define _MV_DUMP 0
+#define MVS_ID_NOT_MAPPED 0x7f
+/* #define DISABLE_HOTPLUG_DMA_FIX */
+#define MAX_EXP_RUNNING_REQ 2
+#define WIDE_PORT_MAX_PHY 4
+#define MV_DISABLE_NCQ 0
+#define mv_printk(fmt, arg ...) \
+ printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
+#ifdef MV_DEBUG
+#define mv_dprintk(format, arg...) \
+ printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
+#else
+#define mv_dprintk(format, arg...)
+#endif
+#define MV_MAX_U32 0xffffffff
+
+extern struct mvs_tgt_initiator mvs_tgt;
+extern struct mvs_info *tgt_mvi;
+extern const struct mvs_dispatch mvs_64xx_dispatch;
+extern const struct mvs_dispatch mvs_94xx_dispatch;
+
+#define DEV_IS_EXPANDER(type) \
+ ((type == EDGE_DEV) || (type == FANOUT_DEV))
+
+#define bit(n) ((u32)1 << n)
+
+#define for_each_phy(__lseq_mask, __mc, __lseq) \
+ for ((__mc) = (__lseq_mask), (__lseq) = 0; \
+ (__mc) != 0 ; \
+ (++__lseq), (__mc) >>= 1)
+
+#define MV_INIT_DELAYED_WORK(w, f, d) INIT_DELAYED_WORK(w, f)
+#define UNASSOC_D2H_FIS(id) \
+ ((void *) mvi->rx_fis + 0x100 * id)
+#define SATA_RECEIVED_FIS_LIST(reg_set) \
+ ((void *) mvi->rx_fis + mvi->chip->fis_offs + 0x100 * reg_set)
+#define SATA_RECEIVED_SDB_FIS(reg_set) \
+ (SATA_RECEIVED_FIS_LIST(reg_set) + 0x58)
+#define SATA_RECEIVED_D2H_FIS(reg_set) \
+ (SATA_RECEIVED_FIS_LIST(reg_set) + 0x40)
+#define SATA_RECEIVED_PIO_FIS(reg_set) \
+ (SATA_RECEIVED_FIS_LIST(reg_set) + 0x20)
+#define SATA_RECEIVED_DMA_FIS(reg_set) \
+ (SATA_RECEIVED_FIS_LIST(reg_set) + 0x00)
+
+enum dev_status {
+ MVS_DEV_NORMAL = 0x0,
+ MVS_DEV_EH = 0x1,
+};
+
+
+struct mvs_info;
+
+struct mvs_dispatch {
+ char *name;
+ int (*chip_init)(struct mvs_info *mvi);
+ int (*spi_init)(struct mvs_info *mvi);
+ int (*chip_ioremap)(struct mvs_info *mvi);
+ void (*chip_iounmap)(struct mvs_info *mvi);
+ irqreturn_t (*isr)(struct mvs_info *mvi, int irq, u32 stat);
+ u32 (*isr_status)(struct mvs_info *mvi, int irq);
+ void (*interrupt_enable)(struct mvs_info *mvi);
+ void (*interrupt_disable)(struct mvs_info *mvi);
+
+ u32 (*read_phy_ctl)(struct mvs_info *mvi, u32 port);
+ void (*write_phy_ctl)(struct mvs_info *mvi, u32 port, u32 val);
+
+ u32 (*read_port_cfg_data)(struct mvs_info *mvi, u32 port);
+ void (*write_port_cfg_data)(struct mvs_info *mvi, u32 port, u32 val);
+ void (*write_port_cfg_addr)(struct mvs_info *mvi, u32 port, u32 addr);
+
+ u32 (*read_port_vsr_data)(struct mvs_info *mvi, u32 port);
+ void (*write_port_vsr_data)(struct mvs_info *mvi, u32 port, u32 val);
+ void (*write_port_vsr_addr)(struct mvs_info *mvi, u32 port, u32 addr);
+
+ u32 (*read_port_irq_stat)(struct mvs_info *mvi, u32 port);
+ void (*write_port_irq_stat)(struct mvs_info *mvi, u32 port, u32 val);
+
+ u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
+ void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);
+
+ void (*get_sas_addr)(void *buf, u32 buflen);
+ void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
+ void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
+ u32 tfs);
+ void (*start_delivery)(struct mvs_info *mvi, u32 tx);
+ u32 (*rx_update)(struct mvs_info *mvi);
+ void (*int_full)(struct mvs_info *mvi);
+ u8 (*assign_reg_set)(struct mvs_info *mvi, u8 *tfs);
+ void (*free_reg_set)(struct mvs_info *mvi, u8 *tfs);
+ u32 (*prd_size)(void);
+ u32 (*prd_count)(void);
+ void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
+ void (*detect_porttype)(struct mvs_info *mvi, int i);
+ int (*oob_done)(struct mvs_info *mvi, int i);
+ void (*fix_phy_info)(struct mvs_info *mvi, int i,
+ struct sas_identify_frame *id);
+ void (*phy_work_around)(struct mvs_info *mvi, int i);
+ void (*phy_set_link_rate)(struct mvs_info *mvi, u32 phy_id,
+ struct sas_phy_linkrates *rates);
+ u32 (*phy_max_link_rate)(void);
+ void (*phy_disable)(struct mvs_info *mvi, u32 phy_id);
+ void (*phy_enable)(struct mvs_info *mvi, u32 phy_id);
+ void (*phy_reset)(struct mvs_info *mvi, u32 phy_id, int hard);
+ void (*stp_reset)(struct mvs_info *mvi, u32 phy_id);
+ void (*clear_active_cmds)(struct mvs_info *mvi);
+ u32 (*spi_read_data)(struct mvs_info *mvi);
+ void (*spi_write_data)(struct mvs_info *mvi, u32 data);
+ int (*spi_buildcmd)(struct mvs_info *mvi,
+ u32 *dwCmd,
+ u8 cmd,
+ u8 read,
+ u8 length,
+ u32 addr
+ );
+ int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
+ int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ void (*dma_fix)(dma_addr_t buf_dma, int buf_len, int from, void *prd);
+#endif
+
+};
+
+struct mvs_chip_info {
+ u32 n_host;
+ u32 n_phy;
+ u32 fis_offs;
+ u32 fis_count;
+ u32 srs_sz;
+ u32 slot_width;
+ const struct mvs_dispatch *dispatch;
+};
+#define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width)
+#define MVS_RX_FISL_SZ \
+ (mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
+#define MVS_CHIP_DISP (mvi->chip->dispatch)
+
+struct mvs_err_info {
+ __le32 flags;
+ __le32 flags2;
+};
+
+struct mvs_cmd_hdr {
+ __le32 flags; /* PRD tbl len; SAS, SATA ctl */
+ __le32 lens; /* cmd, max resp frame len */
+ __le32 tags; /* targ port xfer tag; tag */
+ __le32 data_len; /* data xfer len */
+ __le64 cmd_tbl; /* command table address */
+ __le64 open_frame; /* open addr frame address */
+ __le64 status_buf; /* status buffer address */
+ __le64 prd_tbl; /* PRD tbl address */
+ __le32 reserved[4];
+};
+
+struct mvs_port {
+ struct asd_sas_port sas_port;
+ u8 port_attached;
+ u8 wide_port_phymap;
+ struct list_head list;
+};
+
+struct mvs_phy {
+ struct mvs_info *mvi;
+ struct mvs_port *port;
+ struct asd_sas_phy sas_phy;
+ struct sas_identify identify;
+ struct scsi_device *sdev;
+ struct timer_list timer;
+ u64 dev_sas_addr;
+ u64 att_dev_sas_addr;
+ u32 att_dev_info;
+ u32 dev_info;
+ u32 phy_type;
+ u32 phy_status;
+ u32 irq_status;
+ u32 frame_rcvd_size;
+ u8 frame_rcvd[32];
+ u8 phy_attached;
+ u8 phy_mode;
+ u8 reserved[2];
+ u32 phy_event;
+ enum sas_linkrate minimum_linkrate;
+ enum sas_linkrate maximum_linkrate;
+};
+
+struct mvs_device {
+ struct list_head dev_entry;
+ enum sas_dev_type dev_type;
+ struct mvs_info *mvi_info;
+ struct domain_device *sas_device;
+ u32 attached_phy;
+ u32 device_id;
+ u32 runing_req;
+ u8 taskfileset;
+ u8 dev_status;
+ u16 reserved;
+};
+
+struct mvs_slot_info {
+ struct list_head entry;
+ union {
+ struct sas_task *task;
+ void *tdata;
+ };
+ u32 n_elem;
+ u32 tx;
+ u32 slot_tag;
+
+ /* DMA buffer for storing cmd tbl, open addr frame, status buffer,
+ * and PRD table
+ */
+ void *buf;
+ dma_addr_t buf_dma;
+#if _MV_DUMP
+ u32 cmd_size;
+#endif
+ void *response;
+ struct mvs_port *port;
+ struct mvs_device *device;
+ void *open_frame;
+};
+
+struct mvs_info {
+ unsigned long flags;
+
+ /* host-wide lock */
+ spinlock_t lock;
+
+ /* our device */
+ struct pci_dev *pdev;
+ struct device *dev;
+
+ /* enhanced mode registers */
+ void __iomem *regs;
+
+ /* peripheral or soc registers */
+ void __iomem *regs_ex;
+ u8 sas_addr[SAS_ADDR_SIZE];
+
+ /* SCSI/SAS glue */
+ struct sas_ha_struct *sas;
+ struct Scsi_Host *shost;
+
+ /* TX (delivery) DMA ring */
+ __le32 *tx;
+ dma_addr_t tx_dma;
+
+ /* cached next-producer idx */
+ u32 tx_prod;
+
+ /* RX (completion) DMA ring */
+ __le32 *rx;
+ dma_addr_t rx_dma;
+
+ /* RX consumer idx */
+ u32 rx_cons;
+
+ /* RX'd FIS area */
+ __le32 *rx_fis;
+ dma_addr_t rx_fis_dma;
+
+ /* DMA command header slots */
+ struct mvs_cmd_hdr *slot;
+ dma_addr_t slot_dma;
+
+ u32 chip_id;
+ const struct mvs_chip_info *chip;
+
+ int tags_num;
+ DECLARE_BITMAP(tags, MVS_SLOTS);
+ /* further per-slot information */
+ struct mvs_phy phy[MVS_MAX_PHYS];
+ struct mvs_port port[MVS_MAX_PHYS];
+ u32 irq;
+ u32 exp_req;
+ u32 id;
+ u64 sata_reg_set;
+ struct list_head *hba_list;
+ struct list_head soc_entry;
+ struct list_head wq_list;
+ unsigned long instance;
+ u16 flashid;
+ u32 flashsize;
+ u32 flashsectSize;
+
+ void *addon;
+ struct mvs_device devices[MVS_MAX_DEVICES];
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+ void *bulk_buffer;
+ dma_addr_t bulk_buffer_dma;
+#define TRASH_BUCKET_SIZE 0x20000
+#endif
+ struct mvs_slot_info slot_info[0];
+};
+
+struct mvs_prv_info{
+ u8 n_host;
+ u8 n_phy;
+ u16 reserve;
+ struct mvs_info *mvi[2];
+};
+
+struct mvs_wq {
+ struct delayed_work work_q;
+ struct mvs_info *mvi;
+ void *data;
+ int handler;
+ struct list_head entry;
+};
+
+struct mvs_task_exec_info {
+ struct sas_task *task;
+ struct mvs_cmd_hdr *hdr;
+ struct mvs_port *port;
+ u32 tag;
+ int n_elem;
+};
+
+
+/******************** function prototype *********************/
+void mvs_get_sas_addr(void *buf, u32 buflen);
+void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
+void mvs_tag_free(struct mvs_info *mvi, u32 tag);
+void mvs_tag_set(struct mvs_info *mvi, unsigned int tag);
+int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out);
+void mvs_tag_init(struct mvs_info *mvi);
+void mvs_iounmap(void __iomem *regs);
+int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex);
+void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard);
+int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata);
+void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
+ u32 off_lo, u32 off_hi, u64 sas_addr);
+int mvs_slave_alloc(struct scsi_device *scsi_dev);
+int mvs_slave_configure(struct scsi_device *sdev);
+void mvs_scan_start(struct Scsi_Host *shost);
+int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
+int mvs_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags);
+int mvs_abort_task(struct sas_task *task);
+int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
+int mvs_clear_aca(struct domain_device *dev, u8 *lun);
+int mvs_clear_task_set(struct domain_device *dev, u8 * lun);
+void mvs_port_formed(struct asd_sas_phy *sas_phy);
+void mvs_port_deformed(struct asd_sas_phy *sas_phy);
+int mvs_dev_found(struct domain_device *dev);
+void mvs_dev_gone(struct domain_device *dev);
+int mvs_lu_reset(struct domain_device *dev, u8 *lun);
+int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags);
+int mvs_I_T_nexus_reset(struct domain_device *dev);
+int mvs_query_task(struct sas_task *task);
+void mvs_release_task(struct mvs_info *mvi, int phy_no,
+ struct domain_device *dev);
+void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
+void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
+int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
+void mvs_hexdump(u32 size, u8 *data, u32 baseaddr);
+#endif
+
diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild
index 0e207aa67d16..5fd73d77c3af 100644
--- a/drivers/scsi/osd/Kbuild
+++ b/drivers/scsi/osd/Kbuild
@@ -11,31 +11,6 @@
# it under the terms of the GNU General Public License version 2
#
-ifneq ($(OSD_INC),)
-# we are built out-of-tree Kconfigure everything as on
-
-CONFIG_SCSI_OSD_INITIATOR=m
-ccflags-y += -DCONFIG_SCSI_OSD_INITIATOR -DCONFIG_SCSI_OSD_INITIATOR_MODULE
-
-CONFIG_SCSI_OSD_ULD=m
-ccflags-y += -DCONFIG_SCSI_OSD_ULD -DCONFIG_SCSI_OSD_ULD_MODULE
-
-# CONFIG_SCSI_OSD_DPRINT_SENSE =
-# 0 - no print of errors
-# 1 - print errors
-# 2 - errors + warrnings
-ccflags-y += -DCONFIG_SCSI_OSD_DPRINT_SENSE=1
-
-# Uncomment to turn debug on
-# ccflags-y += -DCONFIG_SCSI_OSD_DEBUG
-
-# if we are built out-of-tree and the hosting kernel has OSD headers
-# then "ccflags-y +=" will not pick the out-off-tree headers. Only by doing
-# this it will work. This might break in future kernels
-LINUXINCLUDE := -I$(OSD_INC) $(LINUXINCLUDE)
-
-endif
-
# libosd.ko - osd-initiator library
libosd-y := osd_initiator.o
obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o
diff --git a/drivers/scsi/osd/Makefile b/drivers/scsi/osd/Makefile
deleted file mode 100755
index d905344f83ba..000000000000
--- a/drivers/scsi/osd/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Makefile for the OSD modules (out of tree)
-#
-# Copyright (C) 2008 Panasas Inc. All rights reserved.
-#
-# Authors:
-# Boaz Harrosh <bharrosh@panasas.com>
-# Benny Halevy <bhalevy@panasas.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
-#
-# This Makefile is used to call the kernel Makefile in case of an out-of-tree
-# build.
-# $KSRC should point to a Kernel source tree otherwise host's default is
-# used. (eg. /lib/modules/`uname -r`/build)
-
-# include path for out-of-tree Headers
-OSD_INC ?= `pwd`/../../../include
-
-# allow users to override these
-# e.g. to compile for a kernel that you aren't currently running
-KSRC ?= /lib/modules/$(shell uname -r)/build
-KBUILD_OUTPUT ?=
-ARCH ?=
-V ?= 0
-
-# this is the basic Kbuild out-of-tree invocation, with the M= option
-KBUILD_BASE = +$(MAKE) -C $(KSRC) M=`pwd` KBUILD_OUTPUT=$(KBUILD_OUTPUT) ARCH=$(ARCH) V=$(V)
-
-all: libosd
-
-libosd: ;
- $(KBUILD_BASE) OSD_INC=$(OSD_INC) modules
-
-clean:
- $(KBUILD_BASE) clean
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index d178a8b799ec..7a117c18114c 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -118,39 +118,39 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
_osd_ver_desc(or));
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_VENDOR_IDENTIFICATION [%s]\n",
+ OSD_INFO("VENDOR_IDENTIFICATION [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_PRODUCT_IDENTIFICATION [%s]\n",
+ OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_PRODUCT_MODEL [%s]\n",
+ OSD_INFO("PRODUCT_MODEL [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_PRODUCT_REVISION_LEVEL [%u]\n",
+ OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n",
pFirst ? get_unaligned_be32(pFirst) : ~0U);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER [%s]\n",
+ OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a].val_ptr;
- OSD_INFO("OSD_ATTR_RI_OSD_NAME [%s]\n", (char *)pFirst);
+ OSD_INFO("OSD_NAME [%s]\n", (char *)pFirst);
a++;
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_TOTAL_CAPACITY [0x%llx]\n",
+ OSD_INFO("TOTAL_CAPACITY [0x%llx]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_USED_CAPACITY [0x%llx]\n",
+ OSD_INFO("USED_CAPACITY [0x%llx]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_NUMBER_OF_PARTITIONS [%llu]\n",
+ OSD_INFO("NUMBER_OF_PARTITIONS [%llu]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
if (a >= nelem)
@@ -158,7 +158,7 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
/* FIXME: Where are the time utilities */
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("OSD_ATTR_RI_CLOCK [0x%02x%02x%02x%02x%02x%02x]\n",
+ OSD_INFO("CLOCK [0x%02x%02x%02x%02x%02x%02x]\n",
((char *)pFirst)[0], ((char *)pFirst)[1],
((char *)pFirst)[2], ((char *)pFirst)[3],
((char *)pFirst)[4], ((char *)pFirst)[5]);
@@ -169,7 +169,8 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1,
sid_dump, sizeof(sid_dump), true);
- OSD_INFO("OSD_ATTR_RI_OSD_SYSTEM_ID(%d) [%s]\n", len, sid_dump);
+ OSD_INFO("OSD_SYSTEM_ID(%d)\n"
+ " [%s]\n", len, sid_dump);
a++;
}
out:
@@ -205,6 +206,74 @@ static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len)
osdv2_attr_list_elem_size(len);
}
+static void _osd_req_alist_elem_encode(struct osd_request *or,
+ void *attr_last, const struct osd_attr *oa)
+{
+ if (osd_req_is_ver1(or)) {
+ struct osdv1_attributes_list_element *attr = attr_last;
+
+ attr->attr_page = cpu_to_be32(oa->attr_page);
+ attr->attr_id = cpu_to_be32(oa->attr_id);
+ attr->attr_bytes = cpu_to_be16(oa->len);
+ memcpy(attr->attr_val, oa->val_ptr, oa->len);
+ } else {
+ struct osdv2_attributes_list_element *attr = attr_last;
+
+ attr->attr_page = cpu_to_be32(oa->attr_page);
+ attr->attr_id = cpu_to_be32(oa->attr_id);
+ attr->attr_bytes = cpu_to_be16(oa->len);
+ memcpy(attr->attr_val, oa->val_ptr, oa->len);
+ }
+}
+
+static int _osd_req_alist_elem_decode(struct osd_request *or,
+ void *cur_p, struct osd_attr *oa, unsigned max_bytes)
+{
+ unsigned inc;
+ if (osd_req_is_ver1(or)) {
+ struct osdv1_attributes_list_element *attr = cur_p;
+
+ if (max_bytes < sizeof(*attr))
+ return -1;
+
+ oa->len = be16_to_cpu(attr->attr_bytes);
+ inc = _osd_req_alist_elem_size(or, oa->len);
+ if (inc > max_bytes)
+ return -1;
+
+ oa->attr_page = be32_to_cpu(attr->attr_page);
+ oa->attr_id = be32_to_cpu(attr->attr_id);
+
+ /* OSD1: On empty attributes we return a pointer to 2 bytes
+ * of zeros. This keeps similar behaviour with OSD2.
+ * (See below)
+ */
+ oa->val_ptr = likely(oa->len) ? attr->attr_val :
+ (u8 *)&attr->attr_bytes;
+ } else {
+ struct osdv2_attributes_list_element *attr = cur_p;
+
+ if (max_bytes < sizeof(*attr))
+ return -1;
+
+ oa->len = be16_to_cpu(attr->attr_bytes);
+ inc = _osd_req_alist_elem_size(or, oa->len);
+ if (inc > max_bytes)
+ return -1;
+
+ oa->attr_page = be32_to_cpu(attr->attr_page);
+ oa->attr_id = be32_to_cpu(attr->attr_id);
+
+ /* OSD2: For convenience, on empty attributes, we return 8 bytes
+ * of zeros here. This keeps the same behaviour with OSD2r04,
+ * and is nice with null terminating ASCII fields.
+ * oa->val_ptr == NULL marks the end-of-list, or error.
+ */
+ oa->val_ptr = likely(oa->len) ? attr->attr_val : attr->reserved;
+ }
+ return inc;
+}
+
static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head)
{
return osd_req_is_ver1(or) ?
@@ -282,9 +351,9 @@ _osd_req_sec_params(struct osd_request *or)
struct osd_cdb *ocdb = &or->cdb;
if (osd_req_is_ver1(or))
- return &ocdb->v1.sec_params;
+ return (struct osd_security_parameters *)&ocdb->v1.sec_params;
else
- return &ocdb->v2.sec_params;
+ return (struct osd_security_parameters *)&ocdb->v2.sec_params;
}
void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device)
@@ -601,7 +670,7 @@ static int _osd_req_list_objects(struct osd_request *or,
__be16 action, const struct osd_obj_id *obj, osd_id initial_id,
struct osd_obj_id_list *list, unsigned nelem)
{
- struct request_queue *q = or->osd_dev->scsi_device->request_queue;
+ struct request_queue *q = osd_request_queue(or->osd_dev);
u64 len = nelem * sizeof(osd_id) + sizeof(*list);
struct bio *bio;
@@ -612,9 +681,9 @@ static int _osd_req_list_objects(struct osd_request *or,
WARN_ON(or->in.bio);
bio = bio_map_kern(q, list, len, or->alloc_flags);
- if (!bio) {
+ if (IS_ERR(bio)) {
OSD_ERR("!!! Failed to allocate list_objects BIO\n");
- return -ENOMEM;
+ return PTR_ERR(bio);
}
bio->bi_rw &= ~(1 << BIO_RW);
@@ -710,16 +779,32 @@ EXPORT_SYMBOL(osd_req_remove_object);
*/
void osd_req_write(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio, u64 offset)
+ const struct osd_obj_id *obj, u64 offset,
+ struct bio *bio, u64 len)
{
- _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, bio->bi_size);
+ _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
WARN_ON(or->out.bio || or->out.total_bytes);
- bio->bi_rw |= (1 << BIO_RW);
+ WARN_ON(0 == bio_rw_flagged(bio, BIO_RW));
or->out.bio = bio;
- or->out.total_bytes = bio->bi_size;
+ or->out.total_bytes = len;
}
EXPORT_SYMBOL(osd_req_write);
+int osd_req_write_kern(struct osd_request *or,
+ const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
+{
+ struct request_queue *req_q = osd_request_queue(or->osd_dev);
+ struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
+
+ if (IS_ERR(bio))
+ return PTR_ERR(bio);
+
+ bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
+ osd_req_write(or, obj, offset, bio, len);
+ return 0;
+}
+EXPORT_SYMBOL(osd_req_write_kern);
+
/*TODO: void osd_req_append(struct osd_request *,
const struct osd_obj_id *, struct bio *data_out); */
/*TODO: void osd_req_create_write(struct osd_request *,
@@ -745,16 +830,31 @@ void osd_req_flush_object(struct osd_request *or,
EXPORT_SYMBOL(osd_req_flush_object);
void osd_req_read(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio, u64 offset)
+ const struct osd_obj_id *obj, u64 offset,
+ struct bio *bio, u64 len)
{
- _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, bio->bi_size);
+ _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
WARN_ON(or->in.bio || or->in.total_bytes);
- bio->bi_rw &= ~(1 << BIO_RW);
+ WARN_ON(1 == bio_rw_flagged(bio, BIO_RW));
or->in.bio = bio;
- or->in.total_bytes = bio->bi_size;
+ or->in.total_bytes = len;
}
EXPORT_SYMBOL(osd_req_read);
+int osd_req_read_kern(struct osd_request *or,
+ const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
+{
+ struct request_queue *req_q = osd_request_queue(or->osd_dev);
+ struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
+
+ if (IS_ERR(bio))
+ return PTR_ERR(bio);
+
+ osd_req_read(or, obj, offset, bio, len);
+ return 0;
+}
+EXPORT_SYMBOL(osd_req_read_kern);
+
void osd_req_get_attributes(struct osd_request *or,
const struct osd_obj_id *obj)
{
@@ -798,7 +898,6 @@ int osd_req_add_set_attr_list(struct osd_request *or,
attr_last = or->set_attr.buff + total_bytes;
for (; nelem; --nelem) {
- struct osd_attributes_list_element *attr;
unsigned elem_size = _osd_req_alist_elem_size(or, oa->len);
total_bytes += elem_size;
@@ -811,11 +910,7 @@ int osd_req_add_set_attr_list(struct osd_request *or,
or->set_attr.buff + or->set_attr.total_bytes;
}
- attr = attr_last;
- attr->attr_page = cpu_to_be32(oa->attr_page);
- attr->attr_id = cpu_to_be32(oa->attr_id);
- attr->attr_bytes = cpu_to_be16(oa->len);
- memcpy(attr->attr_val, oa->val_ptr, oa->len);
+ _osd_req_alist_elem_encode(or, attr_last, oa);
attr_last += elem_size;
++oa;
@@ -826,26 +921,6 @@ int osd_req_add_set_attr_list(struct osd_request *or,
}
EXPORT_SYMBOL(osd_req_add_set_attr_list);
-static int _append_map_kern(struct request *req,
- void *buff, unsigned len, gfp_t flags)
-{
- struct bio *bio;
- int ret;
-
- bio = bio_map_kern(req->q, buff, len, flags);
- if (IS_ERR(bio)) {
- OSD_ERR("Failed bio_map_kern(%p, %d) => %ld\n", buff, len,
- PTR_ERR(bio));
- return PTR_ERR(bio);
- }
- ret = blk_rq_append_bio(req->q, req, bio);
- if (ret) {
- OSD_ERR("Failed blk_rq_append_bio(%p) => %d\n", bio, ret);
- bio_put(bio);
- }
- return ret;
-}
-
static int _req_append_segment(struct osd_request *or,
unsigned padding, struct _osd_req_data_segment *seg,
struct _osd_req_data_segment *last_seg, struct _osd_io_info *io)
@@ -861,14 +936,14 @@ static int _req_append_segment(struct osd_request *or,
else
pad_buff = io->pad_buff;
- ret = _append_map_kern(io->req, pad_buff, padding,
+ ret = blk_rq_map_kern(io->req->q, io->req, pad_buff, padding,
or->alloc_flags);
if (ret)
return ret;
io->total_bytes += padding;
}
- ret = _append_map_kern(io->req, seg->buff, seg->total_bytes,
+ ret = blk_rq_map_kern(io->req->q, io->req, seg->buff, seg->total_bytes,
or->alloc_flags);
if (ret)
return ret;
@@ -1070,15 +1145,10 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
}
for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) {
- struct osd_attributes_list_element *attr = cur_p;
- unsigned inc;
+ int inc = _osd_req_alist_elem_decode(or, cur_p, oa,
+ returned_bytes - cur_bytes);
- oa->len = be16_to_cpu(attr->attr_bytes);
- inc = _osd_req_alist_elem_size(or, oa->len);
- OSD_DEBUG("oa->len=%d inc=%d cur_bytes=%d\n",
- oa->len, inc, cur_bytes);
- cur_bytes += inc;
- if (cur_bytes > returned_bytes) {
+ if (inc < 0) {
OSD_ERR("BAD FOOD from target. list not valid!"
"c=%d r=%d n=%d\n",
cur_bytes, returned_bytes, n);
@@ -1086,10 +1156,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
break;
}
- oa->attr_page = be32_to_cpu(attr->attr_page);
- oa->attr_id = be32_to_cpu(attr->attr_id);
- oa->val_ptr = attr->attr_val;
-
+ cur_bytes += inc;
cur_p += inc;
++oa;
}
@@ -1159,8 +1226,26 @@ static int _osd_req_finalize_attr_page(struct osd_request *or)
return ret;
}
+static inline void osd_sec_parms_set_out_offset(bool is_v1,
+ struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
+{
+ if (is_v1)
+ sec_parms->v1.data_out_integrity_check_offset = offset;
+ else
+ sec_parms->v2.data_out_integrity_check_offset = offset;
+}
+
+static inline void osd_sec_parms_set_in_offset(bool is_v1,
+ struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
+{
+ if (is_v1)
+ sec_parms->v1.data_in_integrity_check_offset = offset;
+ else
+ sec_parms->v2.data_in_integrity_check_offset = offset;
+}
+
static int _osd_req_finalize_data_integrity(struct osd_request *or,
- bool has_in, bool has_out, const u8 *cap_key)
+ bool has_in, bool has_out, u64 out_data_bytes, const u8 *cap_key)
{
struct osd_security_parameters *sec_parms = _osd_req_sec_params(or);
int ret;
@@ -1175,15 +1260,14 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or,
};
unsigned pad;
- or->out_data_integ.data_bytes = cpu_to_be64(
- or->out.bio ? or->out.bio->bi_size : 0);
+ or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes);
or->out_data_integ.set_attributes_bytes = cpu_to_be64(
or->set_attr.total_bytes);
or->out_data_integ.get_attributes_bytes = cpu_to_be64(
or->enc_get_attr.total_bytes);
- sec_parms->data_out_integrity_check_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &pad);
+ osd_sec_parms_set_out_offset(osd_req_is_ver1(or), sec_parms,
+ osd_req_encode_offset(or, or->out.total_bytes, &pad));
ret = _req_append_segment(or, pad, &seg, or->out.last_seg,
&or->out);
@@ -1203,8 +1287,8 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or,
};
unsigned pad;
- sec_parms->data_in_integrity_check_offset =
- osd_req_encode_offset(or, or->in.total_bytes, &pad);
+ osd_sec_parms_set_in_offset(osd_req_is_ver1(or), sec_parms,
+ osd_req_encode_offset(or, or->in.total_bytes, &pad));
ret = _req_append_segment(or, pad, &seg, or->in.last_seg,
&or->in);
@@ -1220,6 +1304,21 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or,
/*
* osd_finalize_request and helpers
*/
+static struct request *_make_request(struct request_queue *q, bool has_write,
+ struct _osd_io_info *oii, gfp_t flags)
+{
+ if (oii->bio)
+ return blk_make_request(q, oii->bio, flags);
+ else {
+ struct request *req;
+
+ req = blk_get_request(q, has_write ? WRITE : READ, flags);
+ if (unlikely(!req))
+ return ERR_PTR(-ENOMEM);
+
+ return req;
+ }
+}
static int _init_blk_request(struct osd_request *or,
bool has_in, bool has_out)
@@ -1228,14 +1327,18 @@ static int _init_blk_request(struct osd_request *or,
struct scsi_device *scsi_device = or->osd_dev->scsi_device;
struct request_queue *q = scsi_device->request_queue;
struct request *req;
- int ret = -ENOMEM;
+ int ret;
- req = blk_get_request(q, has_out, flags);
- if (!req)
+ req = _make_request(q, has_out, has_out ? &or->out : &or->in, flags);
+ if (IS_ERR(req)) {
+ ret = PTR_ERR(req);
goto out;
+ }
or->request = req;
req->cmd_type = REQ_TYPE_BLOCK_PC;
+ req->cmd_flags |= REQ_QUIET;
+
req->timeout = or->timeout;
req->retries = or->retries;
req->sense = or->sense;
@@ -1245,9 +1348,10 @@ static int _init_blk_request(struct osd_request *or,
or->out.req = req;
if (has_in) {
/* allocate bidi request */
- req = blk_get_request(q, READ, flags);
- if (!req) {
+ req = _make_request(q, false, &or->in, flags);
+ if (IS_ERR(req)) {
OSD_DEBUG("blk_get_request for bidi failed\n");
+ ret = PTR_ERR(req);
goto out;
}
req->cmd_type = REQ_TYPE_BLOCK_PC;
@@ -1268,6 +1372,7 @@ int osd_finalize_request(struct osd_request *or,
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
bool has_in, has_out;
+ u64 out_data_bytes = or->out.total_bytes;
int ret;
if (options & OSD_REQ_FUA)
@@ -1291,26 +1396,6 @@ int osd_finalize_request(struct osd_request *or,
return ret;
}
- if (or->out.bio) {
- ret = blk_rq_append_bio(or->request->q, or->out.req,
- or->out.bio);
- if (ret) {
- OSD_DEBUG("blk_rq_append_bio out failed\n");
- return ret;
- }
- OSD_DEBUG("out bytes=%llu (bytes_req=%u)\n",
- _LLU(or->out.total_bytes), blk_rq_bytes(or->out.req));
- }
- if (or->in.bio) {
- ret = blk_rq_append_bio(or->request->q, or->in.req, or->in.bio);
- if (ret) {
- OSD_DEBUG("blk_rq_append_bio in failed\n");
- return ret;
- }
- OSD_DEBUG("in bytes=%llu (bytes_req=%u)\n",
- _LLU(or->in.total_bytes), blk_rq_bytes(or->in.req));
- }
-
or->out.pad_buff = sg_out_pad_buffer;
or->in.pad_buff = sg_in_pad_buffer;
@@ -1337,7 +1422,8 @@ int osd_finalize_request(struct osd_request *or,
}
}
- ret = _osd_req_finalize_data_integrity(or, has_in, has_out, cap_key);
+ ret = _osd_req_finalize_data_integrity(or, has_in, has_out,
+ out_data_bytes, cap_key);
if (ret)
return ret;
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
index f644c9571eab..0bdef3390902 100644
--- a/drivers/scsi/osd/osd_uld.c
+++ b/drivers/scsi/osd/osd_uld.c
@@ -49,6 +49,7 @@
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/major.h>
+#include <linux/file.h>
#include <scsi/scsi.h>
#include <scsi/scsi_driver.h>
@@ -173,65 +174,58 @@ static const struct file_operations osd_fops = {
.unlocked_ioctl = osd_uld_ioctl,
};
-struct osd_dev *osduld_path_lookup(const char *path)
+struct osd_dev *osduld_path_lookup(const char *name)
{
- struct nameidata nd;
- struct inode *inode;
- struct cdev *cdev;
- struct osd_uld_device *uninitialized_var(oud);
+ struct osd_uld_device *oud;
+ struct osd_dev *od;
+ struct file *file;
int error;
- if (!path || !*path) {
+ if (!name || !*name) {
OSD_ERR("Mount with !path || !*path\n");
return ERR_PTR(-EINVAL);
}
- error = path_lookup(path, LOOKUP_FOLLOW, &nd);
- if (error) {
- OSD_ERR("path_lookup of %s faild=>%d\n", path, error);
- return ERR_PTR(error);
- }
+ od = kzalloc(sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return ERR_PTR(-ENOMEM);
- inode = nd.path.dentry->d_inode;
- error = -EINVAL; /* Not the right device e.g osd_uld_device */
- if (!S_ISCHR(inode->i_mode)) {
- OSD_DEBUG("!S_ISCHR()\n");
- goto out;
+ file = filp_open(name, O_RDWR, 0);
+ if (IS_ERR(file)) {
+ error = PTR_ERR(file);
+ goto free_od;
}
- cdev = inode->i_cdev;
- if (!cdev) {
- OSD_ERR("Before mounting an OSD Based filesystem\n");
- OSD_ERR(" user-mode must open+close the %s device\n", path);
- OSD_ERR(" Example: bash: echo < %s\n", path);
- goto out;
+ if (file->f_op != &osd_fops){
+ error = -EINVAL;
+ goto close_file;
}
- /* The Magic wand. Is it our char-dev */
- /* TODO: Support sg devices */
- if (cdev->owner != THIS_MODULE) {
- OSD_ERR("Error mounting %s - is not an OSD device\n", path);
- goto out;
- }
+ oud = file->private_data;
- oud = container_of(cdev, struct osd_uld_device, cdev);
+ *od = oud->od;
+ od->file = file;
- __uld_get(oud);
- error = 0;
+ return od;
-out:
- path_put(&nd.path);
- return error ? ERR_PTR(error) : &oud->od;
+close_file:
+ fput(file);
+free_od:
+ kfree(od);
+ return ERR_PTR(error);
}
EXPORT_SYMBOL(osduld_path_lookup);
void osduld_put_device(struct osd_dev *od)
{
- if (od) {
- struct osd_uld_device *oud = container_of(od,
- struct osd_uld_device, od);
- __uld_put(oud);
+ if (od && !IS_ERR(od)) {
+ struct osd_uld_device *oud = od->file->private_data;
+
+ BUG_ON(od->scsi_device != oud->od.scsi_device);
+
+ fput(od->file);
+ kfree(od);
}
}
EXPORT_SYMBOL(osduld_put_device);
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index 5defe5ea5eda..8371d917a9a2 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -17,9 +17,12 @@
* General Public License for more details.
*
******************************************************************************/
-#define QLA1280_VERSION "3.26"
+#define QLA1280_VERSION "3.27"
/*****************************************************************************
Revision History:
+ Rev 3.27, February 10, 2009, Michael Reed
+ - General code cleanup.
+ - Improve error recovery.
Rev 3.26, January 16, 2006 Jes Sorensen
- Ditch all < 2.6 support
Rev 3.25.1, February 10, 2005 Christoph Hellwig
@@ -435,7 +438,6 @@ static int qla1280_mailbox_command(struct scsi_qla_host *,
uint8_t, uint16_t *);
static int qla1280_bus_reset(struct scsi_qla_host *, int);
static int qla1280_device_reset(struct scsi_qla_host *, int, int);
-static int qla1280_abort_device(struct scsi_qla_host *, int, int, int);
static int qla1280_abort_command(struct scsi_qla_host *, struct srb *, int);
static int qla1280_abort_isp(struct scsi_qla_host *);
#ifdef QLA_64BIT_PTR
@@ -698,7 +700,7 @@ qla1280_info(struct Scsi_Host *host)
}
/**************************************************************************
- * qla1200_queuecommand
+ * qla1280_queuecommand
* Queue a command to the controller.
*
* Note:
@@ -713,12 +715,14 @@ qla1280_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
{
struct Scsi_Host *host = cmd->device->host;
struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
- struct srb *sp = (struct srb *)&cmd->SCp;
+ struct srb *sp = (struct srb *)CMD_SP(cmd);
int status;
cmd->scsi_done = fn;
sp->cmd = cmd;
sp->flags = 0;
+ sp->wait = NULL;
+ CMD_HANDLE(cmd) = (unsigned char *)NULL;
qla1280_print_scsi_cmd(5, cmd);
@@ -738,21 +742,11 @@ qla1280_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
enum action {
ABORT_COMMAND,
- ABORT_DEVICE,
DEVICE_RESET,
BUS_RESET,
ADAPTER_RESET,
- FAIL
};
-/* timer action for error action processor */
-static void qla1280_error_wait_timeout(unsigned long __data)
-{
- struct scsi_cmnd *cmd = (struct scsi_cmnd *)__data;
- struct srb *sp = (struct srb *)CMD_SP(cmd);
-
- complete(sp->wait);
-}
static void qla1280_mailbox_timeout(unsigned long __data)
{
@@ -767,8 +761,67 @@ static void qla1280_mailbox_timeout(unsigned long __data)
complete(ha->mailbox_wait);
}
+static int
+_qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp,
+ struct completion *wait)
+{
+ int status = FAILED;
+ struct scsi_cmnd *cmd = sp->cmd;
+
+ spin_unlock_irq(ha->host->host_lock);
+ wait_for_completion_timeout(wait, 4*HZ);
+ spin_lock_irq(ha->host->host_lock);
+ sp->wait = NULL;
+ if(CMD_HANDLE(cmd) == COMPLETED_HANDLE) {
+ status = SUCCESS;
+ (*cmd->scsi_done)(cmd);
+ }
+ return status;
+}
+
+static int
+qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp)
+{
+ DECLARE_COMPLETION_ONSTACK(wait);
+
+ sp->wait = &wait;
+ return _qla1280_wait_for_single_command(ha, sp, &wait);
+}
+
+static int
+qla1280_wait_for_pending_commands(struct scsi_qla_host *ha, int bus, int target)
+{
+ int cnt;
+ int status;
+ struct srb *sp;
+ struct scsi_cmnd *cmd;
+
+ status = SUCCESS;
+
+ /*
+ * Wait for all commands with the designated bus/target
+ * to be completed by the firmware
+ */
+ for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = ha->outstanding_cmds[cnt];
+ if (sp) {
+ cmd = sp->cmd;
+
+ if (bus >= 0 && SCSI_BUS_32(cmd) != bus)
+ continue;
+ if (target >= 0 && SCSI_TCN_32(cmd) != target)
+ continue;
+
+ status = qla1280_wait_for_single_command(ha, sp);
+ if (status == FAILED)
+ break;
+ }
+ }
+ return status;
+}
+
/**************************************************************************
- * qla1200_error_action
+ * qla1280_error_action
* The function will attempt to perform a specified error action and
* wait for the results (or time out).
*
@@ -780,11 +833,6 @@ static void qla1280_mailbox_timeout(unsigned long __data)
* Returns:
* SUCCESS or FAILED
*
- * Note:
- * Resetting the bus always succeeds - is has to, otherwise the
- * kernel will panic! Try a surgical technique - sending a BUS
- * DEVICE RESET message - on the offending target before pulling
- * the SCSI bus reset line.
**************************************************************************/
static int
qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
@@ -792,13 +840,19 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
struct scsi_qla_host *ha;
int bus, target, lun;
struct srb *sp;
- uint16_t data;
- unsigned char *handle;
- int result, i;
+ int i, found;
+ int result=FAILED;
+ int wait_for_bus=-1;
+ int wait_for_target = -1;
DECLARE_COMPLETION_ONSTACK(wait);
- struct timer_list timer;
+
+ ENTER("qla1280_error_action");
ha = (struct scsi_qla_host *)(CMD_HOST(cmd)->hostdata);
+ sp = (struct srb *)CMD_SP(cmd);
+ bus = SCSI_BUS_32(cmd);
+ target = SCSI_TCN_32(cmd);
+ lun = SCSI_LUN_32(cmd);
dprintk(4, "error_action %i, istatus 0x%04x\n", action,
RD_REG_WORD(&ha->iobase->istatus));
@@ -807,99 +861,47 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
RD_REG_WORD(&ha->iobase->host_cmd),
RD_REG_WORD(&ha->iobase->ictrl), jiffies);
- ENTER("qla1280_error_action");
if (qla1280_verbose)
printk(KERN_INFO "scsi(%li): Resetting Cmnd=0x%p, "
"Handle=0x%p, action=0x%x\n",
ha->host_no, cmd, CMD_HANDLE(cmd), action);
- if (cmd == NULL) {
- printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL "
- "si_Cmnd pointer, failing.\n");
- LEAVE("qla1280_error_action");
- return FAILED;
- }
-
- ha = (struct scsi_qla_host *)cmd->device->host->hostdata;
- sp = (struct srb *)CMD_SP(cmd);
- handle = CMD_HANDLE(cmd);
-
- /* Check for pending interrupts. */
- data = qla1280_debounce_register(&ha->iobase->istatus);
- /*
- * The io_request_lock is held when the reset handler is called, hence
- * the interrupt handler cannot be running in parallel as it also
- * grabs the lock. /Jes
- */
- if (data & RISC_INT)
- qla1280_isr(ha, &ha->done_q);
-
/*
- * Determine the suggested action that the mid-level driver wants
- * us to perform.
+ * Check to see if we have the command in the outstanding_cmds[]
+ * array. If not then it must have completed before this error
+ * action was initiated. If the error_action isn't ABORT_COMMAND
+ * then the driver must proceed with the requested action.
*/
- if (handle == (unsigned char *)INVALID_HANDLE || handle == NULL) {
- if(action == ABORT_COMMAND) {
- /* we never got this command */
- printk(KERN_INFO "qla1280: Aborting a NULL handle\n");
- return SUCCESS; /* no action - we don't have command */
+ found = -1;
+ for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
+ if (sp == ha->outstanding_cmds[i]) {
+ found = i;
+ sp->wait = &wait; /* we'll wait for it to complete */
+ break;
}
- } else {
- sp->wait = &wait;
}
- bus = SCSI_BUS_32(cmd);
- target = SCSI_TCN_32(cmd);
- lun = SCSI_LUN_32(cmd);
+ if (found < 0) { /* driver doesn't have command */
+ result = SUCCESS;
+ if (qla1280_verbose) {
+ printk(KERN_INFO
+ "scsi(%ld:%d:%d:%d): specified command has "
+ "already completed.\n", ha->host_no, bus,
+ target, lun);
+ }
+ }
- /* Overloading result. Here it means the success or fail of the
- * *issue* of the action. When we return from the routine, it must
- * mean the actual success or fail of the action */
- result = FAILED;
switch (action) {
- case FAIL:
- break;
case ABORT_COMMAND:
- if ((sp->flags & SRB_ABORT_PENDING)) {
- printk(KERN_WARNING
- "scsi(): Command has a pending abort "
- "message - ABORT_PENDING.\n");
- /* This should technically be impossible since we
- * now wait for abort completion */
- break;
- }
-
- for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
- if (sp == ha->outstanding_cmds[i]) {
- dprintk(1, "qla1280: RISC aborting command\n");
- if (qla1280_abort_command(ha, sp, i) == 0)
- result = SUCCESS;
- else {
- /*
- * Since we don't know what might
- * have happend to the command, it
- * is unsafe to remove it from the
- * device's queue at this point.
- * Wait and let the escalation
- * process take care of it.
- */
- printk(KERN_WARNING
- "scsi(%li:%i:%i:%i): Unable"
- " to abort command!\n",
- ha->host_no, bus, target, lun);
- }
- }
- }
- break;
-
- case ABORT_DEVICE:
- if (qla1280_verbose)
- printk(KERN_INFO
- "scsi(%ld:%d:%d:%d): Queueing abort device "
- "command.\n", ha->host_no, bus, target, lun);
- if (qla1280_abort_device(ha, bus, target, lun) == 0)
- result = SUCCESS;
+ dprintk(1, "qla1280: RISC aborting command\n");
+ /*
+ * The abort might fail due to race when the host_lock
+ * is released to issue the abort. As such, we
+ * don't bother to check the return status.
+ */
+ if (found >= 0)
+ qla1280_abort_command(ha, sp, found);
break;
case DEVICE_RESET:
@@ -907,16 +909,21 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
printk(KERN_INFO
"scsi(%ld:%d:%d:%d): Queueing device reset "
"command.\n", ha->host_no, bus, target, lun);
- if (qla1280_device_reset(ha, bus, target) == 0)
- result = SUCCESS;
+ if (qla1280_device_reset(ha, bus, target) == 0) {
+ /* issued device reset, set wait conditions */
+ wait_for_bus = bus;
+ wait_for_target = target;
+ }
break;
case BUS_RESET:
if (qla1280_verbose)
printk(KERN_INFO "qla1280(%ld:%d): Issued bus "
"reset.\n", ha->host_no, bus);
- if (qla1280_bus_reset(ha, bus) == 0)
- result = SUCCESS;
+ if (qla1280_bus_reset(ha, bus) == 0) {
+ /* issued bus reset, set wait conditions */
+ wait_for_bus = bus;
+ }
break;
case ADAPTER_RESET:
@@ -929,55 +936,48 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
"continue automatically\n", ha->host_no);
}
ha->flags.reset_active = 1;
- /*
- * We restarted all of the commands automatically, so the
- * mid-level code can expect completions momentitarily.
- */
- if (qla1280_abort_isp(ha) == 0)
- result = SUCCESS;
+
+ if (qla1280_abort_isp(ha) != 0) { /* it's dead */
+ result = FAILED;
+ }
ha->flags.reset_active = 0;
}
- if (!list_empty(&ha->done_q))
- qla1280_done(ha);
-
- /* If we didn't manage to issue the action, or we have no
- * command to wait for, exit here */
- if (result == FAILED || handle == NULL ||
- handle == (unsigned char *)INVALID_HANDLE) {
- /*
- * Clear completion queue to avoid qla1280_done() trying
- * to complete the command at a later stage after we
- * have exited the current context
- */
- sp->wait = NULL;
- goto leave;
- }
+ /*
+ * At this point, the host_lock has been released and retaken
+ * by the issuance of the mailbox command.
+ * Wait for the command passed in by the mid-layer if it
+ * was found by the driver. It might have been returned
+ * between eh recovery steps, hence the check of the "found"
+ * variable.
+ */
- /* set up a timer just in case we're really jammed */
- init_timer(&timer);
- timer.expires = jiffies + 4*HZ;
- timer.data = (unsigned long)cmd;
- timer.function = qla1280_error_wait_timeout;
- add_timer(&timer);
+ if (found >= 0)
+ result = _qla1280_wait_for_single_command(ha, sp, &wait);
- /* wait for the action to complete (or the timer to expire) */
- spin_unlock_irq(ha->host->host_lock);
- wait_for_completion(&wait);
- del_timer_sync(&timer);
- spin_lock_irq(ha->host->host_lock);
- sp->wait = NULL;
+ if (action == ABORT_COMMAND && result != SUCCESS) {
+ printk(KERN_WARNING
+ "scsi(%li:%i:%i:%i): "
+ "Unable to abort command!\n",
+ ha->host_no, bus, target, lun);
+ }
- /* the only action we might get a fail for is abort */
- if (action == ABORT_COMMAND) {
- if(sp->flags & SRB_ABORTED)
- result = SUCCESS;
- else
- result = FAILED;
+ /*
+ * If the command passed in by the mid-layer has been
+ * returned by the board, then wait for any additional
+ * commands which are supposed to complete based upon
+ * the error action.
+ *
+ * All commands are unconditionally returned during a
+ * call to qla1280_abort_isp(), ADAPTER_RESET. No need
+ * to wait for them.
+ */
+ if (result == SUCCESS && wait_for_bus >= 0) {
+ result = qla1280_wait_for_pending_commands(ha,
+ wait_for_bus, wait_for_target);
}
- leave:
dprintk(1, "RESET returning %d\n", result);
LEAVE("qla1280_error_action");
@@ -1280,13 +1280,12 @@ qla1280_done(struct scsi_qla_host *ha)
switch ((CMD_RESULT(cmd) >> 16)) {
case DID_RESET:
/* Issue marker command. */
- qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
+ if (!ha->flags.abort_isp_active)
+ qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
break;
case DID_ABORT:
sp->flags &= ~SRB_ABORT_PENDING;
sp->flags |= SRB_ABORTED;
- if (sp->flags & SRB_TIMEOUT)
- CMD_RESULT(sp->cmd) = DID_TIME_OUT << 16;
break;
default:
break;
@@ -1296,12 +1295,11 @@ qla1280_done(struct scsi_qla_host *ha)
scsi_dma_unmap(cmd);
/* Call the mid-level driver interrupt handler */
- CMD_HANDLE(sp->cmd) = (unsigned char *)INVALID_HANDLE;
ha->actthreads--;
- (*(cmd)->scsi_done)(cmd);
-
- if(sp->wait != NULL)
+ if (sp->wait == NULL)
+ (*(cmd)->scsi_done)(cmd);
+ else
complete(sp->wait);
}
LEAVE("qla1280_done");
@@ -2417,9 +2415,6 @@ static int
qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
{
struct device_reg __iomem *reg = ha->iobase;
-#if 0
- LIST_HEAD(done_q);
-#endif
int status = 0;
int cnt;
uint16_t *optr, *iptr;
@@ -2493,19 +2488,9 @@ qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
mr = MAILBOX_REGISTER_COUNT;
memcpy(optr, iptr, MAILBOX_REGISTER_COUNT * sizeof(uint16_t));
-#if 0
- /* Go check for any response interrupts pending. */
- qla1280_isr(ha, &done_q);
-#endif
-
if (ha->flags.reset_marker)
qla1280_rst_aen(ha);
-#if 0
- if (!list_empty(&done_q))
- qla1280_done(ha, &done_q);
-#endif
-
if (status)
dprintk(2, "qla1280_mailbox_command: **** FAILED, mailbox0 = "
"0x%x ****\n", mb[0]);
@@ -2641,41 +2626,6 @@ qla1280_device_reset(struct scsi_qla_host *ha, int bus, int target)
}
/*
- * qla1280_abort_device
- * Issue an abort message to the device
- *
- * Input:
- * ha = adapter block pointer.
- * bus = SCSI BUS.
- * target = SCSI ID.
- * lun = SCSI LUN.
- *
- * Returns:
- * 0 = success
- */
-static int
-qla1280_abort_device(struct scsi_qla_host *ha, int bus, int target, int lun)
-{
- uint16_t mb[MAILBOX_REGISTER_COUNT];
- int status;
-
- ENTER("qla1280_abort_device");
-
- mb[0] = MBC_ABORT_DEVICE;
- mb[1] = (bus ? target | BIT_7 : target) << 8 | lun;
- status = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
-
- /* Issue marker command. */
- qla1280_marker(ha, bus, target, lun, MK_SYNC_ID_LUN);
-
- if (status)
- dprintk(2, "qla1280_abort_device: **** FAILED ****\n");
-
- LEAVE("qla1280_abort_device");
- return status;
-}
-
-/*
* qla1280_abort_command
* Abort command aborts a specified IOCB.
*
@@ -2833,7 +2783,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
/* If room for request in request ring. */
if ((req_cnt + 2) >= ha->req_q_cnt) {
- status = 1;
+ status = SCSI_MLQUEUE_HOST_BUSY;
dprintk(2, "qla1280_start_scsi: in-ptr=0x%x req_q_cnt="
"0x%xreq_cnt=0x%x", ha->req_ring_index, ha->req_q_cnt,
req_cnt);
@@ -2845,7 +2795,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
ha->outstanding_cmds[cnt] != NULL; cnt++);
if (cnt >= MAX_OUTSTANDING_COMMANDS) {
- status = 1;
+ status = SCSI_MLQUEUE_HOST_BUSY;
dprintk(2, "qla1280_start_scsi: NO ROOM IN "
"OUTSTANDING ARRAY, req_q_cnt=0x%x", ha->req_q_cnt);
goto out;
@@ -3108,7 +3058,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
ha->req_q_cnt, seg_cnt);
/* If room for request in request ring. */
if ((req_cnt + 2) >= ha->req_q_cnt) {
- status = 1;
+ status = SCSI_MLQUEUE_HOST_BUSY;
dprintk(2, "qla1280_32bit_start_scsi: in-ptr=0x%x, "
"req_q_cnt=0x%x, req_cnt=0x%x", ha->req_ring_index,
ha->req_q_cnt, req_cnt);
@@ -3120,7 +3070,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
(ha->outstanding_cmds[cnt] != 0); cnt++) ;
if (cnt >= MAX_OUTSTANDING_COMMANDS) {
- status = 1;
+ status = SCSI_MLQUEUE_HOST_BUSY;
dprintk(2, "qla1280_32bit_start_scsi: NO ROOM IN OUTSTANDING "
"ARRAY, req_q_cnt=0x%x\n", ha->req_q_cnt);
goto out;
@@ -3487,6 +3437,7 @@ qla1280_isr(struct scsi_qla_host *ha, struct list_head *done_q)
/* Save ISP completion status */
CMD_RESULT(sp->cmd) = 0;
+ CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
/* Place block on done queue */
list_add_tail(&sp->list, done_q);
@@ -3495,7 +3446,7 @@ qla1280_isr(struct scsi_qla_host *ha, struct list_head *done_q)
* If we get here we have a real problem!
*/
printk(KERN_WARNING
- "qla1280: ISP invalid handle");
+ "qla1280: ISP invalid handle\n");
}
}
break;
@@ -3753,6 +3704,8 @@ qla1280_status_entry(struct scsi_qla_host *ha, struct response *pkt,
}
}
+ CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
+
/* Place command on done queue. */
list_add_tail(&sp->list, done_q);
out:
@@ -3808,6 +3761,8 @@ qla1280_error_entry(struct scsi_qla_host *ha, struct response *pkt,
CMD_RESULT(sp->cmd) = DID_ERROR << 16;
}
+ CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
+
/* Place command on done queue. */
list_add_tail(&sp->list, done_q);
}
@@ -3858,19 +3813,16 @@ qla1280_abort_isp(struct scsi_qla_host *ha)
struct scsi_cmnd *cmd;
sp = ha->outstanding_cmds[cnt];
if (sp) {
-
cmd = sp->cmd;
CMD_RESULT(cmd) = DID_RESET << 16;
-
- sp->cmd = NULL;
+ CMD_HANDLE(cmd) = COMPLETED_HANDLE;
ha->outstanding_cmds[cnt] = NULL;
-
- (*cmd->scsi_done)(cmd);
-
- sp->flags = 0;
+ list_add_tail(&sp->list, &ha->done_q);
}
}
+ qla1280_done(ha);
+
status = qla1280_load_firmware(ha);
if (status)
goto out;
@@ -3955,13 +3907,6 @@ qla1280_check_for_dead_scsi_bus(struct scsi_qla_host *ha, unsigned int bus)
if (scsi_control == SCSI_PHASE_INVALID) {
ha->bus_settings[bus].scsi_bus_dead = 1;
-#if 0
- CMD_RESULT(cp) = DID_NO_CONNECT << 16;
- CMD_HANDLE(cp) = INVALID_HANDLE;
- /* ha->actthreads--; */
-
- (*(cp)->scsi_done)(cp);
-#endif
return 1; /* bus is dead */
} else {
ha->bus_settings[bus].scsi_bus_dead = 0;
diff --git a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h
index d7c44b8d2b4f..834884b9eed5 100644
--- a/drivers/scsi/qla1280.h
+++ b/drivers/scsi/qla1280.h
@@ -88,7 +88,8 @@
/* Maximum outstanding commands in ISP queues */
#define MAX_OUTSTANDING_COMMANDS 512
-#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS + 2)
+#define COMPLETED_HANDLE ((unsigned char *) \
+ (MAX_OUTSTANDING_COMMANDS + 2))
/* ISP request and response entry counts (37-65535) */
#define REQUEST_ENTRY_CNT 255 /* Number of request entries. */
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index b09993a06576..0f8796201504 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -97,7 +97,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
return 0;
if (IS_NOCACHE_VPD_TYPE(ha))
- ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_nvram << 2,
+ ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2,
ha->nvram_size);
return memory_read_from_buffer(buf, count, &off, ha->nvram,
ha->nvram_size);
@@ -692,6 +692,109 @@ static struct bin_attribute sysfs_edc_status_attr = {
.read = qla2x00_sysfs_read_edc_status,
};
+static ssize_t
+qla2x00_sysfs_read_xgmac_stats(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint16_t actual_size;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count > XGMAC_DATA_SIZE)
+ return 0;
+
+ if (ha->xgmac_data)
+ goto do_read;
+
+ ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+ &ha->xgmac_data_dma, GFP_KERNEL);
+ if (!ha->xgmac_data) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for XGMAC read-data.\n");
+ return 0;
+ }
+
+do_read:
+ actual_size = 0;
+ memset(ha->xgmac_data, 0, XGMAC_DATA_SIZE);
+
+ rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma,
+ XGMAC_DATA_SIZE, &actual_size);
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to read XGMAC data (%x).\n", rval);
+ count = 0;
+ }
+
+ count = actual_size > count ? count: actual_size;
+ memcpy(buf, ha->xgmac_data, count);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_xgmac_stats_attr = {
+ .attr = {
+ .name = "xgmac_stats",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_xgmac_stats,
+};
+
+static ssize_t
+qla2x00_sysfs_read_dcbx_tlv(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint16_t actual_size;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE)
+ return 0;
+
+ if (ha->dcbx_tlv)
+ goto do_read;
+
+ ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+ &ha->dcbx_tlv_dma, GFP_KERNEL);
+ if (!ha->dcbx_tlv) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for DCBX TLV read-data.\n");
+ return 0;
+ }
+
+do_read:
+ actual_size = 0;
+ memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE);
+
+ rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma,
+ DCBX_TLV_DATA_SIZE);
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to read DCBX TLV data (%x).\n", rval);
+ count = 0;
+ }
+
+ memcpy(buf, ha->dcbx_tlv, count);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_dcbx_tlv_attr = {
+ .attr = {
+ .name = "dcbx_tlv",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_dcbx_tlv,
+};
+
static struct sysfs_entry {
char *name;
struct bin_attribute *attr;
@@ -706,6 +809,8 @@ static struct sysfs_entry {
{ "reset", &sysfs_reset_attr, },
{ "edc", &sysfs_edc_attr, 2 },
{ "edc_status", &sysfs_edc_status_attr, 2 },
+ { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 },
+ { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 },
{ NULL },
};
@@ -721,6 +826,8 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
continue;
if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw))
continue;
+ if (iter->is4GBp_only == 3 && !IS_QLA81XX(vha->hw))
+ continue;
ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
iter->attr);
@@ -743,6 +850,8 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
continue;
if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha))
continue;
+ if (iter->is4GBp_only == 3 && !IS_QLA81XX(ha))
+ continue;
sysfs_remove_bin_file(&host->shost_gendev.kobj,
iter->attr);
@@ -1088,6 +1197,58 @@ qla2x00_flash_block_size_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
}
+static ssize_t
+qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ if (!IS_QLA81XX(vha->hw))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id);
+}
+
+static ssize_t
+qla2x00_vn_port_mac_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ if (!IS_QLA81XX(vha->hw))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ vha->fcoe_vn_port_mac[5], vha->fcoe_vn_port_mac[4],
+ vha->fcoe_vn_port_mac[3], vha->fcoe_vn_port_mac[2],
+ vha->fcoe_vn_port_mac[1], vha->fcoe_vn_port_mac[0]);
+}
+
+static ssize_t
+qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap);
+}
+
+static ssize_t
+qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int rval;
+ uint16_t state[5];
+
+ rval = qla2x00_get_firmware_state(vha, state);
+ if (rval != QLA_SUCCESS)
+ memset(state, -1, sizeof(state));
+
+ return snprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0],
+ state[1], state[2], state[3], state[4]);
+}
+
static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
@@ -1116,6 +1277,11 @@ static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL);
static DEVICE_ATTR(phy_version, S_IRUGO, qla2x00_phy_version_show, NULL);
static DEVICE_ATTR(flash_block_size, S_IRUGO, qla2x00_flash_block_size_show,
NULL);
+static DEVICE_ATTR(vlan_id, S_IRUGO, qla2x00_vlan_id_show, NULL);
+static DEVICE_ATTR(vn_port_mac_address, S_IRUGO,
+ qla2x00_vn_port_mac_address_show, NULL);
+static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL);
+static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
struct device_attribute *qla2x00_host_attrs[] = {
&dev_attr_driver_version,
@@ -1138,6 +1304,10 @@ struct device_attribute *qla2x00_host_attrs[] = {
&dev_attr_mpi_version,
&dev_attr_phy_version,
&dev_attr_flash_block_size,
+ &dev_attr_vlan_id,
+ &dev_attr_vn_port_mac_address,
+ &dev_attr_fabric_param,
+ &dev_attr_fw_state,
NULL,
};
@@ -1313,7 +1483,8 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
* At this point all fcport's software-states are cleared. Perform any
* final cleanup of firmware resources (PCBs and XCBs).
*/
- if (fcport->loop_id != FC_NO_LOOP_ID)
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ !test_bit(UNLOADING, &fcport->vha->dpc_flags))
fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
@@ -1437,11 +1608,13 @@ static int
qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
{
int ret = 0;
- int cnt = 0;
- uint8_t qos = QLA_DEFAULT_QUE_QOS;
+ uint8_t qos = 0;
scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
scsi_qla_host_t *vha = NULL;
struct qla_hw_data *ha = base_vha->hw;
+ uint16_t options = 0;
+ int cnt;
+ struct req_que *req = ha->req_q_map[0];
ret = qla24xx_vport_create_req_sanity_check(fc_vport);
if (ret) {
@@ -1497,23 +1670,39 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
qla24xx_vport_disable(fc_vport, disable);
- /* Create a queue pair for the vport */
- if (ha->mqenable) {
- if (ha->npiv_info) {
- for (; cnt < ha->nvram_npiv_size; cnt++) {
- if (ha->npiv_info[cnt].port_name ==
- vha->port_name &&
- ha->npiv_info[cnt].node_name ==
- vha->node_name) {
- qos = ha->npiv_info[cnt].q_qos;
- break;
- }
- }
+ if (ql2xmultique_tag) {
+ req = ha->req_q_map[1];
+ goto vport_queue;
+ } else if (ql2xmaxqueues == 1 || !ha->npiv_info)
+ goto vport_queue;
+ /* Create a request queue in QoS mode for the vport */
+ for (cnt = 0; cnt < ha->nvram_npiv_size; cnt++) {
+ if (memcmp(ha->npiv_info[cnt].port_name, vha->port_name, 8) == 0
+ && memcmp(ha->npiv_info[cnt].node_name, vha->node_name,
+ 8) == 0) {
+ qos = ha->npiv_info[cnt].q_qos;
+ break;
+ }
+ }
+ if (qos) {
+ ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
+ qos);
+ if (!ret)
+ qla_printk(KERN_WARNING, ha,
+ "Can't create request queue for vp_idx:%d\n",
+ vha->vp_idx);
+ else {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "Request Que:%d (QoS: %d) created for vp_idx:%d\n",
+ ret, qos, vha->vp_idx));
+ req = ha->req_q_map[ret];
}
- qla25xx_create_queues(vha, qos);
}
+vport_queue:
+ vha->req = req;
return 0;
+
vport_create_failed_2:
qla24xx_disable_vp(vha);
qla24xx_deallocate_vp_id(vha);
@@ -1554,8 +1743,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
vha->host_no, vha->vp_idx, vha));
}
- if (ha->mqenable) {
- if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS)
+ if (vha->req->id && !ql2xmultique_tag) {
+ if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
qla_printk(KERN_WARNING, ha,
"Queue delete failed.\n");
}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 34760f8d4f17..4a990f4da4ea 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -149,11 +149,9 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg)
int rval = QLA_SUCCESS;
uint32_t cnt;
- if (RD_REG_DWORD(&reg->hccr) & HCCRX_RISC_PAUSE)
- return rval;
-
WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_PAUSE);
- for (cnt = 30000; (RD_REG_DWORD(&reg->hccr) & HCCRX_RISC_PAUSE) == 0 &&
+ for (cnt = 30000;
+ ((RD_REG_DWORD(&reg->host_status) & HSRX_RISC_PAUSED) == 0) &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
@@ -351,7 +349,7 @@ static inline void *
qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
{
uint32_t cnt, que_idx;
- uint8_t req_cnt, rsp_cnt, que_cnt;
+ uint8_t que_cnt;
struct qla2xxx_mq_chain *mq = ptr;
struct device_reg_25xxmq __iomem *reg;
@@ -363,9 +361,8 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
mq->type = __constant_htonl(DUMP_CHAIN_MQ);
mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain));
- req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
- rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
- que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt;
+ que_cnt = ha->max_req_queues > ha->max_rsp_queues ?
+ ha->max_req_queues : ha->max_rsp_queues;
mq->count = htonl(que_cnt);
for (cnt = 0; cnt < que_cnt; cnt++) {
reg = (struct device_reg_25xxmq *) ((void *)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 714ee67567e1..00aa48d975a6 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -93,6 +93,7 @@
#define LSD(x) ((uint32_t)((uint64_t)(x)))
#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
+#define MAKE_HANDLE(x, y) ((uint32_t)((((uint32_t)(x)) << 16) | (uint32_t)(y)))
/*
* I/O register
@@ -179,6 +180,7 @@
#define REQUEST_ENTRY_CNT_24XX 2048 /* Number of request entries. */
#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
+#define RESPONSE_ENTRY_CNT_MQ 128 /* Number of response entries.*/
struct req_que;
@@ -186,7 +188,6 @@ struct req_que;
* SCSI Request Block
*/
typedef struct srb {
- struct req_que *que;
struct fc_port *fcport;
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
@@ -2008,7 +2009,7 @@ typedef struct vport_params {
#define VP_RET_CODE_NOT_FOUND 6
struct qla_hw_data;
-
+struct rsp_que;
/*
* ISP operations
*/
@@ -2030,10 +2031,9 @@ struct isp_operations {
void (*enable_intrs) (struct qla_hw_data *);
void (*disable_intrs) (struct qla_hw_data *);
- int (*abort_command) (struct scsi_qla_host *, srb_t *,
- struct req_que *);
- int (*target_reset) (struct fc_port *, unsigned int);
- int (*lun_reset) (struct fc_port *, unsigned int);
+ int (*abort_command) (srb_t *);
+ int (*target_reset) (struct fc_port *, unsigned int, int);
+ int (*lun_reset) (struct fc_port *, unsigned int, int);
int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t,
uint8_t, uint8_t, uint16_t *, uint8_t);
int (*fabric_logout) (struct scsi_qla_host *, uint16_t, uint8_t,
@@ -2079,7 +2079,6 @@ struct isp_operations {
#define QLA_PCI_MSIX_CONTROL 0xa2
struct scsi_qla_host;
-struct rsp_que;
struct qla_msix_entry {
int have_irq;
@@ -2140,7 +2139,6 @@ struct qla_statistics {
#define MBC_INITIALIZE_MULTIQ 0x1f
#define QLA_QUE_PAGE 0X1000
#define QLA_MQ_SIZE 32
-#define QLA_MAX_HOST_QUES 16
#define QLA_MAX_QUEUES 256
#define ISP_QUE_REG(ha, id) \
((ha->mqenable) ? \
@@ -2170,6 +2168,8 @@ struct rsp_que {
struct qla_hw_data *hw;
struct qla_msix_entry *msix;
struct req_que *req;
+ srb_t *status_srb; /* status continuation entry */
+ struct work_struct q_work;
};
/* Request queue data structure */
@@ -2222,6 +2222,8 @@ struct qla_hw_data {
uint32_t fce_enabled :1;
uint32_t fac_supported :1;
uint32_t chip_reset_done :1;
+ uint32_t port0 :1;
+ uint32_t running_gold_fw :1;
} flags;
/* This spinlock is used to protect "io transactions", you must
@@ -2246,7 +2248,8 @@ struct qla_hw_data {
struct rsp_que **rsp_q_map;
unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
- uint16_t max_queues;
+ uint8_t max_req_queues;
+ uint8_t max_rsp_queues;
struct qla_npiv_entry *npiv_info;
uint16_t nvram_npiv_size;
@@ -2255,6 +2258,9 @@ struct qla_hw_data {
#define FLOGI_MID_SUPPORT BIT_10
#define FLOGI_VSAN_SUPPORT BIT_12
#define FLOGI_SP_SUPPORT BIT_13
+
+ uint8_t port_no; /* Physical port of adapter */
+
/* Timeout timers. */
uint8_t loop_down_abort_time; /* port down timer */
atomic_t loop_down_timer; /* loop down timer */
@@ -2392,6 +2398,14 @@ struct qla_hw_data {
dma_addr_t edc_data_dma;
uint16_t edc_data_len;
+#define XGMAC_DATA_SIZE PAGE_SIZE
+ void *xgmac_data;
+ dma_addr_t xgmac_data_dma;
+
+#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+ void *dcbx_tlv;
+ dma_addr_t dcbx_tlv_dma;
+
struct task_struct *dpc_thread;
uint8_t dpc_active; /* DPC routine is active */
@@ -2510,6 +2524,7 @@ struct qla_hw_data {
uint32_t flt_region_vpd;
uint32_t flt_region_nvram;
uint32_t flt_region_npiv_conf;
+ uint32_t flt_region_gold_fw;
/* Needed for BEACON */
uint16_t beacon_blink_led;
@@ -2536,6 +2551,7 @@ struct qla_hw_data {
struct qla_chip_state_84xx *cs84xx;
struct qla_statistics qla_stats;
struct isp_operations *isp_ops;
+ struct workqueue_struct *wq;
};
/*
@@ -2545,6 +2561,8 @@ typedef struct scsi_qla_host {
struct list_head list;
struct list_head vp_fcports; /* list of fcports */
struct list_head work_list;
+ spinlock_t work_lock;
+
/* Commonly used flags and state information. */
struct Scsi_Host *host;
unsigned long host_no;
@@ -2591,8 +2609,6 @@ typedef struct scsi_qla_host {
#define SWITCH_FOUND BIT_0
#define DFLG_NO_CABLE BIT_1
- srb_t *status_srb; /* Status continuation entry. */
-
/* ISP configuration data. */
uint16_t loop_id; /* Host adapter loop id */
@@ -2618,6 +2634,11 @@ typedef struct scsi_qla_host {
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
uint8_t fabric_node_name[WWN_SIZE];
+
+ uint16_t fcoe_vlan_id;
+ uint16_t fcoe_fcf_idx;
+ uint8_t fcoe_vn_port_mac[6];
+
uint32_t vp_abort_cnt;
struct fc_vport *fc_vport; /* holds fc_vport * for each vport */
@@ -2643,7 +2664,7 @@ typedef struct scsi_qla_host {
#define VP_ERR_FAB_LOGOUT 4
#define VP_ERR_ADAP_NORESOURCES 5
struct qla_hw_data *hw;
- int req_ques[QLA_MAX_HOST_QUES];
+ struct req_que *req;
} scsi_qla_host_t;
/*
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index 96ccb9642ba0..dfde2dd865cb 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -878,7 +878,6 @@ struct device_reg_24xx {
/* HCCR statuses. */
#define HCCRX_HOST_INT BIT_6 /* Host to RISC interrupt bit. */
#define HCCRX_RISC_RESET BIT_5 /* RISC Reset mode bit. */
-#define HCCRX_RISC_PAUSE BIT_4 /* RISC Pause mode bit. */
/* HCCR commands. */
/* NOOP. */
#define HCCRX_NOOP 0x00000000
@@ -1241,6 +1240,7 @@ struct qla_flt_header {
#define FLT_REG_HW_EVENT_1 0x1f
#define FLT_REG_NPIV_CONF_0 0x29
#define FLT_REG_NPIV_CONF_1 0x2a
+#define FLT_REG_GOLD_FW 0x2f
struct qla_flt_region {
uint32_t code;
@@ -1405,6 +1405,8 @@ struct access_chip_rsp_84xx {
#define MBC_IDC_ACK 0x101
#define MBC_RESTART_MPI_FW 0x3d
#define MBC_FLASH_ACCESS_CTRL 0x3e /* Control flash access. */
+#define MBC_GET_XGMAC_STATS 0x7a
+#define MBC_GET_DCBX_PARAMS 0x51
/* Flash access control option field bit definitions */
#define FAC_OPT_FORCE_SEMAPHORE BIT_15
@@ -1711,7 +1713,7 @@ struct ex_init_cb_81xx {
#define FA_VPD0_ADDR_81 0xD0000
#define FA_VPD1_ADDR_81 0xD0400
#define FA_NVRAM0_ADDR_81 0xD0080
-#define FA_NVRAM1_ADDR_81 0xD0480
+#define FA_NVRAM1_ADDR_81 0xD0180
#define FA_FEATURE_ADDR_81 0xD4000
#define FA_FLASH_DESCR_ADDR_81 0xD8000
#define FA_FLASH_LAYOUT_ADDR_81 0xD8400
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 528913f6bed9..65b12d82867c 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -65,8 +65,11 @@ extern int ql2xfdmienable;
extern int ql2xallocfwdump;
extern int ql2xextended_error_logging;
extern int ql2xqfullrampup;
+extern int ql2xqfulltracking;
extern int ql2xiidmaenable;
extern int ql2xmaxqueues;
+extern int ql2xmultique_tag;
+extern int ql2xfwloadbin;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -145,7 +148,7 @@ qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
extern int
qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
-extern void
+extern int
qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
uint16_t *, uint32_t *, uint8_t *, uint32_t *, uint8_t *);
@@ -165,13 +168,13 @@ extern int
qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
extern int
-qla2x00_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
+qla2x00_abort_command(srb_t *);
extern int
-qla2x00_abort_target(struct fc_port *, unsigned int);
+qla2x00_abort_target(struct fc_port *, unsigned int, int);
extern int
-qla2x00_lun_reset(struct fc_port *, unsigned int);
+qla2x00_lun_reset(struct fc_port *, unsigned int, int);
extern int
qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
@@ -236,9 +239,11 @@ extern int
qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *,
dma_addr_t);
-extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
-extern int qla24xx_abort_target(struct fc_port *, unsigned int);
-extern int qla24xx_lun_reset(struct fc_port *, unsigned int);
+extern int qla24xx_abort_command(srb_t *);
+extern int
+qla24xx_abort_target(struct fc_port *, unsigned int, int);
+extern int
+qla24xx_lun_reset(struct fc_port *, unsigned int, int);
extern int
qla2x00_system_error(scsi_qla_host_t *);
@@ -288,6 +293,18 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *, int);
extern int
qla81xx_fac_erase_sector(scsi_qla_host_t *, uint32_t, uint32_t);
+extern int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t *);
+
+extern int
+qla2x00_get_dcbx_params(scsi_qla_host_t *, dma_addr_t, uint16_t);
+
+extern int
+qla2x00_read_ram_word(scsi_qla_host_t *, uint32_t, uint32_t *);
+
+extern int
+qla2x00_write_ram_word(scsi_qla_host_t *, uint32_t, uint32_t);
+
/*
* Global Function Prototypes in qla_isr.c source file.
*/
@@ -295,8 +312,8 @@ extern irqreturn_t qla2100_intr_handler(int, void *);
extern irqreturn_t qla2300_intr_handler(int, void *);
extern irqreturn_t qla24xx_intr_handler(int, void *);
extern void qla2x00_process_response_queue(struct rsp_que *);
-extern void qla24xx_process_response_queue(struct rsp_que *);
-
+extern void
+qla24xx_process_response_queue(struct scsi_qla_host *, struct rsp_que *);
extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
extern void qla2x00_free_irqs(scsi_qla_host_t *);
@@ -401,19 +418,21 @@ extern int qla25xx_request_irq(struct rsp_que *);
extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *);
extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *);
extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t,
- uint16_t, uint8_t, uint8_t);
+ uint16_t, int, uint8_t);
extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t,
- uint16_t);
+ uint16_t, int);
extern int qla25xx_update_req_que(struct scsi_qla_host *, uint8_t, uint8_t);
extern void qla2x00_init_response_q_entries(struct rsp_que *);
extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
extern int qla25xx_delete_rsp_que(struct scsi_qla_host *, struct rsp_que *);
extern int qla25xx_create_queues(struct scsi_qla_host *, uint8_t);
-extern int qla25xx_delete_queues(struct scsi_qla_host *, uint8_t);
+extern int qla25xx_delete_queues(struct scsi_qla_host *);
extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern struct scsi_qla_host * qla25xx_get_host(struct rsp_que *);
+
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 557f58d5bf88..917534b9f221 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -1107,7 +1107,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
return ret;
ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
- mb, BIT_1);
+ mb, BIT_1|BIT_0);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: "
"loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n",
@@ -1879,6 +1879,9 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
case BIT_13:
list[i].fp_speed = PORT_SPEED_4GB;
break;
+ case BIT_12:
+ list[i].fp_speed = PORT_SPEED_10GB;
+ break;
case BIT_11:
list[i].fp_speed = PORT_SPEED_8GB;
break;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index bd7dd84c0648..262026129325 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -634,7 +634,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha)
goto chip_diag_failed;
DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n",
- ha->host_no));
+ vha->host_no));
/* Reset RISC processor. */
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
@@ -655,7 +655,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha)
goto chip_diag_failed;
/* Check product ID of chip */
- DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", ha->host_no));
+ DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", vha->host_no));
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
@@ -730,9 +730,6 @@ qla24xx_chip_diag(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
struct req_que *req = ha->req_q_map[0];
- /* Perform RISC reset. */
- qla24xx_reset_risc(vha);
-
ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length;
rval = qla2x00_mbx_reg_test(vha);
@@ -786,7 +783,6 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
sizeof(uint32_t);
if (ha->mqenable)
mq_size = sizeof(struct qla2xxx_mq_chain);
-
/* Allocate memory for Fibre Channel Event Buffer. */
if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))
goto try_eft;
@@ -850,8 +846,7 @@ cont_alloc:
rsp_q_size = rsp->length * sizeof(response_t);
dump_size = offsetof(struct qla2xxx_fw_dump, isp);
- dump_size += fixed_size + mem_size + req_q_size + rsp_q_size +
- eft_size;
+ dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size;
ha->chain_offset = dump_size;
dump_size += mq_size + fce_size;
@@ -891,6 +886,56 @@ cont_alloc:
htonl(offsetof(struct qla2xxx_fw_dump, isp));
}
+static int
+qla81xx_mpi_sync(scsi_qla_host_t *vha)
+{
+#define MPS_MASK 0xe0
+ int rval;
+ uint16_t dc;
+ uint32_t dw;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_SUCCESS;
+
+ rval = qla2x00_write_ram_word(vha, 0x7c00, 1);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Sync-MPI: Unable to acquire semaphore.\n"));
+ goto done;
+ }
+
+ pci_read_config_word(vha->hw->pdev, 0x54, &dc);
+ rval = qla2x00_read_ram_word(vha, 0x7a15, &dw);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Sync-MPI: Unable to read sync.\n"));
+ goto done_release;
+ }
+
+ dc &= MPS_MASK;
+ if (dc == (dw & MPS_MASK))
+ goto done_release;
+
+ dw &= ~MPS_MASK;
+ dw |= dc;
+ rval = qla2x00_write_ram_word(vha, 0x7a15, dw);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Sync-MPI: Unable to gain sync.\n"));
+ }
+
+done_release:
+ rval = qla2x00_write_ram_word(vha, 0x7c00, 0);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Sync-MPI: Unable to release semaphore.\n"));
+ }
+
+done:
+ return rval;
+}
+
/**
* qla2x00_setup_chip() - Load and start RISC firmware.
* @ha: HA context
@@ -915,6 +960,8 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+ qla81xx_mpi_sync(vha);
+
/* Load firmware sequences */
rval = ha->isp_ops->load_risc(vha, &srisc_address);
if (rval == QLA_SUCCESS) {
@@ -931,13 +978,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
/* Retrieve firmware information. */
if (rval == QLA_SUCCESS) {
fw_major_version = ha->fw_major_version;
- qla2x00_get_fw_version(vha,
+ rval = qla2x00_get_fw_version(vha,
&ha->fw_major_version,
&ha->fw_minor_version,
&ha->fw_subminor_version,
&ha->fw_attributes, &ha->fw_memory_size,
ha->mpi_version, &ha->mpi_capabilities,
ha->phy_version);
+ if (rval != QLA_SUCCESS)
+ goto failed;
+
ha->flags.npiv_supported = 0;
if (IS_QLA2XXX_MIDTYPE(ha) &&
(ha->fw_attributes & BIT_2)) {
@@ -989,7 +1039,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
ha->fw_subminor_version);
}
}
-
+failed:
if (rval) {
DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
vha->host_no));
@@ -1013,12 +1063,14 @@ qla2x00_init_response_q_entries(struct rsp_que *rsp)
uint16_t cnt;
response_t *pkt;
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
+ rsp->status_srb = NULL;
pkt = rsp->ring_ptr;
for (cnt = 0; cnt < rsp->length; cnt++) {
pkt->signature = RESPONSE_PROCESSED;
pkt++;
}
-
}
/**
@@ -1176,7 +1228,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
if (ha->flags.msix_enabled) {
msix = &ha->msix_entries[1];
DEBUG2_17(printk(KERN_INFO
- "Reistering vector 0x%x for base que\n", msix->entry));
+ "Registering vector 0x%x for base que\n", msix->entry));
icb->msix = cpu_to_le16(msix->entry);
}
/* Use alternate PCI bus number */
@@ -1230,14 +1282,14 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Clear outstanding commands array. */
- for (que = 0; que < ha->max_queues; que++) {
+ for (que = 0; que < ha->max_req_queues; que++) {
req = ha->req_q_map[que];
if (!req)
continue;
- for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
req->outstanding_cmds[cnt] = NULL;
- req->current_outstanding_cmd = 0;
+ req->current_outstanding_cmd = 1;
/* Initialize firmware. */
req->ring_ptr = req->ring;
@@ -1245,13 +1297,10 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
req->cnt = req->length;
}
- for (que = 0; que < ha->max_queues; que++) {
+ for (que = 0; que < ha->max_rsp_queues; que++) {
rsp = ha->rsp_q_map[que];
if (!rsp)
continue;
- rsp->ring_ptr = rsp->ring;
- rsp->ring_index = 0;
-
/* Initialize response queue entries */
qla2x00_init_response_q_entries(rsp);
}
@@ -1307,7 +1356,7 @@ qla2x00_fw_ready(scsi_qla_host_t *vha)
unsigned long wtime, mtime, cs84xx_time;
uint16_t min_wait; /* Minimum wait time if loop is down */
uint16_t wait_time; /* Wait time if loop is coming ready */
- uint16_t state[3];
+ uint16_t state[5];
struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
@@ -1406,8 +1455,9 @@ qla2x00_fw_ready(scsi_qla_host_t *vha)
vha->host_no, state[0], jiffies));
} while (1);
- DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
- vha->host_no, state[0], jiffies));
+ DEBUG(printk("scsi(%ld): fw_state=%x (%x, %x, %x, %x) curr time=%lx.\n",
+ vha->host_no, state[0], state[1], state[2], state[3], state[4],
+ jiffies));
if (rval) {
DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
@@ -1541,6 +1591,7 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
char *st, *en;
uint16_t index;
struct qla_hw_data *ha = vha->hw;
+ int use_tbl = !IS_QLA25XX(ha) && !IS_QLA81XX(ha);
if (memcmp(model, BINZERO, len) != 0) {
strncpy(ha->model_number, model, len);
@@ -1553,14 +1604,16 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
}
index = (ha->pdev->subsystem_device & 0xff);
- if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+ if (use_tbl &&
+ ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
index < QLA_MODEL_NAMES)
strncpy(ha->model_desc,
qla2x00_model_name[index * 2 + 1],
sizeof(ha->model_desc) - 1);
} else {
index = (ha->pdev->subsystem_device & 0xff);
- if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+ if (use_tbl &&
+ ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
index < QLA_MODEL_NAMES) {
strcpy(ha->model_number,
qla2x00_model_name[index * 2]);
@@ -2061,8 +2114,10 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
- if (test_bit(RSCN_UPDATE, &save_flags))
+ if (test_bit(RSCN_UPDATE, &save_flags)) {
set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ vha->flags.rscn_queue_overflow = 1;
+ }
}
return (rval);
@@ -2110,7 +2165,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
goto cleanup_allocation;
DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n",
- ha->host_no, entries));
+ vha->host_no, entries));
DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list,
entries * sizeof(struct gid_list_info)));
@@ -2243,7 +2298,8 @@ static void
qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
#define LS_UNKNOWN 2
- static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
+ static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
+ char *link_speed;
int rval;
uint16_t mb[6];
struct qla_hw_data *ha = vha->hw;
@@ -2266,10 +2322,15 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
fcport->port_name[6], fcport->port_name[7], rval,
fcport->fp_speed, mb[0], mb[1]));
} else {
+ link_speed = link_speeds[LS_UNKNOWN];
+ if (fcport->fp_speed < 5)
+ link_speed = link_speeds[fcport->fp_speed];
+ else if (fcport->fp_speed == 0x13)
+ link_speed = link_speeds[5];
DEBUG2(qla_printk(KERN_INFO, ha,
"iIDMA adjusted to %s GB/s on "
"%02x%02x%02x%02x%02x%02x%02x%02x.\n",
- link_speeds[fcport->fp_speed], fcport->port_name[0],
+ link_speed, fcport->port_name[0],
fcport->port_name[1], fcport->port_name[2],
fcport->port_name[3], fcport->port_name[4],
fcport->port_name[5], fcport->port_name[6],
@@ -3180,9 +3241,14 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
uint32_t wait_time;
- struct qla_hw_data *ha = vha->hw;
- struct req_que *req = ha->req_q_map[vha->req_ques[0]];
- struct rsp_que *rsp = req->rsp;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ if (ql2xmultique_tag)
+ req = vha->hw->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
atomic_set(&vha->loop_state, LOOP_UPDATE);
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
@@ -3448,7 +3514,7 @@ qla25xx_init_queues(struct qla_hw_data *ha)
int ret = -1;
int i;
- for (i = 1; i < ha->max_queues; i++) {
+ for (i = 1; i < ha->max_rsp_queues; i++) {
rsp = ha->rsp_q_map[i];
if (rsp) {
rsp->options &= ~BIT_0;
@@ -3462,6 +3528,8 @@ qla25xx_init_queues(struct qla_hw_data *ha)
"%s Rsp que:%d inited\n", __func__,
rsp->id));
}
+ }
+ for (i = 1; i < ha->max_req_queues; i++) {
req = ha->req_q_map[i];
if (req) {
/* Clear outstanding commands array. */
@@ -3566,14 +3634,15 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
nv = ha->nvram;
/* Determine NVRAM starting address. */
- ha->nvram_size = sizeof(struct nvram_24xx);
- ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
- ha->vpd_size = FA_NVRAM_VPD_SIZE;
- ha->vpd_base = FA_NVRAM_VPD0_ADDR;
- if (PCI_FUNC(ha->pdev->devfn)) {
+ if (ha->flags.port0) {
+ ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
+ ha->vpd_base = FA_NVRAM_VPD0_ADDR;
+ } else {
ha->nvram_base = FA_NVRAM_FUNC1_ADDR;
ha->vpd_base = FA_NVRAM_VPD1_ADDR;
}
+ ha->nvram_size = sizeof(struct nvram_24xx);
+ ha->vpd_size = FA_NVRAM_VPD_SIZE;
/* Get VPD data into cache */
ha->vpd = ha->nvram + VPD_OFFSET;
@@ -3587,7 +3656,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
chksum += le32_to_cpu(*dptr++);
- DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
/* Bad NVRAM data, set defaults parameters. */
@@ -3612,7 +3681,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
nv->exchange_count = __constant_cpu_to_le16(0);
nv->hard_address = __constant_cpu_to_le16(124);
nv->port_name[0] = 0x21;
- nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn);
+ nv->port_name[1] = 0x00 + ha->port_no;
nv->port_name[2] = 0x00;
nv->port_name[3] = 0xe0;
nv->port_name[4] = 0x8b;
@@ -3798,11 +3867,11 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
}
static int
-qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
+ uint32_t faddr)
{
int rval = QLA_SUCCESS;
int segments, fragment;
- uint32_t faddr;
uint32_t *dcode, dlen;
uint32_t risc_addr;
uint32_t risc_size;
@@ -3811,12 +3880,11 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
struct req_que *req = ha->req_q_map[0];
qla_printk(KERN_INFO, ha,
- "FW: Loading from flash (%x)...\n", ha->flt_region_fw);
+ "FW: Loading from flash (%x)...\n", faddr);
rval = QLA_SUCCESS;
segments = FA_RISC_CODE_SEGMENTS;
- faddr = ha->flt_region_fw;
dcode = (uint32_t *)req->ring;
*srisc_addr = 0;
@@ -4104,6 +4172,9 @@ qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
+ if (ql2xfwloadbin == 1)
+ return qla81xx_load_risc(vha, srisc_addr);
+
/*
* FW Load priority:
* 1) Firmware via request-firmware interface (.bin file).
@@ -4113,24 +4184,45 @@ qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
if (rval == QLA_SUCCESS)
return rval;
- return qla24xx_load_risc_flash(vha, srisc_addr);
+ return qla24xx_load_risc_flash(vha, srisc_addr,
+ vha->hw->flt_region_fw);
}
int
qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ql2xfwloadbin == 2)
+ goto try_blob_fw;
/*
* FW Load priority:
* 1) Firmware residing in flash.
* 2) Firmware via request-firmware interface (.bin file).
+ * 3) Golden-Firmware residing in flash -- limited operation.
*/
- rval = qla24xx_load_risc_flash(vha, srisc_addr);
+ rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_fw);
if (rval == QLA_SUCCESS)
return rval;
- return qla24xx_load_risc_blob(vha, srisc_addr);
+try_blob_fw:
+ rval = qla24xx_load_risc_blob(vha, srisc_addr);
+ if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw)
+ return rval;
+
+ qla_printk(KERN_ERR, ha,
+ "FW: Attempting to fallback to golden firmware...\n");
+ rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_gold_fw);
+ if (rval != QLA_SUCCESS)
+ return rval;
+
+ qla_printk(KERN_ERR, ha,
+ "FW: Please update operational firmware...\n");
+ ha->flags.running_gold_fw = 1;
+
+ return rval;
}
void
@@ -4146,7 +4238,7 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
ret = qla2x00_stop_firmware(vha);
for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
- retries ; retries--) {
+ ret != QLA_INVALID_COMMAND && retries ; retries--) {
ha->isp_ops->reset_chip(vha);
if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS)
continue;
@@ -4165,13 +4257,19 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
uint16_t mb[MAILBOX_REGISTER_COUNT];
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
- struct req_que *req = ha->req_q_map[vha->req_ques[0]];
- struct rsp_que *rsp = req->rsp;
+ struct req_que *req;
+ struct rsp_que *rsp;
if (!vha->vp_idx)
return -EINVAL;
rval = qla2x00_fw_ready(base_vha);
+ if (ql2xmultique_tag)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+
if (rval == QLA_SUCCESS) {
clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
@@ -4305,7 +4403,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
chksum += le32_to_cpu(*dptr++);
- DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
/* Bad NVRAM data, set defaults parameters. */
@@ -4329,7 +4427,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
nv->execution_throttle = __constant_cpu_to_le16(0xFFFF);
nv->exchange_count = __constant_cpu_to_le16(0);
nv->port_name[0] = 0x21;
- nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn);
+ nv->port_name[1] = 0x00 + ha->port_no;
nv->port_name[2] = 0x00;
nv->port_name[3] = 0xe0;
nv->port_name[4] = 0x8b;
@@ -4358,12 +4456,12 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
nv->max_luns_per_target = __constant_cpu_to_le16(128);
nv->port_down_retry_count = __constant_cpu_to_le16(30);
nv->link_down_timeout = __constant_cpu_to_le16(30);
- nv->enode_mac[0] = 0x01;
+ nv->enode_mac[0] = 0x00;
nv->enode_mac[1] = 0x02;
nv->enode_mac[2] = 0x03;
nv->enode_mac[3] = 0x04;
nv->enode_mac[4] = 0x05;
- nv->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn);
+ nv->enode_mac[5] = 0x06 + ha->port_no;
rval = 1;
}
@@ -4396,7 +4494,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
icb->enode_mac[2] = 0x03;
icb->enode_mac[3] = 0x04;
icb->enode_mac[4] = 0x05;
- icb->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn);
+ icb->enode_mac[5] = 0x06 + ha->port_no;
}
/* Use extended-initialization control block. */
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index a8abbb95730d..13396beae2ce 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -15,6 +15,7 @@ static request_t *qla2x00_req_pkt(struct scsi_qla_host *, struct req_que *,
struct rsp_que *rsp);
static void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *);
+static void qla25xx_set_que(srb_t *, struct rsp_que **);
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
* @cmd: SCSI command
@@ -92,9 +93,10 @@ qla2x00_calc_iocbs_64(uint16_t dsds)
* Returns a pointer to the Continuation Type 0 IOCB packet.
*/
static inline cont_entry_t *
-qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
+qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
{
cont_entry_t *cont_pkt;
+ struct req_que *req = vha->req;
/* Adjust ring index. */
req->ring_index++;
if (req->ring_index == req->length) {
@@ -120,10 +122,11 @@ qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
* Returns a pointer to the continuation type 1 IOCB packet.
*/
static inline cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(struct req_que *req, scsi_qla_host_t *vha)
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha)
{
cont_a64_entry_t *cont_pkt;
+ struct req_que *req = vha->req;
/* Adjust ring index. */
req->ring_index++;
if (req->ring_index == req->length) {
@@ -159,7 +162,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
- struct req_que *req;
cmd = sp->cmd;
@@ -174,8 +176,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
}
vha = sp->fcport->vha;
- req = sp->que;
-
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
/* Three DSDs are available in the Command Type 2 IOCB */
@@ -192,7 +192,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
* Seven DSDs are available in the Continuation
* Type 0 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type0_iocb(req, vha);
+ cont_pkt = qla2x00_prep_cont_type0_iocb(vha);
cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
avail_dsds = 7;
}
@@ -220,7 +220,6 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
- struct req_que *req;
cmd = sp->cmd;
@@ -235,8 +234,6 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
}
vha = sp->fcport->vha;
- req = sp->que;
-
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
/* Two DSDs are available in the Command Type 3 IOCB */
@@ -254,7 +251,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -353,7 +350,6 @@ qla2x00_start_scsi(srb_t *sp)
/* Build command packet */
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
- sp->que = req;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
@@ -453,6 +449,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
mrk24->lun[2] = MSB(lun);
host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
mrk24->vp_index = vha->vp_idx;
+ mrk24->handle = MAKE_HANDLE(req->id, mrk24->handle);
} else {
SET_TARGET_ID(ha, mrk->target, loop_id);
mrk->lun = cpu_to_le16(lun);
@@ -531,9 +528,6 @@ qla2x00_req_pkt(struct scsi_qla_host *vha, struct req_que *req,
for (cnt = 0; cnt < REQUEST_ENTRY_SIZE / 4; cnt++)
*dword_ptr++ = 0;
- /* Set system defined field. */
- pkt->sys_define = (uint8_t)req->ring_index;
-
/* Set entry count. */
pkt->entry_count = 1;
@@ -656,7 +650,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
}
vha = sp->fcport->vha;
- req = sp->que;
+ req = vha->req;
/* Set transfer direction */
if (cmd->sc_data_direction == DMA_TO_DEVICE) {
@@ -687,7 +681,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -724,19 +718,13 @@ qla24xx_start_scsi(srb_t *sp)
struct scsi_cmnd *cmd = sp->cmd;
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
- uint16_t que_id;
/* Setup device pointers. */
ret = 0;
- que_id = vha->req_ques[0];
- req = ha->req_q_map[que_id];
- sp->que = req;
+ qla25xx_set_que(sp, &rsp);
+ req = vha->req;
- if (req->rsp)
- rsp = req->rsp;
- else
- rsp = ha->rsp_q_map[que_id];
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
@@ -794,7 +782,7 @@ qla24xx_start_scsi(srb_t *sp)
req->cnt -= req_cnt;
cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
- cmd_pkt->handle = handle;
+ cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
/* Zero out remaining portion of packet. */
/* tagged queuing modifier -- default is TSK_SIMPLE (0). */
@@ -823,6 +811,8 @@ qla24xx_start_scsi(srb_t *sp)
/* Set total data segment count. */
cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Specify response queue number where completion should happen */
+ cmd_pkt->entry_status = (uint8_t) rsp->id;
wmb();
/* Adjust ring index. */
@@ -842,7 +832,7 @@ qla24xx_start_scsi(srb_t *sp)
/* Manage unprocessed RIO/ZIO commands in response queue. */
if (vha->flags.process_response_queue &&
rsp->ring_ptr->signature != RESPONSE_PROCESSED)
- qla24xx_process_response_queue(rsp);
+ qla24xx_process_response_queue(vha, rsp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
@@ -855,3 +845,16 @@ queuing_error:
return QLA_FUNCTION_FAILED;
}
+
+static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp)
+{
+ struct scsi_cmnd *cmd = sp->cmd;
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ int affinity = cmd->request->cpu;
+
+ if (ql2xmultique_tag && affinity >= 0 &&
+ affinity < ha->max_rsp_queues - 1)
+ *rsp = ha->rsp_q_map[affinity + 1];
+ else
+ *rsp = ha->rsp_q_map[0];
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index d04981848e56..c8d0a176fea4 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -13,10 +13,9 @@ static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
static void qla2x00_process_completed_request(struct scsi_qla_host *,
struct req_que *, uint32_t);
static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
-static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
+static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
sts_entry_t *);
-static struct scsi_qla_host *qla2x00_get_rsp_host(struct rsp_que *);
/**
* qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
@@ -51,7 +50,7 @@ qla2100_intr_handler(int irq, void *dev_id)
status = 0;
spin_lock(&ha->hardware_lock);
- vha = qla2x00_get_rsp_host(rsp);
+ vha = pci_get_drvdata(ha->pdev);
for (iter = 50; iter--; ) {
hccr = RD_REG_WORD(&reg->hccr);
if (hccr & HCCR_RISC_PAUSE) {
@@ -147,7 +146,7 @@ qla2300_intr_handler(int irq, void *dev_id)
status = 0;
spin_lock(&ha->hardware_lock);
- vha = qla2x00_get_rsp_host(rsp);
+ vha = pci_get_drvdata(ha->pdev);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_PAUSED) {
@@ -685,7 +684,7 @@ skip_rio:
vha->host_no));
if (IS_FWI2_CAPABLE(ha))
- qla24xx_process_response_queue(rsp);
+ qla24xx_process_response_queue(vha, rsp);
else
qla2x00_process_response_queue(rsp);
break;
@@ -766,7 +765,10 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
struct qla_hw_data *ha = vha->hw;
struct req_que *req = NULL;
- req = ha->req_q_map[vha->req_ques[0]];
+ if (!ql2xqfulltracking)
+ return;
+
+ req = vha->req;
if (!req)
return;
if (req->max_q_depth <= sdev->queue_depth)
@@ -808,6 +810,9 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *vha, struct req_que *req,
fc_port_t *fcport;
struct scsi_device *sdev;
+ if (!ql2xqfulltracking)
+ return;
+
sdev = sp->cmd->device;
if (sdev->queue_depth >= req->max_q_depth)
return;
@@ -858,8 +863,8 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
qla2x00_ramp_up_queue_depth(vha, req, sp);
qla2x00_sp_compl(ha, sp);
} else {
- DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
- vha->host_no));
+ DEBUG2(printk("scsi(%ld) Req:%d: Invalid ISP SCSI completion"
+ " handle(%d)\n", vha->host_no, req->id, index));
qla_printk(KERN_WARNING, ha,
"Invalid ISP SCSI completion handle\n");
@@ -881,7 +886,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
uint16_t handle_cnt;
uint16_t cnt;
- vha = qla2x00_get_rsp_host(rsp);
+ vha = pci_get_drvdata(ha->pdev);
if (!vha->flags.online)
return;
@@ -926,7 +931,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
}
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
break;
default:
/* Type Not Supported. */
@@ -945,7 +950,8 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
}
static inline void
-qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
+qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len,
+ struct rsp_que *rsp)
{
struct scsi_cmnd *cp = sp->cmd;
@@ -962,7 +968,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
sp->request_sense_ptr += sense_len;
sp->request_sense_length -= sense_len;
if (sp->request_sense_length != 0)
- sp->fcport->vha->status_srb = sp;
+ rsp->status_srb = sp;
DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) "
"cmd=%p pid=%ld\n", __func__, sp->fcport->vha->host_no,
@@ -992,7 +998,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len;
uint8_t *rsp_info, *sense_data;
struct qla_hw_data *ha = vha->hw;
- struct req_que *req = rsp->req;
+ uint32_t handle;
+ uint16_t que;
+ struct req_que *req;
sts = (sts_entry_t *) pkt;
sts24 = (struct sts_entry_24xx *) pkt;
@@ -1003,18 +1011,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
comp_status = le16_to_cpu(sts->comp_status);
scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
}
-
+ handle = (uint32_t) LSW(sts->handle);
+ que = MSW(sts->handle);
+ req = ha->req_q_map[que];
/* Fast path completion. */
if (comp_status == CS_COMPLETE && scsi_status == 0) {
- qla2x00_process_completed_request(vha, req, sts->handle);
+ qla2x00_process_completed_request(vha, req, handle);
return;
}
/* Validate handle. */
- if (sts->handle < MAX_OUTSTANDING_COMMANDS) {
- sp = req->outstanding_cmds[sts->handle];
- req->outstanding_cmds[sts->handle] = NULL;
+ if (handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = req->outstanding_cmds[handle];
+ req->outstanding_cmds[handle] = NULL;
} else
sp = NULL;
@@ -1030,7 +1040,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
cp = sp->cmd;
if (cp == NULL) {
DEBUG2(printk("scsi(%ld): Command already returned back to OS "
- "pkt->handle=%d sp=%p.\n", vha->host_no, sts->handle, sp));
+ "pkt->handle=%d sp=%p.\n", vha->host_no, handle, sp));
qla_printk(KERN_WARNING, ha,
"Command is NULL: already returned to OS (sp=%p)\n", sp);
@@ -1121,6 +1131,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
scsi_status));
/* Adjust queue depth for all luns on the port. */
+ if (!ql2xqfulltracking)
+ break;
fcport->last_queue_full = jiffies;
starget_for_each_device(cp->device->sdev_target,
fcport, qla2x00_adjust_sdev_qdepth_down);
@@ -1133,7 +1145,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
if (!(scsi_status & SS_SENSE_LEN_VALID))
break;
- qla2x00_handle_sense(sp, sense_data, sense_len);
+ qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
break;
case CS_DATA_UNDERRUN:
@@ -1179,6 +1191,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
* Adjust queue depth for all luns on the
* port.
*/
+ if (!ql2xqfulltracking)
+ break;
fcport->last_queue_full = jiffies;
starget_for_each_device(
cp->device->sdev_target, fcport,
@@ -1192,12 +1206,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
if (!(scsi_status & SS_SENSE_LEN_VALID))
break;
- qla2x00_handle_sense(sp, sense_data, sense_len);
+ qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
} else {
/*
* If RISC reports underrun and target does not report
* it then we must have a lost frame, so tell upper
- * layer to retry it by reporting a bus busy.
+ * layer to retry it by reporting an error.
*/
if (!(scsi_status & SS_RESIDUAL_UNDER)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
@@ -1207,7 +1221,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
cp->device->id, cp->device->lun, resid,
scsi_bufflen(cp)));
- cp->result = DID_BUS_BUSY << 16;
+ cp->result = DID_ERROR << 16;
break;
}
@@ -1334,7 +1348,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
}
/* Place command on done queue. */
- if (vha->status_srb == NULL)
+ if (rsp->status_srb == NULL)
qla2x00_sp_compl(ha, sp);
}
@@ -1346,11 +1360,11 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
* Extended sense data.
*/
static void
-qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
{
uint8_t sense_sz = 0;
- struct qla_hw_data *ha = vha->hw;
- srb_t *sp = vha->status_srb;
+ struct qla_hw_data *ha = rsp->hw;
+ srb_t *sp = rsp->status_srb;
struct scsi_cmnd *cp;
if (sp != NULL && sp->request_sense_length != 0) {
@@ -1362,7 +1376,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
"cmd is NULL: already returned to OS (sp=%p)\n",
sp);
- vha->status_srb = NULL;
+ rsp->status_srb = NULL;
return;
}
@@ -1383,7 +1397,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
/* Place command on done queue. */
if (sp->request_sense_length == 0) {
- vha->status_srb = NULL;
+ rsp->status_srb = NULL;
qla2x00_sp_compl(ha, sp);
}
}
@@ -1399,7 +1413,9 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
{
srb_t *sp;
struct qla_hw_data *ha = vha->hw;
- struct req_que *req = rsp->req;
+ uint32_t handle = LSW(pkt->handle);
+ uint16_t que = MSW(pkt->handle);
+ struct req_que *req = ha->req_q_map[que];
#if defined(QL_DEBUG_LEVEL_2)
if (pkt->entry_status & RF_INV_E_ORDER)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
@@ -1417,14 +1433,14 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
#endif
/* Validate handle. */
- if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
- sp = req->outstanding_cmds[pkt->handle];
+ if (handle < MAX_OUTSTANDING_COMMANDS)
+ sp = req->outstanding_cmds[handle];
else
sp = NULL;
if (sp) {
/* Free outstanding command slot. */
- req->outstanding_cmds[pkt->handle] = NULL;
+ req->outstanding_cmds[handle] = NULL;
/* Bad payload or header */
if (pkt->entry_status &
@@ -1486,13 +1502,10 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
* qla24xx_process_response_queue() - Process response queue entries.
* @ha: SCSI driver HA context
*/
-void
-qla24xx_process_response_queue(struct rsp_que *rsp)
+void qla24xx_process_response_queue(struct scsi_qla_host *vha,
+ struct rsp_que *rsp)
{
struct sts_entry_24xx *pkt;
- struct scsi_qla_host *vha;
-
- vha = qla2x00_get_rsp_host(rsp);
if (!vha->flags.online)
return;
@@ -1523,7 +1536,7 @@ qla24xx_process_response_queue(struct rsp_que *rsp)
qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
break;
case VP_RPT_ID_IOCB_TYPE:
qla24xx_report_id_acquisition(vha,
@@ -1626,7 +1639,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
status = 0;
spin_lock(&ha->hardware_lock);
- vha = qla2x00_get_rsp_host(rsp);
+ vha = pci_get_drvdata(ha->pdev);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1664,7 +1677,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
break;
case 0x13:
case 0x14:
- qla24xx_process_response_queue(rsp);
+ qla24xx_process_response_queue(vha, rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
@@ -1692,6 +1705,7 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
struct qla_hw_data *ha;
struct rsp_que *rsp;
struct device_reg_24xx __iomem *reg;
+ struct scsi_qla_host *vha;
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
@@ -1704,7 +1718,8 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
spin_lock_irq(&ha->hardware_lock);
- qla24xx_process_response_queue(rsp);
+ vha = qla25xx_get_host(rsp);
+ qla24xx_process_response_queue(vha, rsp);
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
spin_unlock_irq(&ha->hardware_lock);
@@ -1717,7 +1732,6 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
{
struct qla_hw_data *ha;
struct rsp_que *rsp;
- struct device_reg_24xx __iomem *reg;
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
@@ -1726,13 +1740,8 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
return IRQ_NONE;
}
ha = rsp->hw;
- reg = &ha->iobase->isp24;
- spin_lock_irq(&ha->hardware_lock);
-
- qla24xx_process_response_queue(rsp);
-
- spin_unlock_irq(&ha->hardware_lock);
+ queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work);
return IRQ_HANDLED;
}
@@ -1760,7 +1769,7 @@ qla24xx_msix_default(int irq, void *dev_id)
status = 0;
spin_lock_irq(&ha->hardware_lock);
- vha = qla2x00_get_rsp_host(rsp);
+ vha = pci_get_drvdata(ha->pdev);
do {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1798,7 +1807,7 @@ qla24xx_msix_default(int irq, void *dev_id)
break;
case 0x13:
case 0x14:
- qla24xx_process_response_queue(rsp);
+ qla24xx_process_response_queue(vha, rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
@@ -1822,31 +1831,14 @@ qla24xx_msix_default(int irq, void *dev_id)
/* Interrupt handling helpers. */
struct qla_init_msix_entry {
- uint16_t entry;
- uint16_t index;
const char *name;
irq_handler_t handler;
};
-static struct qla_init_msix_entry base_queue = {
- .entry = 0,
- .index = 0,
- .name = "qla2xxx (default)",
- .handler = qla24xx_msix_default,
-};
-
-static struct qla_init_msix_entry base_rsp_queue = {
- .entry = 1,
- .index = 1,
- .name = "qla2xxx (rsp_q)",
- .handler = qla24xx_msix_rsp_q,
-};
-
-static struct qla_init_msix_entry multi_rsp_queue = {
- .entry = 1,
- .index = 1,
- .name = "qla2xxx (multi_q)",
- .handler = qla25xx_msix_rsp_q,
+static struct qla_init_msix_entry msix_entries[3] = {
+ { "qla2xxx (default)", qla24xx_msix_default },
+ { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q },
+ { "qla2xxx (multiq)", qla25xx_msix_rsp_q },
};
static void
@@ -1873,7 +1865,6 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
int i, ret;
struct msix_entry *entries;
struct qla_msix_entry *qentry;
- struct qla_init_msix_entry *msix_queue;
entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
GFP_KERNEL);
@@ -1900,7 +1891,7 @@ msix_failed:
ha->msix_count, ret);
goto msix_out;
}
- ha->max_queues = ha->msix_count - 1;
+ ha->max_rsp_queues = ha->msix_count - 1;
}
ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
ha->msix_count, GFP_KERNEL);
@@ -1918,45 +1909,27 @@ msix_failed:
qentry->rsp = NULL;
}
- /* Enable MSI-X for AENs for queue 0 */
- qentry = &ha->msix_entries[0];
- ret = request_irq(qentry->vector, base_queue.handler, 0,
- base_queue.name, rsp);
- if (ret) {
- qla_printk(KERN_WARNING, ha,
+ /* Enable MSI-X vectors for the base queue */
+ for (i = 0; i < 2; i++) {
+ qentry = &ha->msix_entries[i];
+ ret = request_irq(qentry->vector, msix_entries[i].handler,
+ 0, msix_entries[i].name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
"MSI-X: Unable to register handler -- %x/%d.\n",
qentry->vector, ret);
- qla24xx_disable_msix(ha);
- goto msix_out;
+ qla24xx_disable_msix(ha);
+ ha->mqenable = 0;
+ goto msix_out;
+ }
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
+ rsp->msix = qentry;
}
- qentry->have_irq = 1;
- qentry->rsp = rsp;
/* Enable MSI-X vector for response queue update for queue 0 */
- if (ha->max_queues > 1 && ha->mqiobase) {
+ if (ha->mqiobase && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
ha->mqenable = 1;
- msix_queue = &multi_rsp_queue;
- qla_printk(KERN_INFO, ha,
- "MQ enabled, Number of Queue Resources: %d \n",
- ha->max_queues);
- } else {
- ha->mqenable = 0;
- msix_queue = &base_rsp_queue;
- }
-
- qentry = &ha->msix_entries[1];
- ret = request_irq(qentry->vector, msix_queue->handler, 0,
- msix_queue->name, rsp);
- if (ret) {
- qla_printk(KERN_WARNING, ha,
- "MSI-X: Unable to register handler -- %x/%d.\n",
- qentry->vector, ret);
- qla24xx_disable_msix(ha);
- ha->mqenable = 0;
- goto msix_out;
- }
- qentry->have_irq = 1;
- qentry->rsp = rsp;
msix_out:
kfree(entries);
@@ -2063,35 +2036,11 @@ qla2x00_free_irqs(scsi_qla_host_t *vha)
}
}
-static struct scsi_qla_host *
-qla2x00_get_rsp_host(struct rsp_que *rsp)
-{
- srb_t *sp;
- struct qla_hw_data *ha = rsp->hw;
- struct scsi_qla_host *vha = NULL;
- struct sts_entry_24xx *pkt;
- struct req_que *req;
-
- if (rsp->id) {
- pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
- req = rsp->req;
- if (pkt && pkt->handle < MAX_OUTSTANDING_COMMANDS) {
- sp = req->outstanding_cmds[pkt->handle];
- if (sp)
- vha = sp->fcport->vha;
- }
- }
- if (!vha)
- /* handle it in base queue */
- vha = pci_get_drvdata(ha->pdev);
-
- return vha;
-}
int qla25xx_request_irq(struct rsp_que *rsp)
{
struct qla_hw_data *ha = rsp->hw;
- struct qla_init_msix_entry *intr = &multi_rsp_queue;
+ struct qla_init_msix_entry *intr = &msix_entries[2];
struct qla_msix_entry *msix = rsp->msix;
int ret;
@@ -2106,3 +2055,30 @@ int qla25xx_request_irq(struct rsp_que *rsp)
msix->rsp = rsp;
return ret;
}
+
+struct scsi_qla_host *
+qla25xx_get_host(struct rsp_que *rsp)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = rsp->hw;
+ struct scsi_qla_host *vha = NULL;
+ struct sts_entry_24xx *pkt;
+ struct req_que *req;
+ uint16_t que;
+ uint32_t handle;
+
+ pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
+ que = MSW(pkt->handle);
+ handle = (uint32_t) LSW(pkt->handle);
+ req = ha->req_q_map[que];
+ if (handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = req->outstanding_cmds[handle];
+ if (sp)
+ return sp->fcport->vha;
+ else
+ goto base_que;
+ }
+base_que:
+ vha = pci_get_drvdata(ha->pdev);
+ return vha;
+}
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index e67c1660bf46..451ece0760b0 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -408,7 +408,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
* Context:
* Kernel context.
*/
-void
+int
qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
uint16_t *subminor, uint16_t *attributes, uint32_t *memory, uint8_t *mpi,
uint32_t *mpi_caps, uint8_t *phy)
@@ -427,6 +427,8 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
mcp->flags = 0;
mcp->tov = MBX_TOV_SECONDS;
rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ goto failed;
/* Return mailbox data. */
*major = mcp->mb[1];
@@ -446,7 +448,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
phy[1] = mcp->mb[9] >> 8;
phy[2] = mcp->mb[9] & 0xff;
}
-
+failed:
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
@@ -455,6 +457,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
/*EMPTY*/
DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
+ return rval;
}
/*
@@ -748,20 +751,20 @@ qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr,
* Kernel context.
*/
int
-qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
+qla2x00_abort_command(srb_t *sp)
{
unsigned long flags = 0;
- fc_port_t *fcport;
int rval;
uint32_t handle = 0;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ fc_port_t *fcport = sp->fcport;
+ scsi_qla_host_t *vha = fcport->vha;
struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = vha->req;
DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no));
- fcport = sp->fcport;
-
spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
if (req->outstanding_cmds[handle] == sp)
@@ -800,7 +803,7 @@ qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
}
int
-qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
+qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
{
int rval, rval2;
mbx_cmd_t mc;
@@ -813,8 +816,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
l = l;
vha = fcport->vha;
- req = vha->hw->req_q_map[0];
- rsp = vha->hw->rsp_q_map[0];
+ req = vha->hw->req_q_map[tag];
+ rsp = vha->hw->rsp_q_map[tag];
mcp->mb[0] = MBC_ABORT_TARGET;
mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(vha->hw)) {
@@ -850,7 +853,7 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
}
int
-qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
+qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
{
int rval, rval2;
mbx_cmd_t mc;
@@ -862,8 +865,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
vha = fcport->vha;
- req = vha->hw->req_q_map[0];
- rsp = vha->hw->rsp_q_map[0];
+ req = vha->hw->req_q_map[tag];
+ rsp = vha->hw->rsp_q_map[tag];
mcp->mb[0] = MBC_LUN_RESET;
mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(vha->hw))
@@ -931,6 +934,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_0;
mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw))
+ mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -952,9 +957,19 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
vha->host_no, rval));
} else {
- /*EMPTY*/
DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
vha->host_no));
+
+ if (IS_QLA81XX(vha->hw)) {
+ vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
+ vha->fcoe_fcf_idx = mcp->mb[10];
+ vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8;
+ vha->fcoe_vn_port_mac[4] = mcp->mb[11] & 0xff;
+ vha->fcoe_vn_port_mac[3] = mcp->mb[12] >> 8;
+ vha->fcoe_vn_port_mac[2] = mcp->mb[12] & 0xff;
+ vha->fcoe_vn_port_mac[1] = mcp->mb[13] >> 8;
+ vha->fcoe_vn_port_mac[0] = mcp->mb[13] & 0xff;
+ }
}
return rval;
@@ -1252,7 +1267,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
- mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -1261,6 +1276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
states[0] = mcp->mb[1];
states[1] = mcp->mb[2];
states[2] = mcp->mb[3];
+ states[3] = mcp->mb[4];
+ states[4] = mcp->mb[5];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
@@ -1480,9 +1497,17 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
dma_addr_t lg_dma;
uint32_t iop[2];
struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ struct rsp_que *rsp;
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+ if (ql2xmultique_tag)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+
lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
if (lg == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n",
@@ -1493,6 +1518,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
lg->entry_count = 1;
+ lg->handle = MAKE_HANDLE(req->id, lg->handle);
lg->nport_handle = cpu_to_le16(loop_id);
lg->control_flags = __constant_cpu_to_le16(LCF_COMMAND_PLOGI);
if (opt & BIT_0)
@@ -1741,6 +1767,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ struct rsp_que *rsp;
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
@@ -1752,8 +1780,14 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
}
memset(lg, 0, sizeof(struct logio_entry_24xx));
+ if (ql2xmaxqueues > 1)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
lg->entry_count = 1;
+ lg->handle = MAKE_HANDLE(req->id, lg->handle);
lg->nport_handle = cpu_to_le16(loop_id);
lg->control_flags =
__constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
@@ -1864,9 +1898,6 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (IS_QLA81XX(vha->hw))
- return QLA_SUCCESS;
-
DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
vha->host_no));
@@ -2195,21 +2226,21 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
}
int
-qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
+qla24xx_abort_command(srb_t *sp)
{
int rval;
- fc_port_t *fcport;
unsigned long flags = 0;
struct abort_entry_24xx *abt;
dma_addr_t abt_dma;
uint32_t handle;
+ fc_port_t *fcport = sp->fcport;
+ struct scsi_qla_host *vha = fcport->vha;
struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = vha->req;
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- fcport = sp->fcport;
-
spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
if (req->outstanding_cmds[handle] == sp)
@@ -2231,6 +2262,7 @@ qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
abt->entry_type = ABORT_IOCB_TYPE;
abt->entry_count = 1;
+ abt->handle = MAKE_HANDLE(req->id, abt->handle);
abt->nport_handle = cpu_to_le16(fcport->loop_id);
abt->handle_to_abort = handle;
abt->port_id[0] = fcport->d_id.b.al_pa;
@@ -2272,7 +2304,7 @@ struct tsk_mgmt_cmd {
static int
__qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
- unsigned int l)
+ unsigned int l, int tag)
{
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
@@ -2286,8 +2318,11 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
vha = fcport->vha;
ha = vha->hw;
- req = ha->req_q_map[0];
- rsp = ha->rsp_q_map[0];
+ req = vha->req;
+ if (ql2xmultique_tag)
+ rsp = ha->rsp_q_map[tag + 1];
+ else
+ rsp = req->rsp;
tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
if (tsk == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
@@ -2298,6 +2333,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE;
tsk->p.tsk.entry_count = 1;
+ tsk->p.tsk.handle = MAKE_HANDLE(req->id, tsk->p.tsk.handle);
tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id);
tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
tsk->p.tsk.control_flags = cpu_to_le32(type);
@@ -2344,15 +2380,15 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
}
int
-qla24xx_abort_target(struct fc_port *fcport, unsigned int l)
+qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag)
{
- return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l);
+ return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l, tag);
}
int
-qla24xx_lun_reset(struct fc_port *fcport, unsigned int l)
+qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
{
- return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l);
+ return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l, tag);
}
int
@@ -2446,6 +2482,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
vha->host_no, rval));
+ if (mcp->mb[0] == MBS_INVALID_COMMAND)
+ rval = QLA_INVALID_COMMAND;
} else {
DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
@@ -2717,8 +2755,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (vp_idx == 0)
return;
- if (MSB(stat) == 1)
+ if (MSB(stat) == 1) {
+ DEBUG2(printk("scsi(%ld): Could not acquire ID for "
+ "VP[%d].\n", vha->host_no, vp_idx));
return;
+ }
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list)
if (vp_idx == vp->vp_idx)
@@ -3141,6 +3182,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
WRT_REG_DWORD(&reg->req_q_in, 0);
WRT_REG_DWORD(&reg->req_q_out, 0);
}
+ req->req_q_in = &reg->req_q_in;
+ req->req_q_out = &reg->req_q_out;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
rval = qla2x00_mailbox_command(vha, mcp);
@@ -3167,7 +3210,6 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
mcp->mb[6] = MSW(MSD(rsp->dma));
mcp->mb[7] = LSW(MSD(rsp->dma));
mcp->mb[5] = rsp->length;
- mcp->mb[11] = rsp->vp_idx;
mcp->mb[14] = rsp->msix->entry;
mcp->mb[13] = rsp->rid;
@@ -3179,7 +3221,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
mcp->mb[8] = 0;
/* que out ptr index */
mcp->mb[9] = 0;
- mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7
+ mcp->out_mb = MBX_14|MBX_13|MBX_9|MBX_8|MBX_7
|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->flags = MBX_DMA_OUT;
@@ -3384,7 +3426,7 @@ qla2x00_read_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -3428,3 +3470,141 @@ qla2x00_write_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
return rval;
}
+
+int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
+ uint16_t size_in_bytes, uint16_t *actual_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+ mcp->mb[0] = MBC_GET_XGMAC_STATS;
+ mcp->mb[2] = MSW(stats_dma);
+ mcp->mb[3] = LSW(stats_dma);
+ mcp->mb[6] = MSW(MSD(stats_dma));
+ mcp->mb[7] = LSW(MSD(stats_dma));
+ mcp->mb[8] = size_in_bytes >> 2;
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
+ "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+ } else {
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+
+ *actual_size = mcp->mb[2] << 2;
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
+ uint16_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+ mcp->mb[0] = MBC_GET_DCBX_PARAMS;
+ mcp->mb[1] = 0;
+ mcp->mb[2] = MSW(tlv_dma);
+ mcp->mb[3] = LSW(tlv_dma);
+ mcp->mb[6] = MSW(MSD(tlv_dma));
+ mcp->mb[7] = LSW(MSD(tlv_dma));
+ mcp->mb[8] = size;
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
+ "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+ } else {
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+ }
+
+ return rval;
+}
+
+int
+qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+ mcp->mb[0] = MBC_READ_RAM_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
+ vha->host_no, rval, mcp->mb[0]));
+ } else {
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+ *data = mcp->mb[3] << 16 | mcp->mb[2];
+ }
+
+ return rval;
+}
+
+int
+qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+ mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[2] = LSW(data);
+ mcp->mb[3] = MSW(data);
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
+ vha->host_no, rval, mcp->mb[0]));
+ } else {
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+ }
+
+ return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 51716c7e3008..650bcef08f2a 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -398,9 +398,8 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
- memset(vha->req_ques, 0, sizeof(vha->req_ques));
- vha->req_ques[0] = ha->req_q_map[0]->id;
- host->can_queue = ha->req_q_map[0]->length + 128;
+ vha->req = base_vha->req;
+ host->can_queue = base_vha->req->length + 128;
host->this_id = 255;
host->cmd_per_lun = 3;
host->max_cmd_len = MAX_CMDSZ;
@@ -515,76 +514,53 @@ int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos)
/* Delete all queues for a given vhost */
int
-qla25xx_delete_queues(struct scsi_qla_host *vha, uint8_t que_no)
+qla25xx_delete_queues(struct scsi_qla_host *vha)
{
int cnt, ret = 0;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
struct qla_hw_data *ha = vha->hw;
- if (que_no) {
- /* Delete request queue */
- req = ha->req_q_map[que_no];
+ /* Delete request queues */
+ for (cnt = 1; cnt < ha->max_req_queues; cnt++) {
+ req = ha->req_q_map[cnt];
if (req) {
- rsp = req->rsp;
ret = qla25xx_delete_req_que(vha, req);
if (ret != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
- "Couldn't delete req que %d\n", req->id);
+ "Couldn't delete req que %d\n",
+ req->id);
return ret;
}
- /* Delete associated response queue */
- if (rsp) {
- ret = qla25xx_delete_rsp_que(vha, rsp);
- if (ret != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Couldn't delete rsp que %d\n",
- rsp->id);
- return ret;
- }
- }
}
- } else { /* delete all queues of this host */
- for (cnt = 0; cnt < QLA_MAX_HOST_QUES; cnt++) {
- /* Delete request queues */
- req = ha->req_q_map[vha->req_ques[cnt]];
- if (req && req->id) {
- rsp = req->rsp;
- ret = qla25xx_delete_req_que(vha, req);
- if (ret != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Couldn't delete req que %d\n",
- vha->req_ques[cnt]);
- return ret;
- }
- vha->req_ques[cnt] = ha->req_q_map[0]->id;
- /* Delete associated response queue */
- if (rsp && rsp->id) {
- ret = qla25xx_delete_rsp_que(vha, rsp);
- if (ret != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Couldn't delete rsp que %d\n",
- rsp->id);
- return ret;
- }
- }
+ }
+
+ /* Delete response queues */
+ for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ if (rsp) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete rsp que %d\n",
+ rsp->id);
+ return ret;
}
}
}
- qla_printk(KERN_INFO, ha, "Queues deleted for vport:%d\n",
- vha->vp_idx);
return ret;
}
int
qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
- uint8_t vp_idx, uint16_t rid, uint8_t rsp_que, uint8_t qos)
+ uint8_t vp_idx, uint16_t rid, int rsp_que, uint8_t qos)
{
int ret = 0;
struct req_que *req = NULL;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
uint16_t que_id = 0;
device_reg_t __iomem *reg;
+ uint32_t cnt;
req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
if (req == NULL) {
@@ -604,8 +580,8 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
}
mutex_lock(&ha->vport_lock);
- que_id = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
- if (que_id >= ha->max_queues) {
+ que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues);
+ if (que_id >= ha->max_req_queues) {
mutex_unlock(&ha->vport_lock);
qla_printk(KERN_INFO, ha, "No resources to create "
"additional request queue\n");
@@ -617,10 +593,10 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
req->vp_idx = vp_idx;
req->qos = qos;
- if (ha->rsp_q_map[rsp_que]) {
+ if (rsp_que < 0)
+ req->rsp = NULL;
+ else
req->rsp = ha->rsp_q_map[rsp_que];
- req->rsp->req = req;
- }
/* Use alternate PCI bus number */
if (MSB(req->rid))
options |= BIT_4;
@@ -628,13 +604,16 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
if (LSB(req->rid))
options |= BIT_5;
req->options = options;
+
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+ req->outstanding_cmds[cnt] = NULL;
+ req->current_outstanding_cmd = 1;
+
req->ring_ptr = req->ring;
req->ring_index = 0;
req->cnt = req->length;
req->id = que_id;
reg = ISP_QUE_REG(ha, que_id);
- req->req_q_in = &reg->isp25mq.req_q_in;
- req->req_q_out = &reg->isp25mq.req_q_out;
req->max_q_depth = ha->req_q_map[0]->max_q_depth;
mutex_unlock(&ha->vport_lock);
@@ -654,10 +633,19 @@ que_failed:
return 0;
}
+static void qla_do_work(struct work_struct *work)
+{
+ struct rsp_que *rsp = container_of(work, struct rsp_que, q_work);
+ struct scsi_qla_host *vha;
+
+ vha = qla25xx_get_host(rsp);
+ qla24xx_process_response_queue(vha, rsp);
+}
+
/* create response queue */
int
qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
- uint8_t vp_idx, uint16_t rid)
+ uint8_t vp_idx, uint16_t rid, int req)
{
int ret = 0;
struct rsp_que *rsp = NULL;
@@ -672,7 +660,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
goto que_failed;
}
- rsp->length = RESPONSE_ENTRY_CNT_2300;
+ rsp->length = RESPONSE_ENTRY_CNT_MQ;
rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
(rsp->length + 1) * sizeof(response_t),
&rsp->dma, GFP_KERNEL);
@@ -683,8 +671,8 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
}
mutex_lock(&ha->vport_lock);
- que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
- if (que_id >= ha->max_queues) {
+ que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
+ if (que_id >= ha->max_rsp_queues) {
mutex_unlock(&ha->vport_lock);
qla_printk(KERN_INFO, ha, "No resources to create "
"additional response queue\n");
@@ -708,8 +696,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
if (LSB(rsp->rid))
options |= BIT_5;
rsp->options = options;
- rsp->ring_ptr = rsp->ring;
- rsp->ring_index = 0;
rsp->id = que_id;
reg = ISP_QUE_REG(ha, que_id);
rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
@@ -728,9 +714,14 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
mutex_unlock(&ha->vport_lock);
goto que_failed;
}
+ if (req >= 0)
+ rsp->req = ha->req_q_map[req];
+ else
+ rsp->req = NULL;
qla2x00_init_response_q_entries(rsp);
-
+ if (rsp->hw->wq)
+ INIT_WORK(&rsp->q_work, qla_do_work);
return rsp->id;
que_failed:
@@ -744,14 +735,16 @@ qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
uint16_t options = 0;
uint8_t ret = 0;
struct qla_hw_data *ha = vha->hw;
+ struct rsp_que *rsp;
options |= BIT_1;
- ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0);
+ ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0, -1);
if (!ret) {
qla_printk(KERN_WARNING, ha, "Response Que create failed\n");
return ret;
} else
qla_printk(KERN_INFO, ha, "Response Que:%d created.\n", ret);
+ rsp = ha->rsp_q_map[ret];
options = 0;
if (qos & BIT_7)
@@ -759,10 +752,11 @@ qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, ret,
qos & ~BIT_7);
if (ret) {
- vha->req_ques[0] = ret;
+ vha->req = ha->req_q_map[ret];
qla_printk(KERN_INFO, ha, "Request Que:%d created.\n", ret);
} else
qla_printk(KERN_WARNING, ha, "Request Que create failed\n");
+ rsp->req = ha->req_q_map[ret];
return ret;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index e4fdcdad80d0..dcf011679c8b 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -77,6 +77,14 @@ module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(ql2xmaxqdepth,
"Maximum queue depth to report for target devices.");
+int ql2xqfulltracking = 1;
+module_param(ql2xqfulltracking, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xqfulltracking,
+ "Controls whether the driver tracks queue full status "
+ "returns and dynamically adjusts a scsi device's queue "
+ "depth. Default is 1, perform tracking. Set to 0 to "
+ "disable dynamic tracking and adjustment of queue depth.");
+
int ql2xqfullrampup = 120;
module_param(ql2xqfullrampup, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(ql2xqfullrampup,
@@ -96,6 +104,23 @@ MODULE_PARM_DESC(ql2xmaxqueues,
"Enables MQ settings "
"Default is 1 for single queue. Set it to number \
of queues in MQ mode.");
+
+int ql2xmultique_tag;
+module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xmultique_tag,
+ "Enables CPU affinity settings for the driver "
+ "Default is 0 for no affinity of request and response IO. "
+ "Set it to 1 to turn on the cpu affinity.");
+
+int ql2xfwloadbin;
+module_param(ql2xfwloadbin, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xfwloadbin,
+ "Option to specify location from which to load ISP firmware:\n"
+ " 2 -- load firmware via the request_firmware() (hotplug)\n"
+ " interface.\n"
+ " 1 -- load firmware from flash.\n"
+ " 0 -- use default semantics.\n");
+
/*
* SCSI host template entry points
*/
@@ -187,7 +212,7 @@ static void qla2x00_sp_free_dma(srb_t *);
/* -------------------------------------------------------------------------- */
static int qla2x00_alloc_queues(struct qla_hw_data *ha)
{
- ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues,
+ ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
GFP_KERNEL);
if (!ha->req_q_map) {
qla_printk(KERN_WARNING, ha,
@@ -195,7 +220,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha)
goto fail_req_map;
}
- ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues,
+ ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
GFP_KERNEL);
if (!ha->rsp_q_map) {
qla_printk(KERN_WARNING, ha,
@@ -213,16 +238,8 @@ fail_req_map:
return -ENOMEM;
}
-static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
- struct rsp_que *rsp)
+static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
{
- if (rsp && rsp->ring)
- dma_free_coherent(&ha->pdev->dev,
- (rsp->length + 1) * sizeof(response_t),
- rsp->ring, rsp->dma);
-
- kfree(rsp);
- rsp = NULL;
if (req && req->ring)
dma_free_coherent(&ha->pdev->dev,
(req->length + 1) * sizeof(request_t),
@@ -232,22 +249,77 @@ static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
req = NULL;
}
+static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
+{
+ if (rsp && rsp->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ rsp->ring, rsp->dma);
+
+ kfree(rsp);
+ rsp = NULL;
+}
+
static void qla2x00_free_queues(struct qla_hw_data *ha)
{
struct req_que *req;
struct rsp_que *rsp;
int cnt;
- for (cnt = 0; cnt < ha->max_queues; cnt++) {
- rsp = ha->rsp_q_map[cnt];
+ for (cnt = 0; cnt < ha->max_req_queues; cnt++) {
req = ha->req_q_map[cnt];
- qla2x00_free_que(ha, req, rsp);
+ qla2x00_free_req_que(ha, req);
+ }
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+
+ for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ qla2x00_free_rsp_que(ha, rsp);
}
kfree(ha->rsp_q_map);
ha->rsp_q_map = NULL;
+}
- kfree(ha->req_q_map);
- ha->req_q_map = NULL;
+static int qla25xx_setup_mode(struct scsi_qla_host *vha)
+{
+ uint16_t options = 0;
+ int ques, req, ret;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ql2xmultique_tag) {
+ /* CPU affinity mode */
+ ha->wq = create_workqueue("qla2xxx_wq");
+ /* create a request queue for IO */
+ options |= BIT_7;
+ req = qla25xx_create_req_que(ha, options, 0, 0, -1,
+ QLA_DEFAULT_QUE_QOS);
+ if (!req) {
+ qla_printk(KERN_WARNING, ha,
+ "Can't create request queue\n");
+ goto fail;
+ }
+ vha->req = ha->req_q_map[req];
+ options |= BIT_1;
+ for (ques = 1; ques < ha->max_rsp_queues; ques++) {
+ ret = qla25xx_create_rsp_que(ha, options, 0, 0, req);
+ if (!ret) {
+ qla_printk(KERN_WARNING, ha,
+ "Response Queue create failed\n");
+ goto fail2;
+ }
+ }
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "CPU affinity mode enabled, no. of response"
+ " queues:%d, no. of request queues:%d\n",
+ ha->max_rsp_queues, ha->max_req_queues));
+ }
+ return 0;
+fail2:
+ qla25xx_delete_queues(vha);
+fail:
+ ha->mqenable = 0;
+ return 1;
}
static char *
@@ -387,7 +459,6 @@ qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
sp->fcport = fcport;
sp->cmd = cmd;
- sp->que = ha->req_q_map[0];
sp->flags = 0;
CMD_SP(cmd) = (void *)sp;
cmd->scsi_done = done;
@@ -612,7 +683,7 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
void
qla2x00_abort_fcport_cmds(fc_port_t *fcport)
{
- int cnt, que, id;
+ int cnt;
unsigned long flags;
srb_t *sp;
scsi_qla_host_t *vha = fcport->vha;
@@ -620,32 +691,27 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
struct req_que *req;
spin_lock_irqsave(&ha->hardware_lock, flags);
- for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
- id = vha->req_ques[que];
- req = ha->req_q_map[id];
- if (!req)
+ req = vha->req;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (!sp)
+ continue;
+ if (sp->fcport != fcport)
continue;
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = req->outstanding_cmds[cnt];
- if (!sp)
- continue;
- if (sp->fcport != fcport)
- continue;
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(vha, sp, req)) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(sp)) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Abort failed -- %lx\n",
+ sp->cmd->serial_number));
+ } else {
+ if (qla2x00_eh_wait_on_command(sp->cmd) !=
+ QLA_SUCCESS)
DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed -- %lx\n",
+ "Abort failed while waiting -- %lx\n",
sp->cmd->serial_number));
- } else {
- if (qla2x00_eh_wait_on_command(sp->cmd) !=
- QLA_SUCCESS)
- DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed while waiting -- %lx\n",
- sp->cmd->serial_number));
- }
- spin_lock_irqsave(&ha->hardware_lock, flags);
}
+ spin_lock_irqsave(&ha->hardware_lock, flags);
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
@@ -693,7 +759,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
unsigned long flags;
int wait = 0;
struct qla_hw_data *ha = vha->hw;
- struct req_que *req;
+ struct req_que *req = vha->req;
srb_t *spt;
qla2x00_block_error_handler(cmd);
@@ -709,7 +775,6 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
spt = (srb_t *) CMD_SP(cmd);
if (!spt)
return SUCCESS;
- req = spt->que;
/* Check active list for command command. */
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -726,7 +791,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
" pid=%ld.\n", __func__, vha->host_no, sp, serial));
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(vha, sp, req)) {
+ if (ha->isp_ops->abort_command(sp)) {
DEBUG2(printk("%s(%ld): abort_command "
"mbx failed.\n", __func__, vha->host_no));
ret = FAILED;
@@ -777,7 +842,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
return status;
spin_lock_irqsave(&ha->hardware_lock, flags);
- req = sp->que;
+ req = vha->req;
for (cnt = 1; status == QLA_SUCCESS &&
cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
sp = req->outstanding_cmds[cnt];
@@ -820,7 +885,7 @@ static char *reset_errors[] = {
static int
__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
- struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
+ struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int))
{
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
@@ -841,7 +906,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
goto eh_reset_failed;
err = 2;
- if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
+ if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1)
+ != QLA_SUCCESS)
goto eh_reset_failed;
err = 3;
if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
@@ -996,6 +1062,9 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
if (qla2x00_vp_abort_isp(vha))
goto eh_host_reset_lock;
} else {
+ if (ha->wq)
+ flush_workqueue(ha->wq);
+
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
if (qla2x00_abort_isp(base_vha)) {
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
@@ -1037,7 +1106,8 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
struct fc_port *fcport;
struct qla_hw_data *ha = vha->hw;
- if (ha->flags.enable_lip_full_login && !vha->vp_idx) {
+ if (ha->flags.enable_lip_full_login && !vha->vp_idx &&
+ !IS_QLA81XX(ha)) {
ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): failed: "
@@ -1064,7 +1134,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
if (fcport->port_type != FCT_TARGET)
continue;
- ret = ha->isp_ops->target_reset(fcport, 0);
+ ret = ha->isp_ops->target_reset(fcport, 0, 0);
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): bus_reset failed: "
"target_reset=%d d_id=%x.\n", __func__,
@@ -1088,7 +1158,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
struct req_que *req;
spin_lock_irqsave(&ha->hardware_lock, flags);
- for (que = 0; que < ha->max_queues; que++) {
+ for (que = 0; que < ha->max_req_queues; que++) {
req = ha->req_q_map[que];
if (!req)
continue;
@@ -1123,7 +1193,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
scsi_qla_host_t *vha = shost_priv(sdev->host);
struct qla_hw_data *ha = vha->hw;
struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
- struct req_que *req = ha->req_q_map[vha->req_ques[0]];
+ struct req_que *req = vha->req;
if (sdev->tagged_supported)
scsi_activate_tcq(sdev, req->max_q_depth);
@@ -1511,6 +1581,13 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
break;
}
+
+ /* Get adapter physical port no from interrupt pin register. */
+ pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
+ if (ha->port_no & 1)
+ ha->flags.port0 = 1;
+ else
+ ha->flags.port0 = 0;
}
static int
@@ -1518,6 +1595,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
{
resource_size_t pio;
uint16_t msix;
+ int cpus;
if (pci_request_selected_regions(ha->pdev, ha->bars,
QLA2XXX_DRIVER_NAME)) {
@@ -1571,8 +1649,9 @@ skip_pio:
}
/* Determine queue resources */
- ha->max_queues = 1;
- if (ql2xmaxqueues <= 1 || (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ if ((ql2xmaxqueues <= 1 || ql2xmultique_tag < 1) &&
+ (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
goto mqiobase_exit;
ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
pci_resource_len(ha->pdev, 3));
@@ -1582,18 +1661,24 @@ skip_pio:
ha->msix_count = msix;
/* Max queues are bounded by available msix vectors */
/* queue 0 uses two msix vectors */
- if (ha->msix_count - 1 < ql2xmaxqueues)
- ha->max_queues = ha->msix_count - 1;
- else if (ql2xmaxqueues > QLA_MQ_SIZE)
- ha->max_queues = QLA_MQ_SIZE;
- else
- ha->max_queues = ql2xmaxqueues;
+ if (ql2xmultique_tag) {
+ cpus = num_online_cpus();
+ ha->max_rsp_queues = (ha->msix_count - 1 - cpus) ?
+ (cpus + 1) : (ha->msix_count - 1);
+ ha->max_req_queues = 2;
+ } else if (ql2xmaxqueues > 1) {
+ ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
+ QLA_MQ_SIZE : ql2xmaxqueues;
+ DEBUG2(qla_printk(KERN_INFO, ha, "QoS mode set, max no"
+ " of request queues:%d\n", ha->max_req_queues));
+ }
qla_printk(KERN_INFO, ha,
"MSI-X vector count: %d\n", msix);
- }
+ } else
+ qla_printk(KERN_INFO, ha, "BAR 3 not enabled\n");
mqiobase_exit:
- ha->msix_count = ha->max_queues + 1;
+ ha->msix_count = ha->max_rsp_queues + 1;
return (0);
iospace_error_exit:
@@ -1605,6 +1690,9 @@ qla2xxx_scan_start(struct Scsi_Host *shost)
{
scsi_qla_host_t *vha = shost_priv(shost);
+ if (vha->hw->flags.running_gold_fw)
+ return;
+
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
set_bit(RSCN_UPDATE, &vha->dpc_flags);
@@ -1768,6 +1856,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
ha->gid_list_info_size = 8;
ha->optrom_size = OPTROM_SIZE_81XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
ha->isp_ops = &qla81xx_isp_ops;
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
@@ -1803,14 +1892,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ret = -ENOMEM;
qla2x00_mem_free(ha);
- qla2x00_free_que(ha, req, rsp);
+ qla2x00_free_req_que(ha, req);
+ qla2x00_free_rsp_que(ha, rsp);
goto probe_hw_failed;
}
pci_set_drvdata(pdev, base_vha);
host = base_vha->host;
- base_vha->req_ques[0] = req->id;
+ base_vha->req = req;
host->can_queue = req->length + 128;
if (IS_QLA2XXX_MIDTYPE(ha))
base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
@@ -1841,7 +1931,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
ha->rsp_q_map[0] = rsp;
ha->req_q_map[0] = req;
-
+ rsp->req = req;
+ req->rsp = rsp;
+ set_bit(0, ha->req_qid_map);
+ set_bit(0, ha->rsp_qid_map);
/* FWI2-capable only. */
req->req_q_in = &ha->iobase->isp24.req_q_in;
req->req_q_out = &ha->iobase->isp24.req_q_out;
@@ -1866,6 +1959,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
}
+ if (ha->mqenable)
+ if (qla25xx_setup_mode(base_vha))
+ qla_printk(KERN_WARNING, ha,
+ "Can't create queues, falling back to single"
+ " queue mode\n");
+
+ if (ha->flags.running_gold_fw)
+ goto skip_dpc;
+
/*
* Startup the kernel thread for this host adapter
*/
@@ -1878,6 +1980,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
}
+skip_dpc:
list_add_tail(&base_vha->list, &ha->vp_list);
base_vha->host->irq = ha->pdev->irq;
@@ -1917,8 +2020,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
probe_init_failed:
- qla2x00_free_que(ha, req, rsp);
- ha->max_queues = 0;
+ qla2x00_free_req_que(ha, req);
+ qla2x00_free_rsp_que(ha, rsp);
+ ha->max_req_queues = ha->max_rsp_queues = 0;
probe_failed:
if (base_vha->timer_active)
@@ -1976,6 +2080,13 @@ qla2x00_remove_one(struct pci_dev *pdev)
base_vha->flags.online = 0;
+ /* Flush the work queue and remove it */
+ if (ha->wq) {
+ flush_workqueue(ha->wq);
+ destroy_workqueue(ha->wq);
+ ha->wq = NULL;
+ }
+
/* Kill the kernel thread for this host */
if (ha->dpc_thread) {
struct task_struct *t = ha->dpc_thread;
@@ -2017,6 +2128,8 @@ qla2x00_free_device(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
+ qla25xx_delete_queues(vha);
+
if (ha->flags.fce_enabled)
qla2x00_disable_fce_trace(vha, NULL, NULL);
@@ -2329,6 +2442,14 @@ qla2x00_mem_free(struct qla_hw_data *ha)
vfree(ha->fw_dump);
}
+ if (ha->dcbx_tlv)
+ dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+ ha->dcbx_tlv, ha->dcbx_tlv_dma);
+
+ if (ha->xgmac_data)
+ dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+ ha->xgmac_data, ha->xgmac_data_dma);
+
if (ha->sns_cmd)
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
ha->sns_cmd, ha->sns_cmd_dma);
@@ -2412,6 +2533,8 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
INIT_LIST_HEAD(&vha->work_list);
INIT_LIST_HEAD(&vha->list);
+ spin_lock_init(&vha->work_lock);
+
sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
return vha;
@@ -2420,13 +2543,11 @@ fail:
}
static struct qla_work_evt *
-qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
- int locked)
+qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
{
struct qla_work_evt *e;
- e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC:
- GFP_KERNEL);
+ e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
if (!e)
return NULL;
@@ -2437,17 +2558,15 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
}
static int
-qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked)
+qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
{
- unsigned long uninitialized_var(flags);
- struct qla_hw_data *ha = vha->hw;
+ unsigned long flags;
- if (!locked)
- spin_lock_irqsave(&ha->hardware_lock, flags);
+ spin_lock_irqsave(&vha->work_lock, flags);
list_add_tail(&e->list, &vha->work_list);
+ spin_unlock_irqrestore(&vha->work_lock, flags);
qla2xxx_wake_dpc(vha);
- if (!locked)
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
return QLA_SUCCESS;
}
@@ -2457,13 +2576,13 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_AEN);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.aen.code = code;
e->u.aen.data = data;
- return qla2x00_post_work(vha, e, 1);
+ return qla2x00_post_work(vha, e);
}
int
@@ -2471,25 +2590,27 @@ qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb)
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK);
if (!e)
return QLA_FUNCTION_FAILED;
memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
- return qla2x00_post_work(vha, e, 1);
+ return qla2x00_post_work(vha, e);
}
static void
qla2x00_do_work(struct scsi_qla_host *vha)
{
- struct qla_work_evt *e;
- struct qla_hw_data *ha = vha->hw;
+ struct qla_work_evt *e, *tmp;
+ unsigned long flags;
+ LIST_HEAD(work);
- spin_lock_irq(&ha->hardware_lock);
- while (!list_empty(&vha->work_list)) {
- e = list_entry(vha->work_list.next, struct qla_work_evt, list);
+ spin_lock_irqsave(&vha->work_lock, flags);
+ list_splice_init(&vha->work_list, &work);
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+
+ list_for_each_entry_safe(e, tmp, &work, list) {
list_del_init(&e->list);
- spin_unlock_irq(&ha->hardware_lock);
switch (e->type) {
case QLA_EVT_AEN:
@@ -2502,10 +2623,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
- spin_lock_irq(&ha->hardware_lock);
}
- spin_unlock_irq(&ha->hardware_lock);
}
+
/* Relogins all the fcports of a vport
* Context: dpc thread
*/
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 152ecfc26cd2..6260505dceb5 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -219,8 +219,8 @@ qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
- __func__, vha->host_no));
+ DEBUG9_10(qla_printk(KERN_WARNING, ha,
+ "NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -349,7 +349,7 @@ qla2x00_clear_nvram_protection(struct qla_hw_data *ha)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(qla_printk(
+ DEBUG9_10(qla_printk(KERN_WARNING, ha,
"NVRAM didn't go ready...\n"));
break;
}
@@ -408,7 +408,8 @@ qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(qla_printk("NVRAM didn't go ready...\n"));
+ DEBUG9_10(qla_printk(KERN_WARNING, ha,
+ "NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -701,32 +702,35 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
break;
case FLT_REG_VPD_0:
ha->flt_region_vpd_nvram = start;
- if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+ if (ha->flags.port0)
ha->flt_region_vpd = start;
break;
case FLT_REG_VPD_1:
- if (PCI_FUNC(ha->pdev->devfn) & 1)
+ if (!ha->flags.port0)
ha->flt_region_vpd = start;
break;
case FLT_REG_NVRAM_0:
- if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+ if (ha->flags.port0)
ha->flt_region_nvram = start;
break;
case FLT_REG_NVRAM_1:
- if (PCI_FUNC(ha->pdev->devfn) & 1)
+ if (!ha->flags.port0)
ha->flt_region_nvram = start;
break;
case FLT_REG_FDT:
ha->flt_region_fdt = start;
break;
case FLT_REG_NPIV_CONF_0:
- if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+ if (ha->flags.port0)
ha->flt_region_npiv_conf = start;
break;
case FLT_REG_NPIV_CONF_1:
- if (PCI_FUNC(ha->pdev->devfn) & 1)
+ if (!ha->flags.port0)
ha->flt_region_npiv_conf = start;
break;
+ case FLT_REG_GOLD_FW:
+ ha->flt_region_gold_fw = start;
+ break;
}
}
goto done;
@@ -744,12 +748,12 @@ no_flash_data:
ha->flt_region_fw = def_fw[def];
ha->flt_region_boot = def_boot[def];
ha->flt_region_vpd_nvram = def_vpd_nvram[def];
- ha->flt_region_vpd = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+ ha->flt_region_vpd = ha->flags.port0 ?
def_vpd0[def]: def_vpd1[def];
- ha->flt_region_nvram = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+ ha->flt_region_nvram = ha->flags.port0 ?
def_nvram0[def]: def_nvram1[def];
ha->flt_region_fdt = def_fdt[def];
- ha->flt_region_npiv_conf = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+ ha->flt_region_npiv_conf = ha->flags.port0 ?
def_npiv_conf0[def]: def_npiv_conf1[def];
done:
DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x "
@@ -924,6 +928,8 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
struct fc_vport_identifiers vid;
struct fc_vport *vport;
+ memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
+
flags = le16_to_cpu(entry->flags);
if (flags == 0xffff)
continue;
@@ -937,9 +943,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
vid.port_name = wwn_to_u64(entry->port_name);
vid.node_name = wwn_to_u64(entry->node_name);
- memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
-
- DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx "
+ DEBUG2(qla_printk(KERN_INFO, ha, "NPIV[%02x]: wwpn=%llx "
"wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
vid.port_name, vid.node_name, le16_to_cpu(entry->vf_id),
entry->q_qos, entry->f_qos));
@@ -955,7 +959,6 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
}
done:
kfree(data);
- ha->npiv_info = NULL;
}
static int
@@ -1079,8 +1082,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
0xff0000) | ((fdata >> 16) & 0xff));
ret = qla24xx_erase_sector(vha, fdata);
if (ret != QLA_SUCCESS) {
- DEBUG9(qla_printk("Unable to erase sector: "
- "address=%x.\n", faddr));
+ DEBUG9(qla_printk(KERN_WARNING, ha,
+ "Unable to erase sector: address=%x.\n",
+ faddr));
break;
}
}
@@ -1240,8 +1244,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
ret = qla24xx_write_flash_dword(ha,
nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr));
if (ret != QLA_SUCCESS) {
- DEBUG9(qla_printk("Unable to program nvram address=%x "
- "data=%x.\n", naddr, *dwptr));
+ DEBUG9(qla_printk(KERN_WARNING, ha,
+ "Unable to program nvram address=%x data=%x.\n",
+ naddr, *dwptr));
break;
}
}
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 19d1afc3a343..b63feaf43126 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.01-k1"
+#define QLA2XXX_VERSION "8.03.01-k3"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 166417a6afba..2de5f3ad640b 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1225,8 +1225,8 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);
* @starget: SCSI target pointer
* @lun: SCSI Logical Unit Number
*
- * Description: Looks up the scsi_device with the specified @channel, @id, @lun
- * for a given host. The returned scsi_device has an additional reference that
+ * Description: Looks up the scsi_device with the specified @lun for a given
+ * @starget. The returned scsi_device has an additional reference that
* needs to be released with scsi_device_put once you're done with it.
**/
struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget,
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 213123b0486b..41a21772df12 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -887,7 +887,7 @@ static int resp_start_stop(struct scsi_cmnd * scp,
static sector_t get_sdebug_capacity(void)
{
if (scsi_debug_virtual_gb > 0)
- return 2048 * 1024 * scsi_debug_virtual_gb;
+ return 2048 * 1024 * (sector_t)scsi_debug_virtual_gb;
else
return sdebug_store_sectors;
}
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 0c2c73be1974..a1689353d7fd 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -641,9 +641,9 @@ EXPORT_SYMBOL(scsi_eh_prep_cmnd);
/**
* scsi_eh_restore_cmnd - Restore a scsi command info as part of error recory
* @scmd: SCSI command structure to restore
- * @ses: saved information from a coresponding call to scsi_prep_eh_cmnd
+ * @ses: saved information from a coresponding call to scsi_eh_prep_cmnd
*
- * Undo any damage done by above scsi_prep_eh_cmnd().
+ * Undo any damage done by above scsi_eh_prep_cmnd().
*/
void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
{
@@ -1451,28 +1451,21 @@ static void eh_lock_door_done(struct request *req, int uptodate)
* @sdev: SCSI device to prevent medium removal
*
* Locking:
- * We must be called from process context; scsi_allocate_request()
- * may sleep.
+ * We must be called from process context.
*
* Notes:
* We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request on the
* head of the devices request queue, and continue.
- *
- * Bugs:
- * scsi_allocate_request() may sleep waiting for existing requests to
- * be processed. However, since we haven't kicked off any request
- * processing for this host, this may deadlock.
- *
- * If scsi_allocate_request() fails for what ever reason, we
- * completely forget to lock the door.
*/
static void scsi_eh_lock_door(struct scsi_device *sdev)
{
struct request *req;
+ /*
+ * blk_get_request with GFP_KERNEL (__GFP_WAIT) sleeps until a
+ * request becomes available
+ */
req = blk_get_request(sdev->request_queue, READ, GFP_KERNEL);
- if (!req)
- return;
req->cmd[0] = ALLOW_MEDIUM_REMOVAL;
req->cmd[1] = 0;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index e410d667910d..30f3275e119e 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -706,7 +706,6 @@ EXPORT_SYMBOL(scsi_release_buffers);
void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
{
int result = cmd->result;
- int this_count;
struct request_queue *q = cmd->device->request_queue;
struct request *req = cmd->request;
int error = 0;
@@ -789,7 +788,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
*/
if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL)
return;
- this_count = blk_rq_bytes(req);
error = -EIO;
@@ -1264,10 +1262,8 @@ static inline int scsi_target_queue_ready(struct Scsi_Host *shost,
if (--starget->target_blocked == 0) {
SCSI_LOG_MLQUEUE(3, starget_printk(KERN_INFO, starget,
"unblocking target at zero depth\n"));
- } else {
- blk_plug_device(sdev->request_queue);
+ } else
return 0;
- }
}
if (scsi_target_is_busy(starget)) {
@@ -2416,20 +2412,18 @@ int
scsi_internal_device_unblock(struct scsi_device *sdev)
{
struct request_queue *q = sdev->request_queue;
- int err;
unsigned long flags;
/*
* Try to transition the scsi device to SDEV_RUNNING
* and goose the device queue if successful.
*/
- err = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (err) {
- err = scsi_device_set_state(sdev, SDEV_CREATED);
-
- if (err)
- return err;
- }
+ if (sdev->sdev_state == SDEV_BLOCK)
+ sdev->sdev_state = SDEV_RUNNING;
+ else if (sdev->sdev_state == SDEV_CREATED_BLOCK)
+ sdev->sdev_state = SDEV_CREATED;
+ else
+ return -EINVAL;
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 6f51ca485f35..c44783801402 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -115,12 +115,12 @@ MODULE_PARM_DESC(max_report_luns,
"REPORT LUNS maximum number of LUNS received (should be"
" between 1 and 16384)");
-static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3;
+static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ + 18;
module_param_named(inq_timeout, scsi_inq_timeout, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(inq_timeout,
"Timeout (in seconds) waiting for devices to answer INQUIRY."
- " Default is 5. Some non-compliant devices need more.");
+ " Default is 20. Some devices may need more; most need less.");
/* This lock protects only this list */
static DEFINE_SPINLOCK(async_scan_lock);
@@ -425,6 +425,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
INIT_LIST_HEAD(&starget->devices);
starget->state = STARGET_CREATED;
starget->scsi_level = SCSI_2;
+ starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED;
retry:
spin_lock_irqsave(shost->host_lock, flags);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 094795455293..f3e664628d7a 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -37,7 +37,6 @@
#define ISCSI_TRANSPORT_VERSION "2.0-870"
struct iscsi_internal {
- int daemon_pid;
struct scsi_transport_template t;
struct iscsi_transport *iscsi_transport;
struct list_head list;
@@ -357,7 +356,7 @@ int iscsi_session_chkready(struct iscsi_cls_session *session)
err = 0;
break;
case ISCSI_SESSION_FAILED:
- err = DID_TRANSPORT_DISRUPTED << 16;
+ err = DID_IMM_RETRY << 16;
break;
case ISCSI_SESSION_FREE:
err = DID_TRANSPORT_FAILFAST << 16;
@@ -938,23 +937,9 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt)
}
static int
-iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
+iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
{
- return netlink_broadcast(nls, skb, 0, 1, gfp);
-}
-
-static int
-iscsi_unicast_skb(struct sk_buff *skb, int pid)
-{
- int rc;
-
- rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
- if (rc < 0) {
- printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc);
- return rc;
- }
-
- return 0;
+ return nlmsg_multicast(nls, skb, 0, group, gfp);
}
int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
@@ -980,7 +965,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
return -ENOMEM;
}
- nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
memset(ev, 0, sizeof(*ev));
ev->transport_handle = iscsi_handle(conn->transport);
@@ -991,10 +976,45 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
- return iscsi_unicast_skb(skb, priv->daemon_pid);
+ return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
}
EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
+int iscsi_offload_mesg(struct Scsi_Host *shost,
+ struct iscsi_transport *transport, uint32_t type,
+ char *data, uint16_t data_size)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct iscsi_uevent *ev;
+ int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+ skb = alloc_skb(len, GFP_NOIO);
+ if (!skb) {
+ printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
+ return -ENOMEM;
+ }
+
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ memset(ev, 0, sizeof(*ev));
+ ev->type = type;
+ ev->transport_handle = iscsi_handle(transport);
+ switch (type) {
+ case ISCSI_KEVENT_PATH_REQ:
+ ev->r.req_path.host_no = shost->host_no;
+ break;
+ case ISCSI_KEVENT_IF_DOWN:
+ ev->r.notify_if_down.host_no = shost->host_no;
+ break;
+ }
+
+ memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+ return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_NOIO);
+}
+EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
+
void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
{
struct nlmsghdr *nlh;
@@ -1014,7 +1034,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
return;
}
- nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
ev->transport_handle = iscsi_handle(conn->transport);
ev->type = ISCSI_KEVENT_CONN_ERROR;
@@ -1022,7 +1042,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
ev->r.connerror.cid = conn->cid;
ev->r.connerror.sid = iscsi_conn_get_sid(conn);
- iscsi_broadcast_skb(skb, GFP_ATOMIC);
+ iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
error);
@@ -1030,8 +1050,8 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
static int
-iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
- void *payload, int size)
+iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
+ void *payload, int size)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
@@ -1045,10 +1065,10 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
return -ENOMEM;
}
- nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+ nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
nlh->nlmsg_flags = flags;
memcpy(NLMSG_DATA(nlh), payload, size);
- return iscsi_unicast_skb(skb, pid);
+ return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
}
static int
@@ -1085,7 +1105,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
return -ENOMEM;
}
- nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0,
+ nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
(len - sizeof(*nlhstat)), 0);
evstat = NLMSG_DATA(nlhstat);
memset(evstat, 0, sizeof(*evstat));
@@ -1109,7 +1129,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
skb_trim(skbstat, NLMSG_ALIGN(actual_size));
nlhstat->nlmsg_len = actual_size;
- err = iscsi_unicast_skb(skbstat, priv->daemon_pid);
+ err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
+ GFP_ATOMIC);
} while (err < 0 && err != -ECONNREFUSED);
return err;
@@ -1143,7 +1164,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
return -ENOMEM;
}
- nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
ev->transport_handle = iscsi_handle(session->transport);
@@ -1172,7 +1193,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
* this will occur if the daemon is not up, so we just warn
* the user and when the daemon is restarted it will handle it
*/
- rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
+ rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
if (rc == -ESRCH)
iscsi_cls_session_printk(KERN_ERR, session,
"Cannot notify userspace of session "
@@ -1268,26 +1289,54 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
return err;
}
+static int iscsi_if_ep_connect(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev, int msg_type)
+{
+ struct iscsi_endpoint *ep;
+ struct sockaddr *dst_addr;
+ struct Scsi_Host *shost = NULL;
+ int non_blocking, err = 0;
+
+ if (!transport->ep_connect)
+ return -EINVAL;
+
+ if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
+ shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
+ if (!shost) {
+ printk(KERN_ERR "ep connect failed. Could not find "
+ "host no %u\n",
+ ev->u.ep_connect_through_host.host_no);
+ return -ENODEV;
+ }
+ non_blocking = ev->u.ep_connect_through_host.non_blocking;
+ } else
+ non_blocking = ev->u.ep_connect.non_blocking;
+
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+ ep = transport->ep_connect(shost, dst_addr, non_blocking);
+ if (IS_ERR(ep)) {
+ err = PTR_ERR(ep);
+ goto release_host;
+ }
+
+ ev->r.ep_connect_ret.handle = ep->id;
+release_host:
+ if (shost)
+ scsi_host_put(shost);
+ return err;
+}
+
static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
struct iscsi_uevent *ev, int msg_type)
{
struct iscsi_endpoint *ep;
- struct sockaddr *dst_addr;
int rc = 0;
switch (msg_type) {
+ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
- if (!transport->ep_connect)
- return -EINVAL;
-
- dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
- ep = transport->ep_connect(dst_addr,
- ev->u.ep_connect.non_blocking);
- if (IS_ERR(ep))
- return PTR_ERR(ep);
-
- ev->r.ep_connect_ret.handle = ep->id;
+ rc = iscsi_if_ep_connect(transport, ev, msg_type);
break;
case ISCSI_UEVENT_TRANSPORT_EP_POLL:
if (!transport->ep_poll)
@@ -1365,7 +1414,31 @@ iscsi_set_host_param(struct iscsi_transport *transport,
}
static int
-iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_path *params;
+ int err;
+
+ if (!transport->set_path)
+ return -ENOSYS;
+
+ shost = scsi_host_lookup(ev->u.set_path.host_no);
+ if (!shost) {
+ printk(KERN_ERR "set path could not find host no %u\n",
+ ev->u.set_path.host_no);
+ return -ENODEV;
+ }
+
+ params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
+ err = transport->set_path(shost, params);
+
+ scsi_host_put(shost);
+ return err;
+}
+
+static int
+iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
{
int err = 0;
struct iscsi_uevent *ev = NLMSG_DATA(nlh);
@@ -1375,6 +1448,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct iscsi_cls_conn *conn;
struct iscsi_endpoint *ep = NULL;
+ if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
+ *group = ISCSI_NL_GRP_UIP;
+ else
+ *group = ISCSI_NL_GRP_ISCSID;
+
priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
if (!priv)
return -EINVAL;
@@ -1383,8 +1461,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!try_module_get(transport->owner))
return -EINVAL;
- priv->daemon_pid = NETLINK_CREDS(skb)->pid;
-
switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
err = iscsi_if_create_session(priv, ep, ev,
@@ -1469,6 +1545,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
case ISCSI_UEVENT_TRANSPORT_EP_POLL:
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
+ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
break;
case ISCSI_UEVENT_TGT_DSCVR:
@@ -1477,6 +1554,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
case ISCSI_UEVENT_SET_HOST_PARAM:
err = iscsi_set_host_param(transport, ev);
break;
+ case ISCSI_UEVENT_PATH_UPDATE:
+ err = iscsi_set_path(transport, ev);
+ break;
default:
err = -ENOSYS;
break;
@@ -1499,6 +1579,7 @@ iscsi_if_rx(struct sk_buff *skb)
uint32_t rlen;
struct nlmsghdr *nlh;
struct iscsi_uevent *ev;
+ uint32_t group;
nlh = nlmsg_hdr(skb);
if (nlh->nlmsg_len < sizeof(*nlh) ||
@@ -1511,7 +1592,7 @@ iscsi_if_rx(struct sk_buff *skb)
if (rlen > skb->len)
rlen = skb->len;
- err = iscsi_if_recv_msg(skb, nlh);
+ err = iscsi_if_recv_msg(skb, nlh, &group);
if (err) {
ev->type = ISCSI_KEVENT_IF_ERROR;
ev->iferror = err;
@@ -1525,8 +1606,7 @@ iscsi_if_rx(struct sk_buff *skb)
*/
if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
break;
- err = iscsi_if_send_reply(
- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+ err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
} while (err < 0 && err != -ECONNREFUSED);
skb_pull(skb, rlen);
@@ -1774,7 +1854,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
if (!priv)
return NULL;
INIT_LIST_HEAD(&priv->list);
- priv->daemon_pid = -1;
priv->iscsi_transport = tt;
priv->t.user_scan = iscsi_user_scan;
priv->t.create_work_queue = 1;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 70c4dd99bbf0..878b17a9af30 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -50,6 +50,7 @@
#include <linux/string_helpers.h>
#include <linux/async.h>
#include <asm/uaccess.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -1344,12 +1345,8 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
return -EINVAL;
}
- sector_size = (buffer[8] << 24) | (buffer[9] << 16) |
- (buffer[10] << 8) | buffer[11];
- lba = (((u64)buffer[0] << 56) | ((u64)buffer[1] << 48) |
- ((u64)buffer[2] << 40) | ((u64)buffer[3] << 32) |
- ((u64)buffer[4] << 24) | ((u64)buffer[5] << 16) |
- ((u64)buffer[6] << 8) | (u64)buffer[7]);
+ sector_size = get_unaligned_be32(&buffer[8]);
+ lba = get_unaligned_be64(&buffer[0]);
sd_read_protection_type(sdkp, buffer);
@@ -1400,10 +1397,8 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
return -EINVAL;
}
- sector_size = (buffer[4] << 24) | (buffer[5] << 16) |
- (buffer[6] << 8) | buffer[7];
- lba = (buffer[0] << 24) | (buffer[1] << 16) |
- (buffer[2] << 8) | buffer[3];
+ sector_size = get_unaligned_be32(&buffer[4]);
+ lba = get_unaligned_be32(&buffer[0]);
if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
@@ -1515,7 +1510,7 @@ got_data:
*/
sector_size = 512;
}
- blk_queue_hardsect_size(sdp->request_queue, sector_size);
+ blk_queue_logical_block_size(sdp->request_queue, sector_size);
{
char cap_str_2[10], cap_str_10[10];
@@ -1907,24 +1902,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
index = sdkp->index;
dev = &sdp->sdev_gendev;
- if (!sdp->request_queue->rq_timeout) {
- if (sdp->type != TYPE_MOD)
- blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
- else
- blk_queue_rq_timeout(sdp->request_queue,
- SD_MOD_TIMEOUT);
- }
-
- device_initialize(&sdkp->dev);
- sdkp->dev.parent = &sdp->sdev_gendev;
- sdkp->dev.class = &sd_disk_class;
- dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev));
-
- if (device_add(&sdkp->dev))
- goto out_free_index;
-
- get_device(&sdp->sdev_gendev);
-
if (index < SD_MAX_DISKS) {
gd->major = sd_major((index & 0xf0) >> 4);
gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
@@ -1959,11 +1936,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
-
- return;
-
- out_free_index:
- ida_remove(&sd_index_ida, index);
}
/**
@@ -2031,6 +2003,24 @@ static int sd_probe(struct device *dev)
sdkp->openers = 0;
sdkp->previous_state = 1;
+ if (!sdp->request_queue->rq_timeout) {
+ if (sdp->type != TYPE_MOD)
+ blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
+ else
+ blk_queue_rq_timeout(sdp->request_queue,
+ SD_MOD_TIMEOUT);
+ }
+
+ device_initialize(&sdkp->dev);
+ sdkp->dev.parent = &sdp->sdev_gendev;
+ sdkp->dev.class = &sd_disk_class;
+ dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev));
+
+ if (device_add(&sdkp->dev))
+ goto out_free_index;
+
+ get_device(&sdp->sdev_gendev);
+
async_schedule(sd_probe_async, sdkp);
return 0;
@@ -2060,8 +2050,10 @@ static int sd_probe(struct device *dev)
**/
static int sd_remove(struct device *dev)
{
- struct scsi_disk *sdkp = dev_get_drvdata(dev);
+ struct scsi_disk *sdkp;
+ async_synchronize_full();
+ sdkp = dev_get_drvdata(dev);
device_del(&sdkp->dev);
del_gendisk(sdkp->disk);
sd_shutdown(dev);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index dec4c70677de..8201387b4daa 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -179,7 +179,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */
/* tasklet or soft irq callback */
static void sg_rq_end_io(struct request *rq, int uptodate);
static int sg_start_req(Sg_request *srp, unsigned char *cmd);
-static void sg_finish_rem_req(Sg_request * srp);
+static int sg_finish_rem_req(Sg_request * srp);
static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
Sg_request * srp);
@@ -289,8 +289,8 @@ sg_open(struct inode *inode, struct file *filp)
if (list_empty(&sdp->sfds)) { /* no existing opens on this device */
sdp->sgdebug = 0;
q = sdp->device->request_queue;
- sdp->sg_tablesize = min(q->max_hw_segments,
- q->max_phys_segments);
+ sdp->sg_tablesize = min(queue_max_hw_segments(q),
+ queue_max_phys_segments(q));
}
if ((sfp = sg_add_sfp(sdp, dev)))
filp->private_data = sfp;
@@ -518,7 +518,7 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp)
goto err_out;
}
err_out:
- sg_finish_rem_req(srp);
+ err = sg_finish_rem_req(srp);
return (0 == err) ? count : err;
}
@@ -909,7 +909,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
if (val < 0)
return -EINVAL;
val = min_t(int, val,
- sdp->device->request_queue->max_sectors * 512);
+ queue_max_sectors(sdp->device->request_queue) * 512);
if (val != sfp->reserve.bufflen) {
if (sg_res_in_use(sfp) || sfp->mmap_called)
return -EBUSY;
@@ -919,7 +919,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
return 0;
case SG_GET_RESERVED_SIZE:
val = min_t(int, sfp->reserve.bufflen,
- sdp->device->request_queue->max_sectors * 512);
+ queue_max_sectors(sdp->device->request_queue) * 512);
return put_user(val, ip);
case SG_SET_COMMAND_Q:
result = get_user(val, ip);
@@ -1059,12 +1059,13 @@ sg_ioctl(struct inode *inode, struct file *filp,
return -ENODEV;
return scsi_ioctl(sdp->device, cmd_in, p);
case BLKSECTGET:
- return put_user(sdp->device->request_queue->max_sectors * 512,
+ return put_user(queue_max_sectors(sdp->device->request_queue) * 512,
ip);
case BLKTRACESETUP:
return blk_trace_setup(sdp->device->request_queue,
sdp->disk->disk_name,
MKDEV(SCSI_GENERIC_MAJOR, sdp->index),
+ NULL,
(char *)arg);
case BLKTRACESTART:
return blk_trace_startstop(sdp->device->request_queue, 1);
@@ -1377,7 +1378,8 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
sdp->device = scsidp;
INIT_LIST_HEAD(&sdp->sfds);
init_waitqueue_head(&sdp->o_excl_wait);
- sdp->sg_tablesize = min(q->max_hw_segments, q->max_phys_segments);
+ sdp->sg_tablesize = min(queue_max_hw_segments(q),
+ queue_max_phys_segments(q));
sdp->index = k;
kref_init(&sdp->d_ref);
@@ -1696,9 +1698,10 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
return res;
}
-static void
-sg_finish_rem_req(Sg_request * srp)
+static int sg_finish_rem_req(Sg_request * srp)
{
+ int ret = 0;
+
Sg_fd *sfp = srp->parentfp;
Sg_scatter_hold *req_schp = &srp->data;
@@ -1710,12 +1713,14 @@ sg_finish_rem_req(Sg_request * srp)
if (srp->rq) {
if (srp->bio)
- blk_rq_unmap_user(srp->bio);
+ ret = blk_rq_unmap_user(srp->bio);
blk_put_request(srp->rq);
}
sg_remove_request(sfp, srp);
+
+ return ret;
}
static int
@@ -2052,7 +2057,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
sg_big_buff = def_reserved_size;
bufflen = min_t(int, sg_big_buff,
- sdp->device->request_queue->max_sectors * 512);
+ queue_max_sectors(sdp->device->request_queue) * 512);
sg_build_reserve(sfp, bufflen);
SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n",
sfp->reserve.bufflen, sfp->reserve.k_use_sg));
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index fddba53c7fe5..cd350dfc1216 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -727,7 +727,7 @@ static void get_sectorsize(struct scsi_cd *cd)
}
queue = cd->device->request_queue;
- blk_queue_hardsect_size(queue, sector_size);
+ blk_queue_logical_block_size(queue, sector_size);
return;
}
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index d92ff512d213..291236e6e435 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -309,6 +309,11 @@ int sr_drive_status(struct cdrom_device_info *cdi, int slot)
if (0 == sr_test_unit_ready(cd->device, &sshdr))
return CDS_DISC_OK;
+ /* SK/ASC/ASCQ of 2/4/1 means "unit is becoming ready" */
+ if (scsi_sense_valid(&sshdr) && sshdr.sense_key == NOT_READY
+ && sshdr.asc == 0x04 && sshdr.ascq == 0x01)
+ return CDS_DRIVE_NOT_READY;
+
if (!cdrom_get_media_event(cdi, &med)) {
if (med.media_present)
return CDS_DISC_OK;
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 8681b708344f..b33d04250bbc 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -2964,7 +2964,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
!(STp->use_pf & PF_TESTED)) {
/* Try the other possible state of Page Format if not
already tried */
- STp->use_pf = !STp->use_pf | PF_TESTED;
+ STp->use_pf = (STp->use_pf ^ USE_PF) | PF_TESTED;
st_release_request(SRpnt);
SRpnt = NULL;
return st_int_ioctl(STp, cmd_in, arg);
@@ -3983,8 +3983,8 @@ static int st_probe(struct device *dev)
return -ENODEV;
}
- i = min(SDp->request_queue->max_hw_segments,
- SDp->request_queue->max_phys_segments);
+ i = min(queue_max_hw_segments(SDp->request_queue),
+ queue_max_phys_segments(SDp->request_queue));
if (st_max_sg_segs < i)
i = st_max_sg_segs;
buffer = new_tape_buffer((SDp->host)->unchecked_isa_dma, i);
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 583966ec8266..45374d66d26a 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -737,11 +737,14 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev)
struct sym_hcb *np = sym_get_hcb(sdev->host);
struct sym_tcb *tp = &np->target[sdev->id];
struct sym_lcb *lp;
+ unsigned long flags;
+ int error;
if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN)
return -ENXIO;
- tp->starget = sdev->sdev_target;
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+
/*
* Fail the device init if the device is flagged NOSCAN at BOOT in
* the NVRAM. This may speed up boot and maintain coherency with
@@ -753,26 +756,37 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev)
if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) {
tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;
- starget_printk(KERN_INFO, tp->starget,
+ starget_printk(KERN_INFO, sdev->sdev_target,
"Scan at boot disabled in NVRAM\n");
- return -ENXIO;
+ error = -ENXIO;
+ goto out;
}
if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) {
- if (sdev->lun != 0)
- return -ENXIO;
- starget_printk(KERN_INFO, tp->starget,
+ if (sdev->lun != 0) {
+ error = -ENXIO;
+ goto out;
+ }
+ starget_printk(KERN_INFO, sdev->sdev_target,
"Multiple LUNs disabled in NVRAM\n");
}
lp = sym_alloc_lcb(np, sdev->id, sdev->lun);
- if (!lp)
- return -ENOMEM;
+ if (!lp) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (tp->nlcb == 1)
+ tp->starget = sdev->sdev_target;
spi_min_period(tp->starget) = tp->usr_period;
spi_max_width(tp->starget) = tp->usr_width;
- return 0;
+ error = 0;
+out:
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
+
+ return error;
}
/*
@@ -819,12 +833,34 @@ static int sym53c8xx_slave_configure(struct scsi_device *sdev)
static void sym53c8xx_slave_destroy(struct scsi_device *sdev)
{
struct sym_hcb *np = sym_get_hcb(sdev->host);
- struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun);
+ struct sym_tcb *tp = &np->target[sdev->id];
+ struct sym_lcb *lp = sym_lp(tp, sdev->lun);
+ unsigned long flags;
+
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+
+ if (lp->busy_itlq || lp->busy_itl) {
+ /*
+ * This really shouldn't happen, but we can't return an error
+ * so let's try to stop all on-going I/O.
+ */
+ starget_printk(KERN_WARNING, tp->starget,
+ "Removing busy LCB (%d)\n", sdev->lun);
+ sym_reset_scsi_bus(np, 1);
+ }
- if (lp->itlq_tbl)
- sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL");
- kfree(lp->cb_tags);
- sym_mfree_dma(lp, sizeof(*lp), "LCB");
+ if (sym_free_lcb(np, sdev->id, sdev->lun) == 0) {
+ /*
+ * It was the last unit for this target.
+ */
+ tp->head.sval = 0;
+ tp->head.wval = np->rv_scntl3;
+ tp->head.uval = 0;
+ tp->tgoal.check_nego = 1;
+ tp->starget = NULL;
+ }
+
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
}
/*
@@ -890,6 +926,8 @@ static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc)
if (!((uc->target >> t) & 1))
continue;
tp = &np->target[t];
+ if (!tp->nlcb)
+ continue;
switch (uc->cmd) {
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index ffa70d1ed182..69ad4945c936 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -1896,6 +1896,15 @@ void sym_start_up(struct Scsi_Host *shost, int reason)
tp->head.sval = 0;
tp->head.wval = np->rv_scntl3;
tp->head.uval = 0;
+ if (tp->lun0p)
+ tp->lun0p->to_clear = 0;
+ if (tp->lunmp) {
+ int ln;
+
+ for (ln = 1; ln < SYM_CONF_MAX_LUN; ln++)
+ if (tp->lunmp[ln])
+ tp->lunmp[ln]->to_clear = 0;
+ }
}
/*
@@ -4988,7 +4997,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln)
*/
if (ln && !tp->lunmp) {
tp->lunmp = kcalloc(SYM_CONF_MAX_LUN, sizeof(struct sym_lcb *),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!tp->lunmp)
goto fail;
}
@@ -5008,6 +5017,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln)
tp->lun0p = lp;
tp->head.lun0_sa = cpu_to_scr(vtobus(lp));
}
+ tp->nlcb++;
/*
* Let the itl task point to error handling.
@@ -5085,6 +5095,43 @@ fail:
}
/*
+ * Lun control block deallocation. Returns the number of valid remaing LCBs
+ * for the target.
+ */
+int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln)
+{
+ struct sym_tcb *tp = &np->target[tn];
+ struct sym_lcb *lp = sym_lp(tp, ln);
+
+ tp->nlcb--;
+
+ if (ln) {
+ if (!tp->nlcb) {
+ kfree(tp->lunmp);
+ sym_mfree_dma(tp->luntbl, 256, "LUNTBL");
+ tp->lunmp = NULL;
+ tp->luntbl = NULL;
+ tp->head.luntbl_sa = cpu_to_scr(vtobus(np->badluntbl));
+ } else {
+ tp->luntbl[ln] = cpu_to_scr(vtobus(&np->badlun_sa));
+ tp->lunmp[ln] = NULL;
+ }
+ } else {
+ tp->lun0p = NULL;
+ tp->head.lun0_sa = cpu_to_scr(vtobus(&np->badlun_sa));
+ }
+
+ if (lp->itlq_tbl) {
+ sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+ kfree(lp->cb_tags);
+ }
+
+ sym_mfree_dma(lp, sizeof(*lp), "LCB");
+
+ return tp->nlcb;
+}
+
+/*
* Queue a SCSI IO to the controller.
*/
int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp)
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h
index 9ebc8706b6bf..053e63c86822 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.h
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h
@@ -401,6 +401,7 @@ struct sym_tcb {
* An array of bus addresses is used on reselection.
*/
u32 *luntbl; /* LCBs bus address table */
+ int nlcb; /* Number of valid LCBs (including LUN #0) */
/*
* LUN table used by the C code.
@@ -1065,6 +1066,7 @@ int sym_clear_tasks(struct sym_hcb *np, int cam_status, int target, int lun, int
struct sym_ccb *sym_get_ccb(struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order);
void sym_free_ccb(struct sym_hcb *np, struct sym_ccb *cp);
struct sym_lcb *sym_alloc_lcb(struct sym_hcb *np, u_char tn, u_char ln);
+int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln);
int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp);
int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *ccb, int timed_out);
int sym_reset_scsi_target(struct sym_hcb *np, int target);
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index b4b39811b445..fb867a9f55e9 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -137,6 +137,7 @@ struct uart_8250_port {
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
+ unsigned char cur_iotype; /* Running I/O type */
/*
* Some bits in registers are cleared on a read, so they must
@@ -286,6 +287,13 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
+ [PORT_AR7] = {
+ .name = "AR7",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
};
#if defined (CONFIG_SERIAL_8250_AU1X00)
@@ -471,6 +479,7 @@ static void io_serial_out(struct uart_port *p, int offset, int value)
static void set_io_from_upio(struct uart_port *p)
{
+ struct uart_8250_port *up = (struct uart_8250_port *)p;
switch (p->iotype) {
case UPIO_HUB6:
p->serial_in = hub6_serial_in;
@@ -509,6 +518,8 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = io_serial_out;
break;
}
+ /* Remember loaded iotype */
+ up->cur_iotype = p->iotype;
}
static void
@@ -1937,6 +1948,9 @@ static int serial8250_startup(struct uart_port *port)
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;
+ if (up->port.iotype != up->cur_iotype)
+ set_io_from_upio(port);
+
if (up->port.type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
@@ -2563,6 +2577,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (ret < 0)
probeflags &= ~PROBE_RSA;
+ if (up->port.iotype != up->cur_iotype)
+ set_io_from_upio(port);
+
if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags);
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
@@ -2671,6 +2688,11 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
int i;
+ for (i = 0; i < nr_uarts; i++) {
+ struct uart_8250_port *up = &serial8250_ports[i];
+ up->cur_iotype = 0xFF;
+ }
+
serial8250_isa_init_ports();
for (i = 0; i < nr_uarts; i++) {
diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c
index 418b4fe9a0a1..33149d982e82 100644
--- a/drivers/serial/8250_gsc.c
+++ b/drivers/serial/8250_gsc.c
@@ -39,9 +39,9 @@ static int __init serial_init_chip(struct parisc_device *dev)
*/
if (parisc_parent(dev)->id.hw_type != HPHW_IOA)
printk(KERN_INFO
- "Serial: device 0x%lx not configured.\n"
+ "Serial: device 0x%llx not configured.\n"
"Enable support for Wax, Lasi, Asp or Dino.\n",
- dev->hpa.start);
+ (unsigned long long)dev->hpa.start);
return -ENODEV;
}
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index 938bc1b6c3fa..e371a9c15341 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -2776,6 +2776,9 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_1130000 },
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950,
+ PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0,
+ pbn_b0_1_921600 },
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_115200 },
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 343e3a35b6a3..641e800ed693 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -833,6 +833,7 @@ config SERIAL_IMX
bool "IMX serial port support"
depends on ARM && (ARCH_IMX || ARCH_MXC)
select SERIAL_CORE
+ select RATIONAL
help
If you have a machine based on a Motorola IMX CPU you
can enable its onboard serial port by enabling this option.
@@ -1433,4 +1434,11 @@ config SPORT_BAUD_RATE
default 19200 if (SERIAL_SPORT_BAUD_RATE_19200)
default 9600 if (SERIAL_SPORT_BAUD_RATE_9600)
+config SERIAL_TIMBERDALE
+ tristate "Support for timberdale UART"
+ depends on MFD_TIMBERDALE
+ select SERIAL_CORE
+ ---help---
+ Add support for UART controller on timberdale.
+
endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index d438eb2a73de..45a8658f54d5 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -77,3 +77,4 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
+obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index e3a5ad5ef1d6..cdc049d4350f 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -665,7 +665,7 @@ static struct uart_driver amba_reg = {
.cons = AMBA_CONSOLE,
};
-static int pl010_probe(struct amba_device *dev, void *id)
+static int pl010_probe(struct amba_device *dev, struct amba_id *id)
{
struct uart_amba_port *uap;
void __iomem *base;
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index 8b2b9700f3e4..88fdac51b6c5 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -729,7 +729,7 @@ static struct uart_driver amba_reg = {
.cons = AMBA_CONSOLE,
};
-static int pl011_probe(struct amba_device *dev, void *id)
+static int pl011_probe(struct amba_device *dev, struct amba_id *id)
{
struct uart_amba_port *uap;
void __iomem *base;
diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c
index d86123e03391..e2f6b1bfac98 100644
--- a/drivers/serial/bfin_5xx.c
+++ b/drivers/serial/bfin_5xx.c
@@ -330,6 +330,11 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
/* Clear TFI bit */
UART_PUT_LSR(uart, TFI);
#endif
+ /* Anomaly notes:
+ * 05000215 - we always clear ETBEI within last UART TX
+ * interrupt to end a string. It is always set
+ * when start a new tx.
+ */
UART_CLEAR_IER(uart, ETBEI);
return;
}
@@ -415,6 +420,7 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
set_dma_x_modify(uart->tx_dma_channel, 1);
+ SSYNC();
enable_dma(uart->tx_dma_channel);
UART_SET_IER(uart, ETBEI);
@@ -473,27 +479,41 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
{
int x_pos, pos;
- unsigned long flags;
-
- spin_lock_irqsave(&uart->port.lock, flags);
+ dma_disable_irq(uart->rx_dma_channel);
+ spin_lock_bh(&uart->port.lock);
+
+ /* 2D DMA RX buffer ring is used. Because curr_y_count and
+ * curr_x_count can't be read as an atomic operation,
+ * curr_y_count should be read before curr_x_count. When
+ * curr_x_count is read, curr_y_count may already indicate
+ * next buffer line. But, the position calculated here is
+ * still indicate the old line. The wrong position data may
+ * be smaller than current buffer tail, which cause garbages
+ * are received if it is not prohibit.
+ */
uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
- if (uart->rx_dma_nrows == DMA_RX_YCOUNT)
+ if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
uart->rx_dma_nrows = 0;
x_pos = DMA_RX_XCOUNT - x_pos;
if (x_pos == DMA_RX_XCOUNT)
x_pos = 0;
pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
- if (pos != uart->rx_dma_buf.tail) {
+ /* Ignore receiving data if new position is in the same line of
+ * current buffer tail and small.
+ */
+ if (pos > uart->rx_dma_buf.tail ||
+ uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
uart->rx_dma_buf.head = pos;
bfin_serial_dma_rx_chars(uart);
uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
}
- spin_unlock_irqrestore(&uart->port.lock, flags);
+ spin_unlock_bh(&uart->port.lock);
+ dma_enable_irq(uart->rx_dma_channel);
mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
}
@@ -514,6 +534,11 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
disable_dma(uart->tx_dma_channel);
clear_dma_irqstat(uart->tx_dma_channel);
+ /* Anomaly notes:
+ * 05000215 - we always clear ETBEI within last UART TX
+ * interrupt to end a string. It is always set
+ * when start a new tx.
+ */
UART_CLEAR_IER(uart, ETBEI);
xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx += uart->tx_count;
@@ -532,11 +557,26 @@ static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
unsigned short irqstat;
+ int x_pos, pos;
spin_lock(&uart->port.lock);
irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
clear_dma_irqstat(uart->rx_dma_channel);
- bfin_serial_dma_rx_chars(uart);
+
+ uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
+ x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
+ uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
+ if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
+ uart->rx_dma_nrows = 0;
+
+ pos = uart->rx_dma_nrows * DMA_RX_XCOUNT;
+ if (pos > uart->rx_dma_buf.tail ||
+ uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
+ uart->rx_dma_buf.head = pos;
+ bfin_serial_dma_rx_chars(uart);
+ uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
+ }
+
spin_unlock(&uart->port.lock);
return IRQ_HANDLED;
@@ -789,8 +829,16 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
__func__);
}
- if (termios->c_cflag & CSTOPB)
- lcr |= STB;
+ /* Anomaly notes:
+ * 05000231 - STOP bit is always set to 1 whatever the user is set.
+ */
+ if (termios->c_cflag & CSTOPB) {
+ if (ANOMALY_05000231)
+ printk(KERN_WARNING "STOP bits other than 1 is not "
+ "supported in case of anomaly 05000231.\n");
+ else
+ lcr |= STB;
+ }
if (termios->c_cflag & PARENB)
lcr |= PEN;
if (!(termios->c_cflag & PARODD))
@@ -940,6 +988,10 @@ static void bfin_serial_reset_irda(struct uart_port *port)
}
#ifdef CONFIG_CONSOLE_POLL
+/* Anomaly notes:
+ * 05000099 - Because we only use THRE in poll_put and DR in poll_get,
+ * losing other bits of UART_LSR is not a problem here.
+ */
static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
@@ -1245,12 +1297,17 @@ static __init void early_serial_write(struct console *con, const char *s,
}
}
+/*
+ * This should have a .setup or .early_setup in it, but then things get called
+ * without the command line options, and the baud rate gets messed up - so
+ * don't let the common infrastructure play with things. (see calls to setup
+ * & earlysetup in ./kernel/printk.c:register_console()
+ */
static struct __initdata console bfin_early_serial_console = {
.name = "early_BFuart",
.write = early_serial_write,
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
- .setup = bfin_serial_console_setup,
.index = -1,
.data = &bfin_serial_reg,
};
diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c
index 529c0ff7952c..34b4ae0fe760 100644
--- a/drivers/serial/bfin_sport_uart.c
+++ b/drivers/serial/bfin_sport_uart.c
@@ -101,15 +101,16 @@ static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
{
pr_debug("%s value:%x\n", __func__, value);
/* Place a Start and Stop bit */
- __asm__ volatile (
- "R2 = b#01111111100;\n\t"
- "R3 = b#10000000001;\n\t"
- "%0 <<= 2;\n\t"
- "%0 = %0 & R2;\n\t"
- "%0 = %0 | R3;\n\t"
- :"=r"(value)
- :"0"(value)
- :"R2", "R3");
+ __asm__ __volatile__ (
+ "R2 = b#01111111100;"
+ "R3 = b#10000000001;"
+ "%0 <<= 2;"
+ "%0 = %0 & R2;"
+ "%0 = %0 | R3;"
+ : "=d"(value)
+ : "d"(value)
+ : "ASTAT", "R2", "R3"
+ );
pr_debug("%s value:%x\n", __func__, value);
SPORT_PUT_TX(up, value);
@@ -118,27 +119,30 @@ static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
static inline unsigned int rx_one_byte(struct sport_uart_port *up)
{
unsigned int value, extract;
+ u32 tmp_mask1, tmp_mask2, tmp_shift, tmp;
value = SPORT_GET_RX32(up);
pr_debug("%s value:%x\n", __func__, value);
/* Extract 8 bits data */
- __asm__ volatile (
- "R5 = 0;\n\t"
- "P0 = 8;\n\t"
- "R1 = 0x1801(Z);\n\t"
- "R3 = 0x0300(Z);\n\t"
- "R4 = 0;\n\t"
- "LSETUP(loop_s, loop_e) LC0 = P0;\nloop_s:\t"
- "R2 = extract(%1, R1.L)(Z);\n\t"
- "R2 <<= R4;\n\t"
- "R5 = R5 | R2;\n\t"
- "R1 = R1 - R3;\nloop_e:\t"
- "R4 += 1;\n\t"
- "%0 = R5;\n\t"
- :"=r"(extract)
- :"r"(value)
- :"P0", "R1", "R2","R3","R4", "R5");
+ __asm__ __volatile__ (
+ "%[extr] = 0;"
+ "%[mask1] = 0x1801(Z);"
+ "%[mask2] = 0x0300(Z);"
+ "%[shift] = 0;"
+ "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];"
+ ".Lloop_s:"
+ "%[tmp] = extract(%[val], %[mask1].L)(Z);"
+ "%[tmp] <<= %[shift];"
+ "%[extr] = %[extr] | %[tmp];"
+ "%[mask1] = %[mask1] - %[mask2];"
+ ".Lloop_e:"
+ "%[shift] += 1;"
+ : [val]"=d"(value), [extr]"=d"(extract), [shift]"=d"(tmp_shift), [tmp]"=d"(tmp),
+ [mask1]"=d"(tmp_mask1), [mask2]"=d"(tmp_mask2)
+ : "d"(value), [lc]"a"(8)
+ : "ASTAT", "LB0", "LC0", "LT0"
+ );
pr_debug(" extract:%x\n", extract);
return extract;
@@ -149,7 +153,7 @@ static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate)
int tclkdiv, tfsdiv, rclkdiv;
/* Set TCR1 and TCR2 */
- SPORT_PUT_TCR1(up, (LTFS | ITFS | TFSR | TLSBIT | ITCLK));
+ SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK));
SPORT_PUT_TCR2(up, 10);
pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up));
@@ -419,7 +423,7 @@ static void sport_shutdown(struct uart_port *port)
}
static void sport_set_termios(struct uart_port *port,
- struct termios *termios, struct termios *old)
+ struct ktermios *termios, struct ktermios *old)
{
pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
uart_update_timeout(port, CS8 ,port->uartclk);
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
index 7ba7d70f04d6..7be52fe288eb 100644
--- a/drivers/serial/crisv10.c
+++ b/drivers/serial/crisv10.c
@@ -23,16 +23,18 @@ static char *serial_version = "$Revision: 1.25 $";
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/system.h>
-#include <linux/delay.h>
#include <arch/svinto.h>
@@ -456,7 +458,6 @@ static struct e100_serial rs_table[] = {
#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
-static struct ktermios *serial_termios[NR_PORTS];
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
static struct fast_timer fast_timers[NR_PORTS];
#endif
@@ -4257,151 +4258,132 @@ rs_open(struct tty_struct *tty, struct file * filp)
return 0;
}
+#ifdef CONFIG_PROC_FS
/*
* /proc fs routines....
*/
-static int line_info(char *buf, struct e100_serial *info)
+static void seq_line_info(struct seq_file *m, struct e100_serial *info)
{
- char stat_buf[30];
- int ret;
unsigned long tmp;
- ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
- info->line, (unsigned long)info->ioport, info->irq);
+ seq_printf(m, "%d: uart:E100 port:%lX irq:%d",
+ info->line, (unsigned long)info->ioport, info->irq);
if (!info->ioport || (info->type == PORT_UNKNOWN)) {
- ret += sprintf(buf+ret, "\n");
- return ret;
+ seq_printf(m, "\n");
+ return;
}
- stat_buf[0] = 0;
- stat_buf[1] = 0;
- if (!E100_RTS_GET(info))
- strcat(stat_buf, "|RTS");
- if (!E100_CTS_GET(info))
- strcat(stat_buf, "|CTS");
- if (!E100_DTR_GET(info))
- strcat(stat_buf, "|DTR");
- if (!E100_DSR_GET(info))
- strcat(stat_buf, "|DSR");
- if (!E100_CD_GET(info))
- strcat(stat_buf, "|CD");
- if (!E100_RI_GET(info))
- strcat(stat_buf, "|RI");
-
- ret += sprintf(buf+ret, " baud:%d", info->baud);
-
- ret += sprintf(buf+ret, " tx:%lu rx:%lu",
+ seq_printf(m, " baud:%d", info->baud);
+ seq_printf(m, " tx:%lu rx:%lu",
(unsigned long)info->icount.tx,
(unsigned long)info->icount.rx);
tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
- if (tmp) {
- ret += sprintf(buf+ret, " tx_pend:%lu/%lu",
- (unsigned long)tmp,
- (unsigned long)SERIAL_XMIT_SIZE);
- }
+ if (tmp)
+ seq_printf(m, " tx_pend:%lu/%lu",
+ (unsigned long)tmp,
+ (unsigned long)SERIAL_XMIT_SIZE);
- ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
- (unsigned long)info->recv_cnt,
- (unsigned long)info->max_recv_cnt);
+ seq_printf(m, " rx_pend:%lu/%lu",
+ (unsigned long)info->recv_cnt,
+ (unsigned long)info->max_recv_cnt);
#if 1
if (info->port.tty) {
-
if (info->port.tty->stopped)
- ret += sprintf(buf+ret, " stopped:%i",
- (int)info->port.tty->stopped);
+ seq_printf(m, " stopped:%i",
+ (int)info->port.tty->stopped);
if (info->port.tty->hw_stopped)
- ret += sprintf(buf+ret, " hw_stopped:%i",
- (int)info->port.tty->hw_stopped);
+ seq_printf(m, " hw_stopped:%i",
+ (int)info->port.tty->hw_stopped);
}
{
unsigned char rstat = info->ioport[REG_STATUS];
- if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) )
- ret += sprintf(buf+ret, " xoff_detect:1");
+ if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect))
+ seq_printf(m, " xoff_detect:1");
}
#endif
-
-
-
if (info->icount.frame)
- ret += sprintf(buf+ret, " fe:%lu",
- (unsigned long)info->icount.frame);
+ seq_printf(m, " fe:%lu", (unsigned long)info->icount.frame);
if (info->icount.parity)
- ret += sprintf(buf+ret, " pe:%lu",
- (unsigned long)info->icount.parity);
+ seq_printf(m, " pe:%lu", (unsigned long)info->icount.parity);
if (info->icount.brk)
- ret += sprintf(buf+ret, " brk:%lu",
- (unsigned long)info->icount.brk);
+ seq_printf(m, " brk:%lu", (unsigned long)info->icount.brk);
if (info->icount.overrun)
- ret += sprintf(buf+ret, " oe:%lu",
- (unsigned long)info->icount.overrun);
+ seq_printf(m, " oe:%lu", (unsigned long)info->icount.overrun);
/*
* Last thing is the RS-232 status lines
*/
- ret += sprintf(buf+ret, " %s\n", stat_buf+1);
- return ret;
+ if (!E100_RTS_GET(info))
+ seq_puts(m, "|RTS");
+ if (!E100_CTS_GET(info))
+ seq_puts(m, "|CTS");
+ if (!E100_DTR_GET(info))
+ seq_puts(m, "|DTR");
+ if (!E100_DSR_GET(info))
+ seq_puts(m, "|DSR");
+ if (!E100_CD_GET(info))
+ seq_puts(m, "|CD");
+ if (!E100_RI_GET(info))
+ seq_puts(m, "|RI");
+ seq_puts(m, "\n");
}
-int rs_read_proc(char *page, char **start, off_t off, int count,
- int *eof, void *data)
+
+static int crisv10_proc_show(struct seq_file *m, void *v)
{
- int i, len = 0, l;
- off_t begin = 0;
+ int i;
- len += sprintf(page, "serinfo:1.0 driver:%s\n",
- serial_version);
- for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
+
+ for (i = 0; i < NR_PORTS; i++) {
if (!rs_table[i].enabled)
continue;
- l = line_info(page + len, &rs_table[i]);
- len += l;
- if (len+begin > off+count)
- goto done;
- if (len+begin < off) {
- begin += len;
- len = 0;
- }
+ seq_line_info(m, &rs_table[i]);
}
#ifdef DEBUG_LOG_INCLUDED
for (i = 0; i < debug_log_pos; i++) {
- len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data));
- len += sprintf(page + len, debug_log[i].string, debug_log[i].value);
- if (len+begin > off+count)
- goto done;
- if (len+begin < off) {
- begin += len;
- len = 0;
- }
+ seq_printf(m, "%-4i %lu.%lu ",
+ i, debug_log[i].time,
+ timer_data_to_ns(debug_log[i].timer_data));
+ seq_printf(m, debug_log[i].string, debug_log[i].value);
}
- len += sprintf(page + len, "debug_log %i/%i %li bytes\n",
- i, DEBUG_LOG_SIZE, begin+len);
+ seq_printf(m, "debug_log %i/%i\n", i, DEBUG_LOG_SIZE);
debug_log_pos = 0;
#endif
+ return 0;
+}
- *eof = 1;
-done:
- if (off >= len+begin)
- return 0;
- *start = page + (off-begin);
- return ((count < begin+len-off) ? count : begin+len-off);
+static int crisv10_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, crisv10_proc_show, NULL);
}
+static const struct file_operations crisv10_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = crisv10_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+
/* Finally, routines used to initialize the serial driver. */
-static void
-show_serial_version(void)
+static void show_serial_version(void)
{
printk(KERN_INFO
- "ETRAX 100LX serial-driver %s, (c) 2000-2004 Axis Communications AB\r\n",
+ "ETRAX 100LX serial-driver %s, "
+ "(c) 2000-2004 Axis Communications AB\r\n",
&serial_version[11]); /* "$Revision: x.yy" */
}
@@ -4425,13 +4407,14 @@ static const struct tty_operations rs_ops = {
.break_ctl = rs_break,
.send_xchar = rs_send_xchar,
.wait_until_sent = rs_wait_until_sent,
- .read_proc = rs_read_proc,
.tiocmget = rs_tiocmget,
- .tiocmset = rs_tiocmset
+ .tiocmset = rs_tiocmset,
+#ifdef CONFIG_PROC_FS
+ .proc_fops = &crisv10_proc_fops,
+#endif
};
-static int __init
-rs_init(void)
+static int __init rs_init(void)
{
int i;
struct e100_serial *info;
diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c
index 6579e2be1dd1..9f2891c2c4a2 100644
--- a/drivers/serial/icom.c
+++ b/drivers/serial/icom.c
@@ -137,7 +137,12 @@ static LIST_HEAD(icom_adapter_head);
static spinlock_t icom_lock;
#ifdef ICOM_TRACE
-static inline void trace(struct icom_port *, char *, unsigned long) {};
+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);
+}
#else
static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
#endif
@@ -408,7 +413,7 @@ static void load_code(struct icom_port *icom_port)
release_firmware(fw);
/* Set Hardware level */
- if ((icom_port->adapter->version | ADAPTER_V2) == ADAPTER_V2)
+ if (icom_port->adapter->version == ADAPTER_V2)
writeb(V2_HARDWARE, &(icom_port->dram->misc_flags));
/* Start the processor in Adapter */
@@ -861,7 +866,7 @@ static irqreturn_t icom_interrupt(int irq, void *dev_id)
/* find icom_port for this interrupt */
icom_adapter = (struct icom_adapter *) dev_id;
- if ((icom_adapter->version | ADAPTER_V2) == ADAPTER_V2) {
+ if (icom_adapter->version == ADAPTER_V2) {
int_reg = icom_adapter->base_addr + 0x8024;
adapter_interrupts = readl(int_reg);
@@ -1472,8 +1477,8 @@ static void icom_remove_adapter(struct icom_adapter *icom_adapter)
free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter);
iounmap(icom_adapter->base_addr);
- icom_free_adapter(icom_adapter);
pci_release_regions(icom_adapter->pci_dev);
+ icom_free_adapter(icom_adapter);
}
static void icom_kref_release(struct kref *kref)
@@ -1647,15 +1652,6 @@ static void __exit icom_exit(void)
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 <mjanders@us.ibm.com>");
MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
MODULE_SUPPORTED_DEVICE
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 9f460b175c50..7b5d1de9cfe3 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -8,6 +8,9 @@
* Author: Sascha Hauer <sascha@saschahauer.de>
* Copyright (C) 2004 Pengutronix
*
+ * Copyright (C) 2009 emlix GmbH
+ * Author: Fabian Godehardt (added IrDA support for iMX)
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -41,6 +44,8 @@
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -148,6 +153,7 @@
#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
+#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
#define USR1_RTSS (1<<14) /* RTS pin status */
@@ -211,10 +217,20 @@ struct imx_port {
struct timer_list timer;
unsigned int old_status;
int txirq,rxirq,rtsirq;
- int have_rtscts:1;
+ unsigned int have_rtscts:1;
+ unsigned int use_irda:1;
+ unsigned int irda_inv_rx:1;
+ unsigned int irda_inv_tx:1;
+ unsigned short trcv_delay; /* transceiver delay */
struct clk *clk;
};
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport) ((sport)->use_irda)
+#else
+#define USE_IRDA(sport) (0)
+#endif
+
/*
* Handle any change of modem status signal since we were last called.
*/
@@ -268,6 +284,48 @@ static void imx_stop_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ if (USE_IRDA(sport)) {
+ /* half duplex - wait for end of transmission */
+ int n = 256;
+ while ((--n > 0) &&
+ !(readl(sport->port.membase + USR2) & USR2_TXDC)) {
+ udelay(5);
+ barrier();
+ }
+ /*
+ * irda transceiver - wait a bit more to avoid
+ * cutoff, hardware dependent
+ */
+ udelay(sport->trcv_delay);
+
+ /*
+ * half duplex - reactivate receive mode,
+ * flush receive pipe echo crap
+ */
+ if (readl(sport->port.membase + USR2) & USR2_TXDC) {
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~(UCR4_TCEN);
+ writel(temp, sport->port.membase + UCR4);
+
+ while (readl(sport->port.membase + URXD0) &
+ URXD_CHARRDY)
+ barrier();
+
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RRDYEN;
+ writel(temp, sport->port.membase + UCR1);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_DREN;
+ writel(temp, sport->port.membase + UCR4);
+ }
+ return;
+ }
+
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
}
@@ -302,13 +360,15 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
/* send xmit->buf[xmit->tail]
* out the port here */
writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
- xmit->tail = (xmit->tail + 1) &
- (UART_XMIT_SIZE - 1);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
}
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
if (uart_circ_empty(xmit))
imx_stop_tx(&sport->port);
}
@@ -321,9 +381,30 @@ static void imx_start_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ if (USE_IRDA(sport)) {
+ /* half duplex in IrDA mode; have to disable receive mode */
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~(UCR4_DREN);
+ writel(temp, sport->port.membase + UCR4);
+
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+ }
+
temp = readl(sport->port.membase + UCR1);
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ if (USE_IRDA(sport)) {
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_TRDYEN;
+ writel(temp, sport->port.membase + UCR1);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_TCEN;
+ writel(temp, sport->port.membase + UCR4);
+ }
+
if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
imx_transmit_buffer(sport);
}
@@ -395,8 +476,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
continue;
}
- if (uart_handle_sysrq_char
- (&sport->port, (unsigned char)rx))
+ if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
continue;
if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
@@ -471,26 +551,26 @@ static unsigned int imx_tx_empty(struct uart_port *port)
*/
static unsigned int imx_get_mctrl(struct uart_port *port)
{
- struct imx_port *sport = (struct imx_port *)port;
- unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+ struct imx_port *sport = (struct imx_port *)port;
+ unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
- if (readl(sport->port.membase + USR1) & USR1_RTSS)
- tmp |= TIOCM_CTS;
+ if (readl(sport->port.membase + USR1) & USR1_RTSS)
+ tmp |= TIOCM_CTS;
- if (readl(sport->port.membase + UCR2) & UCR2_CTS)
- tmp |= TIOCM_RTS;
+ if (readl(sport->port.membase + UCR2) & UCR2_CTS)
+ tmp |= TIOCM_RTS;
- return tmp;
+ return tmp;
}
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- struct imx_port *sport = (struct imx_port *)port;
+ struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
- if (mctrl & TIOCM_RTS)
+ if (mctrl & TIOCM_RTS)
temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
@@ -534,12 +614,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
if(!ufcr_rfdiv)
ufcr_rfdiv = 1;
- if(ufcr_rfdiv >= 7)
- ufcr_rfdiv = 6;
- else
- ufcr_rfdiv = 6 - ufcr_rfdiv;
-
- val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
+ val |= UFCR_RFDIV_REG(ufcr_rfdiv);
writel(val, sport->port.membase + UFCR);
@@ -558,8 +633,24 @@ static int imx_startup(struct uart_port *port)
* requesting IRQs
*/
temp = readl(sport->port.membase + UCR4);
+
+ if (USE_IRDA(sport))
+ temp |= UCR4_IRSC;
+
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
+ if (USE_IRDA(sport)) {
+ /* reset fifo's and state machines */
+ int i = 100;
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~UCR2_SRST;
+ writel(temp, sport->port.membase + UCR2);
+ while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
+ (--i > 0)) {
+ udelay(1);
+ }
+ }
+
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
* chips only have one interrupt.
@@ -575,12 +666,16 @@ static int imx_startup(struct uart_port *port)
if (retval)
goto error_out2;
- retval = request_irq(sport->rtsirq, imx_rtsint,
- (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- DRIVER_NAME, sport);
- if (retval)
- goto error_out3;
+ /* do not use RTS IRQ on IrDA */
+ if (!USE_IRDA(sport)) {
+ retval = request_irq(sport->rtsirq, imx_rtsint,
+ (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING,
+ DRIVER_NAME, sport);
+ if (retval)
+ goto error_out3;
+ }
} else {
retval = request_irq(sport->port.irq, imx_int, 0,
DRIVER_NAME, sport);
@@ -597,18 +692,49 @@ static int imx_startup(struct uart_port *port)
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+
+ if (USE_IRDA(sport)) {
+ temp |= UCR1_IREN;
+ temp &= ~(UCR1_RTSDEN);
+ }
+
writel(temp, sport->port.membase + UCR1);
temp = readl(sport->port.membase + UCR2);
temp |= (UCR2_RXEN | UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);
+ if (USE_IRDA(sport)) {
+ /* clear RX-FIFO */
+ int i = 64;
+ while ((--i > 0) &&
+ (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
+ barrier();
+ }
+ }
+
#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
temp = readl(sport->port.membase + UCR3);
temp |= UCR3_RXDMUXSEL;
writel(temp, sport->port.membase + UCR3);
#endif
+ if (USE_IRDA(sport)) {
+ temp = readl(sport->port.membase + UCR4);
+ if (sport->irda_inv_rx)
+ temp |= UCR4_INVR;
+ else
+ temp &= ~(UCR4_INVR);
+ writel(temp | UCR4_DREN, sport->port.membase + UCR4);
+
+ temp = readl(sport->port.membase + UCR3);
+ if (sport->irda_inv_tx)
+ temp |= UCR3_INVT;
+ else
+ temp &= ~(UCR3_INVT);
+ writel(temp, sport->port.membase + UCR3);
+ }
+
/*
* Enable modem status interrupts
*/
@@ -616,6 +742,16 @@ static int imx_startup(struct uart_port *port)
imx_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock,flags);
+ if (USE_IRDA(sport)) {
+ struct imxuart_platform_data *pdata;
+ pdata = sport->port.dev->platform_data;
+ sport->irda_inv_rx = pdata->irda_inv_rx;
+ sport->irda_inv_tx = pdata->irda_inv_tx;
+ sport->trcv_delay = pdata->transceiver_delay;
+ if (pdata->irda_enable)
+ pdata->irda_enable(1);
+ }
+
return 0;
error_out3:
@@ -633,6 +769,17 @@ static void imx_shutdown(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_TXEN);
+ writel(temp, sport->port.membase + UCR2);
+
+ if (USE_IRDA(sport)) {
+ struct imxuart_platform_data *pdata;
+ pdata = sport->port.dev->platform_data;
+ if (pdata->irda_enable)
+ pdata->irda_enable(0);
+ }
+
/*
* Stop our timer.
*/
@@ -642,7 +789,8 @@ static void imx_shutdown(struct uart_port *port)
* Free the interrupts
*/
if (sport->txirq > 0) {
- free_irq(sport->rtsirq, sport);
+ if (!USE_IRDA(sport))
+ free_irq(sport->rtsirq, sport);
free_irq(sport->txirq, sport);
free_irq(sport->rxirq, sport);
} else
@@ -654,6 +802,9 @@ static void imx_shutdown(struct uart_port *port)
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+ if (USE_IRDA(sport))
+ temp &= ~(UCR1_IREN);
+
writel(temp, sport->port.membase + UCR1);
}
@@ -665,7 +816,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long flags;
unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned int div, num, denom, ufcr;
+ unsigned int div, ufcr;
+ unsigned long num, denom;
+ uint64_t tdiv64;
/*
* If we don't support modem control lines, don't allow
@@ -761,38 +914,39 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
sport->port.membase + UCR2);
old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
- div = sport->port.uartclk / (baud * 16);
- if (div > 7)
- div = 7;
- if (!div)
+ if (USE_IRDA(sport)) {
+ /*
+ * use maximum available submodule frequency to
+ * avoid missing short pulses due to low sampling rate
+ */
div = 1;
-
- num = baud;
- denom = port->uartclk / div / 16;
-
- /* shift num and denom right until they fit into 16 bits */
- while (num > 0x10000 || denom > 0x10000) {
- num >>= 1;
- denom >>= 1;
+ } else {
+ div = sport->port.uartclk / (baud * 16);
+ if (div > 7)
+ div = 7;
+ if (!div)
+ div = 1;
}
- if (num > 0)
- num -= 1;
- if (denom > 0)
- denom -= 1;
- writel(num, sport->port.membase + UBIR);
- writel(denom, sport->port.membase + UBMR);
+ rational_best_approximation(16 * div * baud, sport->port.uartclk,
+ 1 << 16, 1 << 16, &num, &denom);
- if (div == 7)
- div = 6; /* 6 in RFDIV means divide by 7 */
- else
- div = 6 - div;
+ tdiv64 = sport->port.uartclk;
+ tdiv64 *= num;
+ do_div(tdiv64, denom * 16 * div);
+ tty_encode_baud_rate(sport->port.info->port.tty,
+ (speed_t)tdiv64, (speed_t)tdiv64);
+
+ num -= 1;
+ denom -= 1;
ufcr = readl(sport->port.membase + UFCR);
- ufcr = (ufcr & (~UFCR_RFDIV)) |
- (div << 7);
+ ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
writel(ufcr, sport->port.membase + UFCR);
+ writel(num, sport->port.membase + UBIR);
+ writel(denom, sport->port.membase + UBMR);
+
#ifdef ONEMS
writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
#endif
@@ -1031,6 +1185,8 @@ imx_console_setup(struct console *co, char *options)
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
co->index = 0;
sport = imx_ports[co->index];
+ if(sport == NULL)
+ return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1070,22 +1226,22 @@ static struct uart_driver imx_reg = {
static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
{
- struct imx_port *sport = platform_get_drvdata(dev);
+ struct imx_port *sport = platform_get_drvdata(dev);
- if (sport)
- uart_suspend_port(&imx_reg, &sport->port);
+ if (sport)
+ uart_suspend_port(&imx_reg, &sport->port);
- return 0;
+ return 0;
}
static int serial_imx_resume(struct platform_device *dev)
{
- struct imx_port *sport = platform_get_drvdata(dev);
+ struct imx_port *sport = platform_get_drvdata(dev);
- if (sport)
- uart_resume_port(&imx_reg, &sport->port);
+ if (sport)
+ uart_resume_port(&imx_reg, &sport->port);
- return 0;
+ return 0;
}
static int serial_imx_probe(struct platform_device *pdev)
@@ -1141,19 +1297,29 @@ static int serial_imx_probe(struct platform_device *pdev)
imx_ports[pdev->id] = sport;
pdata = pdev->dev.platform_data;
- if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
+ if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
sport->have_rtscts = 1;
+#ifdef CONFIG_IRDA
+ if (pdata && (pdata->flags & IMXUART_IRDA))
+ sport->use_irda = 1;
+#endif
+
if (pdata->init) {
ret = pdata->init(pdev);
if (ret)
goto clkput;
}
- uart_add_one_port(&imx_reg, &sport->port);
+ ret = uart_add_one_port(&imx_reg, &sport->port);
+ if (ret)
+ goto deinit;
platform_set_drvdata(pdev, &sport->port);
return 0;
+deinit:
+ if (pdata->exit)
+ pdata->exit(pdev);
clkput:
clk_put(sport->clk);
clk_disable(sport->clk);
@@ -1191,13 +1357,13 @@ static int serial_imx_remove(struct platform_device *pdev)
}
static struct platform_driver serial_imx_driver = {
- .probe = serial_imx_probe,
- .remove = serial_imx_remove,
+ .probe = serial_imx_probe,
+ .remove = serial_imx_remove,
.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.driver = {
- .name = "imx-uart",
+ .name = "imx-uart",
.owner = THIS_MODULE,
},
};
diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index 8871aaa3dba6..4e5f3bde0461 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -61,6 +61,7 @@ enum {
if ((DBG_##nlevel & jsm_debug)) \
dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+#define MAXLINES 256
#define MAXPORTS 8
#define MAX_STOPS_SENT 5
@@ -130,8 +131,6 @@ struct jsm_board
struct pci_dev *pci_dev;
u32 maxports; /* MAX ports this board can handle */
- spinlock_t bd_lock; /* Used to protect board */
-
spinlock_t bd_intr_lock; /* Used to protect the poller tasklet and
* the interrupt routine from each other.
*/
diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c
index d2d32a198629..b3604aa322a4 100644
--- a/drivers/serial/jsm/jsm_driver.c
+++ b/drivers/serial/jsm/jsm_driver.c
@@ -88,7 +88,6 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
else
brd->maxports = 2;
- spin_lock_init(&brd->bd_lock);
spin_lock_init(&brd->bd_intr_lock);
/* store which revision we have */
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 31496dc0a0d1..107ce2e187b8 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -33,6 +33,8 @@
#include "jsm.h"
+static DECLARE_BITMAP(linemap, MAXLINES);
+
static void jsm_carrier(struct jsm_channel *ch);
static inline int jsm_get_mstat(struct jsm_channel *ch)
@@ -433,6 +435,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
int __devinit jsm_uart_port_init(struct jsm_board *brd)
{
int i;
+ unsigned int line;
struct jsm_channel *ch;
if (!brd)
@@ -459,9 +462,15 @@ int __devinit jsm_uart_port_init(struct jsm_board *brd)
brd->channels[i]->uart_port.membase = brd->re_map_membase;
brd->channels[i]->uart_port.fifosize = 16;
brd->channels[i]->uart_port.ops = &jsm_ops;
- brd->channels[i]->uart_port.line = brd->channels[i]->ch_portnum + brd->boardnum * 2;
+ line = find_first_zero_bit(linemap, MAXLINES);
+ if (line >= MAXLINES) {
+ printk(KERN_INFO "jsm: linemap is full, added device failed\n");
+ continue;
+ } else
+ set_bit((int)line, linemap);
+ brd->channels[i]->uart_port.line = line;
if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port))
- printk(KERN_INFO "Added device failed\n");
+ printk(KERN_INFO "jsm: add device failed\n");
else
printk(KERN_INFO "Added device \n");
}
@@ -494,6 +503,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
ch = brd->channels[i];
+ clear_bit((int)(ch->uart_port.line), linemap);
uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
}
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 7f72f8ceaa6f..b3feb6198d57 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -988,7 +988,7 @@ mpc52xx_console_setup(struct console *co, char *options)
pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n",
co, co->index, options);
- if ((co->index < 0) || (co->index > MPC52xx_PSC_MAXNUM)) {
+ if ((co->index < 0) || (co->index >= MPC52xx_PSC_MAXNUM)) {
pr_debug("PSC%x out of range\n", co->index);
return -EINVAL;
}
diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c
index 32f3eaf0d262..9e150b19d726 100644
--- a/drivers/serial/nwpserial.c
+++ b/drivers/serial/nwpserial.c
@@ -145,11 +145,13 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
ch = dcr_read(up->dcr_host, UART_RX);
if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID)
tty_insert_flip_char(tty, ch, TTY_NORMAL);
- } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR);
+ } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
tty_flip_buffer_push(tty);
ret = IRQ_HANDLED;
+ /* clear interrupt */
+ dcr_write(up->dcr_host, UART_IIR, 1);
out:
spin_unlock(&up->port.lock);
return ret;
diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c
index 06936d13393f..3e3785233682 100644
--- a/drivers/serial/s3c6400.c
+++ b/drivers/serial/s3c6400.c
@@ -102,6 +102,7 @@ static struct s3c24xx_uart_info s3c6400_uart_inf = {
.name = "Samsung S3C6400 UART",
.type = PORT_S3C6400,
.fifosize = 64,
+ .has_divslot = 1,
.rx_fifomask = S3C2440_UFSTAT_RXMASK,
.rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
.rx_fifofull = S3C2440_UFSTAT_RXFULL,
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index e06686ae858b..93b5d75db126 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -508,6 +508,7 @@ s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
struct baud_calc {
struct s3c24xx_uart_clksrc *clksrc;
unsigned int calc;
+ unsigned int divslot;
unsigned int quot;
struct clk *src;
};
@@ -517,6 +518,7 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
struct s3c24xx_uart_clksrc *clksrc,
unsigned int baud)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
unsigned long rate;
calc->src = clk_get(port->dev, clksrc->name);
@@ -527,8 +529,24 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
rate /= clksrc->divisor;
calc->clksrc = clksrc;
- calc->quot = (rate + (8 * baud)) / (16 * baud);
- calc->calc = (rate / (calc->quot * 16));
+
+ if (ourport->info->has_divslot) {
+ unsigned long div = rate / baud;
+
+ /* The UDIVSLOT register on the newer UARTs allows us to
+ * get a divisor adjustment of 1/16th on the baud clock.
+ *
+ * We don't keep the UDIVSLOT value (the 16ths we calculated
+ * by not multiplying the baud by 16) as it is easy enough
+ * to recalculate.
+ */
+
+ calc->quot = div / 16;
+ calc->calc = rate / div;
+ } else {
+ calc->quot = (rate + (8 * baud)) / (16 * baud);
+ calc->calc = (rate / (calc->quot * 16));
+ }
calc->quot--;
return 1;
@@ -611,6 +629,30 @@ static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
return best->quot;
}
+/* udivslot_table[]
+ *
+ * This table takes the fractional value of the baud divisor and gives
+ * the recommended setting for the UDIVSLOT register.
+ */
+static u16 udivslot_table[16] = {
+ [0] = 0x0000,
+ [1] = 0x0080,
+ [2] = 0x0808,
+ [3] = 0x0888,
+ [4] = 0x2222,
+ [5] = 0x4924,
+ [6] = 0x4A52,
+ [7] = 0x54AA,
+ [8] = 0x5555,
+ [9] = 0xD555,
+ [10] = 0xD5D5,
+ [11] = 0xDDD5,
+ [12] = 0xDDDD,
+ [13] = 0xDFDD,
+ [14] = 0xDFDF,
+ [15] = 0xFFDF,
+};
+
static void s3c24xx_serial_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
@@ -623,6 +665,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
unsigned int baud, quot;
unsigned int ulcon;
unsigned int umcon;
+ unsigned int udivslot = 0;
/*
* We don't support modem control lines.
@@ -644,6 +687,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
/* check to see if we need to change clock source */
if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
+ dbg("selecting clock %p\n", clk);
s3c24xx_serial_setsource(port, clksrc);
if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
@@ -658,6 +702,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
+ if (ourport->info->has_divslot) {
+ unsigned int div = ourport->baudclk_rate / baud;
+
+ udivslot = udivslot_table[div & 15];
+ dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ }
+
switch (termios->c_cflag & CSIZE) {
case CS5:
dbg("config: 5bits/char\n");
@@ -697,12 +748,16 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
spin_lock_irqsave(&port->lock, flags);
- dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
+ dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
+ ulcon, quot, udivslot);
wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
wr_regl(port, S3C2410_UMCON, umcon);
+ if (ourport->info->has_divslot)
+ wr_regl(port, S3C2443_DIVSLOT, udivslot);
+
dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
rd_regl(port, S3C2410_ULCON),
rd_regl(port, S3C2410_UCON),
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h
index 571d6b90d206..7afb94843a08 100644
--- a/drivers/serial/samsung.h
+++ b/drivers/serial/samsung.h
@@ -21,6 +21,10 @@ struct s3c24xx_uart_info {
unsigned long tx_fifoshift;
unsigned long tx_fifofull;
+ /* uart port features */
+
+ unsigned int has_divslot:1;
+
/* clock source control */
int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index dbf5357a77b3..a4cf1079b312 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -47,12 +47,17 @@
#include <linux/clk.h>
#include <linux/ctype.h>
#include <linux/err.h>
+#include <linux/list.h>
#ifdef CONFIG_SUPERH
#include <asm/clock.h>
#include <asm/sh_bios.h>
#endif
+#ifdef CONFIG_H8300
+#include <asm/gpio.h>
+#endif
+
#include "sh-sci.h"
struct sci_port {
@@ -75,14 +80,22 @@ struct sci_port {
int break_flag;
#ifdef CONFIG_HAVE_CLK
- /* Port clock */
- struct clk *clk;
+ /* Interface clock */
+ struct clk *iclk;
+ /* Data clock */
+ struct clk *dclk;
#endif
+ struct list_head node;
};
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
-static struct sci_port *serial_console_port;
+struct sh_sci_priv {
+ spinlock_t lock;
+ struct list_head ports;
+
+#ifdef CONFIG_HAVE_CLK
+ struct notifier_block clk_nb;
#endif
+};
/* Function prototypes */
static void sci_stop_tx(struct uart_port *port);
@@ -138,9 +151,8 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
status = sci_in(port, SCxSR);
} while (!(status & SCxSR_TDxE(port)));
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
sci_out(port, SCxTDR, c);
+ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
@@ -159,12 +171,12 @@ static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
*mstpcrl &= ~mask;
}
-static inline void h8300_sci_enable(struct uart_port *port)
+static void h8300_sci_enable(struct uart_port *port)
{
h8300_sci_config(port, sci_enable);
}
-static inline void h8300_sci_disable(struct uart_port *port)
+static void h8300_sci_disable(struct uart_port *port)
{
h8300_sci_config(port, sci_disable);
}
@@ -611,7 +623,7 @@ static inline int sci_handle_breaks(struct uart_port *port)
int copied = 0;
unsigned short status = sci_in(port, SCxSR);
struct tty_struct *tty = port->info->port.tty;
- struct sci_port *s = &sci_ports[port->line];
+ struct sci_port *s = to_sci_port(port);
if (uart_handle_break(port))
return 0;
@@ -726,19 +738,43 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
static int sci_notifier(struct notifier_block *self,
unsigned long phase, void *p)
{
- int i;
+ struct sh_sci_priv *priv = container_of(self,
+ struct sh_sci_priv, clk_nb);
+ struct sci_port *sci_port;
+ unsigned long flags;
if ((phase == CPUFREQ_POSTCHANGE) ||
- (phase == CPUFREQ_RESUMECHANGE))
- for (i = 0; i < SCI_NPORTS; i++) {
- struct sci_port *s = &sci_ports[i];
- s->port.uartclk = clk_get_rate(s->clk);
- }
+ (phase == CPUFREQ_RESUMECHANGE)) {
+ spin_lock_irqsave(&priv->lock, flags);
+ list_for_each_entry(sci_port, &priv->ports, node)
+ sci_port->port.uartclk = clk_get_rate(sci_port->dclk);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
return NOTIFY_OK;
}
-static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 };
+static void sci_clk_enable(struct uart_port *port)
+{
+ struct sci_port *sci_port = to_sci_port(port);
+
+ clk_enable(sci_port->dclk);
+ sci_port->port.uartclk = clk_get_rate(sci_port->dclk);
+
+ if (sci_port->iclk)
+ clk_enable(sci_port->iclk);
+}
+
+static void sci_clk_disable(struct uart_port *port)
+{
+ struct sci_port *sci_port = to_sci_port(port);
+
+ if (sci_port->iclk)
+ clk_disable(sci_port->iclk);
+
+ clk_disable(sci_port->dclk);
+}
#endif
static int sci_request_irq(struct sci_port *port)
@@ -865,15 +901,11 @@ static void sci_break_ctl(struct uart_port *port, int break_state)
static int sci_startup(struct uart_port *port)
{
- struct sci_port *s = &sci_ports[port->line];
+ struct sci_port *s = to_sci_port(port);
if (s->enable)
s->enable(port);
-#ifdef CONFIG_HAVE_CLK
- s->clk = clk_get(NULL, "module_clk");
-#endif
-
sci_request_irq(s);
sci_start_tx(port);
sci_start_rx(port, 1);
@@ -883,7 +915,7 @@ static int sci_startup(struct uart_port *port)
static void sci_shutdown(struct uart_port *port)
{
- struct sci_port *s = &sci_ports[port->line];
+ struct sci_port *s = to_sci_port(port);
sci_stop_rx(port);
sci_stop_tx(port);
@@ -891,11 +923,6 @@ static void sci_shutdown(struct uart_port *port)
if (s->disable)
s->disable(port);
-
-#ifdef CONFIG_HAVE_CLK
- clk_put(s->clk);
- s->clk = NULL;
-#endif
}
static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -980,25 +1007,31 @@ static int sci_request_port(struct uart_port *port)
static void sci_config_port(struct uart_port *port, int flags)
{
- struct sci_port *s = &sci_ports[port->line];
+ struct sci_port *s = to_sci_port(port);
port->type = s->type;
- if (port->flags & UPF_IOREMAP && !port->membase) {
-#if defined(CONFIG_SUPERH64)
- port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF");
- port->membase = (void __iomem *)port->mapbase;
-#else
+ if (port->membase)
+ return;
+
+ if (port->flags & UPF_IOREMAP) {
port->membase = ioremap_nocache(port->mapbase, 0x40);
-#endif
- dev_err(port->dev, "can't remap port#%d\n", port->line);
+ if (IS_ERR(port->membase))
+ dev_err(port->dev, "can't remap port#%d\n", port->line);
+ } else {
+ /*
+ * For the simple (and majority of) cases where we don't
+ * need to do any remapping, just cast the cookie
+ * directly.
+ */
+ port->membase = (void __iomem *)port->mapbase;
}
}
static int sci_verify_port(struct uart_port *port, struct serial_struct *ser)
{
- struct sci_port *s = &sci_ports[port->line];
+ struct sci_port *s = to_sci_port(port);
if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs)
return -EINVAL;
@@ -1032,63 +1065,60 @@ static struct uart_ops sci_uart_ops = {
#endif
};
-static void __init sci_init_ports(void)
+static void __devinit sci_init_single(struct platform_device *dev,
+ struct sci_port *sci_port,
+ unsigned int index,
+ struct plat_sci_port *p)
{
- static int first = 1;
- int i;
-
- if (!first)
- return;
-
- first = 0;
-
- for (i = 0; i < SCI_NPORTS; i++) {
- sci_ports[i].port.ops = &sci_uart_ops;
- sci_ports[i].port.iotype = UPIO_MEM;
- sci_ports[i].port.line = i;
- sci_ports[i].port.fifosize = 1;
+ sci_port->port.ops = &sci_uart_ops;
+ sci_port->port.iotype = UPIO_MEM;
+ sci_port->port.line = index;
+ sci_port->port.fifosize = 1;
#if defined(__H8300H__) || defined(__H8300S__)
#ifdef __H8300S__
- sci_ports[i].enable = h8300_sci_enable;
- sci_ports[i].disable = h8300_sci_disable;
+ sci_port->enable = h8300_sci_enable;
+ sci_port->disable = h8300_sci_disable;
#endif
- sci_ports[i].port.uartclk = CONFIG_CPU_CLOCK;
+ sci_port->port.uartclk = CONFIG_CPU_CLOCK;
#elif defined(CONFIG_HAVE_CLK)
- /*
- * XXX: We should use a proper SCI/SCIF clock
- */
- {
- struct clk *clk = clk_get(NULL, "module_clk");
- sci_ports[i].port.uartclk = clk_get_rate(clk);
- clk_put(clk);
- }
+ sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL;
+ sci_port->dclk = clk_get(&dev->dev, "peripheral_clk");
+ sci_port->enable = sci_clk_enable;
+ sci_port->disable = sci_clk_disable;
#else
#error "Need a valid uartclk"
#endif
- sci_ports[i].break_timer.data = (unsigned long)&sci_ports[i];
- sci_ports[i].break_timer.function = sci_break_timer;
+ sci_port->break_timer.data = (unsigned long)sci_port;
+ sci_port->break_timer.function = sci_break_timer;
+ init_timer(&sci_port->break_timer);
- init_timer(&sci_ports[i].break_timer);
- }
-}
-
-int __init early_sci_setup(struct uart_port *port)
-{
- if (unlikely(port->line > SCI_NPORTS))
- return -ENODEV;
+ sci_port->port.mapbase = p->mapbase;
+ sci_port->port.membase = p->membase;
- sci_init_ports();
+ sci_port->port.irq = p->irqs[SCIx_TXI_IRQ];
+ sci_port->port.flags = p->flags;
+ sci_port->port.dev = &dev->dev;
+ sci_port->type = sci_port->port.type = p->type;
- sci_ports[port->line].port.membase = port->membase;
- sci_ports[port->line].port.mapbase = port->mapbase;
- sci_ports[port->line].port.type = port->type;
+ memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs));
- return 0;
}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+static struct tty_driver *serial_console_device(struct console *co, int *index)
+{
+ struct uart_driver *p = &sci_uart_driver;
+ *index = co->index;
+ return p->tty_driver;
+}
+
+static void serial_console_putchar(struct uart_port *port, int ch)
+{
+ sci_poll_put_char(port, ch);
+}
+
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
@@ -1096,25 +1126,27 @@ int __init early_sci_setup(struct uart_port *port)
static void serial_console_write(struct console *co, const char *s,
unsigned count)
{
- struct uart_port *port = &serial_console_port->port;
+ struct uart_port *port = co->data;
+ struct sci_port *sci_port = to_sci_port(port);
unsigned short bits;
- int i;
- for (i = 0; i < count; i++) {
- if (*s == 10)
- sci_poll_put_char(port, '\r');
+ if (sci_port->enable)
+ sci_port->enable(port);
- sci_poll_put_char(port, *s++);
- }
+ uart_console_write(port, s, count, serial_console_putchar);
/* wait until fifo is empty and last bit has been transmitted */
bits = SCxSR_TDxE(port) | SCxSR_TEND(port);
while ((sci_in(port, SCxSR) & bits) != bits)
cpu_relax();
+
+ if (sci_port->disable);
+ sci_port->disable(port);
}
static int __init serial_console_setup(struct console *co, char *options)
{
+ struct sci_port *sci_port;
struct uart_port *port;
int baud = 115200;
int bits = 8;
@@ -1130,8 +1162,9 @@ static int __init serial_console_setup(struct console *co, char *options)
if (co->index >= SCI_NPORTS)
co->index = 0;
- serial_console_port = &sci_ports[co->index];
- port = &serial_console_port->port;
+ sci_port = &sci_ports[co->index];
+ port = &sci_port->port;
+ co->data = port;
/*
* Also need to check port->type, we don't actually have any
@@ -1141,21 +1174,11 @@ static int __init serial_console_setup(struct console *co, char *options)
*/
if (!port->type)
return -ENODEV;
- if (!port->membase || !port->mapbase)
- return -ENODEV;
-
- port->type = serial_console_port->type;
-
-#ifdef CONFIG_HAVE_CLK
- if (!serial_console_port->clk)
- serial_console_port->clk = clk_get(NULL, "module_clk");
-#endif
- if (port->flags & UPF_IOREMAP)
- sci_config_port(port, 0);
+ sci_config_port(port, 0);
- if (serial_console_port->enable)
- serial_console_port->enable(port);
+ if (sci_port->enable)
+ sci_port->enable(port);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1166,22 +1189,21 @@ static int __init serial_console_setup(struct console *co, char *options)
if (ret == 0)
sci_stop_rx(port);
#endif
+ /* TODO: disable clock */
return ret;
}
static struct console serial_console = {
.name = "ttySC",
- .device = uart_console_device,
+ .device = serial_console_device,
.write = serial_console_write,
.setup = serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
- .data = &sci_uart_driver,
};
static int __init sci_console_init(void)
{
- sci_init_ports();
register_console(&serial_console);
return 0;
}
@@ -1207,6 +1229,61 @@ static struct uart_driver sci_uart_driver = {
.cons = SCI_CONSOLE,
};
+
+static int sci_remove(struct platform_device *dev)
+{
+ struct sh_sci_priv *priv = platform_get_drvdata(dev);
+ struct sci_port *p;
+ unsigned long flags;
+
+#ifdef CONFIG_HAVE_CLK
+ cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
+#endif
+
+ spin_lock_irqsave(&priv->lock, flags);
+ list_for_each_entry(p, &priv->ports, node)
+ uart_remove_one_port(&sci_uart_driver, &p->port);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ kfree(priv);
+ return 0;
+}
+
+static int __devinit sci_probe_single(struct platform_device *dev,
+ unsigned int index,
+ struct plat_sci_port *p,
+ struct sci_port *sciport)
+{
+ struct sh_sci_priv *priv = platform_get_drvdata(dev);
+ unsigned long flags;
+ int ret;
+
+ /* Sanity check */
+ if (unlikely(index >= SCI_NPORTS)) {
+ dev_notice(&dev->dev, "Attempting to register port "
+ "%d when only %d are available.\n",
+ index+1, SCI_NPORTS);
+ dev_notice(&dev->dev, "Consider bumping "
+ "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
+ return 0;
+ }
+
+ sci_init_single(dev, sciport, index, p);
+
+ ret = uart_add_one_port(&sci_uart_driver, &sciport->port);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&sciport->node);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ list_add(&sciport->node, &priv->ports);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
/*
* Register a set of serial devices attached to a platform device. The
* list is terminated with a zero flags entry, which means we expect
@@ -1216,57 +1293,34 @@ static struct uart_driver sci_uart_driver = {
static int __devinit sci_probe(struct platform_device *dev)
{
struct plat_sci_port *p = dev->dev.platform_data;
+ struct sh_sci_priv *priv;
int i, ret = -EINVAL;
- for (i = 0; p && p->flags != 0; p++, i++) {
- struct sci_port *sciport = &sci_ports[i];
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- /* Sanity check */
- if (unlikely(i == SCI_NPORTS)) {
- dev_notice(&dev->dev, "Attempting to register port "
- "%d when only %d are available.\n",
- i+1, SCI_NPORTS);
- dev_notice(&dev->dev, "Consider bumping "
- "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
- break;
- }
+ INIT_LIST_HEAD(&priv->ports);
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(dev, priv);
- sciport->port.mapbase = p->mapbase;
+#ifdef CONFIG_HAVE_CLK
+ priv->clk_nb.notifier_call = sci_notifier;
+ cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
+#endif
- if (p->mapbase && !p->membase) {
- if (p->flags & UPF_IOREMAP) {
- p->membase = ioremap_nocache(p->mapbase, 0x40);
- if (IS_ERR(p->membase)) {
- ret = PTR_ERR(p->membase);
- goto err_unreg;
- }
- } else {
- /*
- * For the simple (and majority of) cases
- * where we don't need to do any remapping,
- * just cast the cookie directly.
- */
- p->membase = (void __iomem *)p->mapbase;
- }
+ if (dev->id != -1) {
+ ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]);
+ if (ret)
+ goto err_unreg;
+ } else {
+ for (i = 0; p && p->flags != 0; p++, i++) {
+ ret = sci_probe_single(dev, i, p, &sci_ports[i]);
+ if (ret)
+ goto err_unreg;
}
-
- sciport->port.membase = p->membase;
-
- sciport->port.irq = p->irqs[SCIx_TXI_IRQ];
- sciport->port.flags = p->flags;
- sciport->port.dev = &dev->dev;
-
- sciport->type = sciport->port.type = p->type;
-
- memcpy(&sciport->irqs, &p->irqs, sizeof(p->irqs));
-
- uart_add_one_port(&sci_uart_driver, &sciport->port);
}
-#ifdef CONFIG_HAVE_CLK
- cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
-
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
@@ -1274,50 +1328,36 @@ static int __devinit sci_probe(struct platform_device *dev)
return 0;
err_unreg:
- for (i = i - 1; i >= 0; i--)
- uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
-
+ sci_remove(dev);
return ret;
}
-static int __devexit sci_remove(struct platform_device *dev)
-{
- int i;
-
-#ifdef CONFIG_HAVE_CLK
- cpufreq_unregister_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
-
- for (i = 0; i < SCI_NPORTS; i++)
- uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
-
- return 0;
-}
-
static int sci_suspend(struct platform_device *dev, pm_message_t state)
{
- int i;
+ struct sh_sci_priv *priv = platform_get_drvdata(dev);
+ struct sci_port *p;
+ unsigned long flags;
- for (i = 0; i < SCI_NPORTS; i++) {
- struct sci_port *p = &sci_ports[i];
+ spin_lock_irqsave(&priv->lock, flags);
+ list_for_each_entry(p, &priv->ports, node)
+ uart_suspend_port(&sci_uart_driver, &p->port);
- if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
- uart_suspend_port(&sci_uart_driver, &p->port);
- }
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int sci_resume(struct platform_device *dev)
{
- int i;
+ struct sh_sci_priv *priv = platform_get_drvdata(dev);
+ struct sci_port *p;
+ unsigned long flags;
- for (i = 0; i < SCI_NPORTS; i++) {
- struct sci_port *p = &sci_ports[i];
+ spin_lock_irqsave(&priv->lock, flags);
+ list_for_each_entry(p, &priv->ports, node)
+ uart_resume_port(&sci_uart_driver, &p->port);
- if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
- uart_resume_port(&sci_uart_driver, &p->port);
- }
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -1339,8 +1379,6 @@ static int __init sci_init(void)
printk(banner);
- sci_init_ports();
-
ret = uart_register_driver(&sci_uart_driver);
if (likely(ret == 0)) {
ret = platform_driver_register(&sci_driver);
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index d0aa82d7fce0..38072c15b845 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -91,6 +91,9 @@
# define SCSPTR5 0xa4050128
# define SCIF_ORER 0x0001 /* overrun error bit */
# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
+# define SCIF_ORER 0x0001 /* overrun error bit */
+# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* overrun error bit */
@@ -314,7 +317,18 @@
} \
}
-#define CPU_SCIF_FNS(name, scif_offset, scif_size) \
+#ifdef CONFIG_H8300
+/* h8300 don't have SCIF */
+#define CPU_SCIF_FNS(name) \
+ static inline unsigned int sci_##name##_in(struct uart_port *port) \
+ { \
+ return 0; \
+ } \
+ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
+ { \
+ }
+#else
+#define CPU_SCIF_FNS(name, scif_offset, scif_size) \
static inline unsigned int sci_##name##_in(struct uart_port *port) \
{ \
SCI_IN(scif_size, scif_offset); \
@@ -323,6 +337,7 @@
{ \
SCI_OUT(scif_size, scif_offset, value); \
}
+#endif
#define CPU_SCI_FNS(name, sci_offset, sci_size) \
static inline unsigned int sci_##name##_in(struct uart_port* port) \
@@ -360,8 +375,10 @@
sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
h8_sci_offset, h8_sci_size) \
CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size)
-#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
+ CPU_SCIF_FNS(name)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
+ defined(CONFIG_CPU_SUBTYPE_SH7724)
#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \
CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size)
#define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \
@@ -390,7 +407,8 @@ SCIF_FNS(SCFDR, 0x1c, 16)
SCIF_FNS(SCxTDR, 0x20, 8)
SCIF_FNS(SCxRDR, 0x24, 8)
SCIF_FNS(SCLSR, 0x24, 16)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
+ defined(CONFIG_CPU_SUBTYPE_SH7724)
SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8)
SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16)
@@ -604,10 +622,21 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inb(SCSPTR5) & 0x0008 ? 1 : 0; /* SCIF5 */
return 1;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
+# define SCFSR 0x0010
+# define SCASSR 0x0014
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->type == PORT_SCIF)
+ return ctrl_inw((port->mapbase + SCFSR)) & SCIF_BRK ? 1 : 0;
+ if (port->type == PORT_SCIFA)
+ return ctrl_inw((port->mapbase + SCASSR)) & SCIF_BRK ? 1 : 0;
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
static inline int sci_rxd_in(struct uart_port *port)
{
- return sci_in(port, SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
+ return sci_in(port, SCSPTR)&0x0001 ? 1 : 0; /* SCIF */
}
#elif defined(__H8300H__) || defined(__H8300S__)
static inline int sci_rxd_in(struct uart_port *port)
@@ -757,7 +786,8 @@ static inline int sci_rxd_in(struct uart_port *port)
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721)
#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
+ defined(CONFIG_CPU_SUBTYPE_SH7724)
static inline int scbrr_calc(struct uart_port *port, int bps, int clk)
{
if (port->type == PORT_SCIF)
diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c
new file mode 100644
index 000000000000..ac9e5d5f742e
--- /dev/null
+++ b/drivers/serial/timbuart.c
@@ -0,0 +1,526 @@
+/*
+ * timbuart.c timberdale FPGA UART driver
+ * Copyright (c) 2009 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA UART
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/serial_core.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+
+#include "timbuart.h"
+
+struct timbuart_port {
+ struct uart_port port;
+ struct tasklet_struct tasklet;
+ int usedma;
+ u8 last_ier;
+ struct platform_device *dev;
+};
+
+static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800,
+ 921600, 1843200, 3250000};
+
+static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier);
+
+static irqreturn_t timbuart_handleinterrupt(int irq, void *devid);
+
+static void timbuart_stop_rx(struct uart_port *port)
+{
+ /* spin lock held by upper layer, disable all RX interrupts */
+ u8 ier = ioread8(port->membase + TIMBUART_IER) & ~RXFLAGS;
+ iowrite8(ier, port->membase + TIMBUART_IER);
+}
+
+static void timbuart_stop_tx(struct uart_port *port)
+{
+ /* spinlock held by upper layer, disable TX interrupt */
+ u8 ier = ioread8(port->membase + TIMBUART_IER) & ~TXBAE;
+ iowrite8(ier, port->membase + TIMBUART_IER);
+}
+
+static void timbuart_start_tx(struct uart_port *port)
+{
+ struct timbuart_port *uart =
+ container_of(port, struct timbuart_port, port);
+
+ /* do not transfer anything here -> fire off the tasklet */
+ tasklet_schedule(&uart->tasklet);
+}
+
+static void timbuart_flush_buffer(struct uart_port *port)
+{
+ u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX;
+
+ iowrite8(ctl, port->membase + TIMBUART_CTRL);
+ iowrite8(TXBF, port->membase + TIMBUART_ISR);
+}
+
+static void timbuart_rx_chars(struct uart_port *port)
+{
+ struct tty_struct *tty = port->info->port.tty;
+
+ while (ioread8(port->membase + TIMBUART_ISR) & RXDP) {
+ u8 ch = ioread8(port->membase + TIMBUART_RXFIFO);
+ port->icount.rx++;
+ tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ }
+
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(port->info->port.tty);
+ spin_lock(&port->lock);
+
+ dev_dbg(port->dev, "%s - total read %d bytes\n",
+ __func__, port->icount.rx);
+}
+
+static void timbuart_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->info->xmit;
+
+ while (!(ioread8(port->membase + TIMBUART_ISR) & TXBF) &&
+ !uart_circ_empty(xmit)) {
+ iowrite8(xmit->buf[xmit->tail],
+ port->membase + TIMBUART_TXFIFO);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ dev_dbg(port->dev,
+ "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n",
+ __func__,
+ port->icount.tx,
+ ioread8(port->membase + TIMBUART_CTRL),
+ port->mctrl & TIOCM_RTS,
+ ioread8(port->membase + TIMBUART_BAUDRATE));
+}
+
+static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier)
+{
+ struct timbuart_port *uart =
+ container_of(port, struct timbuart_port, port);
+ struct circ_buf *xmit = &port->info->xmit;
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return;
+
+ if (port->x_char)
+ return;
+
+ if (isr & TXFLAGS) {
+ timbuart_tx_chars(port);
+ /* clear all TX interrupts */
+ iowrite8(TXFLAGS, port->membase + TIMBUART_ISR);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ } else
+ /* Re-enable any tx interrupt */
+ *ier |= uart->last_ier & TXFLAGS;
+
+ /* enable interrupts if there are chars in the transmit buffer,
+ * Or if we delivered some bytes and want the almost empty interrupt
+ * we wake up the upper layer later when we got the interrupt
+ * to give it some time to go out...
+ */
+ if (!uart_circ_empty(xmit))
+ *ier |= TXBAE;
+
+ dev_dbg(port->dev, "%s - leaving\n", __func__);
+}
+
+void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier)
+{
+ if (isr & RXFLAGS) {
+ /* Some RX status is set */
+ if (isr & RXBF) {
+ u8 ctl = ioread8(port->membase + TIMBUART_CTRL) |
+ TIMBUART_CTRL_FLSHRX;
+ iowrite8(ctl, port->membase + TIMBUART_CTRL);
+ port->icount.overrun++;
+ } else if (isr & (RXDP))
+ timbuart_rx_chars(port);
+
+ /* ack all RX interrupts */
+ iowrite8(RXFLAGS, port->membase + TIMBUART_ISR);
+ }
+
+ /* always have the RX interrupts enabled */
+ *ier |= RXBAF | RXBF | RXTT;
+
+ dev_dbg(port->dev, "%s - leaving\n", __func__);
+}
+
+void timbuart_tasklet(unsigned long arg)
+{
+ struct timbuart_port *uart = (struct timbuart_port *)arg;
+ u8 isr, ier = 0;
+
+ spin_lock(&uart->port.lock);
+
+ isr = ioread8(uart->port.membase + TIMBUART_ISR);
+ dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr);
+
+ if (!uart->usedma)
+ timbuart_handle_tx_port(&uart->port, isr, &ier);
+
+ timbuart_mctrl_check(&uart->port, isr, &ier);
+
+ if (!uart->usedma)
+ timbuart_handle_rx_port(&uart->port, isr, &ier);
+
+ iowrite8(ier, uart->port.membase + TIMBUART_IER);
+
+ spin_unlock(&uart->port.lock);
+ dev_dbg(uart->port.dev, "%s leaving\n", __func__);
+}
+
+static unsigned int timbuart_tx_empty(struct uart_port *port)
+{
+ u8 isr = ioread8(port->membase + TIMBUART_ISR);
+
+ return (isr & TXBAE) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int timbuart_get_mctrl(struct uart_port *port)
+{
+ u8 cts = ioread8(port->membase + TIMBUART_CTRL);
+ dev_dbg(port->dev, "%s - cts %x\n", __func__, cts);
+
+ if (cts & TIMBUART_CTRL_CTS)
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+ else
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ dev_dbg(port->dev, "%s - %x\n", __func__, mctrl);
+
+ if (mctrl & TIOCM_RTS)
+ iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL);
+ else
+ iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL);
+}
+
+static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier)
+{
+ unsigned int cts;
+
+ if (isr & CTS_DELTA) {
+ /* ack */
+ iowrite8(CTS_DELTA, port->membase + TIMBUART_ISR);
+ cts = timbuart_get_mctrl(port);
+ uart_handle_cts_change(port, cts & TIOCM_CTS);
+ wake_up_interruptible(&port->info->delta_msr_wait);
+ }
+
+ *ier |= CTS_DELTA;
+}
+
+static void timbuart_enable_ms(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void timbuart_break_ctl(struct uart_port *port, int ctl)
+{
+ /* N/A */
+}
+
+static int timbuart_startup(struct uart_port *port)
+{
+ struct timbuart_port *uart =
+ container_of(port, struct timbuart_port, port);
+
+ dev_dbg(port->dev, "%s\n", __func__);
+
+ iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL);
+ iowrite8(0xff, port->membase + TIMBUART_ISR);
+ /* Enable all but TX interrupts */
+ iowrite8(RXBAF | RXBF | RXTT | CTS_DELTA,
+ port->membase + TIMBUART_IER);
+
+ return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED,
+ "timb-uart", uart);
+}
+
+static void timbuart_shutdown(struct uart_port *port)
+{
+ struct timbuart_port *uart =
+ container_of(port, struct timbuart_port, port);
+ dev_dbg(port->dev, "%s\n", __func__);
+ free_irq(port->irq, uart);
+ iowrite8(0, port->membase + TIMBUART_IER);
+}
+
+static int get_bindex(int baud)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(baudrates); i++)
+ if (baud <= baudrates[i])
+ return i;
+
+ return -1;
+}
+
+static void timbuart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud;
+ short bindex;
+ unsigned long flags;
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ bindex = get_bindex(baud);
+ dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex);
+
+ if (bindex < 0)
+ bindex = 0;
+ baud = baudrates[bindex];
+
+ /* The serial layer calls into this once with old = NULL when setting
+ up initially */
+ if (old)
+ tty_termios_copy_hw(termios, old);
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+ iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE);
+ uart_update_timeout(port, termios->c_cflag, baud);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *timbuart_type(struct uart_port *port)
+{
+ return port->type == PORT_UNKNOWN ? "timbuart" : NULL;
+}
+
+/* We do not request/release mappings of the registers here,
+ * currently it's done in the proble function.
+ */
+static void timbuart_release_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ int size =
+ resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
+
+ if (port->flags & UPF_IOREMAP) {
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+
+ release_mem_region(port->mapbase, size);
+}
+
+static int timbuart_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ int size =
+ resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
+
+ if (!request_mem_region(port->mapbase, size, "timb-uart"))
+ return -EBUSY;
+
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = ioremap(port->mapbase, size);
+ if (port->membase == NULL) {
+ release_mem_region(port->mapbase, size);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t timbuart_handleinterrupt(int irq, void *devid)
+{
+ struct timbuart_port *uart = (struct timbuart_port *)devid;
+
+ if (ioread8(uart->port.membase + TIMBUART_IPR)) {
+ uart->last_ier = ioread8(uart->port.membase + TIMBUART_IER);
+
+ /* disable interrupts, the tasklet enables them again */
+ iowrite8(0, uart->port.membase + TIMBUART_IER);
+
+ /* fire off bottom half */
+ tasklet_schedule(&uart->tasklet);
+
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void timbuart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_TIMBUART;
+ timbuart_request_port(port);
+ }
+}
+
+static int timbuart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+static struct uart_ops timbuart_ops = {
+ .tx_empty = timbuart_tx_empty,
+ .set_mctrl = timbuart_set_mctrl,
+ .get_mctrl = timbuart_get_mctrl,
+ .stop_tx = timbuart_stop_tx,
+ .start_tx = timbuart_start_tx,
+ .flush_buffer = timbuart_flush_buffer,
+ .stop_rx = timbuart_stop_rx,
+ .enable_ms = timbuart_enable_ms,
+ .break_ctl = timbuart_break_ctl,
+ .startup = timbuart_startup,
+ .shutdown = timbuart_shutdown,
+ .set_termios = timbuart_set_termios,
+ .type = timbuart_type,
+ .release_port = timbuart_release_port,
+ .request_port = timbuart_request_port,
+ .config_port = timbuart_config_port,
+ .verify_port = timbuart_verify_port
+};
+
+static struct uart_driver timbuart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "timberdale_uart",
+ .dev_name = "ttyTU",
+ .major = TIMBUART_MAJOR,
+ .minor = TIMBUART_MINOR,
+ .nr = 1
+};
+
+static int timbuart_probe(struct platform_device *dev)
+{
+ int err;
+ struct timbuart_port *uart;
+ struct resource *iomem;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ uart = kzalloc(sizeof(*uart), GFP_KERNEL);
+ if (!uart) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+
+ uart->usedma = 0;
+
+ uart->port.uartclk = 3250000 * 16;
+ uart->port.fifosize = TIMBUART_FIFO_SIZE;
+ uart->port.regshift = 2;
+ uart->port.iotype = UPIO_MEM;
+ uart->port.ops = &timbuart_ops;
+ uart->port.irq = 0;
+ uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
+ uart->port.line = 0;
+ uart->port.dev = &dev->dev;
+
+ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ err = -ENOMEM;
+ goto err_register;
+ }
+ uart->port.mapbase = iomem->start;
+ uart->port.membase = NULL;
+
+ uart->port.irq = platform_get_irq(dev, 0);
+ if (uart->port.irq < 0) {
+ err = -EINVAL;
+ goto err_register;
+ }
+
+ tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart);
+
+ err = uart_register_driver(&timbuart_driver);
+ if (err)
+ goto err_register;
+
+ err = uart_add_one_port(&timbuart_driver, &uart->port);
+ if (err)
+ goto err_add_port;
+
+ platform_set_drvdata(dev, uart);
+
+ return 0;
+
+err_add_port:
+ uart_unregister_driver(&timbuart_driver);
+err_register:
+ kfree(uart);
+err_mem:
+ printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n",
+ err);
+
+ return err;
+}
+
+static int timbuart_remove(struct platform_device *dev)
+{
+ struct timbuart_port *uart = platform_get_drvdata(dev);
+
+ tasklet_kill(&uart->tasklet);
+ uart_remove_one_port(&timbuart_driver, &uart->port);
+ uart_unregister_driver(&timbuart_driver);
+ kfree(uart);
+
+ return 0;
+}
+
+static struct platform_driver timbuart_platform_driver = {
+ .driver = {
+ .name = "timb-uart",
+ .owner = THIS_MODULE,
+ },
+ .probe = timbuart_probe,
+ .remove = timbuart_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init timbuart_init(void)
+{
+ return platform_driver_register(&timbuart_platform_driver);
+}
+
+static void __exit timbuart_exit(void)
+{
+ platform_driver_unregister(&timbuart_platform_driver);
+}
+
+module_init(timbuart_init);
+module_exit(timbuart_exit);
+
+MODULE_DESCRIPTION("Timberdale UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:timb-uart");
+
diff --git a/drivers/serial/timbuart.h b/drivers/serial/timbuart.h
new file mode 100644
index 000000000000..7e566766bc43
--- /dev/null
+++ b/drivers/serial/timbuart.h
@@ -0,0 +1,58 @@
+/*
+ * timbuart.c timberdale FPGA GPIO driver
+ * Copyright (c) 2009 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA UART
+ */
+
+#ifndef _TIMBUART_H
+#define _TIMBUART_H
+
+#define TIMBUART_FIFO_SIZE 2048
+
+#define TIMBUART_RXFIFO 0x08
+#define TIMBUART_TXFIFO 0x0c
+#define TIMBUART_IER 0x10
+#define TIMBUART_IPR 0x14
+#define TIMBUART_ISR 0x18
+#define TIMBUART_CTRL 0x1c
+#define TIMBUART_BAUDRATE 0x20
+
+#define TIMBUART_CTRL_RTS 0x01
+#define TIMBUART_CTRL_CTS 0x02
+#define TIMBUART_CTRL_FLSHTX 0x40
+#define TIMBUART_CTRL_FLSHRX 0x80
+
+#define TXBF 0x01
+#define TXBAE 0x02
+#define CTS_DELTA 0x04
+#define RXDP 0x08
+#define RXBAF 0x10
+#define RXBF 0x20
+#define RXTT 0x40
+#define RXBNAE 0x80
+#define TXBE 0x100
+
+#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE)
+#define TXFLAGS (TXBF | TXBAE)
+
+#define TIMBUART_MAJOR 204
+#define TIMBUART_MINOR 192
+
+#endif /* _TIMBUART_H */
+
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 12d13d99b6f0..d687a9b93d03 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -24,6 +24,7 @@
#include <linux/sh_intc.h>
#include <linux/sysdev.h>
#include <linux/list.h>
+#include <linux/topology.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -770,11 +771,19 @@ void __init register_intc_controller(struct intc_desc *desc)
/* register the vectors one by one */
for (i = 0; i < desc->nr_vectors; i++) {
struct intc_vect *vect = desc->vectors + i;
+ unsigned int irq = evt2irq(vect->vect);
+ struct irq_desc *irq_desc;
if (!vect->enum_id)
continue;
- intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect));
+ irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
+ if (unlikely(!irq_desc)) {
+ printk(KERN_INFO "can not get irq_desc for %d\n", irq);
+ continue;
+ }
+
+ intc_register_irq(desc, d, vect->enum_id, irq);
}
}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 83a185d52961..957494775413 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -212,7 +212,7 @@ config SPI_TXX9
config SPI_XILINX
tristate "Xilinx SPI controller"
- depends on XILINX_VIRTEX && EXPERIMENTAL
+ depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
select SPI_BITBANG
help
This exposes the SPI controller IP from the Xilinx EDK.
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 885194a07418..3f3c08c6ba4e 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -1373,6 +1373,9 @@ static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
+ if (!chip)
+ return;
+
if (gpio_is_valid(chip->gpio_cs))
gpio_free(chip->gpio_cs);
diff --git a/drivers/ssb/embedded.c b/drivers/ssb/embedded.c
index 7dc3a6b41397..a0e0d246b592 100644
--- a/drivers/ssb/embedded.c
+++ b/drivers/ssb/embedded.c
@@ -29,6 +29,7 @@ int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks)
}
return -ENODEV;
}
+EXPORT_SYMBOL(ssb_watchdog_timer_set);
u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask)
{
diff --git a/drivers/staging/comedi/TODO b/drivers/staging/comedi/TODO
index 557812958464..15c9348fb938 100644
--- a/drivers/staging/comedi/TODO
+++ b/drivers/staging/comedi/TODO
@@ -11,4 +11,3 @@ Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
copy:
Ian Abbott <abbotti@mev.co.uk>
Frank Mori Hess <fmhess@users.sourceforge.net>
- David Schleef <ds@schleef.org>
diff --git a/drivers/staging/rt2870/rt2870.h b/drivers/staging/rt2870/rt2870.h
index a42caa370808..a69cf338e498 100644
--- a/drivers/staging/rt2870/rt2870.h
+++ b/drivers/staging/rt2870/rt2870.h
@@ -145,6 +145,7 @@
{USB_DEVICE(0x0789,0x0162)}, /* Logitec */ \
{USB_DEVICE(0x0789,0x0163)}, /* Logitec */ \
{USB_DEVICE(0x0789,0x0164)}, /* Logitec */ \
+ {USB_DEVICE(0x7392,0x7717)}, /* Edimax */ \
{ }/* Terminating entry */ \
}
diff --git a/drivers/staging/rtl8187se/r8180.h b/drivers/staging/rtl8187se/r8180.h
index 12215fc61ddc..db446b7e2e08 100644
--- a/drivers/staging/rtl8187se/r8180.h
+++ b/drivers/staging/rtl8187se/r8180.h
@@ -19,7 +19,7 @@
#define R8180H
-#define RTL8180_MODULE_NAME "rtl8180"
+#define RTL8180_MODULE_NAME "r8180"
#define DMESG(x,a...) printk(KERN_INFO RTL8180_MODULE_NAME ": " x "\n", ## a)
#define DMESGW(x,a...) printk(KERN_WARNING RTL8180_MODULE_NAME ": WW:" x "\n", ## a)
#define DMESGE(x,a...) printk(KERN_WARNING RTL8180_MODULE_NAME ": EE:" x "\n", ## a)
diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c
index 6ecd12de4296..e10413cee0df 100644
--- a/drivers/staging/rtl8187se/r8180_core.c
+++ b/drivers/staging/rtl8187se/r8180_core.c
@@ -640,11 +640,9 @@ void rtl8180_proc_init_one(struct net_device *dev)
{
struct proc_dir_entry *e;
struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev);
- priv->dir_dev = create_proc_entry(dev->name,
- S_IFDIR | S_IRUGO | S_IXUGO,
- rtl8180_proc);
+ priv->dir_dev = rtl8180_proc;
if (!priv->dir_dev) {
- DMESGE("Unable to initialize /proc/net/rtl8180/%s\n",
+ DMESGE("Unable to initialize /proc/net/r8180/%s\n",
dev->name);
return;
}
@@ -654,7 +652,7 @@ void rtl8180_proc_init_one(struct net_device *dev)
if (!e) {
DMESGE("Unable to initialize "
- "/proc/net/rtl8180/%s/stats-hw\n",
+ "/proc/net/r8180/%s/stats-hw\n",
dev->name);
}
@@ -663,7 +661,7 @@ void rtl8180_proc_init_one(struct net_device *dev)
if (!e) {
DMESGE("Unable to initialize "
- "/proc/net/rtl8180/%s/stats-rx\n",
+ "/proc/net/r8180/%s/stats-rx\n",
dev->name);
}
@@ -673,7 +671,7 @@ void rtl8180_proc_init_one(struct net_device *dev)
if (!e) {
DMESGE("Unable to initialize "
- "/proc/net/rtl8180/%s/stats-tx\n",
+ "/proc/net/r8180/%s/stats-tx\n",
dev->name);
}
#if 0
@@ -702,7 +700,7 @@ void rtl8180_proc_init_one(struct net_device *dev)
if (!e) {
DMESGE("Unable to initialize "
- "/proc/net/rtl8180/%s/registers\n",
+ "/proc/net/r8180/%s/registers\n",
dev->name);
}
}
@@ -977,13 +975,6 @@ void check_tx_ring(struct net_device *dev, int pri)
*tmp & (1<<15)? "ok": "err", *(tmp+4));
}
- DMESG("nic at %d",
- (nic-nicbegin) / 8 /4);
- DMESG("tail at %d", ((int)tail - (int)begin) /8 /4);
- DMESG("head at %d", ((int)head - (int)begin) /8 /4);
- DMESG("check free desc returns %d", check_nic_enought_desc(dev,pri));
- DMESG("free desc is %d\n", get_curr_tx_free_desc(dev,pri));
- //rtl8180_reset(dev);
return;
}
@@ -1736,17 +1727,7 @@ short alloc_tx_desc_ring(struct net_device *dev, int bufsize, int count,
* descriptor's buffer must be 256 byte aligned
* we shouldn't be here, since we set DMA mask !
*/
- DMESGW("Fixing TX alignment");
- desc = (u32*)((u8*)desc + 256);
-#if (defined(CONFIG_HIGHMEM64G) || defined(CONFIG_64BIT_PHYS_ADDR))
- desc = (u32*)((u64)desc &~ 0xff);
- dma_desc = (dma_addr_t)((u8*)dma_desc + 256);
- dma_desc = (dma_addr_t)((u64)dma_desc &~ 0xff);
-#else
- desc = (u32*)((u32)desc &~ 0xff);
- dma_desc = (dma_addr_t)((u8*)dma_desc + 256);
- dma_desc = (dma_addr_t)((u32)dma_desc &~ 0xff);
-#endif
+ WARN(1, "DMA buffer is not aligned\n");
}
tmp=desc;
for (i=0;i<count;i++)
@@ -1984,18 +1965,7 @@ short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count)
* descriptor's buffer must be 256 byte aligned
* should never happen since we specify the DMA mask
*/
-
- DMESGW("Fixing RX alignment");
- desc = (u32*)((u8*)desc + 256);
-#if (defined(CONFIG_HIGHMEM64G) || defined(CONFIG_64BIT_PHYS_ADDR))
- desc = (u32*)((u64)desc &~ 0xff);
- dma_desc = (dma_addr_t)((u8*)dma_desc + 256);
- dma_desc = (dma_addr_t)((u64)dma_desc &~ 0xff);
-#else
- desc = (u32*)((u32)desc &~ 0xff);
- dma_desc = (dma_addr_t)((u8*)dma_desc + 256);
- dma_desc = (dma_addr_t)((u32)dma_desc &~ 0xff);
-#endif
+ WARN(1, "DMA buffer is not aligned\n");
}
priv->rxring=desc;
diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c
index 9c3f9439f35e..3b2d52819b4c 100644
--- a/drivers/staging/winbond/wbusb.c
+++ b/drivers/staging/winbond/wbusb.c
@@ -386,7 +386,7 @@ static int wb35_probe(struct usb_interface *intf, const struct usb_device_id *id
if (err)
goto error_free_hw;
- usb_set_intfdata(intf, priv);
+ usb_set_intfdata(intf, dev);
return 0;
@@ -415,10 +415,15 @@ static void wb35_hw_halt(struct wbsoft_priv *adapter)
static void wb35_disconnect(struct usb_interface *intf)
{
- struct wbsoft_priv *priv = usb_get_intfdata(intf);
+ struct ieee80211_hw *hw = usb_get_intfdata(intf);
+ struct wbsoft_priv *priv = hw->priv;
wb35_hw_halt(priv);
+ ieee80211_stop_queues(hw);
+ ieee80211_unregister_hw(hw);
+ ieee80211_free_hw(hw);
+
usb_set_intfdata(intf, NULL);
usb_put_dev(interface_to_usbdev(intf));
}
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index d0b093b66adc..5e38ba10a3a9 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -961,7 +961,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
switch (trip_type) {
case THERMAL_TRIP_CRITICAL:
- if (temp > trip_temp) {
+ if (temp >= trip_temp) {
if (tz->ops->notify)
ret = tz->ops->notify(tz, count,
trip_type);
@@ -974,7 +974,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
}
break;
case THERMAL_TRIP_HOT:
- if (temp > trip_temp)
+ if (temp >= trip_temp)
if (tz->ops->notify)
tz->ops->notify(tz, count, trip_type);
break;
@@ -986,14 +986,14 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
cdev = instance->cdev;
- if (temp > trip_temp)
+ if (temp >= trip_temp)
cdev->ops->set_cur_state(cdev, 1);
else
cdev->ops->set_cur_state(cdev, 0);
}
break;
case THERMAL_TRIP_PASSIVE:
- if (temp > trip_temp || tz->passive)
+ if (temp >= trip_temp || tz->passive)
thermal_zone_device_passive(tz, temp,
trip_temp, count);
break;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index c6c816b7ecb5..5eee3f82be5d 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -22,6 +22,7 @@ config USB_ARCH_HAS_HCD
default y if PCMCIA && !M32R # sl811_cs
default y if ARM # SL-811
default y if SUPERH # r8a66597-hcd
+ default y if MICROBLAZE
default PCI
# many non-PCI SOC chips embed OHCI
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 89299a5ce168..0a3dc5ece634 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_PCI) += host/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
-obj-$(CONFIG_USB_ISP1760_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/
@@ -27,6 +26,8 @@ obj-$(CONFIG_USB_WUSB) += wusbcore/
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_PRINTER) += class/
+obj-$(CONFIG_USB_WDM) += class/
+obj-$(CONFIG_USB_TMC) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB) += storage/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 6789089e2461..56802d2e994b 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -227,8 +227,14 @@ static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
- value / 100, abs(value) % 100);
+ if (likely(value >= 0)) {
+ return snprintf(buf, PAGE_SIZE, "%u.%02u\n",
+ value / 100, value % 100);
+ } else {
+ value = -value;
+ return snprintf(buf, PAGE_SIZE, "-%u.%02u\n",
+ value / 100, value % 100);
+ }
}
static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0a69c0977e3f..ddeb69192537 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -16,7 +16,8 @@
* v0.9 - thorough cleaning, URBification, almost a rewrite
* v0.10 - some more cleanups
* v0.11 - fixed flow control, read error doesn't stop reads
- * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
+ * v0.12 - added TIOCM ioctls, added break handling, made struct acm
+ * kmalloced
* v0.13 - added termios, added hangup
* v0.14 - sized down struct acm
* v0.15 - fixed flow control again - characters could be lost
@@ -62,7 +63,7 @@
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <asm/byteorder.h>
@@ -87,7 +88,10 @@ static struct acm *acm_table[ACM_TTY_MINORS];
static DEFINE_MUTEX(open_mutex);
-#define ACM_READY(acm) (acm && acm->dev && acm->used)
+#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
+
+static const struct tty_port_operations acm_port_ops = {
+};
#ifdef VERBOSE_DEBUG
#define verbose 1
@@ -99,13 +103,15 @@ static DEFINE_MUTEX(open_mutex);
* Functions for ACM control messages.
*/
-static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
+static int acm_ctrl_msg(struct acm *acm, int request, int value,
+ void *buf, int len)
{
int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
request, USB_RT_ACM, value,
acm->control->altsetting[0].desc.bInterfaceNumber,
buf, len, 5000);
- dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
+ dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
+ request, value, len, retval);
return retval < 0 ? retval : 0;
}
@@ -150,9 +156,8 @@ static int acm_wb_is_avail(struct acm *acm)
n = ACM_NW;
spin_lock_irqsave(&acm->write_lock, flags);
- for (i = 0; i < ACM_NW; i++) {
+ for (i = 0; i < ACM_NW; i++)
n -= acm->wb[i].use;
- }
spin_unlock_irqrestore(&acm->write_lock, flags);
return n;
}
@@ -183,7 +188,8 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
wb->urb->transfer_buffer_length = wb->len;
wb->urb->dev = acm->dev;
- if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
+ rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
+ if (rc < 0) {
dbg("usb_submit_urb(write bulk) failed: %d", rc);
acm_write_done(acm, wb);
}
@@ -262,6 +268,7 @@ static void acm_ctrl_irq(struct urb *urb)
{
struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer;
+ struct tty_struct *tty;
unsigned char *data;
int newctrl;
int retval;
@@ -287,40 +294,45 @@ static void acm_ctrl_irq(struct urb *urb)
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
+ case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+ dbg("%s network", dr->wValue ?
+ "connected to" : "disconnected from");
+ break;
- case USB_CDC_NOTIFY_NETWORK_CONNECTION:
-
- dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
- break;
-
- case USB_CDC_NOTIFY_SERIAL_STATE:
-
- newctrl = get_unaligned_le16(data);
+ case USB_CDC_NOTIFY_SERIAL_STATE:
+ tty = tty_port_tty_get(&acm->port);
+ newctrl = get_unaligned_le16(data);
- if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
+ if (tty) {
+ if (!acm->clocal &&
+ (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
dbg("calling hangup");
- tty_hangup(acm->tty);
+ tty_hangup(tty);
}
+ tty_kref_put(tty);
+ }
- acm->ctrlin = newctrl;
-
- dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
- acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
- acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
- acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
- acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
+ acm->ctrlin = newctrl;
+ dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+ acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
break;
- default:
- dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
- dr->bNotificationType, dr->wIndex,
- dr->wLength, data[0], data[1]);
- break;
+ default:
+ dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
+ dr->bNotificationType, dr->wIndex,
+ dr->wLength, data[0], data[1]);
+ break;
}
exit:
usb_mark_last_busy(acm->dev);
- retval = usb_submit_urb (urb, GFP_ATOMIC);
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
"result %d", __func__, retval);
@@ -371,15 +383,14 @@ static void acm_rx_tasklet(unsigned long _acm)
{
struct acm *acm = (void *)_acm;
struct acm_rb *buf;
- struct tty_struct *tty = acm->tty;
+ struct tty_struct *tty;
struct acm_ru *rcv;
unsigned long flags;
unsigned char throttled;
dbg("Entering acm_rx_tasklet");
- if (!ACM_READY(acm))
- {
+ if (!ACM_READY(acm)) {
dbg("acm_rx_tasklet: ACM not ready");
return;
}
@@ -387,12 +398,13 @@ static void acm_rx_tasklet(unsigned long _acm)
spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags);
- if (throttled)
- {
+ if (throttled) {
dbg("acm_rx_tasklet: throttled");
return;
}
+ tty = tty_port_tty_get(&acm->port);
+
next_buffer:
spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->filled_read_bufs)) {
@@ -406,20 +418,22 @@ next_buffer:
dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
- tty_buffer_request_room(tty, buf->size);
- spin_lock_irqsave(&acm->throttle_lock, flags);
- throttled = acm->throttle;
- spin_unlock_irqrestore(&acm->throttle_lock, flags);
- if (!throttled)
- tty_insert_flip_string(tty, buf->base, buf->size);
- tty_flip_buffer_push(tty);
-
- if (throttled) {
- dbg("Throttling noticed");
- spin_lock_irqsave(&acm->read_lock, flags);
- list_add(&buf->list, &acm->filled_read_bufs);
- spin_unlock_irqrestore(&acm->read_lock, flags);
- return;
+ if (tty) {
+ spin_lock_irqsave(&acm->throttle_lock, flags);
+ throttled = acm->throttle;
+ spin_unlock_irqrestore(&acm->throttle_lock, flags);
+ if (!throttled) {
+ tty_buffer_request_room(tty, buf->size);
+ tty_insert_flip_string(tty, buf->base, buf->size);
+ tty_flip_buffer_push(tty);
+ } else {
+ tty_kref_put(tty);
+ dbg("Throttling noticed");
+ spin_lock_irqsave(&acm->read_lock, flags);
+ list_add(&buf->list, &acm->filled_read_bufs);
+ spin_unlock_irqrestore(&acm->read_lock, flags);
+ return;
+ }
}
spin_lock_irqsave(&acm->read_lock, flags);
@@ -428,6 +442,8 @@ next_buffer:
goto next_buffer;
urbs:
+ tty_kref_put(tty);
+
while (!list_empty(&acm->spare_read_bufs)) {
spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->spare_read_urbs)) {
@@ -454,10 +470,11 @@ urbs:
rcv->urb->transfer_dma = buf->dma;
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- /* This shouldn't kill the driver as unsuccessful URBs are returned to the
- free-urbs-pool and resubmited ASAP */
+ /* This shouldn't kill the driver as unsuccessful URBs are
+ returned to the free-urbs-pool and resubmited ASAP */
spin_lock_irqsave(&acm->read_lock, flags);
- if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
+ if (acm->susp_count ||
+ usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
list_add(&buf->list, &acm->spare_read_bufs);
list_add(&rcv->list, &acm->spare_read_urbs);
acm->processing = 0;
@@ -499,11 +516,14 @@ static void acm_write_bulk(struct urb *urb)
static void acm_softint(struct work_struct *work)
{
struct acm *acm = container_of(work, struct acm, work);
+ struct tty_struct *tty;
dev_vdbg(&acm->data->dev, "tx work\n");
if (!ACM_READY(acm))
return;
- tty_wakeup(acm->tty);
+ tty = tty_port_tty_get(&acm->port);
+ tty_wakeup(tty);
+ tty_kref_put(tty);
}
static void acm_waker(struct work_struct *waker)
@@ -543,8 +563,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
rv = 0;
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
+
tty->driver_data = acm;
- acm->tty = tty;
+ tty_port_tty_set(&acm->port, tty);
if (usb_autopm_get_interface(acm->control) < 0)
goto early_bail;
@@ -552,11 +573,10 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
acm->control->needs_remote_wakeup = 1;
mutex_lock(&acm->mutex);
- if (acm->used++) {
+ if (acm->port.count++) {
usb_autopm_put_interface(acm->control);
goto done;
- }
-
+ }
acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
@@ -567,22 +587,22 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
(acm->ctrl_caps & USB_CDC_CAP_LINE))
goto full_bailout;
+
usb_autopm_put_interface(acm->control);
INIT_LIST_HEAD(&acm->spare_read_urbs);
INIT_LIST_HEAD(&acm->spare_read_bufs);
INIT_LIST_HEAD(&acm->filled_read_bufs);
- for (i = 0; i < acm->rx_buflimit; i++) {
+
+ for (i = 0; i < acm->rx_buflimit; i++)
list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
- }
- for (i = 0; i < acm->rx_buflimit; i++) {
+ for (i = 0; i < acm->rx_buflimit; i++)
list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
- }
acm->throttle = 0;
tasklet_schedule(&acm->urb_task);
-
+ rv = tty_port_block_til_ready(&acm->port, tty, filp);
done:
mutex_unlock(&acm->mutex);
err_out:
@@ -593,16 +613,17 @@ full_bailout:
usb_kill_urb(acm->ctrlurb);
bail_out:
usb_autopm_put_interface(acm->control);
- acm->used--;
+ acm->port.count--;
mutex_unlock(&acm->mutex);
early_bail:
mutex_unlock(&open_mutex);
+ tty_port_tty_set(&acm->port, NULL);
return -EIO;
}
static void acm_tty_unregister(struct acm *acm)
{
- int i,nr;
+ int i, nr;
nr = acm->rx_buflimit;
tty_unregister_device(acm_tty_driver, acm->minor);
@@ -619,41 +640,56 @@ static void acm_tty_unregister(struct acm *acm)
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
+static void acm_port_down(struct acm *acm, int drain)
+{
+ int i, nr = acm->rx_buflimit;
+ mutex_lock(&open_mutex);
+ if (acm->dev) {
+ usb_autopm_get_interface(acm->control);
+ acm_set_control(acm, acm->ctrlout = 0);
+ /* try letting the last writes drain naturally */
+ if (drain) {
+ wait_event_interruptible_timeout(acm->drain_wait,
+ (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
+ ACM_CLOSE_TIMEOUT * HZ);
+ }
+ usb_kill_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_kill_urb(acm->wb[i].urb);
+ for (i = 0; i < nr; i++)
+ usb_kill_urb(acm->ru[i].urb);
+ acm->control->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(acm->control);
+ }
+ mutex_unlock(&open_mutex);
+}
+
+static void acm_tty_hangup(struct tty_struct *tty)
+{
+ struct acm *acm = tty->driver_data;
+ tty_port_hangup(&acm->port);
+ acm_port_down(acm, 0);
+}
+
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
- int i,nr;
- if (!acm || !acm->used)
+ /* Perform the closing process and see if we need to do the hardware
+ shutdown */
+ if (tty_port_close_start(&acm->port, tty, filp) == 0)
return;
-
- nr = acm->rx_buflimit;
+ acm_port_down(acm, 0);
+ tty_port_close_end(&acm->port, tty);
mutex_lock(&open_mutex);
- if (!--acm->used) {
- if (acm->dev) {
- usb_autopm_get_interface(acm->control);
- acm_set_control(acm, acm->ctrlout = 0);
-
- /* try letting the last writes drain naturally */
- wait_event_interruptible_timeout(acm->drain_wait,
- (ACM_NW == acm_wb_is_avail(acm))
- || !acm->dev,
- ACM_CLOSE_TIMEOUT * HZ);
-
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < nr; i++)
- usb_kill_urb(acm->ru[i].urb);
- acm->control->needs_remote_wakeup = 0;
- usb_autopm_put_interface(acm->control);
- } else
- acm_tty_unregister(acm);
- }
+ tty_port_tty_set(&acm->port, NULL);
+ if (!acm->dev)
+ acm_tty_unregister(acm);
mutex_unlock(&open_mutex);
}
-static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static int acm_tty_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
{
struct acm *acm = tty->driver_data;
int stat;
@@ -669,7 +705,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
return 0;
spin_lock_irqsave(&acm->write_lock, flags);
- if ((wbn = acm_wb_alloc(acm)) < 0) {
+ wbn = acm_wb_alloc(acm);
+ if (wbn < 0) {
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0;
}
@@ -681,7 +718,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
wb->len = count;
spin_unlock_irqrestore(&acm->write_lock, flags);
- if ((stat = acm_write_start(acm, wbn)) < 0)
+ stat = acm_write_start(acm, wbn);
+ if (stat < 0)
return stat;
return count;
}
@@ -767,8 +805,10 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
return -EINVAL;
newctrl = acm->ctrlout;
- set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
- clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+ set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
+ (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+ clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
+ (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
newctrl = (newctrl & ~clear) | set;
@@ -777,7 +817,8 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
return acm_set_control(acm, acm->ctrlout = newctrl);
}
-static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct acm *acm = tty->driver_data;
@@ -799,7 +840,8 @@ static const __u8 acm_tty_size[] = {
5, 6, 7, 8
};
-static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
+static void acm_tty_set_termios(struct tty_struct *tty,
+ struct ktermios *termios_old)
{
struct acm *acm = tty->driver_data;
struct ktermios *termios = tty->termios;
@@ -809,19 +851,23 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios
if (!ACM_READY(acm))
return;
+ /* FIXME: Needs to support the tty_baud interface */
+ /* FIXME: Broken on sparc */
newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
newline.bParityType = termios->c_cflag & PARENB ?
- (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
+ (termios->c_cflag & PARODD ? 1 : 2) +
+ (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
-
+ /* FIXME: Needs to clear unsupported bits in the termios */
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
if (!newline.dwDTERate) {
newline.dwDTERate = acm->line.dwDTERate;
newctrl &= ~ACM_CTRL_DTR;
- } else newctrl |= ACM_CTRL_DTR;
+ } else
+ newctrl |= ACM_CTRL_DTR;
if (newctrl != acm->ctrlout)
acm_set_control(acm, acm->ctrlout = newctrl);
@@ -846,9 +892,8 @@ static void acm_write_buffers_free(struct acm *acm)
struct acm_wb *wb;
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
- for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
+ for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
- }
}
static void acm_read_buffers_free(struct acm *acm)
@@ -857,7 +902,8 @@ static void acm_read_buffers_free(struct acm *acm)
int i, n = acm->rx_buflimit;
for (i = 0; i < n; i++)
- usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+ usb_buffer_free(usb_dev, acm->readsize,
+ acm->rb[i].base, acm->rb[i].dma);
}
/* Little helper: write buffers allocate */
@@ -882,8 +928,8 @@ static int acm_write_buffers_alloc(struct acm *acm)
return 0;
}
-static int acm_probe (struct usb_interface *intf,
- const struct usb_device_id *id)
+static int acm_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
struct usb_cdc_country_functional_desc *cfd = NULL;
@@ -897,7 +943,7 @@ static int acm_probe (struct usb_interface *intf,
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct acm *acm;
int minor;
- int ctrlsize,readsize;
+ int ctrlsize, readsize;
u8 *buf;
u8 ac_management_function = 0;
u8 call_management_function = 0;
@@ -917,7 +963,7 @@ static int acm_probe (struct usb_interface *intf,
control_interface = usb_ifnum_to_if(usb_dev, 0);
goto skip_normal_probe;
}
-
+
/* normal probing*/
if (!buffer) {
dev_err(&intf->dev, "Weird descriptor references\n");
@@ -925,8 +971,10 @@ static int acm_probe (struct usb_interface *intf,
}
if (!buflen) {
- if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
- dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
+ if (intf->cur_altsetting->endpoint->extralen &&
+ intf->cur_altsetting->endpoint->extra) {
+ dev_dbg(&intf->dev,
+ "Seeking extra descriptors on endpoint\n");
buflen = intf->cur_altsetting->endpoint->extralen;
buffer = intf->cur_altsetting->endpoint->extra;
} else {
@@ -937,47 +985,43 @@ static int acm_probe (struct usb_interface *intf,
}
while (buflen > 0) {
- if (buffer [1] != USB_DT_CS_INTERFACE) {
+ if (buffer[1] != USB_DT_CS_INTERFACE) {
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
}
- switch (buffer [2]) {
- case USB_CDC_UNION_TYPE: /* we've found it */
- if (union_header) {
- dev_err(&intf->dev, "More than one "
- "union descriptor, "
- "skipping ...\n");
- goto next_desc;
- }
- union_header = (struct usb_cdc_union_desc *)
- buffer;
- break;
- case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
- cfd = (struct usb_cdc_country_functional_desc *)buffer;
- break;
- case USB_CDC_HEADER_TYPE: /* maybe check version */
- break; /* for now we ignore it */
- case USB_CDC_ACM_TYPE:
- ac_management_function = buffer[3];
- break;
- case USB_CDC_CALL_MANAGEMENT_TYPE:
- call_management_function = buffer[3];
- call_interface_num = buffer[4];
- if ((call_management_function & 3) != 3)
- dev_err(&intf->dev, "This device "
- "cannot do calls on its own. "
- "It is no modem.\n");
- break;
- default:
- /* there are LOTS more CDC descriptors that
- * could legitimately be found here.
- */
- dev_dbg(&intf->dev, "Ignoring descriptor: "
- "type %02x, length %d\n",
- buffer[2], buffer[0]);
- break;
+ switch (buffer[2]) {
+ case USB_CDC_UNION_TYPE: /* we've found it */
+ if (union_header) {
+ dev_err(&intf->dev, "More than one "
+ "union descriptor, skipping ...\n");
+ goto next_desc;
}
+ union_header = (struct usb_cdc_union_desc *)buffer;
+ break;
+ case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+ cfd = (struct usb_cdc_country_functional_desc *)buffer;
+ break;
+ case USB_CDC_HEADER_TYPE: /* maybe check version */
+ break; /* for now we ignore it */
+ case USB_CDC_ACM_TYPE:
+ ac_management_function = buffer[3];
+ break;
+ case USB_CDC_CALL_MANAGEMENT_TYPE:
+ call_management_function = buffer[3];
+ call_interface_num = buffer[4];
+ if ((call_management_function & 3) != 3)
+ dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
+ break;
+ default:
+ /* there are LOTS more CDC descriptors that
+ * could legitimately be found here.
+ */
+ dev_dbg(&intf->dev, "Ignoring descriptor: "
+ "type %02x, length %d\n",
+ buffer[2], buffer[0]);
+ break;
+ }
next_desc:
buflen -= buffer[0];
buffer += buffer[0];
@@ -985,33 +1029,36 @@ next_desc:
if (!union_header) {
if (call_interface_num > 0) {
- dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
+ dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
control_interface = intf;
} else {
- dev_dbg(&intf->dev,"No union descriptor, giving up\n");
+ dev_dbg(&intf->dev,
+ "No union descriptor, giving up\n");
return -ENODEV;
}
} else {
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
if (!control_interface || !data_interface) {
- dev_dbg(&intf->dev,"no interfaces\n");
+ dev_dbg(&intf->dev, "no interfaces\n");
return -ENODEV;
}
}
-
+
if (data_interface_num != call_interface_num)
- dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
+ dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
skip_normal_probe:
/*workaround for switched interfaces */
- if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
- if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
+ if (data_interface->cur_altsetting->desc.bInterfaceClass
+ != CDC_DATA_INTERFACE_TYPE) {
+ if (control_interface->cur_altsetting->desc.bInterfaceClass
+ == CDC_DATA_INTERFACE_TYPE) {
struct usb_interface *t;
- dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
-
+ dev_dbg(&intf->dev,
+ "Your device has switched interfaces.\n");
t = control_interface;
control_interface = data_interface;
data_interface = t;
@@ -1023,9 +1070,9 @@ skip_normal_probe:
/* Accept probe requests only for the control interface */
if (intf != control_interface)
return -ENODEV;
-
+
if (usb_interface_claimed(data_interface)) { /* valid in this context */
- dev_dbg(&intf->dev,"The data interface isn't available\n");
+ dev_dbg(&intf->dev, "The data interface isn't available\n");
return -EBUSY;
}
@@ -1042,8 +1089,8 @@ skip_normal_probe:
if (!usb_endpoint_dir_in(epread)) {
/* descriptors are swapped */
struct usb_endpoint_descriptor *t;
- dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
-
+ dev_dbg(&intf->dev,
+ "The data interface has switched endpoints\n");
t = epread;
epread = epwrite;
epwrite = t;
@@ -1056,13 +1103,15 @@ skip_normal_probe:
return -ENODEV;
}
- if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
+ acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
+ if (acm == NULL) {
dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
goto alloc_fail;
}
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
- readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
+ readsize = le16_to_cpu(epread->wMaxPacketSize) *
+ (quirks == SINGLE_RX_URB ? 1 : 2);
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
acm->control = control_interface;
acm->data = data_interface;
@@ -1082,6 +1131,8 @@ skip_normal_probe:
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+ tty_port_init(&acm->port);
+ acm->port.ops = &acm_port_ops;
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
@@ -1103,8 +1154,10 @@ skip_normal_probe:
for (i = 0; i < num_rx_buf; i++) {
struct acm_ru *rcv = &(acm->ru[i]);
- if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
- dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
+ rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (rcv->urb == NULL) {
+ dev_dbg(&intf->dev,
+ "out of memory (read urbs usb_alloc_urb)\n");
goto alloc_fail7;
}
@@ -1117,26 +1170,29 @@ skip_normal_probe:
rb->base = usb_buffer_alloc(acm->dev, readsize,
GFP_KERNEL, &rb->dma);
if (!rb->base) {
- dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
+ dev_dbg(&intf->dev,
+ "out of memory (read bufs usb_buffer_alloc)\n");
goto alloc_fail7;
}
}
- for(i = 0; i < ACM_NW; i++)
- {
+ for (i = 0; i < ACM_NW; i++) {
struct acm_wb *snd = &(acm->wb[i]);
- if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
- dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
+ snd->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (snd->urb == NULL) {
+ dev_dbg(&intf->dev,
+ "out of memory (write urbs usb_alloc_urb)");
goto alloc_fail7;
}
- usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
- NULL, acm->writesize, acm_write_bulk, snd);
+ usb_fill_bulk_urb(snd->urb, usb_dev,
+ usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+ NULL, acm->writesize, acm_write_bulk, snd);
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
snd->instance = acm;
}
- usb_set_intfdata (intf, acm);
+ usb_set_intfdata(intf, acm);
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
if (i < 0)
@@ -1147,7 +1203,8 @@ skip_normal_probe:
if (!acm->country_codes)
goto skip_countries;
acm->country_code_size = cfd->bLength - 4;
- memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
+ memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
+ cfd->bLength - 4);
acm->country_rel_date = cfd->iCountryCodeRelDate;
i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
@@ -1156,7 +1213,8 @@ skip_normal_probe:
goto skip_countries;
}
- i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+ i = device_create_file(&intf->dev,
+ &dev_attr_iCountryCodeRelDate);
if (i < 0) {
kfree(acm->country_codes);
goto skip_countries;
@@ -1164,8 +1222,10 @@ skip_normal_probe:
}
skip_countries:
- usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
- acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
+ usb_fill_int_urb(acm->ctrlurb, usb_dev,
+ usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
+ acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
+ epctrl->bInterval);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
@@ -1212,7 +1272,7 @@ static void stop_data_traffic(struct acm *acm)
tasklet_disable(&acm->urb_task);
usb_kill_urb(acm->ctrlurb);
- for(i = 0; i < ACM_NW; i++)
+ for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < acm->rx_buflimit; i++)
usb_kill_urb(acm->ru[i].urb);
@@ -1227,13 +1287,14 @@ static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct tty_struct *tty;
/* sibling interface is already cleaning up */
if (!acm)
return;
mutex_lock(&open_mutex);
- if (acm->country_codes){
+ if (acm->country_codes) {
device_remove_file(&acm->control->dev,
&dev_attr_wCountryCodes);
device_remove_file(&acm->control->dev,
@@ -1247,22 +1308,25 @@ static void acm_disconnect(struct usb_interface *intf)
stop_data_traffic(acm);
acm_write_buffers_free(acm);
- usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+ usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
+ acm->ctrl_dma);
acm_read_buffers_free(acm);
usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control);
- if (!acm->used) {
+ if (acm->port.count == 0) {
acm_tty_unregister(acm);
mutex_unlock(&open_mutex);
return;
}
mutex_unlock(&open_mutex);
-
- if (acm->tty)
- tty_hangup(acm->tty);
+ tty = tty_port_tty_get(&acm->port);
+ if (tty) {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
}
#ifdef CONFIG_PM
@@ -1297,7 +1361,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
*/
mutex_lock(&acm->mutex);
- if (acm->used)
+ if (acm->port.count)
stop_data_traffic(acm);
mutex_unlock(&acm->mutex);
@@ -1319,7 +1383,7 @@ static int acm_resume(struct usb_interface *intf)
return 0;
mutex_lock(&acm->mutex);
- if (acm->used) {
+ if (acm->port.count) {
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
if (rv < 0)
goto err_out;
@@ -1375,6 +1439,9 @@ static struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+ { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
@@ -1395,7 +1462,7 @@ static struct usb_device_id acm_ids[] = {
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_GSM) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
- USB_CDC_ACM_PROTO_AT_3G ) },
+ USB_CDC_ACM_PROTO_AT_3G) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_CDMA) },
@@ -1403,7 +1470,7 @@ static struct usb_device_id acm_ids[] = {
{ }
};
-MODULE_DEVICE_TABLE (usb, acm_ids);
+MODULE_DEVICE_TABLE(usb, acm_ids);
static struct usb_driver acm_driver = {
.name = "cdc_acm",
@@ -1426,6 +1493,7 @@ static struct usb_driver acm_driver = {
static const struct tty_operations acm_ops = {
.open = acm_tty_open,
.close = acm_tty_close,
+ .hangup = acm_tty_hangup,
.write = acm_tty_write,
.write_room = acm_tty_write_room,
.ioctl = acm_tty_ioctl,
@@ -1457,7 +1525,8 @@ static int __init acm_init(void)
acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
acm_tty_driver->init_termios = tty_std_termios;
- acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
+ HUPCL | CLOCAL;
tty_set_operations(acm_tty_driver, &acm_ops);
retval = tty_register_driver(acm_tty_driver);
@@ -1489,7 +1558,7 @@ static void __exit acm_exit(void)
module_init(acm_init);
module_exit(acm_exit);
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1f95e7aa1b66..4c3856420add 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -89,8 +89,8 @@ struct acm {
struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */
- struct tty_struct *tty; /* the corresponding tty */
- struct urb *ctrlurb; /* urbs */
+ struct tty_port port; /* our tty port data */
+ struct urb *ctrlurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
u8 *country_codes; /* country codes from device */
@@ -120,7 +120,6 @@ struct acm {
unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
- unsigned int used; /* someone has this acm's device open */
unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index dff5760a37f6..ffe75e83787c 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -39,6 +39,7 @@
#include <linux/parser.h>
#include <linux/notifier.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <asm/byteorder.h>
#include "usb.h"
#include "hcd.h"
@@ -265,9 +266,13 @@ static int remount(struct super_block *sb, int *flags, char *data)
return -EINVAL;
}
+ lock_kernel();
+
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
+ unlock_kernel();
+
return 0;
}
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 563d57275448..05c913cc3658 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -794,7 +794,8 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
if (ep->desc) {
list_add_tail(&req->queue, &ep->queue);
- if (ep->is_in || (ep_is_control(ep)
+ if ((!ep_is_control(ep) && ep->is_in) ||
+ (ep_is_control(ep)
&& (ep->state == DATA_STAGE_IN
|| ep->state == STATUS_STAGE_IN)))
usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
@@ -1940,7 +1941,7 @@ static int __init usba_udc_probe(struct platform_device *pdev)
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable(pclk);
- usba_ep = kmalloc(sizeof(struct usba_ep) * pdata->num_ep,
+ usba_ep = kzalloc(sizeof(struct usba_ep) * pdata->num_ep,
GFP_KERNEL);
if (!usba_ep)
goto err_alloc_ep;
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 4154be375c7a..58c4d37d312a 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -38,7 +38,7 @@ static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
uchar = (c & 0x1f) << 6;
c = (u8) *s++;
- if ((c & 0xc0) != 0xc0)
+ if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
@@ -49,13 +49,13 @@ static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
uchar = (c & 0x0f) << 12;
c = (u8) *s++;
- if ((c & 0xc0) != 0xc0)
+ if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (u8) *s++;
- if ((c & 0xc0) != 0xc0)
+ if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index cd07ea3f0c63..15438469f21a 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -1658,6 +1658,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
u32 reg_base, or_reg, skip_reg;
unsigned long flags;
struct ptd ptd;
+ packet_enqueue *pe;
switch (usb_pipetype(urb->pipe)) {
case PIPE_ISOCHRONOUS:
@@ -1669,6 +1670,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
reg_base = INT_REGS_OFFSET;
or_reg = HC_INT_IRQ_MASK_OR_REG;
skip_reg = HC_INT_PTD_SKIPMAP_REG;
+ pe = enqueue_an_INT_packet;
break;
default:
@@ -1676,6 +1678,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
reg_base = ATL_REGS_OFFSET;
or_reg = HC_ATL_IRQ_MASK_OR_REG;
skip_reg = HC_ATL_PTD_SKIPMAP_REG;
+ pe = enqueue_an_ATL_packet;
break;
}
@@ -1687,6 +1690,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
u32 skip_map;
u32 or_map;
struct isp1760_qtd *qtd;
+ struct isp1760_qh *qh = ints->qh;
skip_map = isp1760_readl(hcd->regs + skip_reg);
skip_map |= 1 << i;
@@ -1699,8 +1703,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
priv_write_copy(priv, (u32 *)&ptd, hcd->regs + reg_base
+ i * sizeof(ptd), sizeof(ptd));
qtd = ints->qtd;
-
- clean_up_qtdlist(qtd);
+ qtd = clean_up_qtdlist(qtd);
free_mem(priv, ints->payload);
@@ -1711,7 +1714,24 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
ints->payload = 0;
isp1760_urb_done(priv, urb, status);
+ if (qtd)
+ pe(hcd, qh, qtd);
break;
+
+ } else if (ints->qtd) {
+ struct isp1760_qtd *qtd, *prev_qtd = ints->qtd;
+
+ for (qtd = ints->qtd->hw_next; qtd; qtd = qtd->hw_next) {
+ if (qtd->urb == urb) {
+ prev_qtd->hw_next = clean_up_qtdlist(qtd);
+ isp1760_urb_done(priv, urb, status);
+ break;
+ }
+ prev_qtd = qtd;
+ }
+ /* we found the urb before the end of the list */
+ if (qtd)
+ break;
}
ints++;
}
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index b7eacad4d48c..2bfd6dd85b5a 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -93,8 +93,7 @@ static int belkin_sa_startup(struct usb_serial *serial);
static void belkin_sa_shutdown(struct usb_serial *serial);
static int belkin_sa_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void belkin_sa_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void belkin_sa_close(struct usb_serial_port *port);
static void belkin_sa_read_int_callback(struct urb *urb);
static void belkin_sa_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios * old);
@@ -244,8 +243,7 @@ exit:
} /* belkin_sa_open */
-static void belkin_sa_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void belkin_sa_close(struct usb_serial_port *port)
{
dbg("%s port %d", __func__, port->number);
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index ab4cc277aa65..2830766f5b39 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -262,32 +262,40 @@ error: kfree(priv);
return r;
}
-static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static int ch341_carrier_raised(struct usb_serial_port *port)
+{
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ if (priv->line_status & CH341_BIT_DCD)
+ return 1;
+ return 0;
+}
+
+static void ch341_dtr_rts(struct usb_serial_port *port, int on)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- unsigned int c_cflag;
dbg("%s - port %d", __func__, port->number);
+ /* drop DTR and RTS */
+ spin_lock_irqsave(&priv->lock, flags);
+ if (on)
+ priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
+ else
+ priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ ch341_set_handshake(port->serial->dev, priv->line_control);
+ wake_up_interruptible(&priv->delta_msr_wait);
+}
+
+static void ch341_close(struct usb_serial_port *port)
+{
+ dbg("%s - port %d", __func__, port->number);
/* shutdown our urbs */
dbg("%s - shutting down urbs", __func__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
-
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop DTR and RTS */
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
- ch341_set_handshake(port->serial->dev, 0);
- }
- }
- wake_up_interruptible(&priv->delta_msr_wait);
}
@@ -302,7 +310,6 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port,
dbg("ch341_open()");
priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
r = ch341_configure(serial->dev, priv);
if (r)
@@ -322,7 +329,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port,
if (r) {
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
" error %d\n", __func__, r);
- ch341_close(tty, port, NULL);
+ ch341_close(port);
return -EPROTO;
}
@@ -343,9 +350,6 @@ static void ch341_set_termios(struct tty_struct *tty,
dbg("ch341_set_termios()");
- if (!tty || !tty->termios)
- return;
-
baud_rate = tty_get_baud_rate(tty);
priv->baud_rate = baud_rate;
@@ -568,6 +572,8 @@ static struct usb_serial_driver ch341_device = {
.usb_driver = &ch341_driver,
.num_ports = 1,
.open = ch341_open,
+ .dtr_rts = ch341_dtr_rts,
+ .carrier_raised = ch341_carrier_raised,
.close = ch341_close,
.ioctl = ch341_ioctl,
.set_termios = ch341_set_termios,
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 19e24045b137..247b61bfb7f4 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -169,7 +169,9 @@ static int usb_console_setup(struct console *co, char *options)
kfree(tty);
}
}
-
+ /* So we know not to kill the hardware on a hangup on this
+ port. We have also bumped the use count by one so it won't go
+ idle */
port->console = 1;
retval = 0;
@@ -182,7 +184,7 @@ free_tty:
kfree(tty);
reset_open_count:
port->port.count = 0;
-goto out;
+ goto out;
}
static void usb_console_write(struct console *co,
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index e8d5133ce9c8..16a154d3b2fe 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -1,5 +1,5 @@
/*
- * Silicon Laboratories CP2101/CP2102 USB to RS232 serial adaptor driver
+ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
*
* Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
*
@@ -27,44 +27,46 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.08"
-#define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
+#define DRIVER_VERSION "v0.09"
+#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
/*
* Function Prototypes
*/
-static int cp2101_open(struct tty_struct *, struct usb_serial_port *,
+static int cp210x_open(struct tty_struct *, struct usb_serial_port *,
struct file *);
-static void cp2101_cleanup(struct usb_serial_port *);
-static void cp2101_close(struct tty_struct *, struct usb_serial_port *,
- struct file*);
-static void cp2101_get_termios(struct tty_struct *,
+static void cp210x_cleanup(struct usb_serial_port *);
+static void cp210x_close(struct usb_serial_port *);
+static void cp210x_get_termios(struct tty_struct *,
struct usb_serial_port *port);
-static void cp2101_get_termios_port(struct usb_serial_port *port,
+static void cp210x_get_termios_port(struct usb_serial_port *port,
unsigned int *cflagp, unsigned int *baudp);
-static void cp2101_set_termios(struct tty_struct *, struct usb_serial_port *,
+static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
struct ktermios*);
-static int cp2101_tiocmget(struct tty_struct *, struct file *);
-static int cp2101_tiocmset(struct tty_struct *, struct file *,
+static int cp210x_tiocmget(struct tty_struct *, struct file *);
+static int cp210x_tiocmset(struct tty_struct *, struct file *,
unsigned int, unsigned int);
-static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *,
+static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *,
unsigned int, unsigned int);
-static void cp2101_break_ctl(struct tty_struct *, int);
-static int cp2101_startup(struct usb_serial *);
-static void cp2101_shutdown(struct usb_serial *);
+static void cp210x_break_ctl(struct tty_struct *, int);
+static int cp210x_startup(struct usb_serial *);
+static void cp210x_shutdown(struct usb_serial *);
static int debug;
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
{ USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+ { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */
{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
+ { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
{ USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
{ USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+ { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */
{ USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
@@ -85,10 +87,12 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
{ USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{ USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */
+ { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */
{ USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
{ USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */
{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */
{ USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */
+ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
@@ -99,7 +103,9 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */
{ USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
{ USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
+ { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
+ { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
@@ -108,53 +114,70 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE(usb, id_table);
-static struct usb_driver cp2101_driver = {
- .name = "cp2101",
+static struct usb_driver cp210x_driver = {
+ .name = "cp210x",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
.no_dynamic_id = 1,
};
-static struct usb_serial_driver cp2101_device = {
+static struct usb_serial_driver cp210x_device = {
.driver = {
.owner = THIS_MODULE,
- .name = "cp2101",
+ .name = "cp210x",
},
- .usb_driver = &cp2101_driver,
+ .usb_driver = &cp210x_driver,
.id_table = id_table,
.num_ports = 1,
- .open = cp2101_open,
- .close = cp2101_close,
- .break_ctl = cp2101_break_ctl,
- .set_termios = cp2101_set_termios,
- .tiocmget = cp2101_tiocmget,
- .tiocmset = cp2101_tiocmset,
- .attach = cp2101_startup,
- .shutdown = cp2101_shutdown,
+ .open = cp210x_open,
+ .close = cp210x_close,
+ .break_ctl = cp210x_break_ctl,
+ .set_termios = cp210x_set_termios,
+ .tiocmget = cp210x_tiocmget,
+ .tiocmset = cp210x_tiocmset,
+ .attach = cp210x_startup,
+ .shutdown = cp210x_shutdown,
};
/* Config request types */
#define REQTYPE_HOST_TO_DEVICE 0x41
#define REQTYPE_DEVICE_TO_HOST 0xc1
-/* Config SET requests. To GET, add 1 to the request number */
-#define CP2101_UART 0x00 /* Enable / Disable */
-#define CP2101_BAUDRATE 0x01 /* (BAUD_RATE_GEN_FREQ / baudrate) */
-#define CP2101_BITS 0x03 /* 0x(0)(databits)(parity)(stopbits) */
-#define CP2101_BREAK 0x05 /* On / Off */
-#define CP2101_CONTROL 0x07 /* Flow control line states */
-#define CP2101_MODEMCTL 0x13 /* Modem controls */
-#define CP2101_CONFIG_6 0x19 /* 6 bytes of config data ??? */
-
-/* CP2101_UART */
+/* Config request codes */
+#define CP210X_IFC_ENABLE 0x00
+#define CP210X_SET_BAUDDIV 0x01
+#define CP210X_GET_BAUDDIV 0x02
+#define CP210X_SET_LINE_CTL 0x03
+#define CP210X_GET_LINE_CTL 0x04
+#define CP210X_SET_BREAK 0x05
+#define CP210X_IMM_CHAR 0x06
+#define CP210X_SET_MHS 0x07
+#define CP210X_GET_MDMSTS 0x08
+#define CP210X_SET_XON 0x09
+#define CP210X_SET_XOFF 0x0A
+#define CP210X_SET_EVENTMASK 0x0B
+#define CP210X_GET_EVENTMASK 0x0C
+#define CP210X_SET_CHAR 0x0D
+#define CP210X_GET_CHARS 0x0E
+#define CP210X_GET_PROPS 0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET 0x11
+#define CP210X_PURGE 0x12
+#define CP210X_SET_FLOW 0x13
+#define CP210X_GET_FLOW 0x14
+#define CP210X_EMBED_EVENTS 0x15
+#define CP210X_GET_EVENTSTATE 0x16
+#define CP210X_SET_CHARS 0x19
+
+/* CP210X_IFC_ENABLE */
#define UART_ENABLE 0x0001
#define UART_DISABLE 0x0000
-/* CP2101_BAUDRATE */
+/* CP210X_(SET|GET)_BAUDDIV */
#define BAUD_RATE_GEN_FREQ 0x384000
-/* CP2101_BITS */
+/* CP210X_(SET|GET)_LINE_CTL */
#define BITS_DATA_MASK 0X0f00
#define BITS_DATA_5 0X0500
#define BITS_DATA_6 0X0600
@@ -174,11 +197,11 @@ static struct usb_serial_driver cp2101_device = {
#define BITS_STOP_1_5 0x0001
#define BITS_STOP_2 0x0002
-/* CP2101_BREAK */
+/* CP210X_SET_BREAK */
#define BREAK_ON 0x0000
#define BREAK_OFF 0x0001
-/* CP2101_CONTROL */
+/* CP210X_(SET_MHS|GET_MDMSTS) */
#define CONTROL_DTR 0x0001
#define CONTROL_RTS 0x0002
#define CONTROL_CTS 0x0010
@@ -189,13 +212,13 @@ static struct usb_serial_driver cp2101_device = {
#define CONTROL_WRITE_RTS 0x0200
/*
- * cp2101_get_config
- * Reads from the CP2101 configuration registers
+ * cp210x_get_config
+ * Reads from the CP210x configuration registers
* 'size' is specified in bytes.
* 'data' is a pointer to a pre-allocated array of integers large
* enough to hold 'size' bytes (with 4 bytes to each integer)
*/
-static int cp2101_get_config(struct usb_serial_port *port, u8 request,
+static int cp210x_get_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
@@ -211,9 +234,6 @@ static int cp2101_get_config(struct usb_serial_port *port, u8 request,
return -ENOMEM;
}
- /* For get requests, the request number must be incremented */
- request++;
-
/* Issue the request, attempting to read 'size' bytes */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
request, REQTYPE_DEVICE_TO_HOST, 0x0000,
@@ -236,12 +256,12 @@ static int cp2101_get_config(struct usb_serial_port *port, u8 request,
}
/*
- * cp2101_set_config
- * Writes to the CP2101 configuration registers
+ * cp210x_set_config
+ * Writes to the CP210x configuration registers
* Values less than 16 bits wide are sent directly
* 'size' is specified in bytes.
*/
-static int cp2101_set_config(struct usb_serial_port *port, u8 request,
+static int cp210x_set_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
@@ -292,21 +312,21 @@ static int cp2101_set_config(struct usb_serial_port *port, u8 request,
}
/*
- * cp2101_set_config_single
- * Convenience function for calling cp2101_set_config on single data values
+ * cp210x_set_config_single
+ * Convenience function for calling cp210x_set_config on single data values
* without requiring an integer pointer
*/
-static inline int cp2101_set_config_single(struct usb_serial_port *port,
+static inline int cp210x_set_config_single(struct usb_serial_port *port,
u8 request, unsigned int data)
{
- return cp2101_set_config(port, request, &data, 2);
+ return cp210x_set_config(port, request, &data, 2);
}
/*
- * cp2101_quantise_baudrate
+ * cp210x_quantise_baudrate
* Quantises the baud rate as per AN205 Table 1
*/
-static unsigned int cp2101_quantise_baudrate(unsigned int baud) {
+static unsigned int cp210x_quantise_baudrate(unsigned int baud) {
if (baud <= 56) baud = 0;
else if (baud <= 300) baud = 300;
else if (baud <= 600) baud = 600;
@@ -343,7 +363,7 @@ static unsigned int cp2101_quantise_baudrate(unsigned int baud) {
return baud;
}
-static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
+static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp)
{
struct usb_serial *serial = port->serial;
@@ -351,7 +371,7 @@ static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
dbg("%s - port %d", __func__, port->number);
- if (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
+ if (cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_ENABLE)) {
dev_err(&port->dev, "%s - Unable to enable UART\n",
__func__);
return -EPROTO;
@@ -373,17 +393,17 @@ static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
}
/* Configure the termios structure */
- cp2101_get_termios(tty, port);
+ cp210x_get_termios(tty, port);
/* Set the DTR and RTS pins low */
- cp2101_tiocmset_port(tty ? (struct usb_serial_port *) tty->driver_data
+ cp210x_tiocmset_port(tty ? (struct usb_serial_port *) tty->driver_data
: port,
NULL, TIOCM_DTR | TIOCM_RTS, 0);
return 0;
}
-static void cp2101_cleanup(struct usb_serial_port *port)
+static void cp210x_cleanup(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
@@ -398,8 +418,7 @@ static void cp2101_cleanup(struct usb_serial_port *port)
}
}
-static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static void cp210x_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
@@ -410,23 +429,23 @@ static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port,
mutex_lock(&port->serial->disc_mutex);
if (!port->serial->disconnected)
- cp2101_set_config_single(port, CP2101_UART, UART_DISABLE);
+ cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
mutex_unlock(&port->serial->disc_mutex);
}
/*
- * cp2101_get_termios
+ * cp210x_get_termios
* Reads the baud rate, data bits, parity, stop bits and flow control mode
* from the device, corrects any unsupported values, and configures the
* termios structure to reflect the state of the device
*/
-static void cp2101_get_termios(struct tty_struct *tty,
+static void cp210x_get_termios(struct tty_struct *tty,
struct usb_serial_port *port)
{
unsigned int baud;
if (tty) {
- cp2101_get_termios_port(tty->driver_data,
+ cp210x_get_termios_port(tty->driver_data,
&tty->termios->c_cflag, &baud);
tty_encode_baud_rate(tty, baud, baud);
}
@@ -434,15 +453,15 @@ static void cp2101_get_termios(struct tty_struct *tty,
else {
unsigned int cflag;
cflag = 0;
- cp2101_get_termios_port(port, &cflag, &baud);
+ cp210x_get_termios_port(port, &cflag, &baud);
}
}
/*
- * cp2101_get_termios_port
- * This is the heart of cp2101_get_termios which always uses a &usb_serial_port.
+ * cp210x_get_termios_port
+ * This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
*/
-static void cp2101_get_termios_port(struct usb_serial_port *port,
+static void cp210x_get_termios_port(struct usb_serial_port *port,
unsigned int *cflagp, unsigned int *baudp)
{
unsigned int cflag, modem_ctl[4];
@@ -451,17 +470,17 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
dbg("%s - port %d", __func__, port->number);
- cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2);
+ cp210x_get_config(port, CP210X_GET_BAUDDIV, &baud, 2);
/* Convert to baudrate */
if (baud)
- baud = cp2101_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud);
+ baud = cp210x_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud);
dbg("%s - baud rate = %d", __func__, baud);
*baudp = baud;
cflag = *cflagp;
- cp2101_get_config(port, CP2101_BITS, &bits, 2);
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
cflag &= ~CSIZE;
switch (bits & BITS_DATA_MASK) {
case BITS_DATA_5:
@@ -486,14 +505,14 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
default:
dbg("%s - Unknown number of data bits, using 8", __func__);
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
@@ -516,20 +535,20 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
__func__);
cflag &= ~PARENB;
bits &= ~BITS_PARITY_MASK;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
case BITS_PARITY_SPACE:
dbg("%s - parity = SPACE (not supported, disabling parity)",
__func__);
cflag &= ~PARENB;
bits &= ~BITS_PARITY_MASK;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
default:
dbg("%s - Unknown parity mode, disabling parity", __func__);
cflag &= ~PARENB;
bits &= ~BITS_PARITY_MASK;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
@@ -542,7 +561,7 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)",
__func__);
bits &= ~BITS_STOP_MASK;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
case BITS_STOP_2:
dbg("%s - stop bits = 2", __func__);
@@ -552,11 +571,11 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
dbg("%s - Unknown number of stop bits, using 1 stop bit",
__func__);
bits &= ~BITS_STOP_MASK;
- cp2101_set_config(port, CP2101_BITS, &bits, 2);
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
- cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+ cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
if (modem_ctl[0] & 0x0008) {
dbg("%s - flow control = CRTSCTS", __func__);
cflag |= CRTSCTS;
@@ -568,7 +587,7 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
*cflagp = cflag;
}
-static void cp2101_set_termios(struct tty_struct *tty,
+static void cp210x_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
unsigned int cflag, old_cflag;
@@ -583,13 +602,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
tty->termios->c_cflag &= ~CMSPAR;
cflag = tty->termios->c_cflag;
old_cflag = old_termios->c_cflag;
- baud = cp2101_quantise_baudrate(tty_get_baud_rate(tty));
+ baud = cp210x_quantise_baudrate(tty_get_baud_rate(tty));
/* If the baud rate is to be updated*/
if (baud != tty_termios_baud_rate(old_termios) && baud != 0) {
dbg("%s - Setting baud rate to %d baud", __func__,
baud);
- if (cp2101_set_config_single(port, CP2101_BAUDRATE,
+ if (cp210x_set_config_single(port, CP210X_SET_BAUDDIV,
((BAUD_RATE_GEN_FREQ + baud/2) / baud))) {
dbg("Baud rate requested not supported by device\n");
baud = tty_termios_baud_rate(old_termios);
@@ -600,7 +619,7 @@ static void cp2101_set_termios(struct tty_struct *tty,
/* If the number of data bits is to be updated */
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
- cp2101_get_config(port, CP2101_BITS, &bits, 2);
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_DATA_MASK;
switch (cflag & CSIZE) {
case CS5:
@@ -624,19 +643,19 @@ static void cp2101_set_termios(struct tty_struct *tty,
dbg("%s - data bits = 9", __func__);
break;*/
default:
- dbg("cp2101 driver does not "
+ dbg("cp210x driver does not "
"support the number of bits requested,"
" using 8 bit mode\n");
bits |= BITS_DATA_8;
break;
}
- if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dbg("Number of data bits requested "
"not supported by device\n");
}
if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) {
- cp2101_get_config(port, CP2101_BITS, &bits, 2);
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_PARITY_MASK;
if (cflag & PARENB) {
if (cflag & PARODD) {
@@ -647,13 +666,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
dbg("%s - parity = EVEN", __func__);
}
}
- if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dbg("Parity mode not supported "
"by device\n");
}
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
- cp2101_get_config(port, CP2101_BITS, &bits, 2);
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_STOP_MASK;
if (cflag & CSTOPB) {
bits |= BITS_STOP_2;
@@ -662,13 +681,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
bits |= BITS_STOP_1;
dbg("%s - stop bits = 1", __func__);
}
- if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dbg("Number of stop bits requested "
"not supported by device\n");
}
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
- cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+ cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
__func__, modem_ctl[0], modem_ctl[1],
modem_ctl[2], modem_ctl[3]);
@@ -688,19 +707,19 @@ static void cp2101_set_termios(struct tty_struct *tty,
dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
__func__, modem_ctl[0], modem_ctl[1],
modem_ctl[2], modem_ctl[3]);
- cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+ cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
}
}
-static int cp2101_tiocmset (struct tty_struct *tty, struct file *file,
+static int cp210x_tiocmset (struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
- return cp2101_tiocmset_port(port, file, set, clear);
+ return cp210x_tiocmset_port(port, file, set, clear);
}
-static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *file,
+static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear)
{
unsigned int control = 0;
@@ -726,10 +745,10 @@ static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *file,
dbg("%s - control = 0x%.4x", __func__, control);
- return cp2101_set_config(port, CP2101_CONTROL, &control, 2);
+ return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
}
-static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
+static int cp210x_tiocmget (struct tty_struct *tty, struct file *file)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int control;
@@ -737,7 +756,7 @@ static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
dbg("%s - port %d", __func__, port->number);
- cp2101_get_config(port, CP2101_CONTROL, &control, 1);
+ cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
@@ -751,7 +770,7 @@ static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
return result;
}
-static void cp2101_break_ctl (struct tty_struct *tty, int break_state)
+static void cp210x_break_ctl (struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int state;
@@ -763,17 +782,17 @@ static void cp2101_break_ctl (struct tty_struct *tty, int break_state)
state = BREAK_ON;
dbg("%s - turning break %s", __func__,
state == BREAK_OFF ? "off" : "on");
- cp2101_set_config(port, CP2101_BREAK, &state, 2);
+ cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
}
-static int cp2101_startup(struct usb_serial *serial)
+static int cp210x_startup(struct usb_serial *serial)
{
- /* CP2101 buffers behave strangely unless device is reset */
+ /* cp210x buffers behave strangely unless device is reset */
usb_reset_device(serial->dev);
return 0;
}
-static void cp2101_shutdown(struct usb_serial *serial)
+static void cp210x_shutdown(struct usb_serial *serial)
{
int i;
@@ -781,21 +800,21 @@ static void cp2101_shutdown(struct usb_serial *serial)
/* Stop reads and writes on all ports */
for (i = 0; i < serial->num_ports; ++i)
- cp2101_cleanup(serial->port[i]);
+ cp210x_cleanup(serial->port[i]);
}
-static int __init cp2101_init(void)
+static int __init cp210x_init(void)
{
int retval;
- retval = usb_serial_register(&cp2101_device);
+ retval = usb_serial_register(&cp210x_device);
if (retval)
return retval; /* Failed to register */
- retval = usb_register(&cp2101_driver);
+ retval = usb_register(&cp210x_driver);
if (retval) {
/* Failed to register */
- usb_serial_deregister(&cp2101_device);
+ usb_serial_deregister(&cp210x_device);
return retval;
}
@@ -805,14 +824,14 @@ static int __init cp2101_init(void)
return 0;
}
-static void __exit cp2101_exit(void)
+static void __exit cp210x_exit(void)
{
- usb_deregister(&cp2101_driver);
- usb_serial_deregister(&cp2101_device);
+ usb_deregister(&cp210x_driver);
+ usb_serial_deregister(&cp210x_device);
}
-module_init(cp2101_init);
-module_exit(cp2101_exit);
+module_init(cp210x_init);
+module_exit(cp210x_exit);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index dd501bb63ed6..933ba913e66c 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -61,8 +61,7 @@ static int cyberjack_startup(struct usb_serial *serial);
static void cyberjack_shutdown(struct usb_serial *serial);
static int cyberjack_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void cyberjack_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void cyberjack_close(struct usb_serial_port *port);
static int cyberjack_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count);
static int cyberjack_write_room(struct tty_struct *tty);
@@ -185,8 +184,7 @@ static int cyberjack_open(struct tty_struct *tty,
return result;
}
-static void cyberjack_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void cyberjack_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index e568710b263f..669f93848539 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -174,8 +174,8 @@ static int cypress_ca42v2_startup(struct usb_serial *serial);
static void cypress_shutdown(struct usb_serial *serial);
static int cypress_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void cypress_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void cypress_close(struct usb_serial_port *port);
+static void cypress_dtr_rts(struct usb_serial_port *port, int on);
static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static void cypress_send(struct usb_serial_port *port);
@@ -218,6 +218,7 @@ static struct usb_serial_driver cypress_earthmate_device = {
.shutdown = cypress_shutdown,
.open = cypress_open,
.close = cypress_close,
+ .dtr_rts = cypress_dtr_rts,
.write = cypress_write,
.write_room = cypress_write_room,
.ioctl = cypress_ioctl,
@@ -244,6 +245,7 @@ static struct usb_serial_driver cypress_hidcom_device = {
.shutdown = cypress_shutdown,
.open = cypress_open,
.close = cypress_close,
+ .dtr_rts = cypress_dtr_rts,
.write = cypress_write,
.write_room = cypress_write_room,
.ioctl = cypress_ioctl,
@@ -270,6 +272,7 @@ static struct usb_serial_driver cypress_ca42v2_device = {
.shutdown = cypress_shutdown,
.open = cypress_open,
.close = cypress_close,
+ .dtr_rts = cypress_dtr_rts,
.write = cypress_write,
.write_room = cypress_write_room,
.ioctl = cypress_ioctl,
@@ -656,11 +659,7 @@ static int cypress_open(struct tty_struct *tty,
priv->rx_flags = 0;
spin_unlock_irqrestore(&priv->lock, flags);
- /* raise both lines and set termios */
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = CONTROL_DTR | CONTROL_RTS;
- priv->cmd_ctrl = 1;
- spin_unlock_irqrestore(&priv->lock, flags);
+ /* Set termios */
result = cypress_write(tty, port, NULL, 0);
if (result) {
@@ -694,76 +693,42 @@ static int cypress_open(struct tty_struct *tty,
__func__, result);
cypress_set_dead(port);
}
-
+ port->port.drain_delay = 256;
return result;
} /* cypress_open */
+static void cypress_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct cypress_private *priv = usb_get_serial_port_data(port);
+ /* drop dtr and rts */
+ priv = usb_get_serial_port_data(port);
+ spin_lock_irq(&priv->lock);
+ if (on == 0)
+ priv->line_control = 0;
+ else
+ priv->line_control = CONTROL_DTR | CONTROL_RTS;
+ priv->cmd_ctrl = 1;
+ spin_unlock_irq(&priv->lock);
+ cypress_write(NULL, port, NULL, 0);
+}
-static void cypress_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void cypress_close(struct usb_serial_port *port)
{
struct cypress_private *priv = usb_get_serial_port_data(port);
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
dbg("%s - port %d", __func__, port->number);
- /* wait for data to drain from buffer */
- spin_lock_irq(&priv->lock);
- timeout = CYPRESS_CLOSING_WAIT;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&tty->write_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (cypress_buf_data_avail(priv->buf) == 0
- || timeout == 0 || signal_pending(current)
- /* without mutex, allowed due to harmless failure mode */
- || port->serial->disconnected)
- break;
- spin_unlock_irq(&priv->lock);
- timeout = schedule_timeout(timeout);
- spin_lock_irq(&priv->lock);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
- /* clear out any remaining data in the buffer */
- cypress_buf_clear(priv->buf);
- spin_unlock_irq(&priv->lock);
-
/* writing is potentially harmful, lock must be taken */
mutex_lock(&port->serial->disc_mutex);
if (port->serial->disconnected) {
mutex_unlock(&port->serial->disc_mutex);
return;
}
- /* wait for characters to drain from device */
- if (tty) {
- bps = tty_get_baud_rate(tty);
- if (bps > 1200)
- timeout = max((HZ * 2560) / bps, HZ / 10);
- else
- timeout = 2 * HZ;
- schedule_timeout_interruptible(timeout);
- }
-
+ cypress_buf_clear(priv->buf);
dbg("%s - stopping urbs", __func__);
usb_kill_urb(port->interrupt_in_urb);
usb_kill_urb(port->interrupt_out_urb);
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop dtr and rts */
- priv = usb_get_serial_port_data(port);
- spin_lock_irq(&priv->lock);
- priv->line_control = 0;
- priv->cmd_ctrl = 1;
- spin_unlock_irq(&priv->lock);
- cypress_write(tty, port, NULL, 0);
- }
- }
if (stats)
dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 38ba4ea8b6bf..30f5140eff03 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -422,7 +422,6 @@ struct digi_port {
int dp_throttled;
int dp_throttle_restart;
wait_queue_head_t dp_flush_wait;
- int dp_in_close; /* close in progress */
wait_queue_head_t dp_close_wait; /* wait queue for close */
struct work_struct dp_wakeup_work;
struct usb_serial_port *dp_port;
@@ -456,8 +455,9 @@ static int digi_write_room(struct tty_struct *tty);
static int digi_chars_in_buffer(struct tty_struct *tty);
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void digi_close(struct usb_serial_port *port);
+static int digi_carrier_raised(struct usb_serial_port *port);
+static void digi_dtr_rts(struct usb_serial_port *port, int on);
static int digi_startup_device(struct usb_serial *serial);
static int digi_startup(struct usb_serial *serial);
static void digi_shutdown(struct usb_serial *serial);
@@ -510,6 +510,8 @@ static struct usb_serial_driver digi_acceleport_2_device = {
.num_ports = 3,
.open = digi_open,
.close = digi_close,
+ .dtr_rts = digi_dtr_rts,
+ .carrier_raised = digi_carrier_raised,
.write = digi_write,
.write_room = digi_write_room,
.write_bulk_callback = digi_write_bulk_callback,
@@ -1328,6 +1330,19 @@ static int digi_chars_in_buffer(struct tty_struct *tty)
}
+static void digi_dtr_rts(struct usb_serial_port *port, int on)
+{
+ /* Adjust DTR and RTS */
+ digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1);
+}
+
+static int digi_carrier_raised(struct usb_serial_port *port)
+{
+ struct digi_port *priv = usb_get_serial_port_data(port);
+ if (priv->dp_modem_signals & TIOCM_CD)
+ return 1;
+ return 0;
+}
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp)
@@ -1336,7 +1351,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
unsigned char buf[32];
struct digi_port *priv = usb_get_serial_port_data(port);
struct ktermios not_termios;
- unsigned long flags = 0;
dbg("digi_open: TOP: port=%d, open_count=%d",
priv->dp_port_num, port->port.count);
@@ -1345,26 +1359,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
if (digi_startup_device(port->serial) != 0)
return -ENXIO;
- spin_lock_irqsave(&priv->dp_port_lock, flags);
-
- /* don't wait on a close in progress for non-blocking opens */
- if (priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) {
- spin_unlock_irqrestore(&priv->dp_port_lock, flags);
- return -EAGAIN;
- }
-
- /* wait for a close in progress to finish */
- while (priv->dp_in_close) {
- cond_wait_interruptible_timeout_irqrestore(
- &priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
- &priv->dp_port_lock, flags);
- if (signal_pending(current))
- return -EINTR;
- spin_lock_irqsave(&priv->dp_port_lock, flags);
- }
-
- spin_unlock_irqrestore(&priv->dp_port_lock, flags);
-
/* read modem signals automatically whenever they change */
buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
buf[1] = priv->dp_port_num;
@@ -1387,16 +1381,11 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
not_termios.c_iflag = ~tty->termios->c_iflag;
digi_set_termios(tty, port, &not_termios);
}
-
- /* set DTR and RTS */
- digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1);
-
return 0;
}
-static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static void digi_close(struct usb_serial_port *port)
{
DEFINE_WAIT(wait);
int ret;
@@ -1411,28 +1400,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
if (port->serial->disconnected)
goto exit;
- /* do cleanup only after final close on this port */
- spin_lock_irq(&priv->dp_port_lock);
- priv->dp_in_close = 1;
- spin_unlock_irq(&priv->dp_port_lock);
-
- /* tell line discipline to process only XON/XOFF */
- tty->closing = 1;
-
- /* wait for output to drain */
- if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0)
- tty_wait_until_sent(tty, DIGI_CLOSE_TIMEOUT);
-
- /* flush driver and line discipline buffers */
- tty_driver_flush_buffer(tty);
- tty_ldisc_flush(tty);
-
if (port->serial->dev) {
- /* wait for transmit idle */
- if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0)
- digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
- /* drop DTR and RTS */
- digi_set_modem_signals(port, 0, 0);
+ /* FIXME: Transmit idle belongs in the wait_unti_sent path */
+ digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
/* disable input flow control */
buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
@@ -1477,11 +1447,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
/* shutdown any outstanding bulk writes */
usb_kill_urb(port->write_urb);
}
- tty->closing = 0;
exit:
spin_lock_irq(&priv->dp_port_lock);
priv->dp_write_urb_in_use = 0;
- priv->dp_in_close = 0;
wake_up_interruptible(&priv->dp_close_wait);
spin_unlock_irq(&priv->dp_port_lock);
mutex_unlock(&port->serial->disc_mutex);
@@ -1560,7 +1528,6 @@ static int digi_startup(struct usb_serial *serial)
priv->dp_throttled = 0;
priv->dp_throttle_restart = 0;
init_waitqueue_head(&priv->dp_flush_wait);
- priv->dp_in_close = 0;
init_waitqueue_head(&priv->dp_close_wait);
INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
priv->dp_port = serial->port[i];
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index c709ec474a80..2b141ccb0cd9 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -81,8 +81,7 @@ static int debug;
/* function prototypes for an empeg-car player */
static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void empeg_close(struct usb_serial_port *port);
static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf,
int count);
@@ -181,8 +180,7 @@ static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port,
}
-static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static void empeg_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8100f1d25904..683304d60615 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -56,6 +56,7 @@ static __u16 vendor = FTDI_VID;
static __u16 product;
struct ftdi_private {
+ struct kref kref;
ftdi_chip_type_t chip_type;
/* type of device, either SIO or FT8U232AM */
int baud_base; /* baud base clock for divisor setting */
@@ -88,6 +89,7 @@ struct ftdi_private {
int force_rtscts; /* if non-zero, force RTS-CTS to always
be enabled */
+ unsigned int latency; /* latency setting in use */
spinlock_t tx_lock; /* spinlock for transmit state */
unsigned long tx_bytes;
unsigned long tx_outstanding_bytes;
@@ -669,6 +671,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(ADI_VID, ADI_GNICE_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
+ { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -716,8 +720,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port);
static int ftdi_sio_port_remove(struct usb_serial_port *port);
static int ftdi_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void ftdi_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void ftdi_close(struct usb_serial_port *port);
+static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int ftdi_write_room(struct tty_struct *tty);
@@ -755,6 +759,7 @@ static struct usb_serial_driver ftdi_sio_device = {
.port_remove = ftdi_sio_port_remove,
.open = ftdi_open,
.close = ftdi_close,
+ .dtr_rts = ftdi_dtr_rts,
.throttle = ftdi_throttle,
.unthrottle = ftdi_unthrottle,
.write = ftdi_write,
@@ -1034,7 +1039,54 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
return rv;
}
+static int write_latency_timer(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev = port->serial->dev;
+ char buf[1];
+ int rv = 0;
+ int l = priv->latency;
+
+ if (priv->flags & ASYNC_LOW_LATENCY)
+ l = 1;
+
+ dbg("%s: setting latency timer = %i", __func__, l);
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ l, priv->interface,
+ buf, 0, WDR_TIMEOUT);
+
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
+ return rv;
+}
+
+static int read_latency_timer(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev = port->serial->dev;
+ unsigned short latency = 0;
+ int rv = 0;
+
+
+ dbg("%s", __func__);
+
+ rv = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
+ 0, priv->interface,
+ (char *) &latency, 1, WDR_TIMEOUT);
+ if (rv < 0) {
+ dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
+ return -EIO;
+ }
+ return latency;
+}
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
@@ -1094,6 +1146,7 @@ static int set_serial_info(struct tty_struct *tty,
priv->custom_divisor = new_serial.custom_divisor;
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ write_latency_timer(port);
check_and_exit:
if ((old_priv.flags & ASYNC_SPD_MASK) !=
@@ -1189,27 +1242,13 @@ static ssize_t show_latency_timer(struct device *dev,
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev = port->serial->dev;
- unsigned short latency = 0;
- int rv = 0;
-
-
- dbg("%s", __func__);
-
- rv = usb_control_msg(udev,
- usb_rcvctrlpipe(udev, 0),
- FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
- FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
- 0, priv->interface,
- (char *) &latency, 1, WDR_TIMEOUT);
-
- if (rv < 0) {
- dev_err(dev, "Unable to read latency timer: %i\n", rv);
- return -EIO;
- }
- return sprintf(buf, "%i\n", latency);
+ if (priv->flags & ASYNC_LOW_LATENCY)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "%i\n", priv->latency);
}
+
/* Write a new value of the latency timer, in units of milliseconds. */
static ssize_t store_latency_timer(struct device *dev,
struct device_attribute *attr, const char *valbuf,
@@ -1217,25 +1256,13 @@ static ssize_t store_latency_timer(struct device *dev,
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev = port->serial->dev;
- char buf[1];
int v = simple_strtoul(valbuf, NULL, 10);
int rv = 0;
- dbg("%s: setting latency timer = %i", __func__, v);
-
- rv = usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
- FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
- FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
- v, priv->interface,
- buf, 0, WDR_TIMEOUT);
-
- if (rv < 0) {
- dev_err(dev, "Unable to write latency timer: %i\n", rv);
+ priv->latency = v;
+ rv = write_latency_timer(port);
+ if (rv < 0)
return -EIO;
- }
-
return count;
}
@@ -1352,6 +1379,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
return -ENOMEM;
}
+ kref_init(&priv->kref);
spin_lock_init(&priv->rx_lock);
spin_lock_init(&priv->tx_lock);
init_waitqueue_head(&priv->delta_msr_wait);
@@ -1388,6 +1416,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
usb_set_serial_port_data(port, priv);
ftdi_determine_type(port);
+ read_latency_timer(port);
create_sysfs_attrs(port);
return 0;
}
@@ -1468,6 +1497,13 @@ static void ftdi_shutdown(struct usb_serial *serial)
dbg("%s", __func__);
}
+static void ftdi_sio_priv_release(struct kref *k)
+{
+ struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
+
+ kfree(priv);
+}
+
static int ftdi_sio_port_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -1476,14 +1512,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
remove_sysfs_attrs(port);
- /* all open ports are closed at this point
- * (by usbserial.c:__serial_close, which calls ftdi_close)
- */
-
- if (priv) {
- usb_set_serial_port_data(port, NULL);
- kfree(priv);
- }
+ kref_put(&priv->kref, ftdi_sio_priv_release);
return 0;
}
@@ -1510,6 +1539,8 @@ static int ftdi_open(struct tty_struct *tty,
if (tty)
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ write_latency_timer(port);
+
/* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
@@ -1525,11 +1556,6 @@ static int ftdi_open(struct tty_struct *tty,
if (tty)
ftdi_set_termios(tty, port, tty->termios);
- /* FIXME: Flow control might be enabled, so it should be checked -
- we have no control of defaults! */
- /* Turn on RTS and DTR since we are not flow controlling by default */
- set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
-
/* Not throttled */
spin_lock_irqsave(&priv->rx_lock, flags);
priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
@@ -1547,12 +1573,37 @@ static int ftdi_open(struct tty_struct *tty,
dev_err(&port->dev,
"%s - failed submitting read urb, error %d\n",
__func__, result);
-
+ else
+ kref_get(&priv->kref);
return result;
} /* ftdi_open */
+static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ char buf[1];
+
+ mutex_lock(&port->serial->disc_mutex);
+ if (!port->serial->disconnected) {
+ /* Disable flow control */
+ if (!on && usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, priv->interface, buf, 0,
+ WDR_TIMEOUT) < 0) {
+ dev_err(&port->dev, "error from flowcontrol urb\n");
+ }
+ /* drop RTS and DTR */
+ if (on)
+ set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ else
+ clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ }
+ mutex_unlock(&port->serial->disc_mutex);
+}
/*
* usbserial:__serial_close only calls ftdi_close if the point is open
@@ -1562,38 +1613,19 @@ static int ftdi_open(struct tty_struct *tty,
*
*/
-static void ftdi_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void ftdi_close(struct usb_serial_port *port)
{ /* ftdi_close */
- unsigned int c_cflag = tty->termios->c_cflag;
struct ftdi_private *priv = usb_get_serial_port_data(port);
- char buf[1];
dbg("%s", __func__);
- mutex_lock(&port->serial->disc_mutex);
- if (c_cflag & HUPCL && !port->serial->disconnected) {
- /* Disable flow control */
- if (usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, priv->interface, buf, 0,
- WDR_TIMEOUT) < 0) {
- dev_err(&port->dev, "error from flowcontrol urb\n");
- }
-
- /* drop RTS and DTR */
- clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
- } /* Note change no line if hupcl is off */
- mutex_unlock(&port->serial->disc_mutex);
/* cancel any scheduled reading */
- cancel_delayed_work(&priv->rx_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&priv->rx_work);
/* shutdown our bulk read */
usb_kill_urb(port->read_urb);
+ kref_put(&priv->kref, ftdi_sio_priv_release);
} /* ftdi_close */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index c09f658a448b..12330fa1c095 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -920,6 +920,12 @@
#define JETI_SPC1201_PID 0x04b2
/*
+ * Marvell SheevaPlug
+ */
+#define MARVELL_VID 0x9e88
+#define MARVELL_SHEEVAPLUG_PID 0x9e8f
+
+/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_E2_READ
* wValue: 0
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 586d30ff450b..ee25a3fe3b09 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -993,8 +993,7 @@ static int garmin_open(struct tty_struct *tty,
}
-static void garmin_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void garmin_close(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 4cec9906ccf3..be82ea956720 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -184,8 +184,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
}
EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
-void usb_serial_generic_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+void usb_serial_generic_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
generic_cleanup(port);
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index fb4a73d090f6..53ef5996e33d 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -207,8 +207,7 @@ static void edge_bulk_out_cmd_callback(struct urb *urb);
/* function prototypes for the usbserial callbacks */
static int edge_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void edge_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void edge_close(struct usb_serial_port *port);
static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int edge_write_room(struct tty_struct *tty);
@@ -965,7 +964,7 @@ static int edge_open(struct tty_struct *tty,
if (!edge_port->txfifo.fifo) {
dbg("%s - no memory", __func__);
- edge_close(tty, port, filp);
+ edge_close(port);
return -ENOMEM;
}
@@ -975,7 +974,7 @@ static int edge_open(struct tty_struct *tty,
if (!edge_port->write_urb) {
dbg("%s - no memory", __func__);
- edge_close(tty, port, filp);
+ edge_close(port);
return -ENOMEM;
}
@@ -1099,8 +1098,7 @@ static void block_until_tx_empty(struct edgeport_port *edge_port)
* edge_close
* this function is called by the tty driver when a port is closed
*****************************************************************************/
-static void edge_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void edge_close(struct usb_serial_port *port)
{
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 513b25e044c1..eabf20eeb370 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2009,8 +2009,7 @@ release_es_lock:
return status;
}
-static void edge_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void edge_close(struct usb_serial_port *port)
{
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index cd62825a9ac3..c610a99fa477 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -76,8 +76,7 @@ static int initial_wait;
/* Function prototypes for an ipaq */
static int ipaq_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void ipaq_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void ipaq_close(struct usb_serial_port *port);
static int ipaq_calc_num_ports(struct usb_serial *serial);
static int ipaq_startup(struct usb_serial *serial);
static void ipaq_shutdown(struct usb_serial *serial);
@@ -714,8 +713,7 @@ error:
}
-static void ipaq_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void ipaq_close(struct usb_serial_port *port)
{
struct ipaq_private *priv = usb_get_serial_port_data(port);
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index da2a2b46644a..29ad038b9c8d 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -302,23 +302,17 @@ static int ipw_open(struct tty_struct *tty,
return 0;
}
-static void ipw_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void ipw_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_device *dev = port->serial->dev;
int result;
- if (tty_hung_up_p(filp)) {
- dbg("%s: tty_hung_up_p ...", __func__);
- return;
- }
-
/*--1: drop the dtr */
dbg("%s:dropping dtr", __func__);
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
IPW_SIO_SET_PIN,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
- IPW_PIN_CLRDTR,
+ on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
0,
NULL,
0,
@@ -332,7 +326,7 @@ static void ipw_close(struct tty_struct *tty,
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
USB_RECIP_INTERFACE | USB_DIR_OUT,
- IPW_PIN_CLRRTS,
+ on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
0,
NULL,
0,
@@ -340,7 +334,12 @@ static void ipw_close(struct tty_struct *tty,
if (result < 0)
dev_err(&port->dev,
"dropping rts failed (error = %d)\n", result);
+}
+static void ipw_close(struct usb_serial_port *port)
+{
+ struct usb_device *dev = port->serial->dev;
+ int result;
/*--3: purge */
dbg("%s:sending purge", __func__);
@@ -461,6 +460,7 @@ static struct usb_serial_driver ipw_device = {
.num_ports = 1,
.open = ipw_open,
.close = ipw_close,
+ .dtr_rts = ipw_dtr_rts,
.port_probe = ipw_probe,
.port_remove = ipw_disconnect,
.write = ipw_write,
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 4e2cda93da59..66009b6b763a 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -88,8 +88,7 @@ static int xbof = -1;
static int ir_startup (struct usb_serial *serial);
static int ir_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filep);
-static void ir_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filep);
+static void ir_close(struct usb_serial_port *port);
static int ir_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static void ir_write_bulk_callback (struct urb *urb);
@@ -346,8 +345,7 @@ static int ir_open(struct tty_struct *tty,
return result;
}
-static void ir_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file * filp)
+static void ir_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 4473d442b2aa..76a3cc327bb9 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -40,7 +40,7 @@ static int debug;
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.5"
+#define DRIVER_VERSION "v0.10"
#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
static struct usb_device_id id_table[] = {
@@ -70,7 +70,6 @@ static void read_rxcmd_callback(struct urb *urb);
struct iuu_private {
spinlock_t lock; /* store irq state */
wait_queue_head_t delta_msr_wait;
- u8 line_control;
u8 line_status;
u8 termios_initialized;
int tiostatus; /* store IUART SIGNAL for tiocmget call */
@@ -651,32 +650,33 @@ static int iuu_bulk_write(struct usb_serial_port *port)
unsigned long flags;
int result;
int i;
+ int buf_len;
char *buf_ptr = port->write_urb->transfer_buffer;
dbg("%s - enter", __func__);
+ spin_lock_irqsave(&priv->lock, flags);
*buf_ptr++ = IUU_UART_ESC;
*buf_ptr++ = IUU_UART_TX;
*buf_ptr++ = priv->writelen;
- memcpy(buf_ptr, priv->writebuf,
- priv->writelen);
+ memcpy(buf_ptr, priv->writebuf, priv->writelen);
+ buf_len = priv->writelen;
+ priv->writelen = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
if (debug == 1) {
- for (i = 0; i < priv->writelen; i++)
+ for (i = 0; i < buf_len; i++)
sprintf(priv->dbgbuf + i*2 ,
"%02X", priv->writebuf[i]);
- priv->dbgbuf[priv->writelen+i*2] = 0;
+ priv->dbgbuf[buf_len+i*2] = 0;
dbg("%s - writing %i chars : %s", __func__,
- priv->writelen, priv->dbgbuf);
+ buf_len, priv->dbgbuf);
}
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
- port->write_urb->transfer_buffer, priv->writelen + 3,
+ port->write_urb->transfer_buffer, buf_len + 3,
iuu_rxcmd, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
- spin_lock_irqsave(&priv->lock, flags);
- priv->writelen = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
return result;
}
@@ -770,14 +770,10 @@ static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port,
return -ENOMEM;
spin_lock_irqsave(&priv->lock, flags);
- if (priv->writelen > 0) {
- /* buffer already filled but not commited */
- spin_unlock_irqrestore(&priv->lock, flags);
- return 0;
- }
+
/* fill the buffer */
- memcpy(priv->writebuf, buf, count);
- priv->writelen = count;
+ memcpy(priv->writebuf + priv->writelen, buf, count);
+ priv->writelen += count;
spin_unlock_irqrestore(&priv->lock, flags);
return count;
@@ -819,7 +815,7 @@ static int iuu_uart_on(struct usb_serial_port *port)
buf[0] = IUU_UART_ENABLE;
buf[1] = (u8) ((IUU_BAUD_9600 >> 8) & 0x00FF);
buf[2] = (u8) (0x00FF & IUU_BAUD_9600);
- buf[3] = (u8) (0x0F0 & IUU_TWO_STOP_BITS) | (0x07 & IUU_PARITY_EVEN);
+ buf[3] = (u8) (0x0F0 & IUU_ONE_STOP_BIT) | (0x07 & IUU_PARITY_EVEN);
status = bulk_immediate(port, buf, 4);
if (status != IUU_OPERATION_OK) {
@@ -946,19 +942,59 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud,
return status;
}
-static int set_control_lines(struct usb_device *dev, u8 value)
+static void iuu_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
{
- return 0;
+ const u32 supported_mask = CMSPAR|PARENB|PARODD;
+
+ unsigned int cflag = tty->termios->c_cflag;
+ int status;
+ u32 actual;
+ u32 parity;
+ int csize = CS7;
+ int baud = 9600; /* Fixed for the moment */
+ u32 newval = cflag & supported_mask;
+
+ /* compute the parity parameter */
+ parity = 0;
+ if (cflag & CMSPAR) { /* Using mark space */
+ if (cflag & PARODD)
+ parity |= IUU_PARITY_SPACE;
+ else
+ parity |= IUU_PARITY_MARK;
+ } else if (!(cflag & PARENB)) {
+ parity |= IUU_PARITY_NONE;
+ csize = CS8;
+ } else if (cflag & PARODD)
+ parity |= IUU_PARITY_ODD;
+ else
+ parity |= IUU_PARITY_EVEN;
+
+ parity |= (cflag & CSTOPB ? IUU_TWO_STOP_BITS : IUU_ONE_STOP_BIT);
+
+ /* set it */
+ status = iuu_uart_baud(port,
+ (clockmode == 2) ? 16457 : 9600 * boost / 100,
+ &actual, parity);
+
+ /* set the termios value to the real one, so the user now what has
+ * changed. We support few fields so its easies to copy the old hw
+ * settings back over and then adjust them
+ */
+ if (old_termios)
+ tty_termios_copy_hw(tty->termios, old_termios);
+ if (status != 0) /* Set failed - return old bits */
+ return;
+ /* Re-encode speed, parity and csize */
+ tty_encode_baud_rate(tty, baud, baud);
+ tty->termios->c_cflag &= ~(supported_mask|CSIZE);
+ tty->termios->c_cflag |= newval | csize;
}
-static void iuu_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void iuu_close(struct usb_serial_port *port)
{
/* iuu_led (port,255,0,0,0); */
struct usb_serial *serial;
- struct iuu_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int c_cflag;
serial = port->serial;
if (!serial)
@@ -968,17 +1004,6 @@ static void iuu_close(struct tty_struct *tty,
iuu_uart_off(port);
if (serial->dev) {
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop DTR and RTS */
- priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
- set_control_lines(port->serial->dev, 0);
- }
- }
/* free writebuf */
/* shutdown our urbs */
dbg("%s - shutting down urbs", __func__);
@@ -1154,7 +1179,7 @@ static int iuu_open(struct tty_struct *tty,
if (result) {
dev_err(&port->dev, "%s - failed submitting read urb,"
" error %d\n", __func__, result);
- iuu_close(tty, port, NULL);
+ iuu_close(port);
return -EPROTO;
} else {
dbg("%s - rxcmd OK", __func__);
@@ -1175,6 +1200,7 @@ static struct usb_serial_driver iuu_device = {
.read_bulk_callback = iuu_uart_read_callback,
.tiocmget = iuu_tiocmget,
.tiocmset = iuu_tiocmset,
+ .set_termios = iuu_set_termios,
.attach = iuu_startup,
.shutdown = iuu_shutdown,
};
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 00daa8f7759a..f1195a98f316 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -1298,8 +1298,16 @@ static inline void stop_urb(struct urb *urb)
usb_kill_urb(urb);
}
-static void keyspan_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void keyspan_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
+
+ p_priv->rts_state = on;
+ p_priv->dtr_state = on;
+ keyspan_send_setup(port, 0);
+}
+
+static void keyspan_close(struct usb_serial_port *port)
{
int i;
struct usb_serial *serial = port->serial;
@@ -1336,7 +1344,6 @@ static void keyspan_close(struct tty_struct *tty,
stop_urb(p_priv->out_urbs[i]);
}
}
- tty_port_tty_set(&port->port, NULL);
}
/* download the firmware to a pre-renumeration device */
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
index 38b4582e0734..0d4569b60768 100644
--- a/drivers/usb/serial/keyspan.h
+++ b/drivers/usb/serial/keyspan.h
@@ -38,9 +38,8 @@
static int keyspan_open (struct tty_struct *tty,
struct usb_serial_port *port,
struct file *filp);
-static void keyspan_close (struct tty_struct *tty,
- struct usb_serial_port *port,
- struct file *filp);
+static void keyspan_close (struct usb_serial_port *port);
+static void keyspan_dtr_rts (struct usb_serial_port *port, int on);
static int keyspan_startup (struct usb_serial *serial);
static void keyspan_shutdown (struct usb_serial *serial);
static int keyspan_write_room (struct tty_struct *tty);
@@ -562,6 +561,7 @@ static struct usb_serial_driver keyspan_1port_device = {
.num_ports = 1,
.open = keyspan_open,
.close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
@@ -582,6 +582,7 @@ static struct usb_serial_driver keyspan_2port_device = {
.num_ports = 2,
.open = keyspan_open,
.close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
@@ -602,6 +603,7 @@ static struct usb_serial_driver keyspan_4port_device = {
.num_ports = 4,
.open = keyspan_open,
.close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index bf1ae247da66..ab769dbea1b3 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -651,6 +651,35 @@ static int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
}
+static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct usb_serial *serial = port->serial;
+
+ if (serial->dev) {
+ if (on)
+ keyspan_pda_set_modem_info(serial, (1<<7) | (1<< 2));
+ else
+ keyspan_pda_set_modem_info(serial, 0);
+ }
+}
+
+static int keyspan_pda_carrier_raised(struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned char modembits;
+
+ /* If we can read the modem status and the DCD is low then
+ carrier is not raised yet */
+ if (keyspan_pda_get_modem_info(serial, &modembits) >= 0) {
+ if (!(modembits & (1>>6)))
+ return 0;
+ }
+ /* Carrier raised, or we failed (eg disconnected) so
+ progress accordingly */
+ return 1;
+}
+
+
static int keyspan_pda_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp)
{
@@ -682,13 +711,6 @@ static int keyspan_pda_open(struct tty_struct *tty,
priv->tx_room = room;
priv->tx_throttled = room ? 0 : 1;
- /* the normal serial device seems to always turn on DTR and RTS here,
- so do the same */
- if (tty && (tty->termios->c_cflag & CBAUD))
- keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2));
- else
- keyspan_pda_set_modem_info(serial, 0);
-
/*Start reading from the device*/
port->interrupt_in_urb->dev = serial->dev;
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
@@ -700,19 +722,11 @@ static int keyspan_pda_open(struct tty_struct *tty,
error:
return rc;
}
-
-
-static void keyspan_pda_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void keyspan_pda_close(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
if (serial->dev) {
- /* the normal serial device seems to always shut
- off DTR and RTS now */
- if (tty->termios->c_cflag & HUPCL)
- keyspan_pda_set_modem_info(serial, 0);
-
/* shutdown our bulk reads and writes */
usb_kill_urb(port->write_urb);
usb_kill_urb(port->interrupt_in_urb);
@@ -839,6 +853,8 @@ static struct usb_serial_driver keyspan_pda_device = {
.usb_driver = &keyspan_pda_driver,
.id_table = id_table_std,
.num_ports = 1,
+ .dtr_rts = keyspan_pda_dtr_rts,
+ .carrier_raised = keyspan_pda_carrier_raised,
.open = keyspan_pda_open,
.close = keyspan_pda_close,
.write = keyspan_pda_write,
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index fcd9082f3e7f..fa817c66b3e8 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -76,8 +76,7 @@ static int klsi_105_startup(struct usb_serial *serial);
static void klsi_105_shutdown(struct usb_serial *serial);
static int klsi_105_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void klsi_105_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void klsi_105_close(struct usb_serial_port *port);
static int klsi_105_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count);
static void klsi_105_write_bulk_callback(struct urb *urb);
@@ -447,8 +446,7 @@ exit:
} /* klsi_105_open */
-static void klsi_105_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void klsi_105_close(struct usb_serial_port *port)
{
struct klsi_105_private *priv = usb_get_serial_port_data(port);
int rc;
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index c148544953b3..6b570498287f 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -72,8 +72,7 @@ static int kobil_startup(struct usb_serial *serial);
static void kobil_shutdown(struct usb_serial *serial);
static int kobil_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void kobil_close(struct usb_serial_port *port);
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int kobil_write_room(struct tty_struct *tty);
@@ -209,7 +208,7 @@ static void kobil_shutdown(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
while (serial->port[i]->port.count > 0)
- kobil_close(NULL, serial->port[i], NULL);
+ kobil_close(serial->port[i]);
kfree(usb_get_serial_port_data(serial->port[i]));
usb_set_serial_port_data(serial->port[i], NULL);
}
@@ -346,11 +345,11 @@ static int kobil_open(struct tty_struct *tty,
}
-static void kobil_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void kobil_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
+ /* FIXME: Add rts/dtr methods */
if (port->write_urb) {
usb_kill_urb(port->write_urb);
usb_free_urb(port->write_urb);
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 82930a7d5093..873795548fc0 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -95,8 +95,8 @@ static int mct_u232_startup(struct usb_serial *serial);
static void mct_u232_shutdown(struct usb_serial *serial);
static int mct_u232_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void mct_u232_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void mct_u232_close(struct usb_serial_port *port);
+static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
static void mct_u232_read_int_callback(struct urb *urb);
static void mct_u232_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
@@ -140,6 +140,7 @@ static struct usb_serial_driver mct_u232_device = {
.num_ports = 1,
.open = mct_u232_open,
.close = mct_u232_close,
+ .dtr_rts = mct_u232_dtr_rts,
.throttle = mct_u232_throttle,
.unthrottle = mct_u232_unthrottle,
.read_int_callback = mct_u232_read_int_callback,
@@ -496,29 +497,29 @@ error:
return retval;
} /* mct_u232_open */
-
-static void mct_u232_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
{
- unsigned int c_cflag;
unsigned int control_state;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
- dbg("%s port %d", __func__, port->number);
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- mutex_lock(&port->serial->disc_mutex);
- if (c_cflag & HUPCL && !port->serial->disconnected) {
- /* drop DTR and RTS */
- spin_lock_irq(&priv->lock);
+ mutex_lock(&port->serial->disc_mutex);
+ if (!port->serial->disconnected) {
+ /* drop DTR and RTS */
+ spin_lock_irq(&priv->lock);
+ if (on)
+ priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+ else
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
- control_state = priv->control_state;
- spin_unlock_irq(&priv->lock);
- mct_u232_set_modem_ctrl(port->serial, control_state);
- }
- mutex_unlock(&port->serial->disc_mutex);
+ control_state = priv->control_state;
+ spin_unlock_irq(&priv->lock);
+ mct_u232_set_modem_ctrl(port->serial, control_state);
}
+ mutex_unlock(&port->serial->disc_mutex);
+}
+static void mct_u232_close(struct usb_serial_port *port)
+{
+ dbg("%s port %d", __func__, port->number);
if (port->serial->dev) {
/* shutdown our urbs */
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 24e3b5d4b4d4..9e1a013ee7f6 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -533,8 +533,7 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty)
return chars;
}
-static void mos7720_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void mos7720_close(struct usb_serial_port *port)
{
struct usb_serial *serial;
struct moschip_port *mos7720_port;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 84fb1dcd30dc..10b78a37214f 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1135,54 +1135,12 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
}
-/************************************************************************
- *
- * mos7840_block_until_tx_empty
- *
- * This function will block the close until one of the following:
- * 1. TX count are 0
- * 2. The mos7840 has stopped
- * 3. A timeout of 3 seconds without activity has expired
- *
- ************************************************************************/
-static void mos7840_block_until_tx_empty(struct tty_struct *tty,
- struct moschip_port *mos7840_port)
-{
- int timeout = HZ / 10;
- int wait = 30;
- int count;
-
- while (1) {
-
- count = mos7840_chars_in_buffer(tty);
-
- /* Check for Buffer status */
- if (count <= 0)
- return;
-
- /* Block the thread for a while */
- interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
- timeout);
-
- /* No activity.. count down section */
- wait--;
- if (wait == 0) {
- dbg("%s - TIMEOUT", __func__);
- return;
- } else {
- /* Reset timeout value back to seconds */
- wait = 30;
- }
- }
-}
-
/*****************************************************************************
* mos7840_close
* this function is called by the tty driver when a port is closed
*****************************************************************************/
-static void mos7840_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void mos7840_close(struct usb_serial_port *port)
{
struct usb_serial *serial;
struct moschip_port *mos7840_port;
@@ -1223,10 +1181,6 @@ static void mos7840_close(struct tty_struct *tty,
}
}
- if (serial->dev)
- /* flush and block until tx is empty */
- mos7840_block_until_tx_empty(tty, mos7840_port);
-
/* While closing port, shutdown all bulk read, write *
* and interrupt read if they exists */
if (serial->dev) {
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
index bcdcbb822705..f5f3751a888c 100644
--- a/drivers/usb/serial/navman.c
+++ b/drivers/usb/serial/navman.c
@@ -98,8 +98,7 @@ static int navman_open(struct tty_struct *tty,
return result;
}
-static void navman_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void navman_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index df6539712726..1104617334f5 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -66,8 +66,7 @@ static int debug;
/* function prototypes */
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void omninet_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void omninet_close(struct usb_serial_port *port);
static void omninet_read_bulk_callback(struct urb *urb);
static void omninet_write_bulk_callback(struct urb *urb);
static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
@@ -189,8 +188,7 @@ static int omninet_open(struct tty_struct *tty,
return result;
}
-static void omninet_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void omninet_close(struct usb_serial_port *port)
{
dbg("%s - port %d", __func__, port->number);
usb_kill_urb(port->read_urb);
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index b500ad10b758..c20480aa9755 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -173,8 +173,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port,
return result;
}
-static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static void opticon_close(struct usb_serial_port *port)
{
struct opticon_private *priv = usb_get_serial_data(port->serial);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 7817b82889ca..a16d69fadba1 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -45,8 +45,9 @@
/* Function prototypes */
static int option_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void option_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void option_close(struct usb_serial_port *port);
+static void option_dtr_rts(struct usb_serial_port *port, int on);
+
static int option_startup(struct usb_serial *serial);
static void option_shutdown(struct usb_serial *serial);
static int option_write_room(struct tty_struct *tty);
@@ -61,7 +62,7 @@ static void option_set_termios(struct tty_struct *tty,
static int option_tiocmget(struct tty_struct *tty, struct file *file);
static int option_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
-static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port);
+static int option_send_setup(struct usb_serial_port *port);
static int option_suspend(struct usb_serial *serial, pm_message_t message);
static int option_resume(struct usb_serial *serial);
@@ -551,6 +552,7 @@ static struct usb_serial_driver option_1port_device = {
.num_ports = 1,
.open = option_open,
.close = option_close,
+ .dtr_rts = option_dtr_rts,
.write = option_write,
.write_room = option_write_room,
.chars_in_buffer = option_chars_in_buffer,
@@ -630,7 +632,7 @@ static void option_set_termios(struct tty_struct *tty,
dbg("%s", __func__);
/* Doesn't support option setting */
tty_termios_copy_hw(tty->termios, old_termios);
- option_send_setup(tty, port);
+ option_send_setup(port);
}
static int option_tiocmget(struct tty_struct *tty, struct file *file)
@@ -669,7 +671,7 @@ static int option_tiocmset(struct tty_struct *tty, struct file *file,
portdata->rts_state = 0;
if (clear & TIOCM_DTR)
portdata->dtr_state = 0;
- return option_send_setup(tty, port);
+ return option_send_setup(port);
}
/* Write */
@@ -897,10 +899,6 @@ static int option_open(struct tty_struct *tty,
dbg("%s", __func__);
- /* Set some sane defaults */
- portdata->rts_state = 1;
- portdata->dtr_state = 1;
-
/* Reset low level data toggle and start reading from endpoints */
for (i = 0; i < N_IN_URB; i++) {
urb = portdata->in_urbs[i];
@@ -936,37 +934,43 @@ static int option_open(struct tty_struct *tty,
usb_pipeout(urb->pipe), 0); */
}
- option_send_setup(tty, port);
+ option_send_setup(port);
return 0;
}
-static void option_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void option_dtr_rts(struct usb_serial_port *port, int on)
{
- int i;
struct usb_serial *serial = port->serial;
struct option_port_private *portdata;
dbg("%s", __func__);
portdata = usb_get_serial_port_data(port);
+ mutex_lock(&serial->disc_mutex);
+ portdata->rts_state = on;
+ portdata->dtr_state = on;
+ if (serial->dev)
+ option_send_setup(port);
+ mutex_unlock(&serial->disc_mutex);
+}
- portdata->rts_state = 0;
- portdata->dtr_state = 0;
- if (serial->dev) {
- mutex_lock(&serial->disc_mutex);
- if (!serial->disconnected)
- option_send_setup(tty, port);
- mutex_unlock(&serial->disc_mutex);
+static void option_close(struct usb_serial_port *port)
+{
+ int i;
+ struct usb_serial *serial = port->serial;
+ struct option_port_private *portdata;
+
+ dbg("%s", __func__);
+ portdata = usb_get_serial_port_data(port);
+ if (serial->dev) {
/* Stop reading/writing urbs */
for (i = 0; i < N_IN_URB; i++)
usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
}
- tty_port_tty_set(&port->port, NULL);
}
/* Helper functions used by option_setup_urbs */
@@ -1032,28 +1036,24 @@ static void option_setup_urbs(struct usb_serial *serial)
* This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
* CDC.
*/
-static int option_send_setup(struct tty_struct *tty,
- struct usb_serial_port *port)
+static int option_send_setup(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct option_port_private *portdata;
int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+ int val = 0;
dbg("%s", __func__);
portdata = usb_get_serial_port_data(port);
- if (tty) {
- int val = 0;
- if (portdata->dtr_state)
- val |= 0x01;
- if (portdata->rts_state)
- val |= 0x02;
+ if (portdata->dtr_state)
+ val |= 0x01;
+ if (portdata->rts_state)
+ val |= 0x02;
- return usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
- 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
- }
- return 0;
+ return usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
}
static int option_startup(struct usb_serial *serial)
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index ba551f00f16f..7de54781fe61 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -143,8 +143,7 @@ struct oti6858_control_pkt {
/* function prototypes */
static int oti6858_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void oti6858_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void oti6858_close(struct usb_serial_port *port);
static void oti6858_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int oti6858_ioctl(struct tty_struct *tty, struct file *file,
@@ -622,67 +621,30 @@ static int oti6858_open(struct tty_struct *tty,
if (result != 0) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed"
" with error %d\n", __func__, result);
- oti6858_close(tty, port, NULL);
+ oti6858_close(port);
return -EPROTO;
}
/* setup termios */
if (tty)
oti6858_set_termios(tty, port, &tmp_termios);
-
+ port->port.drain_delay = 256; /* FIXME: check the FIFO length */
return 0;
}
-static void oti6858_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void oti6858_close(struct usb_serial_port *port)
{
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- long timeout;
- wait_queue_t wait;
dbg("%s(port = %d)", __func__, port->number);
- /* wait for data to drain from the buffer */
spin_lock_irqsave(&priv->lock, flags);
- timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&tty->write_wait, &wait);
- dbg("%s(): entering wait loop", __func__);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (oti6858_buf_data_avail(priv->buf) == 0
- || timeout == 0 || signal_pending(current)
- || port->serial->disconnected)
- break;
- spin_unlock_irqrestore(&priv->lock, flags);
- timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
- dbg("%s(): after wait loop", __func__);
-
/* clear out any remaining data in the buffer */
oti6858_buf_clear(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
- /* wait for characters to drain from the device */
- /* (this is long enough for the entire 256 byte */
- /* pl2303 hardware buffer to drain with no flow */
- /* control for data rates of 1200 bps or more, */
- /* for lower rates we should really know how much */
- /* data is in the buffer to compute a delay */
- /* that is not unnecessarily long) */
- /* FIXME
- bps = tty_get_baud_rate(tty);
- if (bps > 1200)
- timeout = max((HZ*2560)/bps,HZ/10);
- else
- */
- timeout = 2*HZ;
- schedule_timeout_interruptible(timeout);
- dbg("%s(): after schedule_timeout_interruptible()", __func__);
+ dbg("%s(): after buf_clear()", __func__);
/* cancel scheduled setup */
cancel_delayed_work(&priv->delayed_setup_work);
@@ -694,15 +656,6 @@ static void oti6858_close(struct tty_struct *tty,
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
-
- /*
- if (tty && (tty->termios->c_cflag) & HUPCL) {
- // drop DTR and RTS
- spin_lock_irqsave(&priv->lock, flags);
- priv->pending_setup.control &= ~CONTROL_MASK;
- spin_unlock_irqrestore(&priv->lock, flags);
- }
- */
}
static int oti6858_tiocmset(struct tty_struct *tty, struct file *file,
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 751a533a4347..e02dc3d643c7 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -652,69 +652,41 @@ static void pl2303_set_termios(struct tty_struct *tty,
kfree(buf);
}
-static void pl2303_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ u8 control;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ /* Change DTR and RTS */
+ if (on)
+ priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+ else
+ priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+ control = priv->line_control;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_control_lines(port->serial->dev, control);
+}
+
+static void pl2303_close(struct usb_serial_port *port)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
dbg("%s - port %d", __func__, port->number);
- /* wait for data to drain from the buffer */
spin_lock_irqsave(&priv->lock, flags);
- timeout = PL2303_CLOSING_WAIT;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&tty->write_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (pl2303_buf_data_avail(priv->buf) == 0 ||
- timeout == 0 || signal_pending(current) ||
- port->serial->disconnected)
- break;
- spin_unlock_irqrestore(&priv->lock, flags);
- timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
/* clear out any remaining data in the buffer */
pl2303_buf_clear(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
- /* wait for characters to drain from the device */
- /* (this is long enough for the entire 256 byte */
- /* pl2303 hardware buffer to drain with no flow */
- /* control for data rates of 1200 bps or more, */
- /* for lower rates we should really know how much */
- /* data is in the buffer to compute a delay */
- /* that is not unnecessarily long) */
- bps = tty_get_baud_rate(tty);
- if (bps > 1200)
- timeout = max((HZ*2560)/bps, HZ/10);
- else
- timeout = 2*HZ;
- schedule_timeout_interruptible(timeout);
-
/* shutdown our urbs */
dbg("%s - shutting down urbs", __func__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop DTR and RTS */
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
- set_control_lines(port->serial->dev, 0);
- }
- }
}
static int pl2303_open(struct tty_struct *tty,
@@ -748,7 +720,7 @@ static int pl2303_open(struct tty_struct *tty,
if (result) {
dev_err(&port->dev, "%s - failed submitting read urb,"
" error %d\n", __func__, result);
- pl2303_close(tty, port, NULL);
+ pl2303_close(port);
return -EPROTO;
}
@@ -758,9 +730,10 @@ static int pl2303_open(struct tty_struct *tty,
if (result) {
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
" error %d\n", __func__, result);
- pl2303_close(tty, port, NULL);
+ pl2303_close(port);
return -EPROTO;
}
+ port->port.drain_delay = 256;
return 0;
}
@@ -821,6 +794,14 @@ static int pl2303_tiocmget(struct tty_struct *tty, struct file *file)
return result;
}
+static int pl2303_carrier_raised(struct usb_serial_port *port)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ if (priv->line_status & UART_DCD)
+ return 1;
+ return 0;
+}
+
static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -1125,6 +1106,8 @@ static struct usb_serial_driver pl2303_device = {
.num_ports = 1,
.open = pl2303_open,
.close = pl2303_close,
+ .dtr_rts = pl2303_dtr_rts,
+ .carrier_raised = pl2303_carrier_raised,
.write = pl2303_write,
.ioctl = pl2303_ioctl,
.break_ctl = pl2303_break_ctl,
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 913225c61610..17ac34f4d668 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -26,12 +26,10 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
-#include <linux/usb/ch9.h>
#define SWIMS_USB_REQUEST_SetPower 0x00
#define SWIMS_USB_REQUEST_SetNmea 0x07
-/* per port private data */
#define N_IN_URB 4
#define N_OUT_URB 4
#define IN_BUFLEN 4096
@@ -39,6 +37,12 @@
static int debug;
static int nmea;
+/* Used in interface blacklisting */
+struct sierra_iface_info {
+ const u32 infolen; /* number of interface numbers on blacklist */
+ const u8 *ifaceinfo; /* pointer to the array holding the numbers */
+};
+
static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
{
int result;
@@ -85,6 +89,23 @@ static int sierra_calc_num_ports(struct usb_serial *serial)
return result;
}
+static int is_blacklisted(const u8 ifnum,
+ const struct sierra_iface_info *blacklist)
+{
+ const u8 *info;
+ int i;
+
+ if (blacklist) {
+ info = blacklist->ifaceinfo;
+
+ for (i = 0; i < blacklist->infolen; i++) {
+ if (info[i] == ifnum)
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int sierra_calc_interface(struct usb_serial *serial)
{
int interface;
@@ -153,9 +174,25 @@ static int sierra_probe(struct usb_serial *serial,
*/
usb_set_serial_data(serial, (void *)num_ports);
+ /* ifnum could have changed - by calling usb_set_interface */
+ ifnum = sierra_calc_interface(serial);
+
+ if (is_blacklisted(ifnum,
+ (struct sierra_iface_info *)id->driver_info)) {
+ dev_dbg(&serial->dev->dev,
+ "Ignoring blacklisted interface #%d\n", ifnum);
+ return -ENODEV;
+ }
+
return result;
}
+static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 };
+static const struct sierra_iface_info direct_ip_interface_blacklist = {
+ .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
+ .ifaceinfo = direct_ip_non_serial_ifaces,
+};
+
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
@@ -188,9 +225,11 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */
{ USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */
{ USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */
- { USB_DEVICE(0x1199, 0x683C) }, /* Sierra Wireless MC8790 */
- { USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8790 */
- { USB_DEVICE(0x1199, 0x683E) }, /* Sierra Wireless MC8790 */
+ /* Sierra Wireless MC8790, MC8791, MC8792 Composite */
+ { USB_DEVICE(0x1199, 0x683C) },
+ { USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8791 Composite */
+ /* Sierra Wireless MC8790, MC8791, MC8792 */
+ { USB_DEVICE(0x1199, 0x683E) },
{ USB_DEVICE(0x1199, 0x6850) }, /* Sierra Wireless AirCard 880 */
{ USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */
{ USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880 E */
@@ -211,6 +250,10 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
+ { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ },
+
{ }
};
MODULE_DEVICE_TABLE(usb, id_table);
@@ -229,7 +272,6 @@ struct sierra_port_private {
/* Input endpoints and buffers for this port */
struct urb *in_urbs[N_IN_URB];
- char *in_buffer[N_IN_URB];
/* Settings for the port */
int rts_state; /* Handshaking pins (outputs) */
@@ -240,57 +282,50 @@ struct sierra_port_private {
int ri_state;
};
-static int sierra_send_setup(struct tty_struct *tty,
- struct usb_serial_port *port)
+static int sierra_send_setup(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct sierra_port_private *portdata;
__u16 interface = 0;
+ int val = 0;
dev_dbg(&port->dev, "%s", __func__);
portdata = usb_get_serial_port_data(port);
- if (tty) {
- int val = 0;
- if (portdata->dtr_state)
- val |= 0x01;
- if (portdata->rts_state)
- val |= 0x02;
-
- /* If composite device then properly report interface */
- if (serial->num_ports == 1) {
- interface = sierra_calc_interface(serial);
-
- /* Control message is sent only to interfaces with
- * interrupt_in endpoints
- */
- if (port->interrupt_in_urb) {
- /* send control message */
- return usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
- 0x22, 0x21, val, interface,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- }
- }
-
- /* Otherwise the need to do non-composite mapping */
- else {
- if (port->bulk_out_endpointAddress == 2)
- interface = 0;
- else if (port->bulk_out_endpointAddress == 4)
- interface = 1;
- else if (port->bulk_out_endpointAddress == 5)
- interface = 2;
+ if (portdata->dtr_state)
+ val |= 0x01;
+ if (portdata->rts_state)
+ val |= 0x02;
+ /* If composite device then properly report interface */
+ if (serial->num_ports == 1) {
+ interface = sierra_calc_interface(serial);
+ /* Control message is sent only to interfaces with
+ * interrupt_in endpoints
+ */
+ if (port->interrupt_in_urb) {
+ /* send control message */
return usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
0x22, 0x21, val, interface,
NULL, 0, USB_CTRL_SET_TIMEOUT);
-
}
}
+ /* Otherwise the need to do non-composite mapping */
+ else {
+ if (port->bulk_out_endpointAddress == 2)
+ interface = 0;
+ else if (port->bulk_out_endpointAddress == 4)
+ interface = 1;
+ else if (port->bulk_out_endpointAddress == 5)
+ interface = 2;
+ return usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ 0x22, 0x21, val, interface,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
return 0;
}
@@ -299,7 +334,7 @@ static void sierra_set_termios(struct tty_struct *tty,
{
dev_dbg(&port->dev, "%s", __func__);
tty_termios_copy_hw(tty->termios, old_termios);
- sierra_send_setup(tty, port);
+ sierra_send_setup(port);
}
static int sierra_tiocmget(struct tty_struct *tty, struct file *file)
@@ -338,7 +373,18 @@ static int sierra_tiocmset(struct tty_struct *tty, struct file *file,
portdata->rts_state = 0;
if (clear & TIOCM_DTR)
portdata->dtr_state = 0;
- return sierra_send_setup(tty, port);
+ return sierra_send_setup(port);
+}
+
+static void sierra_release_urb(struct urb *urb)
+{
+ struct usb_serial_port *port;
+ if (urb) {
+ port = urb->context;
+ dev_dbg(&port->dev, "%s: %p\n", __func__, urb);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
}
static void sierra_outdat_callback(struct urb *urb)
@@ -465,7 +511,7 @@ static void sierra_indat_callback(struct urb *urb)
" received", __func__);
/* Resubmit urb so we continue receiving */
- if (port->port.count && status != -ESHUTDOWN) {
+ if (port->port.count && status != -ESHUTDOWN && status != -EPERM) {
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
dev_err(&port->dev, "resubmit read urb failed."
@@ -557,67 +603,99 @@ static int sierra_write_room(struct tty_struct *tty)
return 2048;
}
-static int sierra_open(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void sierra_stop_rx_urbs(struct usb_serial_port *port)
{
- struct sierra_port_private *portdata;
- struct usb_serial *serial = port->serial;
int i;
- struct urb *urb;
- int result;
+ struct sierra_port_private *portdata = usb_get_serial_port_data(port);
- portdata = usb_get_serial_port_data(port);
+ for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++)
+ usb_kill_urb(portdata->in_urbs[i]);
- dev_dbg(&port->dev, "%s", __func__);
+ usb_kill_urb(port->interrupt_in_urb);
+}
- /* Set some sane defaults */
- portdata->rts_state = 1;
- portdata->dtr_state = 1;
+static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
+{
+ int ok_cnt;
+ int err = -EINVAL;
+ int i;
+ struct urb *urb;
+ struct sierra_port_private *portdata = usb_get_serial_port_data(port);
- /* Reset low level data toggle and start reading from endpoints */
- for (i = 0; i < N_IN_URB; i++) {
+ ok_cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
urb = portdata->in_urbs[i];
if (!urb)
continue;
- if (urb->dev != serial->dev) {
- dev_dbg(&port->dev, "%s: dev %p != %p",
- __func__, urb->dev, serial->dev);
- continue;
+ err = usb_submit_urb(urb, mem_flags);
+ if (err) {
+ dev_err(&port->dev, "%s: submit urb failed: %d\n",
+ __func__, err);
+ } else {
+ ok_cnt++;
}
+ }
- /*
- * make sure endpoint data toggle is synchronized with the
- * device
- */
- usb_clear_halt(urb->dev, urb->pipe);
-
- result = usb_submit_urb(urb, GFP_KERNEL);
- if (result) {
- dev_err(&port->dev, "submit urb %d failed (%d) %d\n",
- i, result, urb->transfer_buffer_length);
+ if (ok_cnt && port->interrupt_in_urb) {
+ err = usb_submit_urb(port->interrupt_in_urb, mem_flags);
+ if (err) {
+ dev_err(&port->dev, "%s: submit intr urb failed: %d\n",
+ __func__, err);
}
}
- sierra_send_setup(tty, port);
+ if (ok_cnt > 0) /* at least one rx urb submitted */
+ return 0;
+ else
+ return err;
+}
+
+static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
+ int dir, void *ctx, int len,
+ gfp_t mem_flags,
+ usb_complete_t callback)
+{
+ struct urb *urb;
+ u8 *buf;
+
+ if (endpoint == -1)
+ return NULL;
- /* start up the interrupt endpoint if we have one */
- if (port->interrupt_in_urb) {
- result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
- if (result)
- dev_err(&port->dev, "submit irq_in urb failed %d\n",
- result);
+ urb = usb_alloc_urb(0, mem_flags);
+ if (urb == NULL) {
+ dev_dbg(&serial->dev->dev, "%s: alloc for endpoint %d failed\n",
+ __func__, endpoint);
+ return NULL;
}
- return 0;
+
+ buf = kmalloc(len, mem_flags);
+ if (buf) {
+ /* Fill URB using supplied data */
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev, endpoint) | dir,
+ buf, len, callback, ctx);
+
+ /* debug */
+ dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
+ dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
+ } else {
+ dev_dbg(&serial->dev->dev, "%s %c u:%p d:%p\n", __func__,
+ dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
+
+ sierra_release_urb(urb);
+ urb = NULL;
+ }
+
+ return urb;
}
-static void sierra_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void sierra_close(struct usb_serial_port *port)
{
int i;
struct usb_serial *serial = port->serial;
struct sierra_port_private *portdata;
- dev_dbg(&port->dev, "%s", __func__);
+ dev_dbg(&port->dev, "%s\n", __func__);
portdata = usb_get_serial_port_data(port);
portdata->rts_state = 0;
@@ -626,25 +704,83 @@ static void sierra_close(struct tty_struct *tty,
if (serial->dev) {
mutex_lock(&serial->disc_mutex);
if (!serial->disconnected)
- sierra_send_setup(tty, port);
+ sierra_send_setup(port);
mutex_unlock(&serial->disc_mutex);
- /* Stop reading/writing urbs */
- for (i = 0; i < N_IN_URB; i++)
- usb_kill_urb(portdata->in_urbs[i]);
+ /* Stop reading urbs */
+ sierra_stop_rx_urbs(port);
+ /* .. and release them */
+ for (i = 0; i < N_IN_URB; i++) {
+ sierra_release_urb(portdata->in_urbs[i]);
+ portdata->in_urbs[i] = NULL;
+ }
}
+}
- usb_kill_urb(port->interrupt_in_urb);
- tty_port_tty_set(&port->port, NULL);
+static int sierra_open(struct tty_struct *tty,
+ struct usb_serial_port *port, struct file *filp)
+{
+ struct sierra_port_private *portdata;
+ struct usb_serial *serial = port->serial;
+ int i;
+ int err;
+ int endpoint;
+ struct urb *urb;
+
+ portdata = usb_get_serial_port_data(port);
+
+ dev_dbg(&port->dev, "%s", __func__);
+
+ /* Set some sane defaults */
+ portdata->rts_state = 1;
+ portdata->dtr_state = 1;
+
+
+ endpoint = port->bulk_in_endpointAddress;
+ for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
+ urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
+ IN_BUFLEN, GFP_KERNEL,
+ sierra_indat_callback);
+ portdata->in_urbs[i] = urb;
+ }
+ /* clear halt condition */
+ usb_clear_halt(serial->dev,
+ usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
+
+ err = sierra_submit_rx_urbs(port, GFP_KERNEL);
+ if (err) {
+ /* get rid of everything as in close */
+ sierra_close(port);
+ return err;
+ }
+ sierra_send_setup(port);
+
+ return 0;
+}
+
+
+static void sierra_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct usb_serial *serial = port->serial;
+ struct sierra_port_private *portdata;
+
+ portdata = usb_get_serial_port_data(port);
+ portdata->rts_state = on;
+ portdata->dtr_state = on;
+
+ if (serial->dev) {
+ mutex_lock(&serial->disc_mutex);
+ if (!serial->disconnected)
+ sierra_send_setup(port);
+ mutex_unlock(&serial->disc_mutex);
+ }
}
static int sierra_startup(struct usb_serial *serial)
{
struct usb_serial_port *port;
struct sierra_port_private *portdata;
- struct urb *urb;
int i;
- int j;
dev_dbg(&serial->dev->dev, "%s", __func__);
@@ -666,34 +802,8 @@ static int sierra_startup(struct usb_serial *serial)
return -ENOMEM;
}
spin_lock_init(&portdata->lock);
- for (j = 0; j < N_IN_URB; j++) {
- portdata->in_buffer[j] = kmalloc(IN_BUFLEN, GFP_KERNEL);
- if (!portdata->in_buffer[j]) {
- for (--j; j >= 0; j--)
- kfree(portdata->in_buffer[j]);
- kfree(portdata);
- return -ENOMEM;
- }
- }
-
+ /* Set the port private data pointer */
usb_set_serial_port_data(port, portdata);
-
- /* initialize the in urbs */
- for (j = 0; j < N_IN_URB; ++j) {
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (urb == NULL) {
- dev_dbg(&port->dev, "%s: alloc for in "
- "port failed.", __func__);
- continue;
- }
- /* Fill URB using supplied data. */
- usb_fill_bulk_urb(urb, serial->dev,
- usb_rcvbulkpipe(serial->dev,
- port->bulk_in_endpointAddress),
- portdata->in_buffer[j], IN_BUFLEN,
- sierra_indat_callback, port);
- portdata->in_urbs[j] = urb;
- }
}
return 0;
@@ -701,7 +811,7 @@ static int sierra_startup(struct usb_serial *serial)
static void sierra_shutdown(struct usb_serial *serial)
{
- int i, j;
+ int i;
struct usb_serial_port *port;
struct sierra_port_private *portdata;
@@ -714,12 +824,6 @@ static void sierra_shutdown(struct usb_serial *serial)
portdata = usb_get_serial_port_data(port);
if (!portdata)
continue;
-
- for (j = 0; j < N_IN_URB; j++) {
- usb_kill_urb(portdata->in_urbs[j]);
- usb_free_urb(portdata->in_urbs[j]);
- kfree(portdata->in_buffer[j]);
- }
kfree(portdata);
usb_set_serial_port_data(port, NULL);
}
@@ -737,6 +841,7 @@ static struct usb_serial_driver sierra_device = {
.probe = sierra_probe,
.open = sierra_open,
.close = sierra_close,
+ .dtr_rts = sierra_dtr_rts,
.write = sierra_write,
.write_room = sierra_write_room,
.set_termios = sierra_set_termios,
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index 5e7528cc81a8..8f7ed8f13996 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -446,66 +446,47 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value,
"RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret);
}
+static int spcp8x5_carrier_raised(struct usb_serial_port *port)
+{
+ struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+ if (priv->line_status & MSR_STATUS_LINE_DCD)
+ return 1;
+ return 0;
+}
+
+static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct spcp8x5_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ u8 control;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (on)
+ priv->line_control = MCR_CONTROL_LINE_DTR
+ | MCR_CONTROL_LINE_RTS;
+ else
+ priv->line_control &= ~ (MCR_CONTROL_LINE_DTR
+ | MCR_CONTROL_LINE_RTS);
+ control = priv->line_control;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type);
+}
+
/* close the serial port. We should wait for data sending to device 1st and
* then kill all urb. */
-static void spcp8x5_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void spcp8x5_close(struct usb_serial_port *port)
{
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
int result;
dbg("%s - port %d", __func__, port->number);
- /* wait for data to drain from the buffer */
spin_lock_irqsave(&priv->lock, flags);
- timeout = SPCP8x5_CLOSING_WAIT;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&tty->write_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (ringbuf_avail_data(priv->buf) == 0 ||
- timeout == 0 || signal_pending(current))
- break;
- spin_unlock_irqrestore(&priv->lock, flags);
- timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
-
/* clear out any remaining data in the buffer */
clear_ringbuf(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
- /* wait for characters to drain from the device (this is long enough
- * for the entire all byte spcp8x5 hardware buffer to drain with no
- * flow control for data rates of 1200 bps or more, for lower rates we
- * should really know how much data is in the buffer to compute a delay
- * that is not unnecessarily long) */
- bps = tty_get_baud_rate(tty);
- if (bps > 1200)
- timeout = max((HZ*2560) / bps, HZ/10);
- else
- timeout = 2*HZ;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(timeout);
-
- /* clear control lines */
- if (tty) {
- c_cflag = tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
- spcp8x5_set_ctrlLine(port->serial->dev, 0 , priv->type);
- }
- }
-
/* kill urb */
if (port->write_urb != NULL) {
result = usb_unlink_urb(port->write_urb);
@@ -665,13 +646,6 @@ static int spcp8x5_open(struct tty_struct *tty,
if (ret)
return ret;
- spin_lock_irqsave(&priv->lock, flags);
- if (tty && (tty->termios->c_cflag & CBAUD))
- priv->line_control = MCR_DTR | MCR_RTS;
- else
- priv->line_control = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
-
spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type);
/* Setup termios */
@@ -691,9 +665,10 @@ static int spcp8x5_open(struct tty_struct *tty,
port->read_urb->dev = serial->dev;
ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (ret) {
- spcp8x5_close(tty, port, NULL);
+ spcp8x5_close(port);
return -EPROTO;
}
+ port->port.drain_delay = 256;
return 0;
}
@@ -1033,6 +1008,8 @@ static struct usb_serial_driver spcp8x5_device = {
.num_ports = 1,
.open = spcp8x5_open,
.close = spcp8x5_close,
+ .dtr_rts = spcp8x5_dtr_rts,
+ .carrier_raised = spcp8x5_carrier_raised,
.write = spcp8x5_write,
.set_termios = spcp8x5_set_termios,
.ioctl = spcp8x5_ioctl,
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index 69879e437940..8b07ebc6baeb 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -152,8 +152,7 @@ static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port,
return result;
}
-static void symbol_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp)
+static void symbol_close(struct usb_serial_port *port)
{
struct symbol_private *priv = usb_get_serial_data(port->serial);
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 0a64bac306ee..42cb04c403be 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -100,8 +100,7 @@ static int ti_startup(struct usb_serial *serial);
static void ti_shutdown(struct usb_serial *serial);
static int ti_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *file);
-static void ti_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *file);
+static void ti_close(struct usb_serial_port *port);
static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *data, int count);
static int ti_write_room(struct tty_struct *tty);
@@ -647,8 +646,7 @@ release_lock:
}
-static void ti_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *file)
+static void ti_close(struct usb_serial_port *port)
{
struct ti_device *tdev;
struct ti_port *tport;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 0a566eea49c0..1967a7edc10c 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -238,9 +238,11 @@ static int serial_open (struct tty_struct *tty, struct file *filp)
goto bailout_interface_put;
mutex_unlock(&serial->disc_mutex);
}
-
mutex_unlock(&port->mutex);
- return 0;
+ /* Now do the correct tty layer semantics */
+ retval = tty_port_block_til_ready(&port->port, tty, filp);
+ if (retval == 0)
+ return 0;
bailout_interface_put:
usb_autopm_put_interface(serial->interface);
@@ -259,64 +261,89 @@ bailout_serial_put:
return retval;
}
-static void serial_close(struct tty_struct *tty, struct file *filp)
+/**
+ * serial_do_down - shut down hardware
+ * @port: port to shut down
+ *
+ * Shut down a USB port unless it is the console. We never shut down the
+ * console hardware as it will always be in use.
+ *
+ * Don't free any resources at this point
+ */
+static void serial_do_down(struct usb_serial_port *port)
{
- struct usb_serial_port *port = tty->driver_data;
+ struct usb_serial_driver *drv = port->serial->type;
struct usb_serial *serial;
struct module *owner;
- int count;
- if (!port)
+ /* The console is magical, do not hang up the console hardware
+ or there will be tears */
+ if (port->console)
return;
- dbg("%s - port %d", __func__, port->number);
-
mutex_lock(&port->mutex);
serial = port->serial;
owner = serial->type->driver.owner;
- if (port->port.count == 0) {
- mutex_unlock(&port->mutex);
- return;
- }
-
- if (port->port.count == 1)
- /* only call the device specific close if this
- * port is being closed by the last owner. Ensure we do
- * this before we drop the port count. The call is protected
- * by the port mutex
- */
- serial->type->close(tty, port, filp);
-
- if (port->port.count == (port->console ? 2 : 1)) {
- struct tty_struct *tty = tty_port_tty_get(&port->port);
- if (tty) {
- /* We must do this before we drop the port count to
- zero. */
- if (tty->driver_data)
- tty->driver_data = NULL;
- tty_port_tty_set(&port->port, NULL);
- tty_kref_put(tty);
- }
- }
+ if (drv->close)
+ drv->close(port);
- --port->port.count;
- count = port->port.count;
mutex_unlock(&port->mutex);
- put_device(&port->dev);
+}
+
+/**
+ * serial_do_free - free resources post close/hangup
+ * @port: port to free up
+ *
+ * Do the resource freeing and refcount dropping for the port. We must
+ * be careful about ordering and we must avoid freeing up the console.
+ */
+static void serial_do_free(struct usb_serial_port *port)
+{
+ struct usb_serial *serial;
+ struct module *owner;
+
+ /* The console is magical, do not hang up the console hardware
+ or there will be tears */
+ if (port->console)
+ return;
+
+ serial = port->serial;
+ owner = serial->type->driver.owner;
+ put_device(&port->dev);
/* Mustn't dereference port any more */
- if (count == 0) {
- mutex_lock(&serial->disc_mutex);
- if (!serial->disconnected)
- usb_autopm_put_interface(serial->interface);
- mutex_unlock(&serial->disc_mutex);
- }
+ mutex_lock(&serial->disc_mutex);
+ if (!serial->disconnected)
+ usb_autopm_put_interface(serial->interface);
+ mutex_unlock(&serial->disc_mutex);
usb_serial_put(serial);
-
/* Mustn't dereference serial any more */
- if (count == 0)
- module_put(owner);
+ module_put(owner);
+}
+
+static void serial_close(struct tty_struct *tty, struct file *filp)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ dbg("%s - port %d", __func__, port->number);
+
+
+ if (tty_port_close_start(&port->port, tty, filp) == 0)
+ return;
+
+ serial_do_down(port);
+ tty_port_close_end(&port->port, tty);
+ tty_port_tty_set(&port->port, NULL);
+ serial_do_free(port);
+}
+
+static void serial_hangup(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ serial_do_down(port);
+ tty_port_hangup(&port->port);
+ serial_do_free(port);
}
static int serial_write(struct tty_struct *tty, const unsigned char *buf,
@@ -648,6 +675,29 @@ static struct usb_serial_driver *search_serial_device(
return NULL;
}
+static int serial_carrier_raised(struct tty_port *port)
+{
+ struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+ struct usb_serial_driver *drv = p->serial->type;
+ if (drv->carrier_raised)
+ return drv->carrier_raised(p);
+ /* No carrier control - don't block */
+ return 1;
+}
+
+static void serial_dtr_rts(struct tty_port *port, int on)
+{
+ struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+ struct usb_serial_driver *drv = p->serial->type;
+ if (drv->dtr_rts)
+ drv->dtr_rts(p, on);
+}
+
+static const struct tty_port_operations serial_port_ops = {
+ .carrier_raised = serial_carrier_raised,
+ .dtr_rts = serial_dtr_rts,
+};
+
int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -841,6 +891,7 @@ int usb_serial_probe(struct usb_interface *interface,
if (!port)
goto probe_error;
tty_port_init(&port->port);
+ port->port.ops = &serial_port_ops;
port->serial = serial;
spin_lock_init(&port->lock);
mutex_init(&port->mutex);
@@ -974,6 +1025,7 @@ int usb_serial_probe(struct usb_interface *interface,
if (retval > 0) {
/* quietly accept this device, but don't bind to a
serial port as it's about to disappear */
+ serial->num_ports = 0;
goto exit;
}
}
@@ -1070,6 +1122,9 @@ void usb_serial_disconnect(struct usb_interface *interface)
if (port) {
struct tty_struct *tty = tty_port_tty_get(&port->port);
if (tty) {
+ /* The hangup will occur asynchronously but
+ the object refcounts will sort out all the
+ cleanup */
tty_hangup(tty);
tty_kref_put(tty);
}
@@ -1134,6 +1189,7 @@ static const struct tty_operations serial_ops = {
.open = serial_open,
.close = serial_close,
.write = serial_write,
+ .hangup = serial_hangup,
.write_room = serial_write_room,
.ioctl = serial_ioctl,
.set_termios = serial_set_termios,
@@ -1146,6 +1202,7 @@ static const struct tty_operations serial_ops = {
.proc_fops = &serial_proc_fops,
};
+
struct tty_driver *usb_serial_tty_driver;
static int __init usb_serial_init(void)
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 5ac414bda718..b15f1c0e1d4a 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -38,8 +38,7 @@
/* function prototypes for a handspring visor */
static int visor_open(struct tty_struct *tty, struct usb_serial_port *port,
struct file *filp);
-static void visor_close(struct tty_struct *tty, struct usb_serial_port *port,
- struct file *filp);
+static void visor_close(struct usb_serial_port *port);
static int visor_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int visor_write_room(struct tty_struct *tty);
@@ -324,8 +323,7 @@ exit:
}
-static void visor_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void visor_close(struct usb_serial_port *port)
{
struct visor_private *priv = usb_get_serial_port_data(port);
unsigned char *transfer_buffer;
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 5335d3211c07..7c7295d09f34 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -147,8 +147,7 @@ static int whiteheat_attach(struct usb_serial *serial);
static void whiteheat_shutdown(struct usb_serial *serial);
static int whiteheat_open(struct tty_struct *tty,
struct usb_serial_port *port, struct file *filp);
-static void whiteheat_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp);
+static void whiteheat_close(struct usb_serial_port *port);
static int whiteheat_write(struct tty_struct *tty,
struct usb_serial_port *port,
const unsigned char *buf, int count);
@@ -712,8 +711,7 @@ exit:
}
-static void whiteheat_close(struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp)
+static void whiteheat_close(struct usb_serial_port *port)
{
struct whiteheat_private *info = usb_get_serial_port_data(port);
struct whiteheat_urb_wrap *wrap;
@@ -723,31 +721,7 @@ static void whiteheat_close(struct tty_struct *tty,
dbg("%s - port %d", __func__, port->number);
- mutex_lock(&port->serial->disc_mutex);
- /* filp is NULL when called from usb_serial_disconnect */
- if ((filp && (tty_hung_up_p(filp))) || port->serial->disconnected) {
- mutex_unlock(&port->serial->disc_mutex);
- return;
- }
- mutex_unlock(&port->serial->disc_mutex);
-
- tty->closing = 1;
-
-/*
- * Not currently in use; tty_wait_until_sent() calls
- * serial_chars_in_buffer() which deadlocks on the second semaphore
- * acquisition. This should be fixed at some point. Greg's been
- * notified.
- if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) {
- tty_wait_until_sent(tty, CLOSING_DELAY);
- }
-*/
-
- tty_driver_flush_buffer(tty);
- tty_ldisc_flush(tty);
-
firm_report_tx_done(port);
-
firm_close(port);
/* shutdown our bulk reads and writes */
@@ -775,10 +749,7 @@ static void whiteheat_close(struct tty_struct *tty,
}
spin_unlock_irq(&info->lock);
mutex_unlock(&info->deathwarrant);
-
stop_command_port(port->serial);
-
- tty->closing = 0;
}
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 4ca3b5860643..cfa26d56ce60 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -132,7 +132,7 @@ static int slave_configure(struct scsi_device *sdev)
if (us->fflags & US_FL_MAX_SECTORS_MIN)
max_sectors = PAGE_CACHE_SIZE >> 9;
- if (sdev->request_queue->max_sectors > max_sectors)
+ if (queue_max_sectors(sdev->request_queue) > max_sectors)
blk_queue_max_sectors(sdev->request_queue,
max_sectors);
} else if (sdev->type == TYPE_TAPE) {
@@ -483,7 +483,7 @@ static ssize_t show_max_sectors(struct device *dev, struct device_attribute *att
{
struct scsi_device *sdev = to_scsi_device(dev);
- return sprintf(buf, "%u\n", sdev->request_queue->max_sectors);
+ return sprintf(buf, "%u\n", queue_max_sectors(sdev->request_queue));
}
/* Input routine for the sysfs max_sectors file */
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index fa65a3b08601..4b8b69045fe6 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -160,8 +160,9 @@ UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
-/* Reported by Andrew Nayenko <relan@bk.ru> */
-UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0592,
+/* Reported by Andrew Nayenko <relan@bk.ru>
+ * Updated for new firmware by Phillip Potter <phillipinda@hotmail.com> */
+UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0610,
"Nokia",
"Nokia 6288",
US_SC_DEVICE, US_PR_DEVICE, NULL,
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 7826bdce4bbe..74712cb8399a 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1128,13 +1128,6 @@ config FB_INTEL
830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM/965G/965GM chipsets.
Say Y if you have and plan to use such a board.
- If you say Y here and want DDC/I2C support you must first say Y to
- "I2C support" and "I2C bit-banging support" in the character devices
- section.
-
- If you say M here then "I2C support" and "I2C bit-banging support"
- can be build either as modules or built-in.
-
To compile this driver as a module, choose M here: the
module will be called intelfb.
@@ -1207,11 +1200,10 @@ config FB_MATROX_G
pixel and 32 bpp packed pixel. You can also use font widths
different from 8.
- If you need support for G400 secondary head, you must first say Y to
- "I2C support" in the character devices section, and then to
- "Matrox I2C support" and "G400 second head support" here in the
- framebuffer section. G450/G550 secondary head and digital output
- are supported without additional modules.
+ If you need support for G400 secondary head, you must say Y to
+ "Matrox I2C support" and "G400 second head support" right below.
+ G450/G550 secondary head and digital output are supported without
+ additional modules.
The driver starts in monitor mode. You must use the matroxset tool
(available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
@@ -1310,13 +1302,6 @@ config FB_RADEON
a framebuffer device. There are both PCI and AGP versions. You
don't need to choose this to run the Radeon in plain VGA mode.
- If you say Y here and want DDC/I2C support you must first say Y to
- "I2C support" and "I2C bit-banging support" in the character devices
- section.
-
- If you say M here then "I2C support" and "I2C bit-banging support"
- can be build either as modules or built-in.
-
There is a product page at
http://apps.ati.com/ATIcompare/
@@ -2011,7 +1996,7 @@ config FB_PS3_DEFAULT_SIZE_M
config FB_XILINX
tristate "Xilinx frame buffer support"
- depends on FB && XILINX_VIRTEX
+ depends on FB && (XILINX_VIRTEX || MICROBLAZE)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 0dbd6c68d76b..d8d0be5151e3 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -56,7 +56,6 @@ obj-$(CONFIG_FB_CT65550) += chipsfb.o
obj-$(CONFIG_FB_IMSTT) += imsttfb.o
obj-$(CONFIG_FB_FM2) += fm2fb.o
obj-$(CONFIG_FB_VT8623) += vt8623fb.o
-obj-$(CONFIG_FB_CYBLA) += cyblafb.o
obj-$(CONFIG_FB_TRIDENT) += tridentfb.o
obj-$(CONFIG_FB_LE80578) += vermilion/
obj-$(CONFIG_FB_S3) += s3fb.o
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 61050ab14128..d1f80bac54f0 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -437,7 +437,7 @@ static int clcdfb_register(struct clcd_fb *fb)
return ret;
}
-static int clcdfb_probe(struct amba_device *dev, void *id)
+static int clcdfb_probe(struct amba_device *dev, struct amba_id *id)
{
struct clcd_board *board = dev->dev.platform_data;
struct clcd_fb *fb;
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 9a577a800db5..2fb63f6ea2f1 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -29,14 +29,8 @@
/* configurable parameters */
#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
-#define ATMEL_LCDC_DMA_BURST_LEN 8
-
-#if defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91CAP9) || \
- defined(CONFIG_ARCH_AT91SAM9RL)
-#define ATMEL_LCDC_FIFO_SIZE 2048
-#else
-#define ATMEL_LCDC_FIFO_SIZE 512
-#endif
+#define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */
+#define ATMEL_LCDC_FIFO_SIZE 512 /* words */
#if defined(CONFIG_ARCH_AT91)
#define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index 35e8eb02b9e9..e4e4d433b007 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -354,7 +354,7 @@ static int default_crt_on __devinitdata = 0;
static int default_lcd_on __devinitdata = 1;
#ifdef CONFIG_MTRR
-static int mtrr = 1;
+static bool mtrr = true;
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
diff --git a/drivers/video/cfbfillrect.c b/drivers/video/cfbfillrect.c
index 64b35766b2a2..ba9f58b2a5e8 100644
--- a/drivers/video/cfbfillrect.c
+++ b/drivers/video/cfbfillrect.c
@@ -9,10 +9,6 @@
*
* NOTES:
*
- * The code for depths like 24 that don't have integer number of pixels per
- * long is broken and needs to be fixed. For now I turned these types of
- * mode off.
- *
* Also need to add code to deal with cards endians that are different than
* the native cpu endians. I also need to deal with MSB position in the word.
*
@@ -139,7 +135,7 @@ bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
// Trailing bits
if (last)
- FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
+ FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
}
}
@@ -281,7 +277,7 @@ bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst,
void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
- unsigned long pat, fg;
+ unsigned long pat, pat2, fg;
unsigned long width = rect->width, height = rect->height;
int bits = BITS_PER_LONG, bytes = bits >> 3;
u32 bpp = p->var.bits_per_pixel;
@@ -297,7 +293,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
else
fg = rect->color;
- pat = pixel_to_pat( bpp, fg);
+ pat = pixel_to_pat(bpp, fg);
dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
@@ -333,17 +329,16 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
dst_idx += p->fix.line_length*8;
}
} else {
- int right;
- int r;
- int rot = (left-dst_idx) % bpp;
+ int right, r;
void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,
int dst_idx, unsigned long pat, int left,
int right, unsigned n, int bits) = NULL;
-
- /* rotate pattern to correct start position */
- pat = pat << rot | pat >> (bpp-rot);
-
- right = bpp-left;
+#ifdef __LITTLE_ENDIAN
+ right = left;
+ left = bpp - right;
+#else
+ right = bpp - left;
+#endif
switch (rect->rop) {
case ROP_XOR:
fill_op = bitfill_unaligned_rev;
@@ -352,17 +347,18 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
fill_op = bitfill_unaligned;
break;
default:
- printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
+ printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
fill_op = bitfill_unaligned;
break;
}
while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
+ dst += dst_idx / bits;
dst_idx &= (bits - 1);
- fill_op(p, dst, dst_idx, pat, left, right,
+ r = dst_idx % bpp;
+ /* rotate pattern to the correct start position */
+ pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
+ fill_op(p, dst, dst_idx, pat2, left, right,
width*bpp, bits);
- r = (p->fix.line_length*8) % bpp;
- pat = pat << (bpp-r) | pat >> r;
dst_idx += p->fix.line_length*8;
}
}
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index d012edda6d11..59d7d5ec17a4 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -180,7 +180,7 @@ static inline void vga_set_mem_top(struct vc_data *c)
}
#ifdef CONFIG_VGACON_SOFT_SCROLLBACK
-#include <linux/bootmem.h>
+#include <linux/slab.h>
/* software scrollback */
static void *vgacon_scrollback;
static int vgacon_scrollback_tail;
@@ -210,8 +210,7 @@ static void vgacon_scrollback_init(int pitch)
*/
static void __init_refok vgacon_scrollback_startup(void)
{
- vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE
- * 1024);
+ vgacon_scrollback = kcalloc(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, GFP_NOWAIT);
vgacon_scrollback_init(vga_video_num_columns * 2);
}
@@ -1282,7 +1281,7 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font)
font->charcount = vga_512_chars ? 512 : 256;
if (!font->data)
return 0;
- return vgacon_do_font_op(&state, font->data, 0, 0);
+ return vgacon_do_font_op(&state, font->data, 0, vga_512_chars);
}
#else
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 83c5cefc266c..da7c01b39be2 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -1736,10 +1736,8 @@ static int __init cyber2000fb_init(void)
#ifdef CONFIG_ARCH_SHARK
err = cyberpro_vl_probe();
- if (!err) {
+ if (!err)
ret = 0;
- __module_get(THIS_MODULE);
- }
#endif
#ifdef CONFIG_PCI
err = pci_register_driver(&cyberpro_driver);
@@ -1749,14 +1747,15 @@ static int __init cyber2000fb_init(void)
return ret ? err : 0;
}
+module_init(cyber2000fb_init);
+#ifndef CONFIG_ARCH_SHARK
static void __exit cyberpro_exit(void)
{
pci_unregister_driver(&cyberpro_driver);
}
-
-module_init(cyber2000fb_init);
module_exit(cyberpro_exit);
+#endif
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
diff --git a/drivers/video/fb_draw.h b/drivers/video/fb_draw.h
index 1db622192bde..04c01faaf772 100644
--- a/drivers/video/fb_draw.h
+++ b/drivers/video/fb_draw.h
@@ -33,11 +33,11 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 8:
return 0x0101010101010101ul*pixel;
case 12:
- return 0x0001001001001001ul*pixel;
+ return 0x1001001001001001ul*pixel;
case 16:
return 0x0001000100010001ul*pixel;
case 24:
- return 0x0000000001000001ul*pixel;
+ return 0x0001000001000001ul*pixel;
case 32:
return 0x0000000100000001ul*pixel;
default:
@@ -58,11 +58,11 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 8:
return 0x01010101ul*pixel;
case 12:
- return 0x00001001ul*pixel;
+ return 0x01001001ul*pixel;
case 16:
return 0x00010001ul*pixel;
case 24:
- return 0x00000001ul*pixel;
+ return 0x01000001ul*pixel;
case 32:
return 0x00000001ul*pixel;
default:
@@ -167,4 +167,17 @@ static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
#endif /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
+#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
+#define _cpu_to_le_long(x) __cpu_to_le_long(x)
+#define __cpu_to_le_long(x) cpu_to_le##x
+
+#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
+#define _le_long_to_cpu(x) __le_long_to_cpu(x)
+#define __le_long_to_cpu(x) le##x##_to_cpu
+
+static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
+{
+ return (word << shift) | (word >> (x - shift));
+}
+
#endif /* FB_DRAW_H */
diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c
index fe5b519860b1..1a83709f9611 100644
--- a/drivers/video/gbefb.c
+++ b/drivers/video/gbefb.c
@@ -75,7 +75,7 @@ struct gbefb_par {
static unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024;
static void *gbe_mem;
static dma_addr_t gbe_dma_addr;
-unsigned long gbe_mem_phys;
+static unsigned long gbe_mem_phys;
static struct {
uint16_t *cpu;
@@ -185,8 +185,8 @@ static struct fb_videomode default_mode_LCD __initdata = {
.vmode = FB_VMODE_NONINTERLACED,
};
-struct fb_videomode *default_mode __initdata = &default_mode_CRT;
-struct fb_var_screeninfo *default_var __initdata = &default_var_CRT;
+static struct fb_videomode *default_mode __initdata = &default_mode_CRT;
+static struct fb_var_screeninfo *default_var __initdata = &default_var_CRT;
static int flat_panel_enabled = 0;
@@ -205,7 +205,7 @@ static void gbe_reset(void)
* console.
*/
-void gbe_turn_off(void)
+static void gbe_turn_off(void)
{
int i;
unsigned int val, x, y, vpixen_off;
@@ -1097,7 +1097,7 @@ static void gbefb_create_sysfs(struct device *dev)
* Initialization
*/
-int __init gbefb_setup(char *options)
+static int __init gbefb_setup(char *options)
{
char *this_opt;
@@ -1283,7 +1283,7 @@ static struct platform_driver gbefb_driver = {
static struct platform_device *gbefb_device;
-int __init gbefb_init(void)
+static int __init gbefb_init(void)
{
int ret = platform_driver_register(&gbefb_driver);
if (!ret) {
@@ -1301,7 +1301,7 @@ int __init gbefb_init(void)
return ret;
}
-void __exit gbefb_exit(void)
+static void __exit gbefb_exit(void)
{
platform_device_unregister(gbefb_device);
platform_driver_unregister(&gbefb_driver);
diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c
index e6467cf9f19f..020db7fc9153 100644
--- a/drivers/video/hitfb.c
+++ b/drivers/video/hitfb.c
@@ -335,9 +335,9 @@ static int __init hitfb_probe(struct platform_device *dev)
if (fb_get_options("hitfb", NULL))
return -ENODEV;
- hitfb_fix.mmio_start = CONFIG_HD64461_IOBASE+0x1000;
+ hitfb_fix.mmio_start = HD64461_IO_OFFSET(0x1000);
hitfb_fix.mmio_len = 0x1000;
- hitfb_fix.smem_start = CONFIG_HD64461_IOBASE + 0x02000000;
+ hitfb_fix.smem_start = HD64461_IO_OFFSET(0x02000000);
hitfb_fix.smem_len = 512 * 1024;
lcdclor = fb_readw(HD64461_LCDCLOR);
diff --git a/drivers/video/logo/logo_linux_clut224.ppm b/drivers/video/logo/logo_linux_clut224.ppm
index de93ff3fc1ad..3c14e43b82fe 100644
--- a/drivers/video/logo/logo_linux_clut224.ppm
+++ b/drivers/video/logo/logo_linux_clut224.ppm
@@ -1,2828 +1,1604 @@
P3
-145 113
+# Standard 224-color Linux logo
+80 80
255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 3 4 4 6 7 7
-8 10 10 8 10 10 6 8 8 6 7 7 3 4 4 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 4 5 5 17 18 17
-27 29 28 35 37 36 40 43 41 43 45 43 40 43 41 37 39 37
-32 34 33 27 30 29 23 25 24 17 21 21 15 18 18 12 15 15
-11 13 13 8 10 10 6 7 7 3 4 4 1 1 1 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 13 13 13 32 34 33 49 51 48 60 60 56 58 59 55
-55 57 54 55 56 53 49 51 48 43 45 43 39 40 39 33 37 35
-28 31 30 23 27 26 20 23 23 17 20 20 14 17 17 13 16 16
-11 14 14 10 13 13 10 12 12 9 11 11 8 10 10 6 7 7
-2 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 6 7 7 12 15 15
-12 15 15 8 9 9 2 3 3 0 0 0 1 1 1 25 27 26
-55 56 53 68 70 65 65 66 61 65 66 61 63 64 60 63 64 60
-58 59 55 51 52 50 47 48 46 41 42 42 35 37 36 30 32 31
-26 28 27 20 24 24 18 22 22 16 19 19 14 17 17 13 16 16
-12 15 15 11 14 14 10 13 13 10 12 12 9 11 11 8 10 10
-8 9 9 6 8 8 3 3 3 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 6 7 7 20 24 24 23 27 26
-23 27 26 18 22 22 11 13 13 23 24 24 61 63 57 72 73 67
-72 73 67 68 70 65 68 70 65 68 70 65 63 64 60 58 59 55
-55 56 53 47 48 46 41 42 42 35 37 36 30 32 31 26 28 27
-20 24 24 18 22 22 16 20 20 15 19 19 14 17 17 13 16 16
-12 15 15 12 15 15 11 14 14 10 13 13 10 12 12 9 11 11
-8 10 10 8 9 9 7 9 9 6 7 7 1 2 2 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 4 5 5 5 6 5 4 5 5
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 15 19 19 40 41 39 53 55 47
-33 36 34 27 30 29 51 52 50 72 73 67 72 73 67 72 73 67
-72 73 67 68 70 65 68 70 65 63 64 60 58 59 55 51 52 50
-47 48 46 40 43 41 33 37 35 30 32 31 26 28 27 20 24 24
-18 22 22 17 21 21 16 19 19 14 18 18 14 17 17 13 17 17
-13 16 16 12 15 15 12 15 15 11 14 14 10 13 13 10 12 12
-9 11 11 8 10 10 8 9 9 7 9 9 6 8 8 3 4 4
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-2 2 2 6 8 8 10 12 12 10 12 12 10 12 12 10 12 12
-6 8 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 20 23 23 71 71 57 131 127 93
-115 113 82 63 64 60 72 73 67 72 73 67 72 73 67 72 73 67
-68 70 65 65 66 61 61 63 57 55 57 54 49 51 48 43 45 43
-39 40 39 33 36 34 28 31 30 23 27 26 20 24 24 20 23 23
-17 21 21 16 20 20 15 19 19 15 18 18 14 18 18 14 17 17
-13 17 17 13 16 16 12 15 15 12 15 15 11 14 14 10 13 13
-10 12 12 9 11 11 8 10 10 7 9 9 7 9 9 6 8 8
-4 5 5 0 0 0 0 0 0 0 0 0 1 1 1 6 7 7
-10 12 12 10 12 12 10 12 12 10 12 12 10 12 12 10 12 12
-10 12 12 3 4 4 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 18 22 22 71 71 57 144 139 99
-84 83 72 68 70 65 72 73 67 72 73 67 68 70 65 65 66 61
-63 64 60 55 57 54 51 52 50 47 48 46 40 43 41 35 37 36
-30 32 31 27 29 28 23 27 26 20 24 24 18 22 22 17 21 21
-16 20 20 15 19 19 15 19 19 15 19 19 15 18 18 14 18 18
-14 17 17 13 17 17 13 16 16 12 15 15 12 15 15 11 14 14
-10 13 13 9 12 12 9 11 11 8 10 10 7 9 9 6 8 8
-6 8 8 3 4 4 0 0 0 2 2 2 8 10 10 10 12 12
-10 12 12 10 12 12 11 13 13 36 38 35 61 61 53 48 49 45
-10 12 12 7 9 9 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 15 19 19 61 61 53 84 83 72
-68 70 65 72 73 67 68 70 65 68 70 65 63 64 60 58 59 55
-51 52 50 47 48 46 41 42 42 37 39 37 32 35 33 28 31 30
-23 27 26 20 24 24 20 23 23 18 22 22 17 21 21 17 21 21
-17 21 21 17 21 21 17 20 20 16 20 20 16 20 20 16 19 19
-15 18 18 14 18 18 13 17 17 13 16 16 12 15 15 12 15 15
-11 14 14 10 13 13 9 12 12 9 11 11 8 10 10 7 9 9
-6 8 8 6 8 8 5 6 5 9 11 11 10 12 12 10 12 12
-19 20 18 82 81 62 149 145 103 160 154 106 142 137 94 96 95 69
-10 12 12 10 12 12 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 10 12 12 44 46 43 68 70 65
-72 73 67 68 70 65 68 70 65 63 64 60 55 57 54 49 51 48
-43 45 43 39 40 39 33 37 35 30 32 31 26 28 27 23 27 26
-20 24 24 18 22 22 18 22 22 18 22 22 18 22 22 20 23 23
-20 24 24 23 25 24 23 25 24 22 24 23 20 23 23 18 22 22
-17 20 20 15 19 19 15 18 18 14 17 17 13 16 16 12 15 15
-11 14 14 11 13 13 10 12 12 9 11 11 8 10 10 8 9 9
-7 9 9 7 9 9 10 12 12 10 12 12 10 12 12 71 71 57
-164 159 111 186 182 128 186 182 128 171 165 117 151 147 98 96 95 69
-10 12 12 10 12 12 3 3 3 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 8 10 10 63 64 60 68 70 65
-72 73 67 68 70 65 63 64 60 55 57 54 47 48 46 40 43 41
-33 37 35 30 32 31 27 29 28 23 27 26 20 24 24 20 23 23
-18 22 22 18 22 22 20 23 22 21 25 23 23 27 26 27 29 28
-28 31 30 31 33 31 31 33 31 31 33 31 28 31 30 26 28 27
-23 25 24 20 23 22 16 20 20 15 18 18 14 17 17 13 16 16
-12 15 15 11 14 14 10 13 13 10 12 12 9 11 11 8 10 10
-10 12 12 10 13 13 10 12 12 12 14 14 96 95 69 165 161 109
-186 182 128 192 187 134 192 187 134 176 171 126 160 154 106 103 101 77
-10 12 12 10 12 12 5 6 5 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 35 37 36 68 70 65 72 73 67
-68 70 65 65 66 61 58 59 55 49 51 48 40 43 41 33 37 35
-28 31 30 23 27 26 20 24 24 20 23 23 18 22 22 18 22 22
-18 22 22 20 23 23 23 27 26 27 30 29 32 35 33 37 39 37
-40 43 41 44 46 43 46 47 43 44 46 43 40 43 41 36 38 35
-31 33 31 27 29 28 22 24 23 17 21 21 15 18 18 14 17 17
-13 16 16 12 15 15 11 14 14 11 14 14 11 13 13 13 16 16
-13 16 16 11 14 14 10 12 12 79 78 62 142 137 94 164 159 111
-178 174 128 192 187 134 192 187 134 176 171 126 160 154 106 96 95 69
-10 12 12 10 12 12 6 7 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 55 57 54 68 70 65 72 73 67
-68 70 65 63 64 60 55 56 53 43 45 43 35 37 36 28 31 30
-23 27 26 20 24 24 18 22 22 17 21 21 17 21 21 17 21 21
-20 24 24 25 27 26 31 33 31 38 39 37 46 47 43 53 55 47
-61 61 53 66 65 55 66 65 55 66 65 55 61 61 53 53 55 47
-46 47 43 37 39 37 30 33 30 24 26 24 17 21 21 15 18 18
-13 17 17 12 15 15 12 15 15 13 16 16 14 18 18 14 18 18
-14 17 17 12 15 15 30 31 28 118 116 76 134 131 96 160 154 106
-174 170 121 178 174 128 178 174 128 171 165 117 151 147 98 96 95 69
-10 12 12 10 12 12 6 8 8 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 63 64 60 68 70 65 68 70 65
-65 66 61 58 59 55 49 51 48 39 40 39 30 32 31 23 27 26
-20 24 24 18 22 22 17 21 21 16 20 20 17 21 21 20 23 23
-25 27 26 32 35 33 43 44 41 53 55 47 66 65 55 75 75 61
-82 81 62 84 83 72 87 86 72 87 86 72 82 81 62 75 75 61
-66 65 55 53 55 47 40 41 39 31 33 31 23 25 24 17 20 20
-14 18 18 13 16 16 12 15 15 12 15 15 13 17 17 14 18 18
-14 18 18 13 16 16 46 47 43 96 95 69 125 122 87 142 137 94
-160 154 106 165 161 109 164 159 111 155 149 109 142 137 94 75 75 61
-10 12 12 10 12 12 6 8 8 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 60 60 56 68 70 65 68 70 65
-63 64 60 55 57 54 46 47 45 35 37 36 27 30 29 23 25 24
-18 22 22 17 21 21 16 20 20 17 21 21 18 22 22 23 27 26
-31 33 31 43 44 41 55 56 53 71 71 57 84 83 72 92 91 72
-103 101 77 92 91 72 82 81 62 82 81 62 87 86 72 92 91 72
-84 83 72 71 71 57 55 56 53 43 44 41 30 33 30 22 24 23
-16 19 19 14 17 17 12 15 15 12 15 15 13 16 16 14 18 18
-14 18 18 14 17 17 43 44 41 82 81 62 118 116 76 125 122 87
-142 137 94 144 139 99 144 139 99 134 131 96 118 116 76 53 55 47
-10 12 12 10 12 12 6 8 8 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 47 48 46 63 64 60 63 64 60
-55 57 54 49 51 48 40 43 41 32 34 33 26 28 27 20 24 24
-18 22 22 16 20 20 16 20 20 17 21 21 20 24 24 28 31 30
-40 41 39 53 55 47 75 75 61 90 89 73 87 86 72 48 49 45
-14 14 13 2 2 2 1 2 2 1 1 1 1 1 1 2 2 2
-19 20 18 43 44 41 66 65 55 53 55 47 38 39 37 26 28 27
-18 22 22 14 18 18 13 16 16 12 15 15 12 15 15 13 17 17
-14 18 18 14 18 18 30 31 28 66 65 55 96 95 69 103 101 77
-118 116 76 118 116 76 118 116 76 118 116 76 103 101 77 36 38 35
-10 12 12 10 12 12 6 7 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 28 31 30 55 57 54 51 52 50
-49 51 48 41 42 42 35 37 36 28 31 30 23 27 26 20 23 23
-17 21 21 16 20 20 16 20 20 18 22 22 23 27 26 33 36 34
-48 49 45 71 71 57 82 81 62 43 44 41 8 9 9 6 7 7
-6 7 7 6 7 7 6 7 7 5 6 5 4 5 5 3 4 4
-2 3 3 1 2 2 4 5 4 36 38 35 48 49 45 32 35 33
-21 25 23 16 19 19 13 17 17 12 15 15 12 15 15 13 16 16
-14 18 18 14 18 18 16 18 16 36 38 35 61 61 53 82 81 62
-96 95 69 96 95 69 96 95 69 96 95 69 79 78 62 19 20 18
-10 12 12 10 12 12 4 5 5 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 13 13 13 46 47 45 43 45 43
-40 43 41 35 37 36 30 32 31 23 27 26 20 24 24 18 22 22
-17 21 21 16 20 20 17 21 21 20 23 23 27 30 29 40 41 39
-61 61 53 53 55 47 16 17 16 9 11 11 10 12 12 10 12 12
-10 12 12 10 12 12 10 12 12 9 11 11 8 10 10 8 9 9
-6 8 8 5 6 5 4 5 5 2 3 3 19 20 18 38 39 37
-26 28 27 17 21 21 14 17 17 13 16 16 12 15 15 12 15 15
-13 17 17 14 18 18 12 15 15 13 12 7 30 31 28 46 47 43
-53 55 47 66 65 55 66 65 55 53 55 47 36 38 35 10 12 12
-10 12 12 10 12 12 2 3 3 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 1 33 37 35 35 37 36
-32 35 33 28 31 30 23 27 26 20 24 24 18 22 22 17 21 21
-16 20 20 16 20 20 17 21 21 21 25 23 31 33 31 44 46 43
-31 33 31 11 13 13 12 14 14 12 15 15 13 16 16 14 17 17
-14 17 17 14 17 17 14 17 17 13 16 16 12 15 15 12 14 14
-11 13 13 9 11 11 8 10 10 6 8 8 4 5 5 17 18 17
-30 33 30 20 23 22 15 18 18 13 16 16 12 15 15 12 14 14
-13 16 16 14 17 17 14 18 18 11 12 11 7 7 5 16 17 12
-21 22 20 30 31 28 25 27 25 21 22 20 14 14 13 10 12 12
-10 12 12 9 11 11 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 18 22 22 27 30 29
-27 29 28 40 41 39 53 55 47 53 55 47 53 55 47 46 47 43
-25 27 25 16 20 20 17 21 21 23 25 24 31 33 31 20 20 20
-12 15 15 14 17 17 15 19 19 16 20 20 17 21 21 18 22 22
-18 22 22 18 22 22 18 22 22 17 21 21 17 21 21 16 19 19
-15 18 18 13 16 16 12 15 15 10 12 12 8 10 10 6 8 8
-21 22 21 22 24 23 15 19 19 13 17 17 13 16 16 12 15 15
-12 15 15 13 17 17 14 18 18 14 18 18 13 15 14 10 9 6
-7 7 5 7 7 5 7 7 5 9 11 11 10 12 12 10 12 12
-10 12 12 6 7 7 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 16 17 12 82 81 62
-118 116 76 118 116 76 161 156 96 161 156 96 161 156 96 118 116 76
-118 116 76 96 95 69 53 55 47 22 24 23 14 17 17 13 16 16
-15 19 19 17 21 21 18 22 22 20 24 24 20 24 24 23 27 26
-23 27 26 23 27 26 23 27 26 23 27 26 23 27 26 20 24 24
-20 23 23 17 21 21 16 19 19 14 17 17 12 15 15 10 12 12
-9 11 11 20 23 22 16 19 19 14 17 17 13 16 16 12 15 15
-11 14 14 13 16 16 14 17 17 14 18 18 14 17 17 12 15 15
-10 12 12 10 12 12 10 12 12 10 12 12 10 12 12 10 12 12
-9 11 11 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 53 55 47 161 156 96
-161 156 96 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 161 156 96 118 116 76 96 95 69 21 22 20 16 19 19
-18 22 22 20 24 24 23 27 26 23 27 26 26 28 27 27 30 29
-27 30 29 18 22 22 12 14 14 8 10 10 9 11 11 17 21 21
-23 27 26 23 27 26 20 24 24 18 22 22 16 20 20 14 17 17
-12 14 14 14 17 17 16 20 20 14 17 17 13 17 17 13 16 16
-12 15 15 12 15 15 13 17 17 14 18 18 14 17 17 13 16 16
-11 13 13 10 12 12 10 12 12 10 12 12 10 12 12 10 12 12
-4 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 13 12 7 118 116 76 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 161 156 96 118 116 76 30 31 28
-20 24 24 23 27 26 27 30 29 28 31 30 30 32 31 23 27 26
-16 19 19 17 21 21 12 15 15 9 11 11 10 12 12 9 11 11
-20 24 24 28 31 30 26 28 27 23 27 26 20 24 24 17 21 21
-15 19 19 13 16 16 16 19 19 14 18 18 14 17 17 13 16 16
-12 15 15 11 14 14 13 16 16 14 17 17 14 18 18 14 17 17
-12 15 15 10 12 12 10 12 12 10 12 12 10 12 12 8 9 9
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 82 81 62 161 156 96 230 229 82
-230 229 82 233 233 100 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 161 156 96 118 116 76
-27 29 28 27 30 29 30 32 31 30 32 31 23 27 26 20 24 24
-26 28 27 17 21 21 6 7 7 72 73 67 145 141 105 15 15 15
-14 17 17 33 37 35 30 32 31 28 31 30 26 28 27 23 27 26
-20 23 23 16 20 20 15 19 19 14 18 18 14 17 17 13 16 16
-12 15 15 11 14 14 12 15 15 13 17 17 14 18 18 14 17 17
-13 16 16 11 13 13 10 12 12 10 12 12 9 11 11 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 16 17 12 161 156 96 230 229 82 230 229 82
-243 242 120 235 234 117 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 161 156 96
-82 81 62 28 31 30 28 31 30 27 30 29 28 31 30 30 32 31
-33 37 35 13 16 16 3 3 3 105 104 92 210 208 158 12 14 14
-17 21 21 33 37 35 33 37 35 32 35 33 30 32 31 27 30 29
-23 27 26 20 23 23 17 20 20 15 18 18 14 18 18 13 17 17
-13 16 16 12 15 15 11 14 14 13 16 16 14 17 17 14 18 18
-13 17 17 12 15 15 10 12 12 10 12 12 3 4 4 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 96 95 69 230 229 82 230 229 82 244 244 132
-241 241 143 243 242 120 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-161 156 96 46 47 43 32 35 33 33 37 35 33 37 35 33 37 35
-40 43 41 23 27 26 1 1 1 2 2 2 24 26 24 14 17 17
-23 27 26 33 37 35 33 37 35 33 37 35 33 37 35 30 32 31
-27 30 29 23 27 26 20 23 23 15 18 18 14 18 18 14 17 17
-13 16 16 12 15 15 11 14 14 12 15 15 13 17 17 14 17 17
-14 17 17 13 16 16 11 13 13 6 8 8 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 16 17 12 161 156 96 230 229 82 235 234 117 239 239 170
-239 239 170 236 236 101 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 118 116 76 33 37 35 33 37 35 37 39 37 37 39 37
-43 45 43 49 51 48 20 24 24 8 10 10 17 20 20 35 37 36
-33 37 35 40 43 41 37 39 37 35 37 36 33 37 35 33 37 35
-30 32 31 27 30 29 23 27 26 15 19 19 14 18 18 14 17 17
-13 17 17 13 16 16 12 15 15 11 14 14 13 16 16 14 17 17
-14 17 17 13 17 17 11 14 14 4 5 5 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 96 95 69 230 229 82 230 229 82 239 239 170 251 251 187
-241 241 143 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 161 156 96 36 38 35 33 37 35 33 37 35 33 37 35
-37 39 37 47 48 46 55 57 54 55 57 54 49 51 48 43 45 43
-43 45 43 43 45 43 40 43 41 40 43 41 37 39 37 33 37 35
-33 37 35 28 31 30 26 28 27 16 20 20 15 18 18 14 18 18
-14 17 17 13 16 16 12 15 15 11 14 14 12 15 15 13 17 17
-14 17 17 14 17 17 8 10 10 5 7 7 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-16 17 12 230 229 82 230 229 82 243 242 120 251 251 187 251 251 187
-246 246 123 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 66 65 55 30 32 31 32 35 33 33 37 35
-33 37 35 37 39 37 40 43 41 47 48 46 49 51 48 51 52 50
-55 57 54 55 57 54 51 52 50 47 48 46 43 45 43 39 40 39
-33 37 35 30 32 31 26 28 27 17 21 21 15 19 19 14 18 18
-14 17 17 13 16 16 12 15 15 12 14 14 11 14 14 13 16 16
-14 17 17 12 15 15 7 9 9 6 8 8 1 1 1 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-96 95 69 230 229 82 230 229 82 239 239 170 251 251 187 239 239 170
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 96 95 69 27 30 29 28 31 30 30 32 31
-33 37 35 40 43 41 46 47 45 55 57 54 63 64 60 72 73 67
-72 73 67 72 73 67 72 73 67 65 66 61 55 57 54 47 48 46
-39 40 39 32 35 33 27 30 29 17 21 21 15 19 19 15 18 18
-14 18 18 13 17 17 13 16 16 12 15 15 11 14 14 12 14 14
-13 16 16 9 11 11 7 9 9 9 11 11 66 65 55 115 113 82
-21 22 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 12 7
-230 229 82 230 229 82 236 236 101 251 251 187 251 251 187 246 246 123
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 118 116 76 23 27 26 26 28 27 32 35 33
-51 52 50 90 89 73 110 109 94 145 141 105 168 163 120 177 172 135
-177 172 135 188 184 146 188 184 146 181 176 137 194 191 148 188 184 146
-184 179 149 188 184 146 188 184 146 156 151 111 177 172 135 181 176 137
-177 172 135 168 163 120 168 163 120 158 153 112 156 151 111 158 153 112
-156 151 111 158 153 112 177 172 135 188 184 146 188 184 146 194 189 146
-36 38 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 82 81 62
-230 229 82 230 229 82 244 244 132 251 251 187 244 244 132 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 161 156 96 82 81 62
-96 95 69 230 229 82 181 178 103 110 109 94 156 151 111 188 184 146
-188 184 146 197 193 154 188 184 146 184 181 136 188 184 146 168 163 120
-168 163 120 178 174 128 156 151 111 158 153 112 174 170 121 156 151 111
-156 151 111 158 153 112 156 151 111 168 163 120 178 174 128 181 176 137
-176 171 126 178 174 128 184 181 136 176 171 126 178 174 128 184 181 136
-176 171 126 178 174 128 184 181 136 164 159 111 155 149 109 96 95 69
-1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 2 2 1 161 156 96
-230 229 82 230 229 82 244 244 132 244 244 132 236 236 101 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 46 47 43 82 81 62
-158 153 112 197 193 154 194 189 146 184 181 136 188 184 146 168 163 120
-156 151 111 137 133 100 131 127 93 137 133 100 137 133 100 158 153 112
-121 119 87 137 133 100 156 151 111 145 141 105 99 98 80 84 83 72
-63 64 60 52 53 49 40 43 41 33 36 34 36 38 35 36 38 35
-38 39 37 43 44 41 43 44 41 46 47 43 48 49 45 48 49 45
-46 47 43 36 38 35 30 31 28 19 20 18 6 7 7 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 36 38 35 230 229 82
-230 229 82 230 229 82 246 246 123 236 236 101 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 53 55 47 121 119 87
-176 171 126 171 165 117 161 156 96 82 81 62 53 55 47 33 37 35
-39 40 39 63 64 60 99 98 80 121 119 87 137 133 100 177 172 135
-176 171 126 184 181 136 131 127 93 131 127 93 110 109 94 84 83 72
-51 52 50 39 40 39 27 29 28 18 22 22 16 19 19 15 19 19
-15 19 19 14 18 18 14 17 17 13 16 16 12 15 15 11 14 14
-10 13 13 9 12 12 9 11 11 8 9 9 7 9 9 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 118 116 76 230 229 82
-230 229 82 230 229 82 236 236 101 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 96 95 69 71 71 57
-36 38 35 118 116 76 118 116 76 12 15 15 15 18 18 20 24 24
-33 37 35 55 56 53 84 83 72 110 109 94 145 141 105 110 109 94
-168 163 120 121 119 87 156 151 111 131 127 93 87 86 72 61 63 57
-47 48 46 28 31 30 18 22 22 15 19 19 15 18 18 15 19 19
-15 19 19 14 18 18 14 17 17 13 17 17 13 16 16 12 15 15
-11 13 13 10 12 12 9 11 11 8 10 10 7 9 9 3 3 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 0 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 161 156 96
-161 156 96 230 229 82 118 116 76 11 14 14 14 17 17 18 22 22
-27 30 29 40 43 41 60 60 56 84 83 72 105 104 92 110 109 94
-110 109 94 110 109 94 99 98 80 90 89 73 68 70 65 47 48 46
-32 34 33 23 25 24 20 23 23 17 21 21 15 19 19 14 17 17
-15 19 19 15 18 18 14 18 18 13 17 17 13 16 16 12 15 15
-11 14 14 10 12 12 9 11 11 8 10 10 7 9 9 4 5 5
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 16 17 12 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 161 156 96 118 116 76 11 13 13 13 16 16 15 19 19
-20 24 24 30 32 31 40 43 41 51 52 50 63 64 60 72 73 67
-65 66 61 65 66 61 65 66 61 55 57 54 46 47 45 33 37 35
-27 29 28 20 24 24 17 21 21 16 20 20 16 20 20 15 19 19
-15 19 19 15 19 19 14 18 18 14 17 17 13 16 16 12 15 15
-11 14 14 10 13 13 9 12 12 8 10 10 7 9 9 6 7 7
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 53 55 47 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-161 156 96 118 116 76 53 55 47 10 13 13 12 15 15 14 17 17
-17 20 20 20 24 24 27 29 28 32 34 33 37 39 37 40 43 41
-43 45 43 41 42 42 35 37 36 30 32 31 28 31 30 23 27 26
-20 23 23 17 21 21 16 20 20 16 20 20 16 20 20 16 19 19
-15 19 19 15 19 19 14 18 18 14 17 17 13 16 16 12 15 15
-11 14 14 10 13 13 9 12 12 9 11 11 8 10 10 10 12 12
-1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 82 81 62 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 161 156 96 161 156 96
-118 116 76 82 81 62 13 14 12 10 13 13 12 15 15 13 17 17
-15 19 19 16 20 20 20 23 23 20 24 24 23 27 26 26 28 27
-26 28 27 26 28 27 23 27 26 18 22 22 20 23 23 17 21 21
-17 21 21 16 20 20 16 20 20 16 20 20 16 20 20 16 19 19
-15 19 19 15 19 19 15 18 18 14 17 17 13 17 17 13 16 16
-12 15 15 12 14 14 12 14 14 12 14 14 12 14 14 23 24 24
-6 8 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 118 116 76 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 161 156 96 161 156 96 118 116 76
-71 71 57 13 14 12 9 12 12 10 13 13 12 15 15 13 17 17
-15 18 18 15 19 19 16 20 20 17 21 21 17 21 21 18 22 22
-18 22 22 18 22 22 17 21 21 16 19 19 15 18 18 14 18 18
-16 19 19 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-15 19 19 15 19 19 15 18 18 14 18 18 16 20 20 23 25 24
-17 21 21 25 27 26 47 48 46 47 48 46 51 52 50 72 73 67
-33 36 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 118 116 76 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 161 156 96 118 116 76 118 116 76 46 47 43
-9 11 11 9 11 11 10 12 12 11 13 13 12 15 15 14 17 17
-15 18 18 15 19 19 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-15 19 19 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-15 19 19 16 20 20 20 24 24 55 56 53 32 34 33 84 83 72
-90 89 73 110 109 94 110 109 94 105 104 92 110 109 94 110 109 94
-72 73 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 96 95 69 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 161 156 96 118 116 76 82 81 62 16 17 12 9 11 11
-9 11 11 9 12 12 10 13 13 12 14 14 13 16 16 14 18 18
-15 19 19 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 19 19 33 36 34 99 98 80 156 151 111 145 141 105 184 179 149
-168 163 120 184 179 149 177 172 135 156 151 111 145 141 105 110 109 94
-90 89 73 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 71 71 57 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 230 229 82
-230 229 82 161 156 96 230 229 82 230 229 82 230 229 82 161 156 96
-118 116 76 82 81 62 30 31 28 9 11 11 9 11 11 9 11 11
-10 12 12 10 13 13 11 14 14 13 16 16 14 17 17 15 18 18
-15 19 19 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-18 22 22 58 59 55 137 133 100 197 193 154 214 212 158 210 208 158
-197 193 154 184 179 149 184 179 149 137 133 100 110 109 94 99 98 80
-84 83 72 10 10 9 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 16 17 12 230 229 82 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 230 229 82 161 156 96
-161 156 96 161 156 96 161 156 96 161 156 96 118 116 76 71 71 57
-21 22 20 12 14 14 11 13 13 10 12 12 10 12 12 10 13 13
-11 13 13 12 15 15 13 16 16 14 17 17 14 18 18 15 19 19
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 17 21 21
-23 27 26 84 83 72 184 179 149 251 251 187 210 208 158 184 179 149
-184 179 149 156 151 111 110 109 94 84 83 72 63 64 60 51 52 50
-18 22 22 6 8 8 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 118 116 76 230 229 82
-230 229 82 230 229 82 230 229 82 230 229 82 161 156 96 161 156 96
-161 156 96 161 156 96 118 116 76 53 55 47 20 23 22 16 19 19
-13 16 16 12 15 15 12 14 14 11 14 14 11 14 14 11 14 14
-12 15 15 13 16 16 14 17 17 15 19 19 16 20 20 17 21 21
-23 27 26 18 22 22 20 24 24 23 27 26 30 32 31 17 21 21
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-23 27 26 33 37 35 137 133 100 156 151 111 158 153 112 105 104 92
-105 104 92 68 70 65 39 40 39 18 22 22 12 14 14 12 15 15
-9 11 11 4 5 5 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 16 17 12 230 229 82
-230 229 82 230 229 82 230 229 82 161 156 96 118 116 76 118 116 76
-118 116 76 66 65 55 43 45 43 32 34 33 25 27 26 20 23 22
-17 20 20 15 18 18 14 17 17 15 18 18 13 16 16 14 17 17
-14 18 18 16 20 20 32 34 33 55 57 54 58 59 55 72 73 67
-105 104 92 55 57 54 65 66 61 63 64 60 40 43 41 33 37 35
-41 42 42 20 24 24 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-17 21 21 26 28 27 30 32 31 35 37 36 68 70 65 39 40 39
-23 27 26 15 18 18 13 16 16 11 14 14 9 12 12 8 10 10
-7 9 9 6 7 7 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 38 35
-230 229 82 230 229 82 230 229 82 96 95 69 30 31 28 49 51 48
-90 89 73 68 70 65 55 57 54 47 48 46 47 48 46 43 45 43
-32 34 33 43 45 43 43 45 43 23 27 26 25 27 26 40 43 41
-40 43 41 90 89 73 110 109 94 145 141 105 156 151 111 156 151 111
-184 179 149 184 179 149 177 172 135 184 179 149 137 133 100 84 83 72
-105 104 92 63 64 60 49 51 48 47 48 46 28 31 30 18 22 22
-16 20 20 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 15 19 19 15 19 19 15 19 19 18 22 22 15 19 19
-13 16 16 12 15 15 11 14 14 10 13 13 9 12 12 9 11 11
-8 10 10 6 8 8 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-30 31 28 230 229 82 71 71 57 2 2 1 0 0 0 58 59 55
-105 104 92 84 83 72 65 66 61 84 83 72 110 109 94 110 109 94
-145 141 105 105 104 92 110 109 94 110 109 94 84 83 72 110 109 94
-158 153 112 197 193 154 197 193 154 239 239 170 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 210 208 158 197 193 154
-197 193 154 184 179 149 145 141 105 137 133 100 105 104 92 47 48 46
-20 23 23 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 19 19 15 19 19 15 19 19 14 18 18 14 17 17
-13 17 17 13 16 16 12 14 14 12 14 14 13 13 13 13 13 13
-13 13 13 12 12 12 10 10 9 6 7 7 2 2 2 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 65 66 61
-105 104 92 84 83 72 84 83 72 110 109 94 184 179 149 210 208 158
-210 208 158 210 208 158 214 212 158 197 193 154 214 212 158 210 208 158
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 239 239 170 251 251 187 184 179 149 84 83 72
-26 28 27 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 15 19 19 15 19 19 15 18 18 14 18 18
-13 17 17 13 16 16 15 15 15 14 14 13 14 14 13 14 14 13
-13 13 13 13 13 13 12 12 12 12 12 12 12 12 12 3 4 4
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 72 73 67
-105 104 92 99 98 80 84 83 72 99 98 80 177 172 135 197 193 154
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 214 212 158 197 193 154 99 98 80
-23 27 26 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 15 19 19 15 19 19 15 18 18 14 18 18
-14 17 17 16 16 16 16 16 16 16 16 16 15 15 15 14 14 13
-14 14 13 13 13 13 13 13 13 12 12 12 12 12 12 12 12 12
-3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 84 83 72
-110 109 94 99 98 80 72 73 67 63 64 60 99 98 80 177 172 135
-184 179 149 210 208 158 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 210 208 158 184 179 149 177 172 135 110 109 94 33 37 35
-17 21 21 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-16 20 20 16 20 20 15 19 19 15 19 19 15 19 19 14 18 18
-15 18 18 18 19 18 18 19 18 17 17 17 16 16 16 15 15 15
-14 14 13 13 13 13 13 13 13 12 12 12 12 12 12 12 12 12
-10 10 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 105 104 92
-108 107 93 99 98 80 72 73 67 63 64 60 51 52 50 87 86 72
-105 104 92 110 109 94 108 107 93 156 151 111 184 179 149 184 179 149
-197 193 154 197 193 154 197 193 154 184 179 149 184 179 149 177 172 135
-197 193 154 156 151 111 177 172 135 184 179 149 168 163 120 137 133 100
-145 141 105 110 109 94 99 98 80 47 48 46 55 57 54 15 19 19
-16 19 19 16 20 20 16 20 20 16 20 20 16 20 20 16 20 20
-17 20 20 17 21 21 16 20 20 16 19 19 15 19 19 16 19 19
-20 20 20 21 22 21 20 20 20 19 20 19 18 19 18 16 16 16
-15 15 15 14 14 13 13 13 13 13 13 13 12 12 12 12 12 12
-12 12 12 4 5 5 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 58 59 55 110 109 94
-105 104 92 90 89 73 72 73 67 55 57 54 43 45 43 39 40 39
-43 45 43 46 47 45 43 45 43 68 70 65 65 66 61 63 64 60
-108 107 93 72 73 67 105 104 92 90 89 73 72 73 67 40 43 41
-72 73 67 68 70 65 68 70 65 58 59 55 63 64 60 49 51 48
-43 45 43 33 36 34 27 30 29 20 24 24 16 20 20 15 19 19
-15 19 19 15 19 19 15 19 19 16 19 19 16 20 20 16 20 20
-17 21 21 20 24 24 20 23 22 17 21 21 17 20 20 20 20 20
-21 22 21 21 22 21 21 22 21 21 22 21 20 20 20 18 19 18
-16 16 16 15 15 15 13 13 13 13 13 13 12 12 12 12 12 12
-12 12 12 10 10 9 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 21 22 21 110 109 94 110 109 94
-105 104 92 84 83 72 68 70 65 51 52 50 41 42 42 33 37 35
-28 31 30 23 27 26 20 23 23 18 22 22 17 20 20 25 27 26
-26 28 27 27 30 29 25 27 26 20 23 23 23 27 26 30 32 31
-20 24 24 17 21 21 18 22 22 15 19 19 26 28 27 20 23 23
-14 18 18 15 19 19 15 18 18 15 19 19 15 19 19 15 19 19
-15 19 19 15 19 19 15 19 19 15 19 19 15 19 19 16 19 19
-16 20 20 22 24 23 24 26 24 22 24 23 20 23 22 22 24 23
-24 26 24 24 26 24 23 24 24 22 24 23 21 22 21 19 20 19
-17 17 17 15 15 15 14 14 13 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 2 2 2 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 2 2 2 99 98 80 110 109 94 108 107 93
-105 104 92 84 83 72 63 64 60 49 51 48 39 40 39 32 34 33
-27 30 29 23 25 24 20 23 23 17 20 20 15 19 19 14 18 18
-14 17 17 13 17 17 13 17 17 13 17 17 13 17 17 13 17 17
-14 17 17 14 17 17 14 17 17 14 17 17 14 17 17 14 17 17
-14 18 18 14 18 18 14 18 18 14 18 18 15 18 18 15 19 19
-15 19 19 15 19 19 15 19 19 15 19 19 15 19 19 15 19 19
-15 19 19 17 21 21 27 29 28 26 28 27 25 27 26 25 27 26
-27 29 28 27 29 28 26 28 27 24 26 24 21 22 21 20 20 20
-18 19 18 16 16 16 14 14 13 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 4 5 5 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 51 52 50 110 109 94 110 109 94 105 104 92
-90 89 73 72 73 67 55 57 54 43 45 43 35 37 36 30 32 31
-26 28 27 20 24 24 17 21 21 16 19 19 15 18 18 14 17 17
-13 16 16 13 16 16 13 16 16 13 16 16 13 16 16 13 16 16
-13 16 16 13 16 16 13 16 16 13 17 17 13 17 17 14 17 17
-14 17 17 14 17 17 14 17 17 14 18 18 14 18 18 14 18 18
-15 18 18 15 18 18 15 19 19 15 19 19 15 19 19 15 19 19
-15 19 19 15 19 19 27 29 28 32 34 33 28 31 30 27 29 28
-30 32 31 30 32 31 30 31 28 26 28 27 23 24 24 21 22 21
-19 20 19 16 16 16 14 14 13 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 6 7 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 10 10 9 108 107 93 110 109 94 108 107 93 99 98 80
-84 83 72 63 64 60 49 51 48 40 43 41 33 36 34 27 30 29
-23 27 26 18 22 22 17 20 20 15 18 18 14 17 17 13 16 16
-13 16 16 13 16 16 12 15 15 12 15 15 12 15 15 12 15 15
-13 16 16 13 16 16 13 16 16 13 16 16 13 16 16 13 16 16
-13 17 17 13 17 17 14 17 17 14 17 17 14 17 17 14 18 18
-14 18 18 14 18 18 15 18 18 15 18 18 15 19 19 15 19 19
-15 19 19 15 19 19 17 21 21 33 36 34 32 34 33 31 33 31
-33 36 34 33 36 34 31 33 31 27 29 28 25 27 26 21 22 21
-19 20 19 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 8 8 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 63 64 60 137 133 100 43 45 43 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 68 70 65 110 109 94 110 109 94 105 104 92 84 83 72
-68 70 65 55 57 54 43 45 43 35 37 36 30 32 31 26 28 27
-20 24 24 17 21 21 16 19 19 14 17 17 13 16 16 12 15 15
-12 15 15 12 15 15 12 15 15 12 15 15 12 15 15 12 15 15
-12 15 15 12 15 15 12 15 15 12 15 15 12 15 15 13 16 16
-13 16 16 13 16 16 13 16 16 13 17 17 13 17 17 14 17 17
-14 17 17 14 17 17 14 18 18 14 18 18 14 18 18 15 18 18
-15 19 19 15 19 19 15 19 19 20 24 24 32 34 33 35 37 36
-37 39 37 35 37 36 33 36 34 30 32 31 26 28 27 22 24 23
-20 20 20 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 8 8 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-1 1 1 99 98 80 184 179 149 184 179 149 68 70 65 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-15 15 15 110 109 94 110 109 94 108 107 93 99 98 80 72 73 67
-61 63 57 49 51 48 39 40 39 33 36 34 27 30 29 23 25 24
-18 22 22 16 19 19 14 17 17 13 16 16 12 15 15 12 15 15
-11 14 14 11 14 14 11 14 14 11 14 14 11 14 14 11 14 14
-11 14 14 11 14 14 12 14 14 12 15 15 12 15 15 12 15 15
-12 15 15 13 16 16 13 16 16 13 16 16 13 16 16 13 16 16
-13 17 17 14 17 17 14 17 17 14 17 17 14 18 18 14 18 18
-14 18 18 15 18 18 15 19 19 15 19 19 30 32 31 38 39 37
-39 40 39 39 40 39 35 37 36 31 33 31 27 29 28 22 24 23
-20 20 20 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 8 8 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3
-110 109 94 197 193 154 210 208 158 184 179 149 68 70 65 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-68 70 65 110 109 94 110 109 94 105 104 92 84 83 72 65 66 61
-51 52 50 43 45 43 35 37 36 30 32 31 25 27 26 20 23 23
-17 20 20 15 18 18 13 16 16 12 15 15 12 15 15 11 14 14
-11 14 14 11 14 14 11 13 13 11 13 13 11 13 13 11 13 13
-11 14 14 11 14 14 11 14 14 11 14 14 11 14 14 11 14 14
-12 15 15 12 15 15 12 15 15 12 15 15 13 16 16 13 16 16
-13 16 16 13 16 16 13 17 17 13 17 17 14 17 17 14 17 17
-14 18 18 14 18 18 14 18 18 16 19 19 37 39 37 41 42 42
-41 42 42 41 42 42 38 39 37 32 34 33 27 29 28 23 24 24
-21 22 21 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 8 8 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 11 11 11 137 133 100
-197 193 154 251 251 187 239 239 170 184 179 149 31 33 31 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 12 12
-110 109 94 110 109 94 105 104 92 90 89 73 72 73 67 58 59 55
-46 47 45 37 39 37 31 33 31 26 28 27 20 24 24 17 21 21
-15 18 18 13 16 16 12 15 15 12 14 14 11 13 13 11 13 13
-10 13 13 10 13 13 10 13 13 10 13 13 10 13 13 10 13 13
-10 13 13 10 13 13 11 13 13 11 13 13 11 14 14 11 14 14
-11 14 14 11 14 14 12 14 14 12 15 15 12 15 15 12 15 15
-13 16 16 13 16 16 13 16 16 13 16 16 13 17 17 13 17 17
-14 17 17 14 17 17 14 18 18 23 27 26 41 42 42 41 42 42
-43 45 43 41 42 42 39 40 39 33 36 34 27 29 28 23 24 24
-21 22 21 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 6 7 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 27 29 28 168 163 120 210 208 158
-251 251 187 251 251 187 210 208 158 137 133 100 1 1 1 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 60 60 56
-110 109 94 105 104 92 105 104 92 84 83 72 65 66 61 51 52 50
-40 43 41 33 36 34 27 30 29 23 25 24 18 22 22 16 19 19
-14 17 17 12 15 15 11 14 14 11 14 14 10 13 13 10 13 13
-10 13 13 10 12 12 10 12 12 10 12 12 10 12 12 10 12 12
-10 12 12 10 12 12 10 13 13 10 13 13 10 13 13 11 13 13
-11 13 13 11 14 14 11 14 14 11 14 14 11 14 14 12 15 15
-12 15 15 12 15 15 12 15 15 13 16 16 13 16 16 13 16 16
-13 17 17 13 17 17 14 17 17 32 34 33 43 45 43 43 45 43
-43 45 43 43 45 43 39 40 39 33 36 34 27 29 28 23 24 24
-21 22 21 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 6 7 7 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 68 70 65 184 179 149 210 208 158 251 251 187
-251 251 187 214 212 158 184 179 149 37 39 37 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 6 7 7 105 104 92
-105 104 92 105 104 92 99 98 80 72 73 67 58 59 55 46 47 45
-35 37 36 30 32 31 25 27 26 20 23 23 16 19 19 14 17 17
-12 15 15 12 14 14 11 13 13 10 13 13 10 12 12 10 12 12
-10 12 12 10 12 12 9 12 12 9 12 12 9 12 12 9 12 12
-10 12 12 10 12 12 10 12 12 10 12 12 10 12 12 10 13 13
-10 13 13 10 13 13 11 13 13 11 13 13 11 14 14 11 14 14
-11 14 14 12 15 15 12 15 15 12 15 15 12 15 15 13 16 16
-13 16 16 13 16 16 17 20 20 41 42 42 46 47 45 46 47 45
-46 47 45 43 45 43 40 41 39 33 36 34 27 29 28 23 24 24
-20 20 20 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 4 5 5 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-15 15 15 110 109 94 197 193 154 214 212 158 251 251 187 251 251 187
-239 239 170 184 179 149 84 83 72 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 47 48 46 105 104 92
-105 104 92 99 98 80 84 83 72 68 70 65 51 52 50 40 43 41
-32 34 33 27 29 28 22 24 23 17 21 21 15 18 18 13 16 16
-12 15 15 11 13 13 10 13 13 10 12 12 9 12 12 9 12 12
-9 12 12 9 12 12 9 11 11 9 11 11 9 11 11 9 11 11
-9 12 12 9 12 12 9 12 12 9 12 12 10 12 12 10 12 12
-10 12 12 10 12 12 10 13 13 10 13 13 10 13 13 11 13 13
-11 14 14 11 14 14 11 14 14 12 14 14 12 15 15 12 15 15
-12 15 15 13 16 16 28 31 30 43 45 43 47 48 46 47 48 46
-47 48 46 43 45 43 40 41 39 33 36 34 27 29 28 22 24 23
-20 20 20 17 17 17 15 15 15 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 3 4 4 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 60 60 56
-177 172 135 197 193 154 251 251 187 251 251 187 251 251 187 251 251 187
-184 179 149 110 109 94 3 4 4 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 1 99 98 80 105 104 92
-99 98 80 87 86 72 84 83 72 63 64 60 46 47 45 35 37 36
-30 32 31 25 27 26 18 22 22 16 19 19 14 17 17 12 15 15
-11 14 14 10 13 13 9 12 12 9 12 12 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 12 12 9 12 12
-9 12 12 10 12 12 10 12 12 10 12 12 10 13 13 10 13 13
-10 13 13 11 13 13 11 14 14 11 14 14 11 14 14 12 15 15
-12 15 15 14 17 17 41 42 42 47 48 46 49 51 48 51 52 50
-47 48 46 43 45 43 40 41 39 33 36 34 27 29 28 22 24 23
-19 20 19 16 16 16 14 14 13 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 2 2 2 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 23 24 24 137 133 100 184 179 149
-210 208 158 251 251 187 251 251 187 251 251 187 251 251 187 184 179 149
-110 109 94 13 13 13 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 30 32 31 105 104 92 99 98 80
-84 83 72 84 83 72 72 73 67 55 57 54 41 42 42 32 34 33
-27 29 28 20 24 24 17 20 20 14 17 17 13 16 16 12 14 14
-10 13 13 10 12 12 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 12 12 9 12 12 10 12 12 10 12 12 10 12 12
-10 13 13 10 13 13 10 13 13 11 13 13 11 14 14 11 14 14
-11 14 14 27 29 28 55 56 53 72 73 67 51 52 50 51 52 50
-49 51 48 43 45 43 39 40 39 32 34 33 26 28 27 21 22 21
-19 20 19 16 16 16 18 19 17 13 13 13 12 12 12 12 12 12
-12 12 12 12 12 12 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 8 8 7 84 83 72 184 179 149 197 193 154 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 184 179 149 145 141 105
-19 20 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 14 14 13 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 72 73 67 105 104 92 84 83 72
-72 73 67 84 83 72 68 70 65 49 51 48 39 40 39 30 32 31
-25 27 26 18 22 22 15 18 18 13 16 16 12 15 15 11 13 13
-10 12 12 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 12 12 9 12 12 9 12 12
-10 12 12 10 12 12 10 12 12 10 13 13 10 13 13 11 13 13
-13 16 16 41 42 42 99 98 80 158 153 112 65 66 61 51 52 50
-49 51 48 43 45 43 39 40 39 31 33 31 25 27 26 21 22 21
-21 22 21 68 70 65 55 56 53 13 13 13 12 12 12 12 12 12
-12 12 12 11 11 11 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3
-63 64 60 158 153 112 184 179 149 210 208 158 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 184 179 149 137 133 100 27 29 28
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-21 22 21 110 109 94 5 6 5 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 13 13 13 105 104 92 90 89 73 72 73 67
-68 70 65 84 83 72 63 64 60 46 47 45 35 37 36 27 29 28
-22 24 23 17 20 20 14 17 17 12 15 15 11 14 14 10 12 12
-10 12 12 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 12 12 9 12 12 10 12 12 10 12 12 10 13 13 10 13 13
-30 32 31 47 48 46 177 172 135 210 208 158 137 133 100 55 56 53
-49 51 48 43 45 43 38 39 37 31 33 31 25 27 26 22 24 23
-110 109 94 184 179 149 63 64 60 13 13 13 12 12 12 12 12 12
-12 12 12 8 9 9 0 0 0 1 1 1 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 21 22 21 105 104 92
-184 179 149 210 208 158 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 251 251 187 184 179 149 145 141 105 23 24 24 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-68 70 65 184 179 149 105 104 92 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 51 52 50 99 98 80 84 83 72 63 64 60
-68 70 65 72 73 67 55 57 54 41 42 42 32 34 33 25 27 26
-20 23 23 16 19 19 13 16 16 12 14 14 10 13 13 10 12 12
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 12 12 9 12 12 10 12 12 17 20 20
-46 47 45 72 73 67 210 208 158 251 251 187 210 208 158 63 64 60
-49 51 48 43 45 43 37 39 37 30 32 31 24 26 24 105 104 92
-210 208 158 197 193 154 47 48 46 13 13 13 12 12 12 12 12 12
-12 12 12 6 7 7 33 36 34 48 49 45 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 8 8 7 23 24 24 55 56 53 110 109 94
-210 208 158 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 184 179 149 110 109 94 20 20 20 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-110 109 94 251 251 187 210 208 158 47 48 46 0 0 0 0 0 0
-0 0 0 1 1 1 90 89 73 90 89 73 72 73 67 55 56 53
-72 73 67 68 70 65 51 52 50 37 39 37 28 31 30 23 25 24
-17 21 21 15 18 18 12 15 15 11 14 14 10 13 13 9 12 12
-9 11 11 9 11 11 9 11 11 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 9 11 11 9 11 11 9 11 11 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 9 12 12 13 16 16 41 42 42
-49 51 48 110 109 94 251 251 187 251 251 187 251 251 187 105 104 92
-49 51 48 43 45 43 35 37 36 30 31 28 47 48 46 197 193 154
-251 251 187 197 193 154 31 33 31 12 12 12 12 12 12 12 12 12
-12 12 12 51 52 50 184 179 149 72 73 67 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2
-11 11 11 21 22 21 30 32 31 40 41 39 60 60 56 145 141 105
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 214 212 158
-184 179 149 110 109 94 13 13 13 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 4 5 4 61 61 53 48 49 45 3 4 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-156 151 111 251 251 187 251 251 187 184 179 149 11 11 11 0 0 0
-0 0 0 26 28 27 99 98 80 84 83 72 60 60 56 43 45 43
-72 73 67 65 66 61 49 51 48 35 37 36 27 29 28 20 24 24
-17 20 20 14 17 17 12 15 15 11 13 13 10 12 12 9 11 11
-9 11 11 9 11 11 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 9 11 11 9 11 11
-9 11 11 9 11 11 9 11 11 11 13 13 37 39 37 47 48 46
-51 52 50 184 179 149 251 251 187 251 251 187 251 251 187 145 141 105
-47 48 46 41 42 42 35 37 36 27 29 28 137 133 100 251 251 187
-251 251 187 197 193 154 19 20 19 12 12 12 12 12 12 12 12 12
-27 29 28 184 179 149 214 212 158 63 64 60 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 6 7 7 16 16 16 24 26 24
-30 32 31 38 39 37 47 48 46 55 57 54 68 70 65 110 109 94
-197 193 154 251 251 187 251 251 187 251 251 187 210 208 158 184 179 149
-105 104 92 8 8 7 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 65 66 61 184 179 149 156 151 111
-30 32 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-168 163 120 251 251 187 251 251 187 251 251 187 110 109 94 0 0 0
-0 0 0 60 60 56 84 83 72 68 70 65 51 52 50 38 39 37
-84 83 72 63 64 60 43 45 43 33 36 34 25 27 26 20 23 22
-15 18 18 13 16 16 12 14 14 10 13 13 9 12 12 9 11 11
-9 11 11 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-9 11 11 9 11 11 10 12 12 33 36 34 46 47 45 51 52 50
-72 73 67 210 208 158 251 251 187 251 251 187 251 251 187 177 172 135
-47 48 46 41 42 42 35 37 36 37 39 37 184 179 149 251 251 187
-251 251 187 197 193 154 13 13 13 12 12 12 12 12 12 12 12 12
-110 109 94 251 251 187 251 251 187 37 39 37 0 0 0 0 0 0
-0 0 0 21 22 20 2 2 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 5 5 12 12 12 21 22 21 25 27 26 30 32 31 38 39 37
-46 47 45 55 56 53 60 60 56 65 66 61 68 70 65 105 104 92
-110 109 94 197 193 154 210 208 158 197 193 154 184 179 149 84 83 72
-2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 13 13 13 184 179 149 251 251 187
-197 193 154 43 44 41 0 0 0 0 0 0 0 0 0 0 0 0
-145 141 105 251 251 187 251 251 187 251 251 187 214 212 158 43 45 43
-2 2 2 84 83 72 72 73 67 58 59 55 41 42 42 38 39 37
-72 73 67 58 59 55 41 42 42 31 33 31 25 27 26 18 22 22
-14 17 17 12 15 15 12 14 14 10 12 12 9 12 12 9 11 11
-9 11 11 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 9 12 12 31 33 31 43 45 43 49 51 48 55 56 53
-110 109 94 251 251 187 251 251 187 251 251 187 251 251 187 168 163 120
-47 48 46 41 42 42 33 36 34 63 64 60 197 193 154 251 251 187
-251 251 187 184 179 149 13 13 13 12 12 12 12 12 12 16 16 16
-197 193 154 251 251 187 239 239 170 20 20 20 0 0 0 2 2 1
-108 107 93 110 109 94 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 4 5 5 11 11 11 18 19 18
-22 24 23 26 28 27 32 34 33 39 40 39 46 47 45 51 52 50
-55 57 54 60 60 56 63 64 60 63 64 60 63 64 60 58 59 55
-63 64 60 99 98 80 145 141 105 137 133 100 43 45 43 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 3 4 3 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 110 109 94 251 251 187
-251 251 187 184 179 149 25 27 26 0 0 0 0 0 0 0 0 0
-99 98 80 251 251 187 251 251 187 251 251 187 251 251 187 156 151 111
-25 27 26 84 83 72 65 66 61 47 48 46 32 34 33 39 40 39
-72 73 67 55 57 54 40 41 39 30 32 31 23 25 24 18 22 22
-14 17 17 12 15 15 11 13 13 10 12 12 9 11 11 9 11 11
-9 11 11 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-9 11 11 28 31 30 41 42 42 47 48 46 55 56 53 58 59 55
-137 133 100 251 251 187 251 251 187 251 251 187 210 208 158 137 133 100
-47 48 46 40 41 39 32 34 33 75 75 61 184 179 149 239 239 170
-251 251 187 177 172 135 13 13 13 12 12 12 12 12 12 43 44 41
-197 193 154 251 251 187 210 208 158 10 10 9 0 0 0 84 83 72
-251 251 187 84 83 72 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
-6 7 7 11 11 11 17 17 17 20 20 20 23 24 24 27 29 28
-32 34 33 38 39 37 43 45 43 47 48 46 51 52 50 55 56 53
-58 59 55 58 59 55 55 57 54 55 56 53 47 48 46 41 42 42
-35 37 36 31 33 31 47 48 46 14 14 13 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 66 65 55 99 98 80 20 20 20
-0 0 0 0 0 0 0 0 0 0 0 0 43 45 43 214 212 158
-251 251 187 251 251 187 145 141 105 3 3 3 0 0 0 0 0 0
-48 49 45 184 179 149 239 239 170 251 251 187 239 239 170 177 172 135
-84 83 72 72 73 67 55 56 53 39 40 39 26 28 27 39 40 39
-68 70 65 51 52 50 39 40 39 28 31 30 22 24 23 17 20 20
-14 17 17 12 14 14 10 13 13 9 11 11 9 11 11 9 11 11
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-27 29 28 40 41 39 46 47 45 51 52 50 55 57 54 63 64 60
-131 127 93 197 193 154 210 208 158 197 193 154 168 163 120 96 95 69
-47 48 46 40 41 39 32 34 33 71 71 57 145 141 105 184 179 149
-184 179 149 131 127 93 13 13 13 12 12 12 12 12 12 48 49 45
-168 163 120 184 179 149 156 151 111 6 7 7 14 14 13 177 172 135
-239 239 170 40 41 39 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 3 3 3 6 7 7 11 11 11 16 16 16
-18 19 18 21 22 21 23 24 24 27 29 28 32 34 33 37 39 37
-41 42 42 43 45 43 47 48 46 51 52 50 51 52 50 51 52 50
-51 52 50 49 51 48 46 47 45 40 41 39 32 34 33 25 27 26
-20 20 20 14 14 13 2 2 2 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 33 36 34 197 193 154 184 179 149
-41 42 42 0 0 0 0 0 0 0 0 0 3 3 3 184 179 149
-251 251 187 251 251 187 184 179 149 48 49 45 0 0 0 0 0 0
-16 17 12 121 119 87 177 172 135 194 189 146 188 184 146 145 141 105
-82 81 62 63 64 60 46 47 45 31 33 31 21 22 21 35 37 36
-68 70 65 51 52 50 37 39 37 27 30 29 22 24 23 17 20 20
-13 16 16 12 14 14 10 13 13 9 11 11 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 25 27 26
-38 39 37 43 45 43 51 52 50 55 56 53 60 60 56 63 64 60
-92 91 72 158 153 112 176 171 126 171 165 117 149 143 98 82 81 62
-44 46 43 38 39 37 30 32 31 71 71 57 131 127 93 160 154 106
-149 143 98 82 81 62 13 13 13 12 12 12 12 12 12 46 47 43
-121 119 87 134 131 96 96 95 69 7 7 6 38 39 37 131 127 93
-145 141 105 12 13 12 0 0 0 1 1 1 3 3 3 6 7 7
-10 10 9 12 12 12 14 14 13 16 16 16 18 19 18 21 22 21
-22 24 23 26 28 27 30 31 28 33 36 34 37 39 37 40 41 39
-41 42 42 43 45 43 46 47 45 46 47 45 46 47 45 43 45 43
-41 42 42 37 39 37 31 33 31 26 28 27 21 22 21 16 16 16
-6 7 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 177 172 135 251 251 187
-197 193 154 27 29 28 0 0 0 0 0 0 0 0 0 110 109 94
-239 239 170 239 239 170 184 179 149 87 86 72 2 2 1 0 0 0
-1 1 1 82 81 62 142 137 94 165 161 109 165 161 109 131 127 93
-75 75 61 55 56 53 37 39 37 25 27 26 19 20 19 32 34 33
-65 66 61 49 51 48 35 37 36 27 29 28 20 23 23 16 19 19
-13 16 16 13 13 13 10 12 12 9 11 11 8 10 10 8 10 10
-8 9 9 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 22 24 23 35 37 36
-41 42 42 47 48 46 55 56 53 58 59 55 63 64 60 65 66 61
-71 71 57 131 127 93 160 154 106 160 154 106 142 137 94 82 81 62
-46 47 43 40 41 39 33 36 34 66 65 55 125 122 87 149 143 98
-142 137 94 82 81 62 17 17 17 18 19 17 14 14 13 46 47 43
-118 116 76 125 122 87 96 95 69 16 17 12 71 71 57 103 101 77
-82 81 62 11 11 11 11 11 11 13 13 13 14 14 13 14 14 13
-15 15 15 16 16 16 17 17 17 19 20 19 21 22 21 23 24 24
-26 28 27 27 29 28 31 33 31 33 36 34 35 37 36 38 39 37
-39 40 39 39 40 39 38 39 37 37 39 37 35 37 36 31 33 31
-27 29 28 24 26 24 21 22 21 17 17 17 12 12 12 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 68 70 65 251 251 187
-251 251 187 156 151 111 2 2 1 0 0 0 0 0 0 43 44 41
-177 172 135 184 179 149 158 153 112 103 101 77 19 20 18 0 0 0
-0 0 0 46 47 43 131 127 93 160 154 106 160 154 106 131 127 93
-71 71 57 43 45 43 30 32 31 21 22 21 16 16 16 26 28 27
-63 64 60 47 48 46 35 37 36 26 28 27 20 23 23 16 19 19
-13 16 16 13 13 13 10 12 12 9 11 11 8 10 10 8 10 10
-7 9 9 7 9 9 8 9 9 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 20 20 20 33 36 34 40 41 39
-46 47 45 51 52 50 55 57 54 60 60 56 63 64 60 65 66 61
-66 65 55 118 116 76 151 147 98 165 161 109 151 147 98 121 119 87
-96 95 69 96 95 69 96 95 69 103 101 77 142 137 94 151 147 98
-142 137 94 103 101 77 82 81 62 82 81 62 82 81 62 96 95 69
-131 127 93 142 137 94 103 101 77 46 47 43 96 95 69 118 116 76
-71 71 57 14 14 13 14 14 13 15 15 15 15 15 15 16 16 16
-16 16 16 17 17 17 18 19 18 20 20 20 21 22 21 23 24 24
-25 27 26 27 29 28 30 31 28 30 32 31 31 33 31 31 33 31
-31 33 31 31 33 31 30 31 28 27 29 28 25 27 26 22 24 23
-20 20 20 16 16 16 13 13 13 6 7 7 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-58 59 55 68 70 65 8 8 7 0 0 0 10 10 9 210 208 158
-251 251 187 184 179 149 38 39 37 0 0 0 0 0 0 8 8 7
-103 101 77 149 143 98 149 143 98 118 116 76 40 41 39 25 27 25
-53 55 47 82 81 62 144 139 99 165 161 109 165 161 109 142 137 94
-71 71 57 35 37 36 24 26 24 18 19 18 15 15 15 22 24 23
-63 64 60 46 47 45 33 36 34 26 28 27 20 23 22 17 18 17
-12 15 15 11 13 13 10 12 12 9 11 11 8 10 10 8 10 10
-7 9 9 7 9 9 7 9 9 7 9 9 8 9 9 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 16 16 16 30 31 28 35 37 36 41 42 42
-47 48 46 55 56 53 58 59 55 63 64 60 65 66 61 65 66 61
-61 61 53 103 101 77 151 147 98 171 165 117 171 165 117 168 163 120
-158 153 112 158 153 112 155 149 109 151 147 98 151 147 98 160 154 106
-151 147 98 149 143 98 142 137 94 149 143 98 149 143 98 149 143 98
-155 149 109 151 147 98 131 127 93 103 101 77 125 122 87 118 116 76
-71 71 57 16 16 16 16 16 16 16 16 16 17 17 17 17 17 17
-17 17 17 17 17 17 18 19 18 19 20 19 20 20 20 21 22 21
-23 24 24 24 26 24 25 27 26 26 28 27 26 28 27 26 28 27
-25 27 26 24 26 24 22 24 23 21 22 21 19 20 19 16 16 16
-14 14 13 8 8 7 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-20 20 20 184 179 149 168 163 120 21 22 21 0 0 0 105 104 92
-177 172 135 145 141 105 71 71 57 0 0 0 0 0 0 0 0 0
-66 65 55 131 127 93 151 147 98 142 137 94 118 116 76 121 119 87
-145 141 105 158 153 112 176 171 126 178 174 128 176 171 126 149 145 103
-96 95 69 31 33 31 21 22 21 16 16 16 14 14 13 18 19 18
-60 60 56 46 47 45 33 36 34 25 27 26 21 22 21 15 18 18
-12 15 15 11 13 13 9 11 11 8 10 10 8 10 10 8 9 9
-7 9 9 7 9 9 7 9 9 7 9 9 7 9 9 8 9 9
-8 9 9 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 10 12 12 26 28 27 31 33 31 38 39 37 43 45 43
-51 52 50 55 56 53 60 60 56 63 64 60 65 66 61 68 70 65
-63 64 60 96 95 69 158 153 112 178 174 128 188 184 146 194 189 146
-194 189 146 188 184 146 184 181 136 176 171 126 171 165 117 173 167 111
-173 167 111 165 161 109 171 165 117 174 170 121 176 171 126 178 174 128
-178 174 128 174 170 121 160 154 106 149 143 98 149 143 98 125 122 87
-71 71 57 16 16 16 16 16 16 17 17 17 17 17 17 17 17 17
-17 17 17 17 17 17 17 17 17 18 19 18 19 20 19 20 20 20
-21 22 21 21 22 21 21 22 21 22 24 23 21 22 21 21 22 21
-21 22 21 19 20 19 18 19 18 16 16 16 14 14 13 11 11 11
-3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 105 104 92 197 193 154 110 109 94 9 9 8 36 38 35
-121 119 87 131 127 93 96 95 69 18 19 17 30 31 28 66 65 55
-96 95 69 142 137 94 160 154 106 160 154 106 160 154 106 168 163 120
-184 181 136 194 191 148 197 193 154 197 193 154 194 189 146 168 163 120
-125 122 87 46 47 43 18 19 18 15 15 15 13 13 13 14 14 13
-55 57 54 43 45 43 32 34 33 25 27 26 18 22 22 17 17 17
-12 14 14 10 12 12 9 11 11 8 10 10 8 9 9 7 9 9
-6 8 8 7 9 9 7 9 9 7 9 9 7 9 9 7 9 9
-7 9 9 8 9 9 8 9 9 8 10 10 8 10 10 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 32 34 33 41 42 42 35 37 36 39 40 39 37 39 37
-35 37 36 55 57 54 60 60 56 63 64 60 65 66 61 65 66 61
-61 63 57 115 113 82 168 163 120 194 191 148 204 201 155 210 208 158
-210 208 158 210 208 158 197 193 154 194 189 146 186 182 128 176 171 126
-174 170 121 176 171 126 186 182 128 190 186 136 194 191 148 197 193 154
-197 193 154 188 184 146 181 176 137 174 170 121 165 161 109 142 137 94
-82 81 62 24 26 24 16 16 16 16 16 16 16 16 16 16 16 16
-17 17 17 17 17 17 17 17 17 17 17 17 18 19 18 19 20 19
-19 20 19 19 20 19 20 20 20 19 20 19 19 20 19 18 19 18
-17 17 17 15 15 15 13 13 13 12 12 12 6 7 7 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 17 18 17 137 133 100 115 113 82 53 55 47 19 20 18
-103 101 77 144 139 99 137 133 100 115 113 82 137 133 100 156 151 111
-158 153 112 164 159 111 171 165 117 174 170 121 178 174 128 194 189 146
-204 201 155 214 212 158 214 212 158 214 212 158 210 208 158 188 184 146
-158 153 112 87 86 72 17 17 17 13 13 13 13 13 13 15 15 15
-55 56 53 43 45 43 32 34 33 24 26 24 17 20 20 16 16 16
-12 14 14 10 12 12 8 10 10 8 10 10 7 9 9 6 8 8
-6 8 8 6 8 8 6 8 8 7 9 9 7 9 9 7 9 9
-7 9 9 7 9 9 7 9 9 7 9 9 8 9 9 8 10 10
-8 10 10 8 10 10 8 10 10 8 10 10 8 10 10 8 10 10
-8 10 10 110 109 94 84 83 72 49 51 48 26 28 27 8 10 10
-8 9 9 51 52 50 58 59 55 63 64 60 63 64 60 63 64 60
-66 65 55 134 131 96 181 176 137 210 208 158 214 212 158 239 239 170
-239 239 170 224 223 159 210 208 158 204 201 155 194 189 146 186 182 128
-186 182 128 184 181 136 194 189 146 204 201 155 210 208 158 210 208 158
-210 208 158 210 208 158 197 193 154 190 186 136 176 171 126 155 149 109
-118 116 76 36 38 35 15 15 15 16 16 16 16 16 16 16 16 16
-16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 17 17 17
-17 17 17 17 17 17 17 17 17 16 16 16 16 16 16 15 15 15
-13 13 13 12 12 12 8 8 7 2 2 2 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 53 55 47 103 101 77 96 95 69 53 55 47
-103 101 77 158 153 112 177 172 135 184 179 149 188 184 146 197 193 154
-194 189 146 190 186 136 184 181 136 184 181 136 194 189 146 210 208 158
-214 212 158 239 239 170 251 251 187 251 251 187 224 223 159 204 201 155
-177 172 135 121 119 87 30 31 28 13 13 13 12 12 12 39 40 39
-60 60 56 43 45 43 32 34 33 23 25 24 18 19 18 13 16 16
-13 13 13 9 11 11 8 10 10 8 9 9 6 8 8 6 8 8
-6 8 8 6 8 8 6 8 8 6 8 8 6 8 8 7 9 9
-7 9 9 7 9 9 7 9 9 7 9 9 7 9 9 7 9 9
-7 9 9 8 9 9 8 9 9 8 10 10 8 10 10 8 10 10
-14 17 17 197 193 154 158 153 112 55 57 54 7 9 9 7 9 9
-8 10 10 51 52 50 58 59 55 60 60 56 63 64 60 63 64 60
-71 71 57 155 149 109 194 191 148 214 212 158 251 251 187 251 251 187
-251 251 187 251 251 187 239 239 170 210 208 158 197 193 154 190 186 136
-190 186 136 194 189 146 204 201 155 210 208 158 224 223 159 239 239 170
-239 239 170 224 223 159 210 208 158 204 201 155 190 186 136 164 159 111
-125 122 87 40 41 39 15 15 15 15 15 15 15 15 15 15 15 15
-16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16
-16 16 16 16 16 16 15 15 15 14 14 13 13 13 13 12 12 12
-8 9 9 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 21 22 20 96 95 69 125 122 87 121 119 87
-144 139 99 177 172 135 197 193 154 210 208 158 214 212 158 214 212 158
-210 208 158 204 201 155 194 191 148 194 189 146 204 201 155 214 212 158
-239 239 170 251 251 187 251 251 187 251 251 187 251 251 187 214 212 158
-188 184 146 145 141 105 53 55 47 12 12 12 15 15 15 63 64 60
-63 64 60 41 42 42 31 33 31 23 24 24 17 18 17 12 15 15
-11 13 13 9 11 11 8 9 9 7 9 9 6 8 8 6 8 8
-6 7 7 6 7 7 6 8 8 6 8 8 6 8 8 6 8 8
-6 8 8 7 9 9 7 9 9 7 9 9 7 9 9 7 9 9
-7 9 9 7 9 9 7 9 9 7 9 9 7 9 9 8 8 7
-43 45 43 251 251 187 156 151 111 8 10 10 7 9 9 7 9 9
-21 22 21 51 52 50 55 56 53 55 57 54 58 59 55 58 59 55
-75 75 61 158 153 112 197 193 154 224 223 159 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 214 212 158 204 201 155 194 189 146
-190 186 136 197 193 154 210 208 158 224 223 159 251 251 187 251 251 187
-251 251 187 251 251 187 239 239 170 210 208 158 197 193 154 176 171 126
-125 122 87 36 38 35 14 14 13 14 14 13 15 15 15 15 15 15
-15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15
-15 15 15 14 14 13 13 13 13 12 12 12 10 10 9 3 4 4
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 7 7 5 71 71 57 131 127 93 158 153 112
-177 172 135 197 193 154 214 212 158 239 239 170 251 251 187 251 251 187
-238 237 168 210 208 158 204 201 155 197 193 154 204 201 155 214 212 158
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 214 212 158
-197 193 154 156 151 111 66 65 55 12 12 12 37 39 37 58 59 55
-58 59 55 41 42 42 31 33 31 22 24 23 17 17 17 12 14 14
-10 12 12 8 10 10 6 8 8 6 8 8 6 7 7 6 7 7
-6 7 7 5 7 7 6 7 7 6 7 7 6 8 8 6 8 8
-6 8 8 6 8 8 6 8 8 7 9 9 7 9 9 7 9 9
-7 9 9 6 8 8 6 8 8 6 8 8 6 8 8 6 8 8
-61 63 57 197 193 154 16 19 19 6 8 8 6 8 8 8 9 9
-41 42 42 47 48 46 51 52 50 51 52 50 55 56 53 55 56 53
-71 71 57 158 153 112 197 193 154 224 223 159 251 251 187 251 251 187
-251 251 187 251 251 187 239 239 170 214 212 158 204 201 155 194 189 146
-190 186 136 197 193 154 210 208 158 239 239 170 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 224 223 159 204 201 155 177 172 135
-121 119 87 30 31 28 13 13 13 14 14 13 14 14 13 14 14 13
-14 14 13 14 14 13 15 15 15 15 15 15 14 14 13 13 13 13
-12 12 12 12 12 12 10 10 9 4 5 5 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 48 49 45 131 127 93 174 170 121
-194 189 146 210 208 158 239 239 170 251 251 187 251 251 187 251 251 187
-251 251 187 214 212 158 204 201 155 197 193 154 204 201 155 210 208 158
-239 239 170 251 251 187 251 251 187 251 251 187 239 239 170 214 212 158
-194 191 148 156 151 111 71 71 57 19 20 19 51 52 50 51 52 50
-51 52 50 41 42 42 30 32 31 21 22 21 17 17 17 13 13 13
-9 11 11 8 9 9 6 8 8 6 7 7 6 7 7 5 7 7
-5 6 5 5 6 5 5 7 7 5 7 7 6 7 7 6 7 7
-6 8 8 6 8 8 6 8 8 6 7 7 6 7 7 6 7 7
-6 7 7 6 8 8 6 8 8 6 8 8 6 8 8 6 8 8
-55 56 53 43 45 43 6 8 8 6 8 8 6 8 8 47 48 46
-60 60 56 47 48 46 46 47 45 47 48 46 38 39 37 10 12 12
-66 65 55 145 141 105 197 193 154 214 212 158 251 251 187 251 251 187
-251 251 187 251 251 187 224 223 159 210 208 158 194 191 148 184 181 136
-184 181 136 194 189 146 204 201 155 224 223 159 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 239 239 170 210 208 158 181 176 137
-115 113 82 21 22 20 13 13 13 13 13 13 13 13 13 13 13 13
-14 14 13 13 13 13 13 13 13 13 13 13 12 12 12 11 11 11
-10 10 9 6 7 7 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 2 2 1 66 65 55 144 139 99 178 174 128
-204 201 155 214 212 158 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 214 212 158 204 201 155 194 191 148 197 193 154 204 201 155
-214 212 158 239 239 170 239 239 170 239 239 170 214 212 158 210 208 158
-184 181 136 149 145 103 66 65 55 41 42 42 47 48 46 46 47 45
-43 45 43 39 40 39 28 31 30 21 22 21 16 16 16 10 12 12
-8 10 10 6 8 8 6 7 7 6 7 7 5 6 5 5 6 5
-5 6 5 5 6 5 5 6 5 5 6 5 5 7 7 5 7 7
-6 7 7 6 7 7 6 7 7 5 7 7 5 7 7 5 7 7
-5 7 7 6 7 7 6 7 7 6 7 7 6 7 7 6 8 8
-6 8 8 6 8 8 6 7 7 6 7 7 46 47 45 156 151 111
-105 104 92 58 59 55 43 45 43 32 34 33 6 8 8 6 8 8
-49 51 48 125 122 87 181 176 137 204 201 155 214 212 158 239 239 170
-239 239 170 214 212 158 210 208 158 197 193 154 181 176 137 176 171 126
-176 171 126 184 181 136 197 193 154 210 208 158 239 239 170 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 210 208 158 177 172 135
-99 98 80 13 13 13 12 12 12 12 12 12 13 13 13 12 12 12
-12 12 12 12 12 12 11 11 11 11 11 11 8 9 9 4 5 5
-1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 1 1 0 61 61 53 142 137 94 181 176 137
-204 201 155 224 223 159 251 251 187 251 251 187 251 251 187 251 251 187
-251 251 187 214 212 158 197 193 154 190 186 136 184 181 136 188 184 146
-197 193 154 204 201 155 210 208 158 210 208 158 204 201 155 194 189 146
-176 171 126 134 131 96 66 65 55 43 45 43 41 42 42 39 40 39
-35 37 36 33 36 34 27 29 28 20 20 20 15 15 15 9 11 11
-8 9 9 6 7 7 5 6 5 5 6 5 4 5 5 4 5 5
-4 5 5 4 5 5 4 5 5 4 5 5 5 6 5 4 5 5
-4 5 5 5 6 5 4 5 5 5 6 5 5 6 5 5 6 5
-5 7 7 5 7 7 5 7 7 5 7 7 5 7 7 5 7 7
-6 7 7 6 7 7 6 7 7 28 31 30 184 179 149 184 179 149
-145 141 105 84 83 72 27 29 28 5 7 7 5 6 5 16 16 16
-43 44 41 96 95 69 158 153 112 188 184 146 204 201 155 210 208 158
-204 201 155 197 193 154 184 179 149 177 172 135 168 163 120 164 159 111
-164 159 111 174 170 121 184 181 136 197 193 154 214 212 158 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 210 208 158 177 172 135
-71 71 57 11 11 11 12 12 12 11 11 11 11 11 11 11 11 11
-10 10 9 10 10 9 8 8 7 3 4 4 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 31 33 31 121 119 87 176 171 126
-197 193 154 214 212 158 251 251 187 251 251 187 251 251 187 251 251 187
-239 239 170 210 208 158 194 189 146 178 174 128 174 170 121 176 171 126
-177 172 135 181 176 137 184 179 149 184 179 149 181 176 137 178 174 128
-158 153 112 121 119 87 53 55 47 37 39 37 33 36 34 30 32 31
-27 29 28 25 27 26 24 26 24 19 20 19 13 13 13 8 10 10
-6 8 8 6 7 7 5 6 5 4 5 5 4 5 5 4 5 5
-4 5 5 4 5 5 4 5 5 3 4 4 3 4 4 4 5 5
-4 5 5 4 5 5 4 5 5 4 5 5 4 5 5 4 5 5
-5 6 5 5 6 5 5 6 5 5 6 5 5 6 5 5 6 5
-5 6 5 5 6 5 12 14 14 145 141 105 184 179 149 177 172 135
-90 89 73 21 22 21 5 6 5 5 6 5 4 5 5 37 39 37
-38 39 37 61 61 53 134 131 96 168 163 120 184 181 136 188 184 146
-184 179 149 177 172 135 168 163 120 164 159 111 155 149 109 151 147 98
-151 147 98 164 159 111 176 171 126 184 179 149 210 208 158 239 239 170
-251 251 187 251 251 187 251 251 187 239 239 170 210 208 158 158 153 112
-46 47 43 10 10 9 10 10 9 10 10 9 8 9 9 8 9 9
-6 7 7 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 13 12 7 82 81 62 158 153 112
-188 184 146 210 208 158 239 239 170 251 251 187 251 251 187 251 251 187
-224 223 159 204 201 155 184 181 136 171 165 117 164 159 111 160 154 106
-158 153 112 164 159 111 168 163 120 168 163 120 168 163 120 164 159 111
-142 137 94 96 95 69 43 44 41 27 29 28 26 28 27 23 24 24
-21 22 21 18 19 18 17 17 17 18 19 18 13 13 13 8 8 7
-6 7 7 5 6 5 4 5 5 3 4 4 3 4 4 3 4 4
-3 4 4 3 4 4 3 3 3 3 3 3 3 4 4 3 4 4
-3 4 4 3 4 4 4 5 5 4 5 5 4 5 5 4 5 5
-4 5 5 4 5 5 4 5 5 4 5 5 4 5 5 4 5 5
-4 5 5 4 5 5 4 5 5 4 5 5 4 5 5 4 5 5
-4 5 5 4 5 5 4 5 5 4 5 5 31 33 31 65 66 61
-37 39 37 38 39 37 96 95 69 144 139 99 168 163 120 174 170 121
-168 163 120 164 159 111 155 149 109 149 145 103 149 143 98 142 137 94
-149 143 98 151 147 98 164 159 111 177 172 135 197 193 154 210 208 158
-251 251 187 251 251 187 251 251 187 239 239 170 197 193 154 137 133 100
-24 26 24 8 9 9 8 9 9 8 8 7 6 7 7 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 46 47 43 125 122 87
-176 171 126 197 193 154 210 208 158 239 239 170 251 251 187 239 239 170
-214 212 158 197 193 154 181 176 137 164 159 111 151 147 98 149 143 98
-149 143 98 149 143 98 149 145 103 155 149 109 160 154 106 149 143 98
-118 116 76 82 81 62 30 31 28 21 22 21 19 20 19 17 17 17
-14 14 13 12 12 12 10 10 9 12 12 12 10 12 12 6 8 8
-4 5 5 3 4 4 3 4 4 3 4 4 3 3 3 3 3 3
-3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 2 3 3
-3 4 4 3 4 4 3 4 4 3 4 4 3 4 4 4 5 5
-4 5 5 3 4 4 3 4 4 3 4 4 3 4 4 3 4 4
-4 5 5 4 5 5 4 5 5 4 5 5 4 5 5 4 5 5
-4 5 5 3 4 4 3 4 4 23 24 24 110 109 94 72 73 67
-39 40 39 22 24 23 46 47 43 103 101 77 142 137 94 155 149 109
-160 154 106 155 149 109 149 143 98 142 137 94 142 137 94 142 137 94
-142 137 94 149 143 98 155 149 109 176 171 126 184 179 149 210 208 158
-239 239 170 251 251 187 251 251 187 214 212 158 184 179 149 105 104 92
-10 10 9 6 7 7 3 4 4 1 1 1 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 12 12 9 82 81 62
-149 145 103 181 176 137 197 193 154 210 208 158 214 212 158 214 212 158
-210 208 158 197 193 154 177 172 135 158 153 112 149 143 98 142 137 94
-142 137 94 142 137 94 149 143 98 151 147 98 151 147 98 131 127 93
-103 101 77 71 71 57 22 24 23 15 15 15 13 13 13 11 11 11
-8 9 9 6 7 7 6 7 7 4 5 5 8 9 9 6 7 7
-4 5 5 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3
-2 3 3 2 3 3 2 3 3 3 4 4 3 4 4 3 4 4
-3 4 4 3 4 4 3 3 3 3 4 4 3 4 4 3 4 4
-3 4 4 3 4 4 3 4 4 3 4 4 3 4 4 3 4 4
-3 4 4 3 4 4 21 22 21 145 141 105 145 141 105 72 73 67
-17 18 17 3 4 4 21 22 20 66 65 55 118 116 76 142 137 94
-149 143 98 151 147 98 149 143 98 142 137 94 142 137 94 142 137 94
-142 137 94 149 143 98 155 149 109 168 163 120 184 179 149 210 208 158
-239 239 170 251 251 187 251 251 187 210 208 158 177 172 135 71 71 57
-3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 38 35
-115 113 82 158 153 112 181 176 137 197 193 154 204 201 155 210 208 158
-204 201 155 188 184 146 177 172 135 164 159 111 149 145 103 142 137 94
-142 137 94 142 137 94 149 143 98 151 147 98 149 143 98 125 122 87
-96 95 69 61 61 53 16 17 12 8 9 9 8 8 7 6 7 7
-4 5 5 3 4 4 3 3 3 3 3 3 3 3 3 5 6 5
-3 4 4 2 3 3 2 2 2 2 2 2 2 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2
-2 2 2 2 2 2 2 3 3 2 3 3 2 3 3 2 3 3
-3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
-3 3 3 2 3 3 2 3 3 3 4 4 3 4 4 3 4 4
-3 4 4 3 4 4 3 4 4 8 9 9 8 8 7 3 3 3
-3 3 3 3 3 3 9 9 8 36 38 35 82 81 62 118 116 76
-142 137 94 151 147 98 151 147 98 151 147 98 149 143 98 149 143 98
-149 143 98 151 147 98 160 154 106 176 171 126 188 184 146 210 208 158
-239 239 170 251 251 187 239 239 170 210 208 158 156 151 111 31 33 31
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 7 5
-66 65 55 125 122 87 158 153 112 181 176 137 194 189 146 197 193 154
-197 193 154 184 179 149 177 172 135 168 163 120 156 151 111 151 147 98
-151 147 98 151 147 98 151 147 98 161 156 96 149 143 98 118 116 76
-82 81 62 53 55 47 12 12 9 4 5 5 3 4 4 3 3 3
-3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 2 2
-3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 2
-1 2 2 1 2 2 1 2 2 2 2 2 2 2 2 2 3 3
-2 3 3 2 3 3 2 3 3 2 3 3 2 2 2 2 2 2
-2 3 3 2 3 3 2 3 3 2 3 3 2 3 3 2 3 3
-2 3 3 2 3 3 2 3 3 2 3 3 2 3 3 3 3 3
-3 3 3 3 3 3 72 73 67 61 61 53 53 55 47 96 95 69
-131 127 93 151 147 98 161 156 96 161 156 96 151 147 98 151 147 98
-161 156 96 160 154 106 164 159 111 177 172 135 197 193 154 210 208 158
-239 239 170 251 251 187 224 223 159 197 193 154 131 127 93 9 9 8
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-24 26 24 82 81 62 131 127 93 164 159 111 178 174 128 188 184 146
-188 184 146 188 184 146 181 176 137 176 171 126 168 163 120 164 159 111
-160 154 106 160 154 106 160 154 106 160 154 106 151 147 98 125 122 87
-82 81 62 61 61 53 12 12 9 3 3 3 3 3 3 2 2 2
-2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0
-0 0 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 2 2 1 2 2 1 2 2 1 2 2
-1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-2 3 3 2 3 3 2 3 3 2 3 3 2 3 3 2 3 3
-2 3 3 30 32 31 72 73 67 31 33 31 36 38 35 82 81 62
-118 116 76 149 143 98 161 156 96 161 156 96 161 156 96 160 154 106
-165 161 109 165 161 109 176 171 126 188 184 146 204 201 155 214 212 158
-239 239 170 239 239 170 214 212 158 184 179 149 82 81 62 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-2 2 2 43 44 41 96 95 69 131 127 93 160 154 106 176 171 126
-184 181 136 184 181 136 184 181 136 181 176 137 178 174 128 174 170 121
-171 165 117 173 167 111 173 167 111 173 167 111 160 154 106 131 127 93
-96 95 69 66 65 55 16 17 12 2 2 2 1 1 1 1 1 1
-1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 2 2 1 2 2 1 2 2 1 2 2 1 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 10 9 6 30 31 28 71 71 57
-118 116 76 149 143 98 165 161 109 165 161 109 165 161 109 173 167 111
-173 167 111 176 171 126 184 181 136 197 193 154 210 208 158 224 223 159
-251 251 187 239 239 170 210 208 158 168 163 120 40 41 39 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 13 12 7 61 61 53 96 95 69 131 127 93 160 154 106
-176 171 126 184 181 136 184 181 136 188 184 146 184 181 136 184 181 136
-184 181 136 186 182 128 186 182 128 178 174 128 174 170 121 149 145 103
-118 116 76 82 81 62 21 22 20 1 1 1 1 1 1 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
-1 2 2 1 2 2 1 2 2 1 2 2 1 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-2 2 2 2 2 2 2 2 2 3 3 3 30 31 28 66 65 55
-118 116 76 149 143 98 165 161 109 173 167 111 173 167 111 174 170 121
-186 182 128 190 186 136 197 193 154 210 208 158 224 223 159 251 251 187
-251 251 187 239 239 170 197 193 154 137 133 100 12 12 9 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 30 31 28 71 71 57 103 101 77 134 131 96
-164 159 111 176 171 126 184 181 136 188 184 146 194 189 146 197 193 154
-197 193 154 197 193 154 194 191 148 194 189 146 190 186 136 176 171 126
-145 141 105 103 101 77 40 41 39 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 2
-1 2 2 1 2 2 1 2 2 1 2 2 1 2 2 1 2 2
-1 2 2 1 2 2 1 2 2 1 2 2 30 31 28 71 71 57
-118 116 76 160 154 106 173 167 111 178 174 128 186 182 128 190 186 136
-194 191 148 204 201 155 210 208 158 224 223 159 251 251 187 251 251 187
-251 251 187 214 212 158 184 179 149 84 83 72 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 5 5 3 43 44 41 82 81 62 103 101 77
-142 137 94 165 161 109 178 174 128 190 186 136 197 193 154 204 201 155
-210 208 158 210 208 158 210 208 158 210 208 158 210 208 158 197 193 154
-177 172 135 145 141 105 79 78 62 5 4 3 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 2 2 1 2 2 30 31 28 82 81 62
-142 137 94 165 161 109 178 174 128 190 186 136 194 191 148 204 201 155
-210 208 158 214 212 158 239 239 170 251 251 187 251 251 187 251 251 187
-251 251 187 210 208 158 168 163 120 36 38 35 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 16 17 12 53 55 47 82 81 62
-118 116 76 151 147 98 171 165 117 184 181 136 194 191 148 210 208 158
-214 212 158 224 223 159 239 239 170 239 239 170 224 223 159 214 212 158
-197 193 154 176 171 126 115 113 82 24 26 24 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 40 41 39 103 101 77
-151 147 98 176 171 126 190 186 136 197 193 154 210 208 158 214 212 158
-239 239 170 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-239 239 170 197 193 154 110 109 94 3 4 3 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 30 31 28 66 65 55
-96 95 69 125 122 87 160 154 106 178 174 128 194 189 146 204 201 155
-214 212 158 239 239 170 251 251 187 251 251 187 251 251 187 239 239 170
-210 208 158 188 184 146 149 145 103 61 61 53 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 61 61 53 131 127 93
-164 159 111 184 181 136 197 193 154 210 208 158 224 223 159 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 251 251 187
-210 208 158 168 163 120 43 44 41 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 4 3 2 36 38 35
-71 71 57 96 95 69 142 137 94 165 161 109 184 181 136 197 193 154
-210 208 158 239 239 170 251 251 187 251 251 187 251 251 187 251 251 187
-214 212 158 197 193 154 168 163 120 103 101 77 7 7 5 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-1 1 1 0 0 0 0 0 0 0 0 0 82 81 62 142 137 94
-174 170 121 194 189 146 210 208 158 224 223 159 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 251 251 187 224 223 159
-184 179 149 99 98 80 3 3 3 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 7 5
-43 44 41 82 81 62 118 116 76 142 137 94 171 165 117 190 186 136
-204 201 155 224 223 159 251 251 187 251 251 187 251 251 187 251 251 187
-214 212 158 197 193 154 174 170 121 125 122 87 30 31 28 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
-1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 3 4 3 82 81 62 149 143 98
-176 171 126 194 191 148 210 208 158 239 239 170 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 251 251 187 239 239 170 204 201 155
-145 141 105 30 31 28 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 9 6 46 47 43 82 81 62 118 116 76 149 143 98 174 170 121
-194 189 146 210 208 158 224 223 159 251 251 187 251 251 187 224 223 159
-210 208 158 194 191 148 174 170 121 134 131 96 53 55 47 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 5 96 95 69 149 143 98
-176 171 126 194 191 148 210 208 158 239 239 170 251 251 187 251 251 187
-251 251 187 251 251 187 251 251 187 239 239 170 210 208 158 177 172 135
-75 75 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 10 9 6 46 47 43 82 81 62 118 116 76 149 143 98
-176 171 126 194 191 148 210 208 158 214 212 158 214 212 158 210 208 158
-197 193 154 184 181 136 164 159 111 131 127 93 53 55 47 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 5 96 95 69 149 143 98
-174 170 121 194 189 146 204 201 155 214 212 158 239 239 170 251 251 187
-251 251 187 251 251 187 239 239 170 210 208 158 184 179 149 110 109 94
-12 12 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 10 9 6 43 44 41 82 81 62 115 113 82
-144 139 99 168 163 120 188 184 146 197 193 154 197 193 154 194 189 146
-184 181 136 174 170 121 151 147 98 118 116 76 36 38 35 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 4 3 2 82 81 62 142 137 94
-171 165 117 186 182 128 194 191 148 210 208 158 214 212 158 224 223 159
-239 239 170 224 223 159 210 208 158 184 179 149 137 133 100 36 38 35
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 5 36 38 35 71 71 57
-103 101 77 131 127 93 155 149 109 168 163 120 168 163 120 168 163 120
-164 159 111 149 143 98 125 122 87 82 81 62 13 12 7 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 61 61 53 125 122 87
-160 154 106 174 170 121 184 181 136 194 189 146 204 201 155 210 208 158
-210 208 158 204 201 155 184 179 149 145 141 105 61 61 53 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 3 3 2 30 31 28
-61 61 53 82 81 62 103 101 77 121 119 87 125 122 87 125 122 87
-118 116 76 103 101 77 79 78 62 24 26 24 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 25 27 25 96 95 69
-142 137 94 160 154 106 171 165 117 178 174 128 184 181 136 184 181 136
-181 176 137 177 172 135 145 141 105 75 75 61 5 5 3 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-16 17 12 40 41 39 61 61 53 71 71 57 71 71 57 71 71 57
-66 65 55 43 44 41 12 12 9 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 46 47 43
-96 95 69 125 122 87 142 137 94 149 145 103 155 149 109 155 149 109
-145 141 105 121 119 87 66 65 55 7 7 5 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 16 17 12 24 26 24 25 27 25 19 20 18
-7 7 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 1
-25 27 25 61 61 53 82 81 62 96 95 69 96 95 69 82 81 62
-61 61 53 25 27 25 2 2 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 5 6 5 13 12 7 10 9 6 3 4 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 6 6 6 10 10 10 10 10 10
+ 10 10 10 6 6 6 6 6 6 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 10 10 10 14 14 14
+ 22 22 22 26 26 26 30 30 30 34 34 34
+ 30 30 30 30 30 30 26 26 26 18 18 18
+ 14 14 14 10 10 10 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 14 14 14 26 26 26 42 42 42
+ 54 54 54 66 66 66 78 78 78 78 78 78
+ 78 78 78 74 74 74 66 66 66 54 54 54
+ 42 42 42 26 26 26 18 18 18 10 10 10
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 22 22 22 42 42 42 66 66 66 86 86 86
+ 66 66 66 38 38 38 38 38 38 22 22 22
+ 26 26 26 34 34 34 54 54 54 66 66 66
+ 86 86 86 70 70 70 46 46 46 26 26 26
+ 14 14 14 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 10 10 10 26 26 26
+ 50 50 50 82 82 82 58 58 58 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 6 6 6 54 54 54 86 86 86 66 66 66
+ 38 38 38 18 18 18 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 22 22 22 50 50 50
+ 78 78 78 34 34 34 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 6 6 6 70 70 70
+ 78 78 78 46 46 46 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 18 18 18 42 42 42 82 82 82
+ 26 26 26 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 14 14 14
+ 46 46 46 34 34 34 6 6 6 2 2 6
+ 42 42 42 78 78 78 42 42 42 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 0 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 10 10 10 30 30 30 66 66 66 58 58 58
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 26 26 26
+ 86 86 86 101 101 101 46 46 46 10 10 10
+ 2 2 6 58 58 58 70 70 70 34 34 34
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 14 14 14 42 42 42 86 86 86 10 10 10
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 30 30 30
+ 94 94 94 94 94 94 58 58 58 26 26 26
+ 2 2 6 6 6 6 78 78 78 54 54 54
+ 22 22 22 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 22 22 22 62 62 62 62 62 62 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 26 26 26
+ 54 54 54 38 38 38 18 18 18 10 10 10
+ 2 2 6 2 2 6 34 34 34 82 82 82
+ 38 38 38 14 14 14 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 30 30 30 78 78 78 30 30 30 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 10 10 10
+ 10 10 10 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 78 78 78
+ 50 50 50 18 18 18 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 38 38 38 86 86 86 14 14 14 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 54 54 54
+ 66 66 66 26 26 26 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 42 42 42 82 82 82 2 2 6 2 2 6
+ 2 2 6 6 6 6 10 10 10 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 6 6 6
+ 14 14 14 10 10 10 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 18 18 18
+ 82 82 82 34 34 34 10 10 10 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 46 46 46 86 86 86 2 2 6 2 2 6
+ 6 6 6 6 6 6 22 22 22 34 34 34
+ 6 6 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 18 18 18 34 34 34
+ 10 10 10 50 50 50 22 22 22 2 2 6
+ 2 2 6 2 2 6 2 2 6 10 10 10
+ 86 86 86 42 42 42 14 14 14 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 46 46 46 86 86 86 2 2 6 2 2 6
+ 38 38 38 116 116 116 94 94 94 22 22 22
+ 22 22 22 2 2 6 2 2 6 2 2 6
+ 14 14 14 86 86 86 138 138 138 162 162 162
+154 154 154 38 38 38 26 26 26 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 86 86 86 46 46 46 14 14 14 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 46 46 46 86 86 86 2 2 6 14 14 14
+134 134 134 198 198 198 195 195 195 116 116 116
+ 10 10 10 2 2 6 2 2 6 6 6 6
+101 98 89 187 187 187 210 210 210 218 218 218
+214 214 214 134 134 134 14 14 14 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 86 86 86 50 50 50 18 18 18 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 1 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 46 46 46 86 86 86 2 2 6 54 54 54
+218 218 218 195 195 195 226 226 226 246 246 246
+ 58 58 58 2 2 6 2 2 6 30 30 30
+210 210 210 253 253 253 174 174 174 123 123 123
+221 221 221 234 234 234 74 74 74 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 70 70 70 58 58 58 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 46 46 46 82 82 82 2 2 6 106 106 106
+170 170 170 26 26 26 86 86 86 226 226 226
+123 123 123 10 10 10 14 14 14 46 46 46
+231 231 231 190 190 190 6 6 6 70 70 70
+ 90 90 90 238 238 238 158 158 158 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 70 70 70 58 58 58 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 1 0 0 0
+ 0 0 1 0 0 1 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 42 42 42 86 86 86 6 6 6 116 116 116
+106 106 106 6 6 6 70 70 70 149 149 149
+128 128 128 18 18 18 38 38 38 54 54 54
+221 221 221 106 106 106 2 2 6 14 14 14
+ 46 46 46 190 190 190 198 198 198 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 74 74 74 62 62 62 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 1 0 0 0
+ 0 0 1 0 0 0 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 42 42 42 94 94 94 14 14 14 101 101 101
+128 128 128 2 2 6 18 18 18 116 116 116
+118 98 46 121 92 8 121 92 8 98 78 10
+162 162 162 106 106 106 2 2 6 2 2 6
+ 2 2 6 195 195 195 195 195 195 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 74 74 74 62 62 62 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 1 0 0 1
+ 0 0 1 0 0 0 0 0 1 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 38 38 38 90 90 90 14 14 14 58 58 58
+210 210 210 26 26 26 54 38 6 154 114 10
+226 170 11 236 186 11 225 175 15 184 144 12
+215 174 15 175 146 61 37 26 9 2 2 6
+ 70 70 70 246 246 246 138 138 138 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 70 70 70 66 66 66 26 26 26 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 38 38 38 86 86 86 14 14 14 10 10 10
+195 195 195 188 164 115 192 133 9 225 175 15
+239 182 13 234 190 10 232 195 16 232 200 30
+245 207 45 241 208 19 232 195 16 184 144 12
+218 194 134 211 206 186 42 42 42 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 50 50 50 74 74 74 30 30 30 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 34 34 34 86 86 86 14 14 14 2 2 6
+121 87 25 192 133 9 219 162 10 239 182 13
+236 186 11 232 195 16 241 208 19 244 214 54
+246 218 60 246 218 38 246 215 20 241 208 19
+241 208 19 226 184 13 121 87 25 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 50 50 50 82 82 82 34 34 34 10 10 10
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 34 34 34 82 82 82 30 30 30 61 42 6
+180 123 7 206 145 10 230 174 11 239 182 13
+234 190 10 238 202 15 241 208 19 246 218 74
+246 218 38 246 215 20 246 215 20 246 215 20
+226 184 13 215 174 15 184 144 12 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 26 26 26 94 94 94 42 42 42 14 14 14
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 78 78 78 50 50 50 104 69 6
+192 133 9 216 158 10 236 178 12 236 186 11
+232 195 16 241 208 19 244 214 54 245 215 43
+246 215 20 246 215 20 241 208 19 198 155 10
+200 144 11 216 158 10 156 118 10 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 6 6 6 90 90 90 54 54 54 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 78 78 78 46 46 46 22 22 22
+137 92 6 210 162 10 239 182 13 238 190 10
+238 202 15 241 208 19 246 215 20 246 215 20
+241 208 19 203 166 17 185 133 11 210 150 10
+216 158 10 210 150 10 102 78 10 2 2 6
+ 6 6 6 54 54 54 14 14 14 2 2 6
+ 2 2 6 62 62 62 74 74 74 30 30 30
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 34 34 34 78 78 78 50 50 50 6 6 6
+ 94 70 30 139 102 15 190 146 13 226 184 13
+232 200 30 232 195 16 215 174 15 190 146 13
+168 122 10 192 133 9 210 150 10 213 154 11
+202 150 34 182 157 106 101 98 89 2 2 6
+ 2 2 6 78 78 78 116 116 116 58 58 58
+ 2 2 6 22 22 22 90 90 90 46 46 46
+ 18 18 18 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 38 38 38 86 86 86 50 50 50 6 6 6
+128 128 128 174 154 114 156 107 11 168 122 10
+198 155 10 184 144 12 197 138 11 200 144 11
+206 145 10 206 145 10 197 138 11 188 164 115
+195 195 195 198 198 198 174 174 174 14 14 14
+ 2 2 6 22 22 22 116 116 116 116 116 116
+ 22 22 22 2 2 6 74 74 74 70 70 70
+ 30 30 30 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 50 50 50 101 101 101 26 26 26 10 10 10
+138 138 138 190 190 190 174 154 114 156 107 11
+197 138 11 200 144 11 197 138 11 192 133 9
+180 123 7 190 142 34 190 178 144 187 187 187
+202 202 202 221 221 221 214 214 214 66 66 66
+ 2 2 6 2 2 6 50 50 50 62 62 62
+ 6 6 6 2 2 6 10 10 10 90 90 90
+ 50 50 50 18 18 18 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 10 10 10 34 34 34
+ 74 74 74 74 74 74 2 2 6 6 6 6
+144 144 144 198 198 198 190 190 190 178 166 146
+154 121 60 156 107 11 156 107 11 168 124 44
+174 154 114 187 187 187 190 190 190 210 210 210
+246 246 246 253 253 253 253 253 253 182 182 182
+ 6 6 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 62 62 62
+ 74 74 74 34 34 34 14 14 14 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 10 10 10 22 22 22 54 54 54
+ 94 94 94 18 18 18 2 2 6 46 46 46
+234 234 234 221 221 221 190 190 190 190 190 190
+190 190 190 187 187 187 187 187 187 190 190 190
+190 190 190 195 195 195 214 214 214 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+ 82 82 82 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 14 14 14
+ 86 86 86 54 54 54 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 18 18 18 46 46 46 90 90 90
+ 46 46 46 18 18 18 6 6 6 182 182 182
+253 253 253 246 246 246 206 206 206 190 190 190
+190 190 190 190 190 190 190 190 190 190 190 190
+206 206 206 231 231 231 250 250 250 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+202 202 202 14 14 14 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 42 42 42 86 86 86 42 42 42 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 14 14 14 38 38 38 74 74 74 66 66 66
+ 2 2 6 6 6 6 90 90 90 250 250 250
+253 253 253 253 253 253 238 238 238 198 198 198
+190 190 190 190 190 190 195 195 195 221 221 221
+246 246 246 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 82 82 82 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 78 78 78 70 70 70 34 34 34
+ 14 14 14 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 34 34 34 66 66 66 78 78 78 6 6 6
+ 2 2 6 18 18 18 218 218 218 253 253 253
+253 253 253 253 253 253 253 253 253 246 246 246
+226 226 226 231 231 231 246 246 246 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 178 178 178 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 18 18 18 90 90 90 62 62 62
+ 30 30 30 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 10 10 10 26 26 26
+ 58 58 58 90 90 90 18 18 18 2 2 6
+ 2 2 6 110 110 110 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+250 250 250 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 231 231 231 18 18 18 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 18 18 18 94 94 94
+ 54 54 54 26 26 26 10 10 10 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 22 22 22 50 50 50
+ 90 90 90 26 26 26 2 2 6 2 2 6
+ 14 14 14 195 195 195 250 250 250 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+250 250 250 242 242 242 54 54 54 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 38 38 38
+ 86 86 86 50 50 50 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 14 14 14 38 38 38 82 82 82
+ 34 34 34 2 2 6 2 2 6 2 2 6
+ 42 42 42 195 195 195 246 246 246 253 253 253
+253 253 253 253 253 253 253 253 253 250 250 250
+242 242 242 242 242 242 250 250 250 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 250 250 250 246 246 246 238 238 238
+226 226 226 231 231 231 101 101 101 6 6 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 38 38 38 82 82 82 42 42 42 14 14 14
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 10 10 10 26 26 26 62 62 62 66 66 66
+ 2 2 6 2 2 6 2 2 6 6 6 6
+ 70 70 70 170 170 170 206 206 206 234 234 234
+246 246 246 250 250 250 250 250 250 238 238 238
+226 226 226 231 231 231 238 238 238 250 250 250
+250 250 250 250 250 250 246 246 246 231 231 231
+214 214 214 206 206 206 202 202 202 202 202 202
+198 198 198 202 202 202 182 182 182 18 18 18
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 62 62 62 66 66 66 30 30 30
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 14 14 14 42 42 42 82 82 82 18 18 18
+ 2 2 6 2 2 6 2 2 6 10 10 10
+ 94 94 94 182 182 182 218 218 218 242 242 242
+250 250 250 253 253 253 253 253 253 250 250 250
+234 234 234 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 246 246 246
+238 238 238 226 226 226 210 210 210 202 202 202
+195 195 195 195 195 195 210 210 210 158 158 158
+ 6 6 6 14 14 14 50 50 50 14 14 14
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 6 6 6 86 86 86 46 46 46
+ 18 18 18 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 22 22 22 54 54 54 70 70 70 2 2 6
+ 2 2 6 10 10 10 2 2 6 22 22 22
+166 166 166 231 231 231 250 250 250 253 253 253
+253 253 253 253 253 253 253 253 253 250 250 250
+242 242 242 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 246 246 246
+231 231 231 206 206 206 198 198 198 226 226 226
+ 94 94 94 2 2 6 6 6 6 38 38 38
+ 30 30 30 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 62 62 62 66 66 66
+ 26 26 26 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 74 74 74 50 50 50 2 2 6
+ 26 26 26 26 26 26 2 2 6 106 106 106
+238 238 238 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 246 246 246 218 218 218 202 202 202
+210 210 210 14 14 14 2 2 6 2 2 6
+ 30 30 30 22 22 22 2 2 6 2 2 6
+ 2 2 6 2 2 6 18 18 18 86 86 86
+ 42 42 42 14 14 14 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 42 42 42 90 90 90 22 22 22 2 2 6
+ 42 42 42 2 2 6 18 18 18 218 218 218
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 250 250 250 221 221 221
+218 218 218 101 101 101 2 2 6 14 14 14
+ 18 18 18 38 38 38 10 10 10 2 2 6
+ 2 2 6 2 2 6 2 2 6 78 78 78
+ 58 58 58 22 22 22 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 54 54 54 82 82 82 2 2 6 26 26 26
+ 22 22 22 2 2 6 123 123 123 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 250 250 250
+238 238 238 198 198 198 6 6 6 38 38 38
+ 58 58 58 26 26 26 38 38 38 2 2 6
+ 2 2 6 2 2 6 2 2 6 46 46 46
+ 78 78 78 30 30 30 10 10 10 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 10 10 10 30 30 30
+ 74 74 74 58 58 58 2 2 6 42 42 42
+ 2 2 6 22 22 22 231 231 231 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 250 250 250
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 246 246 246 46 46 46 38 38 38
+ 42 42 42 14 14 14 38 38 38 14 14 14
+ 2 2 6 2 2 6 2 2 6 6 6 6
+ 86 86 86 46 46 46 14 14 14 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 14 14 14 42 42 42
+ 90 90 90 18 18 18 18 18 18 26 26 26
+ 2 2 6 116 116 116 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 250 250 250 238 238 238
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 94 94 94 6 6 6
+ 2 2 6 2 2 6 10 10 10 34 34 34
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 74 74 74 58 58 58 22 22 22 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 10 10 10 26 26 26 66 66 66
+ 82 82 82 2 2 6 38 38 38 6 6 6
+ 14 14 14 210 210 210 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 246 246 246 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 144 144 144 2 2 6
+ 2 2 6 2 2 6 2 2 6 46 46 46
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 42 42 42 74 74 74 30 30 30 10 10 10
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 14 14 14 42 42 42 90 90 90
+ 26 26 26 6 6 6 42 42 42 2 2 6
+ 74 74 74 250 250 250 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 242 242 242 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 182 182 182 2 2 6
+ 2 2 6 2 2 6 2 2 6 46 46 46
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 10 10 10 86 86 86 38 38 38 10 10 10
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 10 10 10 26 26 26 66 66 66 82 82 82
+ 2 2 6 22 22 22 18 18 18 2 2 6
+149 149 149 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 234 234 234 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 206 206 206 2 2 6
+ 2 2 6 2 2 6 2 2 6 38 38 38
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 6 6 6 86 86 86 46 46 46 14 14 14
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 18 18 18 46 46 46 86 86 86 18 18 18
+ 2 2 6 34 34 34 10 10 10 6 6 6
+210 210 210 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 234 234 234 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 221 221 221 6 6 6
+ 2 2 6 2 2 6 6 6 6 30 30 30
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 82 82 82 54 54 54 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 26 26 26 66 66 66 62 62 62 2 2 6
+ 2 2 6 38 38 38 10 10 10 26 26 26
+238 238 238 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 238 238 238
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 6 6 6
+ 2 2 6 2 2 6 10 10 10 30 30 30
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 66 66 66 58 58 58 22 22 22
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 38 38 38 78 78 78 6 6 6 2 2 6
+ 2 2 6 46 46 46 14 14 14 42 42 42
+246 246 246 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 234 234 234 10 10 10
+ 2 2 6 2 2 6 22 22 22 14 14 14
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 66 66 66 62 62 62 22 22 22
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 50 50 50 74 74 74 2 2 6 2 2 6
+ 14 14 14 70 70 70 34 34 34 62 62 62
+250 250 250 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 246 246 246
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 234 234 234 14 14 14
+ 2 2 6 2 2 6 30 30 30 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 66 66 66 62 62 62 22 22 22
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 54 54 54 62 62 62 2 2 6 2 2 6
+ 2 2 6 30 30 30 46 46 46 70 70 70
+250 250 250 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 246 246 246
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 226 226 226 10 10 10
+ 2 2 6 6 6 6 30 30 30 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 66 66 66 58 58 58 22 22 22
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 22 22 22
+ 58 58 58 62 62 62 2 2 6 2 2 6
+ 2 2 6 2 2 6 30 30 30 78 78 78
+250 250 250 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 246 246 246
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 206 206 206 2 2 6
+ 22 22 22 34 34 34 18 14 6 22 22 22
+ 26 26 26 18 18 18 6 6 6 2 2 6
+ 2 2 6 82 82 82 54 54 54 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 26 26 26
+ 62 62 62 106 106 106 74 54 14 185 133 11
+210 162 10 121 92 8 6 6 6 62 62 62
+238 238 238 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 246 246 246
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 158 158 158 18 18 18
+ 14 14 14 2 2 6 2 2 6 2 2 6
+ 6 6 6 18 18 18 66 66 66 38 38 38
+ 6 6 6 94 94 94 50 50 50 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 10 10 10 10 10 10 18 18 18 38 38 38
+ 78 78 78 142 134 106 216 158 10 242 186 14
+246 190 14 246 190 14 156 118 10 10 10 10
+ 90 90 90 238 238 238 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 250 250 250
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 246 230 190
+238 204 91 238 204 91 181 142 44 37 26 9
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 38 38 38 46 46 46
+ 26 26 26 106 106 106 54 54 54 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 14 14 14 22 22 22
+ 30 30 30 38 38 38 50 50 50 70 70 70
+106 106 106 190 142 34 226 170 11 242 186 14
+246 190 14 246 190 14 246 190 14 154 114 10
+ 6 6 6 74 74 74 226 226 226 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 231 231 231 250 250 250
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 228 184 62
+241 196 14 241 208 19 232 195 16 38 30 10
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 6 6 6 30 30 30 26 26 26
+203 166 17 154 142 90 66 66 66 26 26 26
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 18 18 18 38 38 38 58 58 58
+ 78 78 78 86 86 86 101 101 101 123 123 123
+175 146 61 210 150 10 234 174 13 246 186 14
+246 190 14 246 190 14 246 190 14 238 190 10
+102 78 10 2 2 6 46 46 46 198 198 198
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 234 234 234 242 242 242
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 224 178 62
+242 186 14 241 196 14 210 166 10 22 18 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 6 6 6 121 92 8
+238 202 15 232 195 16 82 82 82 34 34 34
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 14 14 14 38 38 38 70 70 70 154 122 46
+190 142 34 200 144 11 197 138 11 197 138 11
+213 154 11 226 170 11 242 186 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+225 175 15 46 32 6 2 2 6 22 22 22
+158 158 158 250 250 250 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 250 250 250 242 242 242 224 178 62
+239 182 13 236 186 11 213 154 11 46 32 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 61 42 6 225 175 15
+238 190 10 236 186 11 112 100 78 42 42 42
+ 14 14 14 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 22 22 22 54 54 54 154 122 46 213 154 11
+226 170 11 230 174 11 226 170 11 226 170 11
+236 178 12 242 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+241 196 14 184 144 12 10 10 10 2 2 6
+ 6 6 6 116 116 116 242 242 242 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 231 231 231 198 198 198 214 170 54
+236 178 12 236 178 12 210 150 10 137 92 6
+ 18 14 6 2 2 6 2 2 6 2 2 6
+ 6 6 6 70 47 6 200 144 11 236 178 12
+239 182 13 239 182 13 124 112 88 58 58 58
+ 22 22 22 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 70 70 70 180 133 36 226 170 11
+239 182 13 242 186 14 242 186 14 246 186 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 232 195 16 98 70 6 2 2 6
+ 2 2 6 2 2 6 66 66 66 221 221 221
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 206 206 206 198 198 198 214 166 58
+230 174 11 230 174 11 216 158 10 192 133 9
+163 110 8 116 81 8 102 78 10 116 81 8
+167 114 7 197 138 11 226 170 11 239 182 13
+242 186 14 242 186 14 162 146 94 78 78 78
+ 34 34 34 14 14 14 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 30 30 30 78 78 78 190 142 34 226 170 11
+239 182 13 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 241 196 14 203 166 17 22 18 6
+ 2 2 6 2 2 6 2 2 6 38 38 38
+218 218 218 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+250 250 250 206 206 206 198 198 198 202 162 69
+226 170 11 236 178 12 224 166 10 210 150 10
+200 144 11 197 138 11 192 133 9 197 138 11
+210 150 10 226 170 11 242 186 14 246 190 14
+246 190 14 246 186 14 225 175 15 124 112 88
+ 62 62 62 30 30 30 14 14 14 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 78 78 78 174 135 50 224 166 10
+239 182 13 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 241 196 14 139 102 15
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 78 78 78 250 250 250 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+250 250 250 214 214 214 198 198 198 190 150 46
+219 162 10 236 178 12 234 174 13 224 166 10
+216 158 10 213 154 11 213 154 11 216 158 10
+226 170 11 239 182 13 246 190 14 246 190 14
+246 190 14 246 190 14 242 186 14 206 162 42
+101 101 101 58 58 58 30 30 30 14 14 14
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 74 74 74 174 135 50 216 158 10
+236 178 12 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 241 196 14 226 184 13
+ 61 42 6 2 2 6 2 2 6 2 2 6
+ 22 22 22 238 238 238 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 226 226 226 187 187 187 180 133 36
+216 158 10 236 178 12 239 182 13 236 178 12
+230 174 11 226 170 11 226 170 11 230 174 11
+236 178 12 242 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 186 14 239 182 13
+206 162 42 106 106 106 66 66 66 34 34 34
+ 14 14 14 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 26 26 26 70 70 70 163 133 67 213 154 11
+236 178 12 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 241 196 14
+190 146 13 18 14 6 2 2 6 2 2 6
+ 46 46 46 246 246 246 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 221 221 221 86 86 86 156 107 11
+216 158 10 236 178 12 242 186 14 246 186 14
+242 186 14 239 182 13 239 182 13 242 186 14
+242 186 14 246 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+242 186 14 225 175 15 142 122 72 66 66 66
+ 30 30 30 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 26 26 26 70 70 70 163 133 67 210 150 10
+236 178 12 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+232 195 16 121 92 8 34 34 34 106 106 106
+221 221 221 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+242 242 242 82 82 82 18 14 6 163 110 8
+216 158 10 236 178 12 242 186 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 242 186 14 163 133 67
+ 46 46 46 18 18 18 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 10 10 10
+ 30 30 30 78 78 78 163 133 67 210 150 10
+236 178 12 246 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+241 196 14 215 174 15 190 178 144 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 218 218 218
+ 58 58 58 2 2 6 22 18 6 167 114 7
+216 158 10 236 178 12 246 186 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 186 14 242 186 14 190 150 46
+ 54 54 54 22 22 22 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 38 38 38 86 86 86 180 133 36 213 154 11
+236 178 12 246 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 232 195 16 190 146 13 214 214 214
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 250 250 250 170 170 170 26 26 26
+ 2 2 6 2 2 6 37 26 9 163 110 8
+219 162 10 239 182 13 246 186 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 186 14 236 178 12 224 166 10 142 122 72
+ 46 46 46 18 18 18 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 50 50 50 109 106 95 192 133 9 224 166 10
+242 186 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+242 186 14 226 184 13 210 162 10 142 110 46
+226 226 226 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+253 253 253 253 253 253 253 253 253 253 253 253
+198 198 198 66 66 66 2 2 6 2 2 6
+ 2 2 6 2 2 6 50 34 6 156 107 11
+219 162 10 239 182 13 246 186 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 242 186 14
+234 174 13 213 154 11 154 122 46 66 66 66
+ 30 30 30 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 22 22 22
+ 58 58 58 154 121 60 206 145 10 234 174 13
+242 186 14 246 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 186 14 236 178 12 210 162 10 163 110 8
+ 61 42 6 138 138 138 218 218 218 250 250 250
+253 253 253 253 253 253 253 253 253 250 250 250
+242 242 242 210 210 210 144 144 144 66 66 66
+ 6 6 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 61 42 6 163 110 8
+216 158 10 236 178 12 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 239 182 13 230 174 11 216 158 10
+190 142 34 124 112 88 70 70 70 38 38 38
+ 18 18 18 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 22 22 22
+ 62 62 62 168 124 44 206 145 10 224 166 10
+236 178 12 239 182 13 242 186 14 242 186 14
+246 186 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 236 178 12 216 158 10 175 118 6
+ 80 54 7 2 2 6 6 6 6 30 30 30
+ 54 54 54 62 62 62 50 50 50 38 38 38
+ 14 14 14 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 6 6 6 80 54 7 167 114 7
+213 154 11 236 178 12 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 190 14 242 186 14 239 182 13 239 182 13
+230 174 11 210 150 10 174 135 50 124 112 88
+ 82 82 82 54 54 54 34 34 34 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 18 18 18
+ 50 50 50 158 118 36 192 133 9 200 144 11
+216 158 10 219 162 10 224 166 10 226 170 11
+230 174 11 236 178 12 239 182 13 239 182 13
+242 186 14 246 186 14 246 190 14 246 190 14
+246 190 14 246 190 14 246 190 14 246 190 14
+246 186 14 230 174 11 210 150 10 163 110 8
+104 69 6 10 10 10 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 6 6 6 91 60 6 167 114 7
+206 145 10 230 174 11 242 186 14 246 190 14
+246 190 14 246 190 14 246 186 14 242 186 14
+239 182 13 230 174 11 224 166 10 213 154 11
+180 133 36 124 112 88 86 86 86 58 58 58
+ 38 38 38 22 22 22 10 10 10 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 14 14 14
+ 34 34 34 70 70 70 138 110 50 158 118 36
+167 114 7 180 123 7 192 133 9 197 138 11
+200 144 11 206 145 10 213 154 11 219 162 10
+224 166 10 230 174 11 239 182 13 242 186 14
+246 186 14 246 186 14 246 186 14 246 186 14
+239 182 13 216 158 10 185 133 11 152 99 6
+104 69 6 18 14 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 2 2 6 2 2 6 2 2 6
+ 2 2 6 6 6 6 80 54 7 152 99 6
+192 133 9 219 162 10 236 178 12 239 182 13
+246 186 14 242 186 14 239 182 13 236 178 12
+224 166 10 206 145 10 192 133 9 154 121 60
+ 94 94 94 62 62 62 42 42 42 22 22 22
+ 14 14 14 6 6 6 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 18 18 18 34 34 34 58 58 58 78 78 78
+101 98 89 124 112 88 142 110 46 156 107 11
+163 110 8 167 114 7 175 118 6 180 123 7
+185 133 11 197 138 11 210 150 10 219 162 10
+226 170 11 236 178 12 236 178 12 234 174 13
+219 162 10 197 138 11 163 110 8 130 83 6
+ 91 60 6 10 10 10 2 2 6 2 2 6
+ 18 18 18 38 38 38 38 38 38 38 38 38
+ 38 38 38 38 38 38 38 38 38 38 38 38
+ 38 38 38 38 38 38 26 26 26 2 2 6
+ 2 2 6 6 6 6 70 47 6 137 92 6
+175 118 6 200 144 11 219 162 10 230 174 11
+234 174 13 230 174 11 219 162 10 210 150 10
+192 133 9 163 110 8 124 112 88 82 82 82
+ 50 50 50 30 30 30 14 14 14 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 14 14 14 22 22 22 34 34 34
+ 42 42 42 58 58 58 74 74 74 86 86 86
+101 98 89 122 102 70 130 98 46 121 87 25
+137 92 6 152 99 6 163 110 8 180 123 7
+185 133 11 197 138 11 206 145 10 200 144 11
+180 123 7 156 107 11 130 83 6 104 69 6
+ 50 34 6 54 54 54 110 110 110 101 98 89
+ 86 86 86 82 82 82 78 78 78 78 78 78
+ 78 78 78 78 78 78 78 78 78 78 78 78
+ 78 78 78 82 82 82 86 86 86 94 94 94
+106 106 106 101 101 101 86 66 34 124 80 6
+156 107 11 180 123 7 192 133 9 200 144 11
+206 145 10 200 144 11 192 133 9 175 118 6
+139 102 15 109 106 95 70 70 70 42 42 42
+ 22 22 22 10 10 10 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 6 6 6 10 10 10
+ 14 14 14 22 22 22 30 30 30 38 38 38
+ 50 50 50 62 62 62 74 74 74 90 90 90
+101 98 89 112 100 78 121 87 25 124 80 6
+137 92 6 152 99 6 152 99 6 152 99 6
+138 86 6 124 80 6 98 70 6 86 66 30
+101 98 89 82 82 82 58 58 58 46 46 46
+ 38 38 38 34 34 34 34 34 34 34 34 34
+ 34 34 34 34 34 34 34 34 34 34 34 34
+ 34 34 34 34 34 34 38 38 38 42 42 42
+ 54 54 54 82 82 82 94 86 76 91 60 6
+134 86 6 156 107 11 167 114 7 175 118 6
+175 118 6 167 114 7 152 99 6 121 87 25
+101 98 89 62 62 62 34 34 34 18 18 18
+ 6 6 6 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 6 6 6 10 10 10
+ 18 18 18 22 22 22 30 30 30 42 42 42
+ 50 50 50 66 66 66 86 86 86 101 98 89
+106 86 58 98 70 6 104 69 6 104 69 6
+104 69 6 91 60 6 82 62 34 90 90 90
+ 62 62 62 38 38 38 22 22 22 14 14 14
+ 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 6 6 6 10 10 10
+ 10 10 10 10 10 10 10 10 10 14 14 14
+ 22 22 22 42 42 42 70 70 70 89 81 66
+ 80 54 7 104 69 6 124 80 6 137 92 6
+134 86 6 116 81 8 100 82 52 86 86 86
+ 58 58 58 30 30 30 14 14 14 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 10 10 10 14 14 14
+ 18 18 18 26 26 26 38 38 38 54 54 54
+ 70 70 70 86 86 86 94 86 76 89 81 66
+ 89 81 66 86 86 86 74 74 74 50 50 50
+ 30 30 30 14 14 14 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 18 18 18 34 34 34 58 58 58
+ 82 82 82 89 81 66 89 81 66 89 81 66
+ 94 86 66 94 86 76 74 74 74 50 50 50
+ 26 26 26 14 14 14 6 6 6 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 6 6 6 6 6 6 14 14 14 18 18 18
+ 30 30 30 38 38 38 46 46 46 54 54 54
+ 50 50 50 42 42 42 30 30 30 18 18 18
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 6 6 6 14 14 14 26 26 26
+ 38 38 38 50 50 50 58 58 58 58 58 58
+ 54 54 54 42 42 42 30 30 30 18 18 18
+ 10 10 10 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 6 6 6 10 10 10 14 14 14 18 18 18
+ 18 18 18 14 14 14 10 10 10 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 6 6 6
+ 14 14 14 18 18 18 22 22 22 22 22 22
+ 18 18 18 14 14 14 10 10 10 6 6 6
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/drivers/video/logo/logo_linux_vga16.ppm b/drivers/video/logo/logo_linux_vga16.ppm
index 12ac3a5454c0..1850c15e6feb 100644
--- a/drivers/video/logo/logo_linux_vga16.ppm
+++ b/drivers/video/logo/logo_linux_vga16.ppm
@@ -1,2739 +1,1604 @@
P3
-142 114
+# Standard 16-color Linux logo
+80 80
255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 170 170 170 170 85 0 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-255 255 85 170 170 170 170 170 170 170 85 0 85 255 85 170 85 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 170 170 170 170 85 0
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 170 85 0 170 170 170
-170 170 170 170 85 0 170 170 170 170 170 170 170 85 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 255 85
-255 85 85 85 255 85 170 170 170 170 85 0 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 85 0 85 85 85 85 85 85
-170 170 170 170 85 0 170 170 170 85 85 85 170 85 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 85 85 85 170 85 0
-85 85 85 85 85 85 170 85 0 85 255 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 170 85 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 170 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 85 0 85 255 85 170 85 0 170 85 0 170 85 0 85 255 85
-170 85 0 170 85 0 0 170 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 170 0 170 85 0
-255 255 85 170 85 0 255 255 85 255 255 85 255 255 85 170 85 0
-255 255 85 85 255 85 170 85 0 170 85 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 85 0 255 255 85
-85 255 85 255 255 85 255 255 85 170 85 0 255 255 85 255 255 85
-255 255 85 170 85 0 255 255 85 85 255 85 170 85 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 255 255 85 170 85 0 85 255 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 85 0 255 255 85 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 85 255 85 255 255 85
-255 255 85 170 85 0 255 255 85 170 85 0 255 255 85 170 85 0
-170 85 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 85 0 255 255 85 85 255 85 255 255 85
-170 170 170 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 85 255 85 255 255 85 255 255 85 255 255 85
-85 255 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 85 0 255 255 85 255 255 85 255 255 85
-255 255 255 255 255 85 255 255 85 170 85 0 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 255 255 85 170 85 0 255 255 85
-170 85 0 170 85 0 0 170 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 85 0 85 255 85 255 255 85 170 170 170 255 255 255
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 85 255 85 255 255 85
-255 255 85 85 255 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 255 255 85 255 255 85 255 255 85 255 255 255 255 255 85
-255 255 85 255 255 85 85 255 85 255 255 85 255 255 85 85 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 170 85 0
-255 255 85 170 85 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 0 0 0 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 85 0 170 85 0 255 255 85 255 255 85 255 255 255 170 170 170
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-170 85 0 255 255 85 255 255 85 170 85 0 255 255 85 255 255 85
-255 255 85 85 255 85 170 85 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-255 255 85 85 255 85 255 255 85 170 170 170 255 255 255 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 85 255 85 170 85 0
-255 255 85 170 85 0 170 85 0 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 170 170 170 170 170 170 170 170 170
-85 255 85 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 0
-170 85 0 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 85 255 85 255 255 85 170 85 0 170 85 0
-170 85 0 85 255 85 255 255 85 85 85 85 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 85 255 85 170 170 170 170 170 170
-85 85 85 170 170 170 170 170 170 170 85 0 170 170 170 170 170 170
-85 255 85 170 170 170 170 85 0 170 170 170 85 255 85 255 85 85
-85 255 85 170 170 170 255 255 85 85 85 85 255 255 85 170 170 170
-85 255 85 170 170 170 255 255 85 170 170 170 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 255 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 170 85 0 0 170 0 85 85 85
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 85 255 85
-255 85 85 85 255 85 85 85 85 255 85 85 85 85 85 170 170 170
-170 85 0 170 170 170 85 85 85 85 255 85 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255 85
-170 85 0 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 85 85 85 85 85 85
-255 255 85 170 170 170 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 170 170 170 170 85 0 170 170 170
-170 170 170 255 255 85 170 170 170 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 85 0 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 170 85 0 170 85 0 0 170 0
-0 0 0 170 85 0 170 85 0 0 170 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 170 170 170 85 85 85
-170 170 170 85 85 85 85 85 85 170 170 170 170 85 0 85 85 85
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 170 85 0
-255 255 85 255 255 85 255 255 85 170 85 0 255 255 85 255 255 85
-170 85 0 255 255 85 85 255 85 255 255 85 255 255 85 170 85 0
-170 85 0 255 255 85 170 85 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 255 255 85 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 170 85 0 255 255 85 255 255 85
-85 255 85 170 85 0 0 170 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 170 0 170 85 0 255 255 85
-85 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-170 85 0 255 255 85 85 255 85 255 255 85 255 255 85 170 85 0
-85 255 85 170 85 0 170 85 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 85 0 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 170 85 0 255 255 85 85 255 85 255 255 85 170 85 0
-255 255 85 255 255 85 170 85 0 255 255 85 170 85 0 85 255 85
-170 85 0 170 85 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 255 255 85 170 85 0
-255 255 85 170 85 0 255 255 85 170 85 0 255 255 85 85 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 170 85 0 255 255 85 85 255 85 170 85 0 170 85 0
-0 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 85 0 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 170 85 0 255 255 85 170 85 0 85 255 85
-255 255 85 85 255 85 170 85 0 170 85 0 85 255 85 170 85 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 255 255 85 85 255 85
-255 255 85 255 255 85 85 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 170 85 0 255 255 85 85 255 85 255 255 85 255 255 85
-170 85 0 170 85 0 85 255 85 170 85 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 255 255 85 255 255 85
-170 85 0 255 255 85 255 255 85 170 85 0 255 255 85 170 85 0
-255 255 85 85 255 85 170 85 0 255 255 85 170 85 0 85 255 85
-170 85 0 170 85 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 85 0 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 85 255 85
-170 85 0 255 255 85 170 85 0 85 255 85 170 85 0 170 85 0
-0 170 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 170 170 170 170 170 170 255 255 255 170 170 170
-170 170 170 170 170 170 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 255 85 255 255 85
-170 85 0 255 255 85 255 255 85 170 85 0 85 255 85 170 85 0
-255 255 85 170 85 0 0 170 0 170 85 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 170 170 170 170 170 170 170 170 170 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 170 85 0 170 85 0
-0 170 0 85 85 85 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-255 255 85 255 255 85 170 85 0 0 170 0 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 170 170 170 170 170 170 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 85 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 170 170 170 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 170 170 170
-170 170 170 170 170 170 170 170 170 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 170 170 170 170 170 170
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 170 170 170
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 255 255 255 170 170 170 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 170 170 170 170 170 170
-255 255 255 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-255 255 255 255 255 255 170 170 170 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 170 170 170
-170 170 170 170 170 170 255 255 255 170 170 170 255 255 255 170 170 170
-255 255 255 170 170 170 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 170 170 170 255 255 255 170 170 170 255 255 255 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 85 85 85 170 170 170
-170 170 170 170 170 170 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 170 170 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 170 170 170 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 170 170 170 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 255 255 255 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170
-170 170 170 255 255 255 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 170 170 170
-255 255 255 255 255 255 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 170 170 170 255 255 255
-255 255 255 170 170 170 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 255 255 255 255 255 255 255 255 255 170 170 170
-170 170 170 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 170 170 170 170 170 170
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 170 170 170 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 170 170 170 255 255 255 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 170 170 170 255 255 255 170 170 170 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-255 255 255 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 255 255 255 255 255 255 255 255 255 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 170 170 170
-255 255 255 170 170 170 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-170 170 170 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 255 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 170 170 170 255 255 255 255 255 255 255 255 255 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 255 255 255
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 85 85 85
-170 170 170 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 255 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 170 170 170 255 255 255 255 255 255 255 255 255 170 170 170
-0 0 0 85 85 85 0 0 0 85 85 85 170 170 170 255 255 255
-255 255 255 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-170 170 170 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 85 85 85 170 170 170 255 255 255
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 255 170 170 170 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 170 170 170 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 255 255 255 255 255 255 255 255 255 170 170 170
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-170 170 170 170 170 170 255 255 255 170 170 170 255 255 255 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 170 170 170 255 255 255
-170 170 170 170 170 170 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 255 255 255 255 255 255 0 0 0 0 0 0 85 85 85
-255 255 255 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 255
-255 255 255 255 255 255 170 170 170 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 255 255 255 170 170 170 170 170 170 170 170 170
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 170 170 170 255 255 255 170 170 170 170 170 170 85 85 85
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 170 170 170
-170 170 170 85 85 85 85 85 85 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 85 85 85 0 0 0 0 0 0 170 170 170
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170
-255 255 255 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 170 170 170 255 255 85 85 85 85
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 0 0 0 85 85 85
-85 85 85 170 170 170 170 170 170 170 170 170 170 85 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 170 170 170 170 85 0
-170 170 170 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-170 85 0 170 170 170 85 85 85 0 0 0 85 85 85 85 85 85
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-255 255 255 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 170 85 0 170 170 170 170 170 170 85 85 85
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 170 85 0 85 255 85 170 85 0 170 170 170 85 85 85
-85 85 85 0 0 0 0 0 0 85 85 85 170 85 0 85 255 85
-170 85 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 170 85 0 0 0 0 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 255
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 170 85 0 170 170 170 170 85 0
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 255 85 85 170 170 170 85 255 85 170 85 0
-85 85 85 85 85 85 170 85 0 85 85 85 170 170 170 85 85 85
-170 170 170 170 85 0 85 85 85 85 85 85 85 85 85 85 85 85
-170 85 0 85 255 85 85 85 85 85 85 85 85 85 85 170 85 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 170 170 170
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 85 0 170 170 170 170 85 0 85 85 85 0 0 0
-85 85 85 85 85 85 85 255 85 170 170 170 170 170 170 170 85 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 85 255 85 255 85 85 170 170 170 170 170 170
-170 170 170 85 255 85 170 170 170 170 85 0 170 170 170 170 85 0
-170 170 170 85 85 85 85 255 85 170 85 0 170 170 170 170 85 0
-170 170 170 170 170 170 170 85 0 85 85 85 85 85 85 85 255 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 170 170 170 170 170 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 255 255 85 85 85 85 85 85 85 85 85 85
-85 255 85 255 85 85 170 170 170 170 85 0 170 170 170 85 255 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 170 85 0 170 170 170 170 170 170 255 255 85
-170 170 170 255 85 85 170 170 170 170 170 170 255 255 85 170 170 170
-85 255 85 170 170 170 255 85 85 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 85 0 170 170 170 170 85 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 85 85 85 0 0 0 85 85 85
-85 85 85 170 85 0 85 85 85 0 0 0 85 85 85 85 85 85
-85 85 85 170 85 0 170 170 170 170 170 170 255 255 85 170 170 170
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 255 85 85
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 85 85 85 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 170 85 0
-170 170 170 255 255 85 170 170 170 255 255 85 170 170 170 255 255 85
-170 170 170 255 255 85 170 170 170 170 170 170 85 255 85 170 85 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 85 85 85 85 85 85 0 0 0
-85 85 85 85 85 85 170 170 170 85 85 85 170 170 170 170 85 0
-170 170 170 85 255 85 170 170 170 170 85 0 170 170 170 170 170 170
-255 255 85 170 170 170 170 170 170 255 255 255 255 255 85 170 170 170
-255 255 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 170 85 0 170 170 170 170 170 170 170 170 170 255 255 255
-170 170 170 255 255 255 255 255 85 170 170 170 255 255 85 170 170 170
-255 255 85 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 255 85 85 170 170 170 170 170 170
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 170 85 0 85 85 85
-85 85 85 255 255 85 170 170 170 170 170 170 170 170 170 170 170 170
-255 255 85 170 170 170 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 255 255 255 255 255 255 170 170 170 255 255 255 170 170 170
-170 170 170 85 85 85 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 255 85 170 170 170 255 255 85 255 255 255 255 255 255
-255 255 255 170 170 170 255 255 255 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 85 170 170 170 255 255 85 255 255 255
-255 255 85 255 255 255 255 255 85 170 170 170 170 170 170 170 85 0
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 170 170 170 255 255 85 170 170 170 255 255 85 170 170 170
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 255 255 85
-255 255 255 255 255 255 255 255 85 255 255 255 255 255 255 170 170 170
-255 255 85 85 85 85 85 85 85 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 170 170 170 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 170 170 170 170 170 170 170 170 170 255 255 255 170 170 170
-255 255 255 255 255 255 255 255 255 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 85 170 170 170 255 255 255 170 170 170 255 255 255
-255 255 255 255 255 255 170 170 170 255 255 85 170 170 170 255 255 85
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 85 0 170 170 170
-255 255 85 170 170 170 170 170 170 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 170 170 170 255 255 85 170 170 170 170 170 170
-255 255 255 170 170 170 255 255 255 255 255 255 170 170 170 255 255 255
-170 170 170 170 170 170 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-85 85 85 170 85 0 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 85 255 255 255 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 255 255 85 255 255 255 255 255 255
-170 170 170 255 255 255 255 255 255 170 170 170 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 255 85 170 170 170
-170 170 170 255 255 85 170 170 170 255 255 255 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 85 170 170 170 170 170 170 255 255 255
-170 170 170 255 255 255 255 255 255 170 170 170 255 255 255 255 255 85
-170 170 170 170 85 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 170 170 170 170 170 170 255 255 85 170 170 170 255 255 255
-255 255 255 170 170 170 255 255 255 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 85 170 170 170 255 255 255 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 255 255 85
-85 85 85 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 85 0 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 85 255 255 255 170 170 170 255 255 255 170 170 170 170 170 170
-170 170 170 170 170 170 85 85 85 0 0 0 85 85 85 85 85 85
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-85 85 85 85 85 85 170 170 170 170 170 170 255 255 255 170 170 170
-255 255 255 170 170 170 255 255 85 170 170 170 170 170 170 170 170 170
-255 85 85 170 170 170 170 170 170 170 170 170 255 255 255 255 255 255
-170 170 170 255 255 255 255 255 255 170 170 170 255 255 255 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 170 170 170
-255 255 85 170 170 170 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 85 255 255 255 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 255 255 85
-170 170 170 170 85 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 170 170 170
-170 170 170 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 255 255 85 170 170 170 170 170 170
-255 255 85 170 170 170 170 170 170 170 170 170 170 170 170 255 85 85
-85 255 85 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 255 255 255 255 255 255 255 255 255 85 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 85
-170 170 170 170 170 170 255 255 255 255 255 255 255 255 255 170 170 170
-255 255 255 170 170 170 170 170 170 170 170 170 170 170 170 255 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 85 85 85 85 85 85 0 0 0 85 85 85 0 0 0
-85 85 85 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 170 85 0 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 85 0 170 170 170 85 255 85
-255 85 85 170 170 170 255 255 85 170 170 170 170 170 170 255 255 255
-255 255 255 170 170 170 255 255 255 255 255 255 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-170 170 170 255 255 85 170 170 170 255 255 85 255 255 255 255 255 255
-170 170 170 170 170 170 255 255 85 170 170 170 255 85 85 85 255 85
-170 170 170 170 85 0 170 170 170 170 170 170 255 255 85 170 170 170
-170 85 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 85 85 85 85 85 85 85 85 85 170 170 170 255 255 85
-170 170 170 170 85 0 170 170 170 170 170 170 170 85 0 85 85 85
-170 170 170 170 85 0 85 85 85 170 170 170 170 170 170 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 85 170 170 170 170 170 170
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 85 0
-170 170 170 170 170 170 255 255 255 170 170 170 255 255 255 170 170 170
-255 255 255 170 170 170 170 170 170 170 85 0 170 170 170 85 85 85
-170 170 170 170 170 170 170 85 0 170 170 170 170 85 0 85 85 85
-85 255 85 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 85 85 85 170 85 0 85 85 85 170 170 170
-170 85 0 170 170 170 85 255 85 170 85 0 170 170 170 85 85 85
-170 85 0 170 170 170 170 170 170 255 255 85 170 170 170 255 255 255
-255 255 85 255 255 255 170 170 170 170 170 170 170 170 170 170 85 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 85 170 170 170 170 170 170 85 255 85 170 170 170
-170 85 0 170 85 0 170 170 170 85 255 85 85 85 85 170 170 170
-170 85 0 85 85 85 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 170 85 0 85 85 85
-170 170 170 170 85 0 170 170 170 85 85 85 170 170 170 170 85 0
-170 170 170 85 255 85 170 85 0 170 170 170 170 170 170 170 170 170
-255 255 255 170 170 170 255 255 255 255 255 255 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 85 0 170 170 170 255 255 85 170 170 170 255 255 255 170 170 170
-170 170 170 170 170 170 170 170 170 170 85 0 170 170 170 170 85 0
-170 170 170 85 255 85 170 85 0 170 170 170 170 85 0 85 85 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 255 85 170 170 170 170 85 0 170 170 170 170 85 0 85 255 85
-170 170 170 170 85 0 170 170 170 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 255 170 170 170 170 170 170 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 85 0 85 85 85 170 170 170 255 255 85 170 170 170
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 170 85 0
-170 170 170 85 85 85 170 170 170 170 85 0 170 170 170 85 85 85
-170 85 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 170 85 0
-85 85 85 255 85 85 85 255 85 170 85 0 170 170 170 170 170 170
-170 85 0 170 170 170 85 85 85 255 255 85 170 170 170 170 170 170
-255 255 255 170 170 170 255 255 255 255 255 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 170 85 0 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 255 255 85 85 85 85 170 170 170
-85 255 85 255 85 85 170 170 170 85 255 85 255 85 85 85 255 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 85 85 85
-85 85 85 85 255 85 255 85 85 170 170 170 85 255 85 170 85 0
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 255 255 255
-170 170 170 255 255 255 170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 85 85 85 255 255 85
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 255 255 85
-85 85 85 255 255 85 170 170 170 170 85 0 170 170 170 85 85 85
-170 85 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 85 0 170 170 170 170 85 0 170 170 170 170 170 170
-170 85 0 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 85 255 255 255 170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 85 0 85 85 85 170 170 170
-170 85 0 170 170 170 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 255 255 85 170 170 170 170 85 0
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 85 0 85 255 85 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 85 170 170 170 170 170 170 170 170 170 255 255 255 255 255 255
-255 255 255 170 170 170 255 255 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 170 85 0
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 85 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 170 85 0 170 170 170 170 85 0 170 170 170 170 170 170
-170 170 170 255 255 85 170 170 170 255 255 85 170 170 170 255 255 255
-170 170 170 255 255 255 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-170 85 0 170 170 170 170 85 0 255 255 85 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 85 170 170 170 255 255 85 170 170 170
-255 255 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 170 170 170 255 255 85 170 170 170 255 255 85 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 170 0 85 85 85
-170 85 0 85 255 85 170 170 170 170 170 170 170 170 170 255 255 85
-255 255 255 170 170 170 255 255 255 170 170 170 255 255 255 170 170 170
-170 170 170 255 255 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 85 0 170 170 170 170 170 170 170 170 170 170 170 170 255 255 85
-170 170 170 255 255 255 170 170 170 255 255 255 170 170 170 255 255 255
-255 255 255 255 255 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 170 85 0 170 170 170 170 170 170 255 255 85 170 170 170
-170 170 170 255 255 255 255 255 255 255 255 255 170 170 170 255 255 255
-170 170 170 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 255 85
-170 170 170 255 255 85 170 170 170 255 255 85 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-170 170 170 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 170 85 0 170 170 170 170 170 170
-255 255 85 170 170 170 255 255 255 255 255 85 255 255 255 255 255 255
-170 170 170 255 255 85 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 85 0
-170 170 170 170 170 170 170 170 170 255 255 255 170 170 170 255 255 255
-170 170 170 255 255 255 170 170 170 255 255 255 255 255 85 255 255 255
-170 170 170 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 85 0 85 85 85 170 170 170 170 170 170 170 170 170
-170 170 170 255 255 255 170 170 170 255 255 255 255 255 255 170 170 170
-255 255 85 170 170 170 170 170 170 170 85 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-255 255 85 170 170 170 255 255 85 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 170 85 0 170 170 170
-255 255 85 170 170 170 255 255 85 255 255 255 170 170 170 255 255 255
-170 170 170 170 170 170 170 170 170 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-170 85 0 170 170 170 255 255 255 170 170 170 255 255 255 170 170 170
-255 255 255 255 255 255 170 170 170 255 255 255 255 255 85 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 85 0 85 85 85 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 85 170 170 170 170 85 0 85 255 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 85 0
-170 170 170 170 170 170 255 255 85 170 170 170 255 255 255 255 255 255
-255 255 85 255 255 255 170 170 170 255 255 255 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 85 0 170 170 170 170 170 170 255 255 85 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 85 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-255 255 85 170 170 170 170 170 170 170 170 170 255 255 85 170 170 170
-255 255 255 170 170 170 255 255 85 170 170 170 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-85 85 85 170 85 0 170 170 170 170 170 170 170 170 170 170 170 170
-170 85 0 170 170 170 170 85 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 170 170 170 170 170 255 255 85 170 170 170 170 170 170 170 170 170
-170 170 170 255 255 255 170 170 170 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 170 85 0 85 85 85 170 85 0
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 85 0
-85 85 85 170 85 0 170 170 170 170 170 170 170 170 170 255 255 85
-170 170 170 170 170 170 170 85 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 255 85 170 85 0 170 170 170 170 85 0 170 170 170
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 85 85 85 85 85 85 85 85 85
+ 85 85 85 85 85 85 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 85 85 85 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 170 170 170 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 170 170 170 170 170 85 85 85 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 170 170 170 170 170
+170 170 170 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 85 85 85 170 170 170 170 170 170 170 170 170
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 170 170 170 255 255 255 255 255 255
+255 255 255 170 170 170 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+170 170 170 170 170 170 255 255 255 255 255 255
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 170 170 255 255 255 170 170 170 170 170 170
+255 255 255 170 170 170 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+170 170 170 0 0 0 0 0 0 255 255 255
+ 85 85 85 0 0 0 0 0 0 0 0 0
+255 255 255 170 170 170 0 0 0 85 85 85
+170 170 170 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+ 85 85 85 0 0 0 0 0 0 170 170 170
+ 85 85 85 0 0 0 0 0 0 0 0 0
+255 255 255 85 85 85 0 0 0 0 0 0
+ 85 85 85 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+170 170 170 0 0 0 0 0 0 170 170 170
+ 85 85 85 85 85 85 85 85 85 85 85 85
+255 255 255 85 85 85 0 0 0 0 0 0
+ 85 85 85 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+255 255 255 0 0 0 0 0 0 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 0 0 0 0 0 0
+ 85 85 85 255 255 255 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+170 170 170 170 170 170 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 170 170 170 170 170 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 170 85 0
+170 85 0 170 85 0 85 85 85 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+ 85 85 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 85 85 85 0 0 0
+ 0 0 0 85 85 85 170 170 170 85 85 85
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+ 85 85 85 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 170 170 170 170 170 170 170 170 0 0 0
+ 0 0 0 0 0 0 170 170 170 170 170 170
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 85 85 85 170 170 170 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 170 170 170 170 170
+170 170 170 170 170 170 170 170 170 85 85 85
+ 0 0 0 0 0 0 85 85 85 85 85 85
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 85 85 85 170 170 170 170 170 170 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 170 170 170 170 170 170 170 170 170 170 170
+255 255 255 255 255 255 255 255 255 170 170 170
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 85 85 85
+255 255 255 255 255 255 170 170 170 170 170 170
+170 170 170 170 170 170 170 170 170 170 170 170
+170 170 170 170 170 170 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 170 170 170
+255 255 255 255 255 255 170 170 170 170 170 170
+170 170 170 170 170 170 170 170 170 170 170 170
+170 170 170 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 85 85 85 255 255 255
+255 255 255 255 255 255 255 255 255 170 170 170
+170 170 170 170 170 170 170 170 170 170 170 170
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 85 85 85 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 85 85 85 170 170 170 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 170 170 170 170 170 170 170 170 170
+255 255 255 255 255 255 255 255 255 170 170 170
+170 170 170 170 170 170 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 170 170 170 170 170 170 170 170 170
+170 170 170 170 170 170 170 170 170 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 170 170 170 170 170 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 170 170 170
+170 170 170 170 170 170 170 170 170 85 85 85
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 170 170 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 170 170 170 170 170 170
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 85 85 85 0 0 0 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 170 170 170
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 170 170 170
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+ 0 0 0 0 0 0 85 85 85 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 0 0 0 85 85 85
+ 85 85 85 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+ 0 0 0 85 85 85 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 0 0 0 85 85 85
+ 85 85 85 0 0 0 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 85 85 85
+ 0 0 0 170 170 170 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 85 85 85 0 0 0
+ 0 0 0 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 85 85 85 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 85 85 85 0 0 0 0 0 0
+170 170 170 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 0 0 0
+ 85 85 85 85 85 85 85 85 85 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 0 0 0 170 85 0
+255 255 85 170 85 0 0 0 0 0 0 0
+ 85 85 85 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 85 85 85 85 85 85 0 0 0
+ 0 0 0 85 85 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 0 0 0
+ 0 0 0 85 85 85 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 85 170 85 0 255 255 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+ 0 0 0 0 0 0 85 85 85 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 85
+170 85 0 255 255 85 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 0 0 0 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 170 85 0
+255 255 85 170 85 0 255 255 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+255 255 85 170 85 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 0 0 0 0 0 0 0 0 0
+ 85 85 85 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 85
+170 85 0 255 255 85 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 255 255 85
+170 85 0 255 255 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 0 0 0 0 0 0
+ 0 0 0 85 85 85 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 0 0 0
+ 0 0 0 0 0 0 85 85 85 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 170 170 170 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 170 170 170 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 170 170 170 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 170 170 170 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 0 0 0 0 0 0 0 0 0
+ 85 85 85 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 170 170 170 85 85 85 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 85 85 85 85 85 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 85 85 85 85 85 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 170 170 170
+ 85 85 85 0 0 0 0 0 0 170 85 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 85 85 85
+ 0 0 0 0 0 0 0 0 0 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 170 170 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+170 170 170 85 85 85 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 170 85 0
+170 85 0 170 170 170 255 255 255 255 255 255
+255 255 255 255 255 255 255 255 255 255 255 255
+255 255 255 255 255 255 170 170 170 85 85 85
+ 85 85 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 170 85 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 170 85 0 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 85 85 85 170 85 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 170 85 0 170 85 0 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 85 85 85 85 85 85 85 85 85 85 85 85
+ 85 85 85 85 85 85 85 85 85 85 85 85
+ 85 85 85 85 85 85 85 85 85 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+170 85 0 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 255 255 85 170 85 0
+170 85 0 170 85 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 255 255 85 170 85 0
+255 255 85 170 85 0 170 85 0 170 85 0
+ 85 85 85 85 85 85 85 85 85 85 85 85
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 85 85 85
+ 85 85 85 85 85 85 85 85 85 170 85 0
+170 85 0 170 85 0 170 85 0 255 255 85
+170 85 0 255 255 85 170 85 0 170 85 0
+170 85 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 170 85 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+170 85 0 170 85 0 170 85 0 170 85 0
+170 85 0 170 85 0 170 85 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index 21b3692092f2..9894de1c9b9f 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -1152,11 +1152,11 @@ static struct fb_ops mx3fb_ops = {
*/
static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct mx3fb_data *drv_data = platform_get_drvdata(pdev);
- struct mx3fb_info *mx3_fbi = drv_data->fbi->par;
+ struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
+ struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
acquire_console_sem();
- fb_set_suspend(drv_data->fbi, 1);
+ fb_set_suspend(mx3fb->fbi, 1);
release_console_sem();
if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
@@ -1172,16 +1172,16 @@ static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state)
*/
static int mx3fb_resume(struct platform_device *pdev)
{
- struct mx3fb_data *drv_data = platform_get_drvdata(pdev);
- struct mx3fb_info *mx3_fbi = drv_data->fbi->par;
+ struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
+ struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
sdc_enable_channel(mx3_fbi);
- sdc_set_brightness(mx3fb, drv_data->backlight_level);
+ sdc_set_brightness(mx3fb, mx3fb->backlight_level);
}
acquire_console_sem();
- fb_set_suspend(drv_data->fbi, 0);
+ fb_set_suspend(mx3fb->fbi, 0);
release_console_sem();
return 0;
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
index dfb72f5e4c96..148cbcc39602 100644
--- a/drivers/video/omap/dispc.c
+++ b/drivers/video/omap/dispc.c
@@ -880,20 +880,22 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
static int get_dss_clocks(void)
{
- if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, "dss_ick")))) {
- dev_err(dispc.fbdev->dev, "can't get dss_ick\n");
+ dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick");
+ if (IS_ERR(dispc.dss_ick)) {
+ dev_err(dispc.fbdev->dev, "can't get ick\n");
return PTR_ERR(dispc.dss_ick);
}
- if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck")))) {
+ dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck");
+ if (IS_ERR(dispc.dss1_fck)) {
dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
clk_put(dispc.dss_ick);
return PTR_ERR(dispc.dss1_fck);
}
- if (IS_ERR((dispc.dss_54m_fck =
- clk_get(dispc.fbdev->dev, "dss_54m_fck")))) {
- dev_err(dispc.fbdev->dev, "can't get dss_54m_fck\n");
+ dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck");
+ if (IS_ERR(dispc.dss_54m_fck)) {
+ dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
clk_put(dispc.dss_ick);
clk_put(dispc.dss1_fck);
return PTR_ERR(dispc.dss_54m_fck);
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c
index a13c8dcad2a8..9332d6ca6456 100644
--- a/drivers/video/omap/rfbi.c
+++ b/drivers/video/omap/rfbi.c
@@ -83,12 +83,14 @@ static inline u32 rfbi_read_reg(int idx)
static int rfbi_get_clocks(void)
{
- if (IS_ERR((rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "dss_ick")))) {
- dev_err(rfbi.fbdev->dev, "can't get dss_ick\n");
+ rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "ick");
+ if (IS_ERR(rfbi.dss_ick)) {
+ dev_err(rfbi.fbdev->dev, "can't get ick\n");
return PTR_ERR(rfbi.dss_ick);
}
- if (IS_ERR((rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck")))) {
+ rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck");
+ if (IS_ERR(rfbi.dss1_fck)) {
dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n");
clk_put(rfbi.dss_ick);
return PTR_ERR(rfbi.dss1_fck);
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 5e9c6302433b..d3a568e6b169 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -947,7 +947,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
int win;
for (win = 0; win <= S3C_FB_MAX_WIN; win++)
- s3c_fb_release_win(sfb, sfb->windows[win]);
+ if (sfb->windows[win])
+ s3c_fb_release_win(sfb, sfb->windows[win]);
iounmap(sfb->regs);
@@ -985,11 +986,20 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state)
static int s3c_fb_resume(struct platform_device *pdev)
{
struct s3c_fb *sfb = platform_get_drvdata(pdev);
+ struct s3c_fb_platdata *pd = sfb->pdata;
struct s3c_fb_win *win;
int win_no;
clk_enable(sfb->bus_clk);
+ /* setup registers */
+ writel(pd->vidcon1, sfb->regs + VIDCON1);
+
+ /* zero all windows before we do anything */
+ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++)
+ s3c_fb_clear_win(sfb, win_no);
+
+ /* restore framebuffers */
for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
win = sfb->windows[win_no];
if (!win)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 92ea0ab44ce2..f10d2fbeda06 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -47,6 +47,7 @@ struct sh_mobile_lcdc_priv {
#endif
unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2];
+ int started;
};
/* shared registers */
@@ -451,6 +452,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* start the lcdc */
sh_mobile_lcdc_start_stop(priv, 1);
+ priv->started = 1;
/* tell the board code to enable the panel */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -493,7 +495,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
}
/* stop the lcdc */
- sh_mobile_lcdc_start_stop(priv, 0);
+ if (priv->started) {
+ sh_mobile_lcdc_start_stop(priv, 0);
+ priv->started = 0;
+ }
/* stop clocks */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
diff --git a/drivers/video/sysfillrect.c b/drivers/video/sysfillrect.c
index f94d6b6e29ee..33ee3d34f9d2 100644
--- a/drivers/video/sysfillrect.c
+++ b/drivers/video/sysfillrect.c
@@ -124,7 +124,7 @@ bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
/* Trailing bits */
if (last)
- *dst = comp(pat, *dst, first);
+ *dst = comp(pat, *dst, last);
}
}
@@ -242,7 +242,7 @@ bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
- unsigned long pat, fg;
+ unsigned long pat, pat2, fg;
unsigned long width = rect->width, height = rect->height;
int bits = BITS_PER_LONG, bytes = bits >> 3;
u32 bpp = p->var.bits_per_pixel;
@@ -292,17 +292,16 @@ void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
dst_idx += p->fix.line_length*8;
}
} else {
- int right;
- int r;
- int rot = (left-dst_idx) % bpp;
+ int right, r;
void (*fill_op)(struct fb_info *p, unsigned long *dst,
int dst_idx, unsigned long pat, int left,
int right, unsigned n, int bits) = NULL;
-
- /* rotate pattern to correct start position */
- pat = pat << rot | pat >> (bpp-rot);
-
- right = bpp-left;
+#ifdef __LITTLE_ENDIAN
+ right = left;
+ left = bpp - right;
+#else
+ right = bpp - left;
+#endif
switch (rect->rop) {
case ROP_XOR:
fill_op = bitfill_unaligned_rev;
@@ -311,18 +310,19 @@ void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
fill_op = bitfill_unaligned;
break;
default:
- printk(KERN_ERR "cfb_fillrect(): unknown rop, "
+ printk(KERN_ERR "sys_fillrect(): unknown rop, "
"defaulting to ROP_COPY\n");
fill_op = bitfill_unaligned;
break;
}
while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
+ dst += dst_idx / bits;
dst_idx &= (bits - 1);
- fill_op(p, dst, dst_idx, pat, left, right,
+ r = dst_idx % bpp;
+ /* rotate pattern to the correct start position */
+ pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
+ fill_op(p, dst, dst_idx, pat2, left, right,
width*bpp, bits);
- r = (p->fix.line_length*8) % bpp;
- pat = pat << (bpp-r) | pat >> r;
dst_idx += p->fix.line_length*8;
}
}
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index 421770b5e6ab..ca5b4643a401 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -45,7 +45,7 @@ static struct fb_fix_screeninfo uvesafb_fix __devinitdata = {
static int mtrr __devinitdata = 3; /* enable mtrr by default */
static int blank = 1; /* enable blanking by default */
static int ypan = 1; /* 0: scroll, 1: ypan, 2: ywrap */
-static int pmi_setpal __devinitdata = 1; /* use PMI for palette changes */
+static bool pmi_setpal __devinitdata = true; /* use PMI for palette changes */
static int nocrtc __devinitdata; /* ignore CRTC settings */
static int noedid __devinitdata; /* don't try DDC transfers */
static int vram_remap __devinitdata; /* set amt. of memory to be used */
@@ -2002,11 +2002,7 @@ static void __devexit uvesafb_exit(void)
module_exit(uvesafb_exit);
-static int param_get_scroll(char *buffer, struct kernel_param *kp)
-{
- return 0;
-}
-
+#define param_get_scroll NULL
static int param_set_scroll(const char *val, struct kernel_param *kp)
{
ypan = 0;
@@ -2017,6 +2013,8 @@ static int param_set_scroll(const char *val, struct kernel_param *kp)
ypan = 1;
else if (!strcmp(val, "ywrap"))
ypan = 2;
+ else
+ return -EINVAL;
return 0;
}
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index e327b84820d2..a0fec298216e 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -2103,7 +2103,7 @@ static void viafb_remove_proc(struct proc_dir_entry *viafb_entry)
static int __devinit via_pci_probe(void)
{
- unsigned int default_xres, default_yres;
+ unsigned long default_xres, default_yres;
char *tmpc, *tmpm;
char *tmpc_sec, *tmpm_sec;
int vmode_index;
@@ -2196,8 +2196,8 @@ static int __devinit via_pci_probe(void)
viafb_FB_MM = viaparinfo->fbmem_virt;
tmpm = viafb_mode;
tmpc = strsep(&tmpm, "x");
- strict_strtoul(tmpc, 0, (unsigned long *)&default_xres);
- strict_strtoul(tmpm, 0, (unsigned long *)&default_yres);
+ strict_strtoul(tmpc, 0, &default_xres);
+ strict_strtoul(tmpm, 0, &default_yres);
vmode_index = viafb_get_mode_index(default_xres, default_yres, 0);
DEBUG_MSG(KERN_INFO "0->index=%d\n", vmode_index);
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 018c070a357f..3a43ebf83a49 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -31,21 +31,37 @@ static ssize_t modalias_show(struct device *_d,
return sprintf(buf, "virtio:d%08Xv%08X\n",
dev->id.device, dev->id.vendor);
}
+static ssize_t features_show(struct device *_d,
+ struct device_attribute *attr, char *buf)
+{
+ struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+ unsigned int i;
+ ssize_t len = 0;
+
+ /* We actually represent this as a bitstring, as it could be
+ * arbitrary length in future. */
+ for (i = 0; i < ARRAY_SIZE(dev->features)*BITS_PER_LONG; i++)
+ len += sprintf(buf+len, "%c",
+ test_bit(i, dev->features) ? '1' : '0');
+ len += sprintf(buf+len, "\n");
+ return len;
+}
static struct device_attribute virtio_dev_attrs[] = {
__ATTR_RO(device),
__ATTR_RO(vendor),
__ATTR_RO(status),
__ATTR_RO(modalias),
+ __ATTR_RO(features),
__ATTR_NULL
};
static inline int virtio_id_match(const struct virtio_device *dev,
const struct virtio_device_id *id)
{
- if (id->device != dev->id.device)
+ if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)
return 0;
- return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor != dev->id.vendor;
+ return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
}
/* This looks through all the IDs a driver claims to support. If any of them
@@ -118,13 +134,14 @@ static int virtio_dev_probe(struct device *_d)
if (device_features & (1 << i))
set_bit(i, dev->features);
+ dev->config->finalize_features(dev);
+
err = drv->probe(dev);
if (err)
add_status(dev, VIRTIO_CONFIG_S_FAILED);
- else {
- dev->config->finalize_features(dev);
+ else
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
- }
+
return err;
}
@@ -185,6 +202,8 @@ int register_virtio_device(struct virtio_device *dev)
/* Acknowledge that we've seen the device. */
add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
+ INIT_LIST_HEAD(&dev->vqs);
+
/* device_register() causes the bus infrastructure to look for a
* matching driver. */
err = device_register(&dev->dev);
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 9c76a061a04d..26b278264796 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -204,6 +204,9 @@ static int balloon(void *_vballoon)
static int virtballoon_probe(struct virtio_device *vdev)
{
struct virtio_balloon *vb;
+ struct virtqueue *vqs[2];
+ vq_callback_t *callbacks[] = { balloon_ack, balloon_ack };
+ const char *names[] = { "inflate", "deflate" };
int err;
vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
@@ -218,22 +221,17 @@ static int virtballoon_probe(struct virtio_device *vdev)
vb->vdev = vdev;
/* We expect two virtqueues. */
- vb->inflate_vq = vdev->config->find_vq(vdev, 0, balloon_ack);
- if (IS_ERR(vb->inflate_vq)) {
- err = PTR_ERR(vb->inflate_vq);
+ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+ if (err)
goto out_free_vb;
- }
- vb->deflate_vq = vdev->config->find_vq(vdev, 1, balloon_ack);
- if (IS_ERR(vb->deflate_vq)) {
- err = PTR_ERR(vb->deflate_vq);
- goto out_del_inflate_vq;
- }
+ vb->inflate_vq = vqs[0];
+ vb->deflate_vq = vqs[1];
vb->thread = kthread_run(balloon, vb, "vballoon");
if (IS_ERR(vb->thread)) {
err = PTR_ERR(vb->thread);
- goto out_del_deflate_vq;
+ goto out_del_vqs;
}
vb->tell_host_first
@@ -241,10 +239,8 @@ static int virtballoon_probe(struct virtio_device *vdev)
return 0;
-out_del_deflate_vq:
- vdev->config->del_vq(vb->deflate_vq);
-out_del_inflate_vq:
- vdev->config->del_vq(vb->inflate_vq);
+out_del_vqs:
+ vdev->config->del_vqs(vdev);
out_free_vb:
kfree(vb);
out:
@@ -264,8 +260,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
/* Now we reset the device so we can clean up the queues. */
vdev->config->reset(vdev);
- vdev->config->del_vq(vb->deflate_vq);
- vdev->config->del_vq(vb->inflate_vq);
+ vdev->config->del_vqs(vdev);
kfree(vb);
}
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 330aacbdec1f..193c8f0e5cc5 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -42,6 +42,26 @@ struct virtio_pci_device
/* a list of queues so we can dispatch IRQs */
spinlock_t lock;
struct list_head virtqueues;
+
+ /* MSI-X support */
+ int msix_enabled;
+ int intx_enabled;
+ struct msix_entry *msix_entries;
+ /* Name strings for interrupts. This size should be enough,
+ * and I'm too lazy to allocate each name separately. */
+ char (*msix_names)[256];
+ /* Number of available vectors */
+ unsigned msix_vectors;
+ /* Vectors allocated */
+ unsigned msix_used_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+ VP_MSIX_CONFIG_VECTOR = 0,
+ VP_MSIX_VQ_VECTOR = 1,
};
struct virtio_pci_vq_info
@@ -60,6 +80,9 @@ struct virtio_pci_vq_info
/* the list node for the virtqueues list */
struct list_head node;
+
+ /* MSI-X vector (or none) */
+ unsigned vector;
};
/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
@@ -109,7 +132,8 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
- void __iomem *ioaddr = vp_dev->ioaddr + VIRTIO_PCI_CONFIG + offset;
+ void __iomem *ioaddr = vp_dev->ioaddr +
+ VIRTIO_PCI_CONFIG(vp_dev) + offset;
u8 *ptr = buf;
int i;
@@ -123,7 +147,8 @@ static void vp_set(struct virtio_device *vdev, unsigned offset,
const void *buf, unsigned len)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
- void __iomem *ioaddr = vp_dev->ioaddr + VIRTIO_PCI_CONFIG + offset;
+ void __iomem *ioaddr = vp_dev->ioaddr +
+ VIRTIO_PCI_CONFIG(vp_dev) + offset;
const u8 *ptr = buf;
int i;
@@ -164,6 +189,37 @@ static void vp_notify(struct virtqueue *vq)
iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
}
+/* Handle a configuration change: Tell driver if it wants to know. */
+static irqreturn_t vp_config_changed(int irq, void *opaque)
+{
+ struct virtio_pci_device *vp_dev = opaque;
+ struct virtio_driver *drv;
+ drv = container_of(vp_dev->vdev.dev.driver,
+ struct virtio_driver, driver);
+
+ if (drv && drv->config_changed)
+ drv->config_changed(&vp_dev->vdev);
+ return IRQ_HANDLED;
+}
+
+/* Notify all virtqueues on an interrupt. */
+static irqreturn_t vp_vring_interrupt(int irq, void *opaque)
+{
+ struct virtio_pci_device *vp_dev = opaque;
+ struct virtio_pci_vq_info *info;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vp_dev->lock, flags);
+ list_for_each_entry(info, &vp_dev->virtqueues, node) {
+ if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&vp_dev->lock, flags);
+
+ return ret;
+}
+
/* A small wrapper to also acknowledge the interrupt when it's handled.
* I really need an EIO hook for the vring so I can ack the interrupt once we
* know that we'll be handling the IRQ but before we invoke the callback since
@@ -173,9 +229,6 @@ static void vp_notify(struct virtqueue *vq)
static irqreturn_t vp_interrupt(int irq, void *opaque)
{
struct virtio_pci_device *vp_dev = opaque;
- struct virtio_pci_vq_info *info;
- irqreturn_t ret = IRQ_NONE;
- unsigned long flags;
u8 isr;
/* reading the ISR has the effect of also clearing it so it's very
@@ -187,34 +240,137 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
return IRQ_NONE;
/* Configuration change? Tell driver if it wants to know. */
- if (isr & VIRTIO_PCI_ISR_CONFIG) {
- struct virtio_driver *drv;
- drv = container_of(vp_dev->vdev.dev.driver,
- struct virtio_driver, driver);
+ if (isr & VIRTIO_PCI_ISR_CONFIG)
+ vp_config_changed(irq, opaque);
- if (drv && drv->config_changed)
- drv->config_changed(&vp_dev->vdev);
+ return vp_vring_interrupt(irq, opaque);
+}
+
+static void vp_free_vectors(struct virtio_device *vdev)
+{
+ struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+ int i;
+
+ if (vp_dev->intx_enabled) {
+ free_irq(vp_dev->pci_dev->irq, vp_dev);
+ vp_dev->intx_enabled = 0;
}
- spin_lock_irqsave(&vp_dev->lock, flags);
- list_for_each_entry(info, &vp_dev->virtqueues, node) {
- if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
- ret = IRQ_HANDLED;
+ for (i = 0; i < vp_dev->msix_used_vectors; ++i)
+ free_irq(vp_dev->msix_entries[i].vector, vp_dev);
+ vp_dev->msix_used_vectors = 0;
+
+ if (vp_dev->msix_enabled) {
+ /* Disable the vector used for configuration */
+ iowrite16(VIRTIO_MSI_NO_VECTOR,
+ vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+ /* Flush the write out to device */
+ ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+
+ vp_dev->msix_enabled = 0;
+ pci_disable_msix(vp_dev->pci_dev);
}
- spin_unlock_irqrestore(&vp_dev->lock, flags);
+}
- return ret;
+static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
+ int *options, int noptions)
+{
+ int i;
+ for (i = 0; i < noptions; ++i)
+ if (!pci_enable_msix(dev, entries, options[i]))
+ return options[i];
+ return -EBUSY;
+}
+
+static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs)
+{
+ struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+ const char *name = dev_name(&vp_dev->vdev.dev);
+ unsigned i, v;
+ int err = -ENOMEM;
+ /* We want at most one vector per queue and one for config changes.
+ * Fallback to separate vectors for config and a shared for queues.
+ * Finally fall back to regular interrupts. */
+ int options[] = { max_vqs + 1, 2 };
+ int nvectors = max(options[0], options[1]);
+
+ vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
+ GFP_KERNEL);
+ if (!vp_dev->msix_entries)
+ goto error_entries;
+ vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names,
+ GFP_KERNEL);
+ if (!vp_dev->msix_names)
+ goto error_names;
+
+ for (i = 0; i < nvectors; ++i)
+ vp_dev->msix_entries[i].entry = i;
+
+ err = vp_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries,
+ options, ARRAY_SIZE(options));
+ if (err < 0) {
+ /* Can't allocate enough MSI-X vectors, use regular interrupt */
+ vp_dev->msix_vectors = 0;
+ err = request_irq(vp_dev->pci_dev->irq, vp_interrupt,
+ IRQF_SHARED, name, vp_dev);
+ if (err)
+ goto error_irq;
+ vp_dev->intx_enabled = 1;
+ } else {
+ vp_dev->msix_vectors = err;
+ vp_dev->msix_enabled = 1;
+
+ /* Set the vector used for configuration */
+ v = vp_dev->msix_used_vectors;
+ snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+ "%s-config", name);
+ err = request_irq(vp_dev->msix_entries[v].vector,
+ vp_config_changed, 0, vp_dev->msix_names[v],
+ vp_dev);
+ if (err)
+ goto error_irq;
+ ++vp_dev->msix_used_vectors;
+
+ iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+ /* Verify we had enough resources to assign the vector */
+ v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+ if (v == VIRTIO_MSI_NO_VECTOR) {
+ err = -EBUSY;
+ goto error_irq;
+ }
+ }
+
+ if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) {
+ /* Shared vector for all VQs */
+ v = vp_dev->msix_used_vectors;
+ snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+ "%s-virtqueues", name);
+ err = request_irq(vp_dev->msix_entries[v].vector,
+ vp_vring_interrupt, 0, vp_dev->msix_names[v],
+ vp_dev);
+ if (err)
+ goto error_irq;
+ ++vp_dev->msix_used_vectors;
+ }
+ return 0;
+error_irq:
+ vp_free_vectors(vdev);
+ kfree(vp_dev->msix_names);
+error_names:
+ kfree(vp_dev->msix_entries);
+error_entries:
+ return err;
}
-/* the config->find_vq() implementation */
static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
- void (*callback)(struct virtqueue *vq))
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
struct virtio_pci_vq_info *info;
struct virtqueue *vq;
unsigned long flags, size;
- u16 num;
+ u16 num, vector;
int err;
/* Select the queue we're interested in */
@@ -233,6 +389,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
info->queue_index = index;
info->num = num;
+ info->vector = VIRTIO_MSI_NO_VECTOR;
size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
@@ -247,7 +404,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
/* create the vring */
vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
- vdev, info->queue, vp_notify, callback);
+ vdev, info->queue, vp_notify, callback, name);
if (!vq) {
err = -ENOMEM;
goto out_activate_queue;
@@ -256,12 +413,43 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
vq->priv = info;
info->vq = vq;
+ /* allocate per-vq vector if available and necessary */
+ if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) {
+ vector = vp_dev->msix_used_vectors;
+ snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names,
+ "%s-%s", dev_name(&vp_dev->vdev.dev), name);
+ err = request_irq(vp_dev->msix_entries[vector].vector,
+ vring_interrupt, 0,
+ vp_dev->msix_names[vector], vq);
+ if (err)
+ goto out_request_irq;
+ info->vector = vector;
+ ++vp_dev->msix_used_vectors;
+ } else
+ vector = VP_MSIX_VQ_VECTOR;
+
+ if (callback && vp_dev->msix_enabled) {
+ iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+ vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+ if (vector == VIRTIO_MSI_NO_VECTOR) {
+ err = -EBUSY;
+ goto out_assign;
+ }
+ }
+
spin_lock_irqsave(&vp_dev->lock, flags);
list_add(&info->node, &vp_dev->virtqueues);
spin_unlock_irqrestore(&vp_dev->lock, flags);
return vq;
+out_assign:
+ if (info->vector != VIRTIO_MSI_NO_VECTOR) {
+ free_irq(vp_dev->msix_entries[info->vector].vector, vq);
+ --vp_dev->msix_used_vectors;
+ }
+out_request_irq:
+ vring_del_virtqueue(vq);
out_activate_queue:
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
free_pages_exact(info->queue, size);
@@ -270,21 +458,27 @@ out_info:
return ERR_PTR(err);
}
-/* the config->del_vq() implementation */
static void vp_del_vq(struct virtqueue *vq)
{
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
struct virtio_pci_vq_info *info = vq->priv;
- unsigned long flags, size;
+ unsigned long size;
- spin_lock_irqsave(&vp_dev->lock, flags);
- list_del(&info->node);
- spin_unlock_irqrestore(&vp_dev->lock, flags);
+ iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+ if (info->vector != VIRTIO_MSI_NO_VECTOR)
+ free_irq(vp_dev->msix_entries[info->vector].vector, vq);
+
+ if (vp_dev->msix_enabled) {
+ iowrite16(VIRTIO_MSI_NO_VECTOR,
+ vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+ /* Flush the write out to device */
+ ioread8(vp_dev->ioaddr + VIRTIO_PCI_ISR);
+ }
vring_del_virtqueue(vq);
/* Select and deactivate the queue */
- iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
@@ -292,14 +486,57 @@ static void vp_del_vq(struct virtqueue *vq)
kfree(info);
}
+/* the config->del_vqs() implementation */
+static void vp_del_vqs(struct virtio_device *vdev)
+{
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ vp_del_vq(vq);
+
+ vp_free_vectors(vdev);
+}
+
+/* the config->find_vqs() implementation */
+static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ int vectors = 0;
+ int i, err;
+
+ /* How many vectors would we like? */
+ for (i = 0; i < nvqs; ++i)
+ if (callbacks[i])
+ ++vectors;
+
+ err = vp_request_vectors(vdev, vectors);
+ if (err)
+ goto error_request;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i]))
+ goto error_find;
+ }
+ return 0;
+
+error_find:
+ vp_del_vqs(vdev);
+
+error_request:
+ return PTR_ERR(vqs[i]);
+}
+
static struct virtio_config_ops virtio_pci_config_ops = {
.get = vp_get,
.set = vp_set,
.get_status = vp_get_status,
.set_status = vp_set_status,
.reset = vp_reset,
- .find_vq = vp_find_vq,
- .del_vq = vp_del_vq,
+ .find_vqs = vp_find_vqs,
+ .del_vqs = vp_del_vqs,
.get_features = vp_get_features,
.finalize_features = vp_finalize_features,
};
@@ -310,7 +547,7 @@ static void virtio_pci_release_dev(struct device *_d)
struct virtio_pci_device *vp_dev = to_vp_device(dev);
struct pci_dev *pci_dev = vp_dev->pci_dev;
- free_irq(pci_dev->irq, vp_dev);
+ vp_del_vqs(dev);
pci_set_drvdata(pci_dev, NULL);
pci_iounmap(pci_dev, vp_dev->ioaddr);
pci_release_regions(pci_dev);
@@ -369,21 +606,13 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
vp_dev->vdev.id.device = pci_dev->subsystem_device;
- /* register a handler for the queue with the PCI device's interrupt */
- err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
- dev_name(&vp_dev->vdev.dev), vp_dev);
- if (err)
- goto out_set_drvdata;
-
/* finally register the virtio device */
err = register_virtio_device(&vp_dev->vdev);
if (err)
- goto out_req_irq;
+ goto out_set_drvdata;
return 0;
-out_req_irq:
- free_irq(pci_dev->irq, vp_dev);
out_set_drvdata:
pci_set_drvdata(pci_dev, NULL);
pci_iounmap(pci_dev, vp_dev->ioaddr);
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 5c52369ab9bb..a882f2606515 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -23,21 +23,30 @@
#ifdef DEBUG
/* For development, we want to crash whenever the ring is screwed. */
-#define BAD_RING(_vq, fmt...) \
- do { dev_err(&(_vq)->vq.vdev->dev, fmt); BUG(); } while(0)
+#define BAD_RING(_vq, fmt, args...) \
+ do { \
+ dev_err(&(_vq)->vq.vdev->dev, \
+ "%s:"fmt, (_vq)->vq.name, ##args); \
+ BUG(); \
+ } while (0)
/* Caller is supposed to guarantee no reentry. */
#define START_USE(_vq) \
do { \
if ((_vq)->in_use) \
- panic("in_use = %i\n", (_vq)->in_use); \
+ panic("%s:in_use = %i\n", \
+ (_vq)->vq.name, (_vq)->in_use); \
(_vq)->in_use = __LINE__; \
mb(); \
- } while(0)
+ } while (0)
#define END_USE(_vq) \
do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0)
#else
-#define BAD_RING(_vq, fmt...) \
- do { dev_err(&_vq->vq.vdev->dev, fmt); (_vq)->broken = true; } while(0)
+#define BAD_RING(_vq, fmt, args...) \
+ do { \
+ dev_err(&_vq->vq.vdev->dev, \
+ "%s:"fmt, (_vq)->vq.name, ##args); \
+ (_vq)->broken = true; \
+ } while (0)
#define START_USE(vq)
#define END_USE(vq)
#endif
@@ -52,6 +61,9 @@ struct vring_virtqueue
/* Other side has made a mess, don't try any more. */
bool broken;
+ /* Host supports indirect buffers */
+ bool indirect;
+
/* Number of free buffers */
unsigned int num_free;
/* Head of free buffer list. */
@@ -76,6 +88,55 @@ struct vring_virtqueue
#define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
+/* Set up an indirect table of descriptors and add it to the queue. */
+static int vring_add_indirect(struct vring_virtqueue *vq,
+ struct scatterlist sg[],
+ unsigned int out,
+ unsigned int in)
+{
+ struct vring_desc *desc;
+ unsigned head;
+ int i;
+
+ desc = kmalloc((out + in) * sizeof(struct vring_desc), GFP_ATOMIC);
+ if (!desc)
+ return vq->vring.num;
+
+ /* Transfer entries from the sg list into the indirect page */
+ for (i = 0; i < out; i++) {
+ desc[i].flags = VRING_DESC_F_NEXT;
+ desc[i].addr = sg_phys(sg);
+ desc[i].len = sg->length;
+ desc[i].next = i+1;
+ sg++;
+ }
+ for (; i < (out + in); i++) {
+ desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
+ desc[i].addr = sg_phys(sg);
+ desc[i].len = sg->length;
+ desc[i].next = i+1;
+ sg++;
+ }
+
+ /* Last one doesn't continue. */
+ desc[i-1].flags &= ~VRING_DESC_F_NEXT;
+ desc[i-1].next = 0;
+
+ /* We're about to use a buffer */
+ vq->num_free--;
+
+ /* Use a single buffer which doesn't continue */
+ head = vq->free_head;
+ vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
+ vq->vring.desc[head].addr = virt_to_phys(desc);
+ vq->vring.desc[head].len = i * sizeof(struct vring_desc);
+
+ /* Update free pointer */
+ vq->free_head = vq->vring.desc[head].next;
+
+ return head;
+}
+
static int vring_add_buf(struct virtqueue *_vq,
struct scatterlist sg[],
unsigned int out,
@@ -85,12 +146,21 @@ static int vring_add_buf(struct virtqueue *_vq,
struct vring_virtqueue *vq = to_vvq(_vq);
unsigned int i, avail, head, uninitialized_var(prev);
+ START_USE(vq);
+
BUG_ON(data == NULL);
+
+ /* If the host supports indirect descriptor tables, and we have multiple
+ * buffers, then go indirect. FIXME: tune this threshold */
+ if (vq->indirect && (out + in) > 1 && vq->num_free) {
+ head = vring_add_indirect(vq, sg, out, in);
+ if (head != vq->vring.num)
+ goto add_head;
+ }
+
BUG_ON(out + in > vq->vring.num);
BUG_ON(out + in == 0);
- START_USE(vq);
-
if (vq->num_free < out + in) {
pr_debug("Can't add buf len %i - avail = %i\n",
out + in, vq->num_free);
@@ -127,6 +197,7 @@ static int vring_add_buf(struct virtqueue *_vq,
/* Update free pointer */
vq->free_head = i;
+add_head:
/* Set token. */
vq->data[head] = data;
@@ -170,6 +241,11 @@ static void detach_buf(struct vring_virtqueue *vq, unsigned int head)
/* Put back on free list: find end */
i = head;
+
+ /* Free the indirect table */
+ if (vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT)
+ kfree(phys_to_virt(vq->vring.desc[i].addr));
+
while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
i = vq->vring.desc[i].next;
vq->num_free++;
@@ -284,7 +360,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
struct virtio_device *vdev,
void *pages,
void (*notify)(struct virtqueue *),
- void (*callback)(struct virtqueue *))
+ void (*callback)(struct virtqueue *),
+ const char *name)
{
struct vring_virtqueue *vq;
unsigned int i;
@@ -303,14 +380,18 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
vq->vq.callback = callback;
vq->vq.vdev = vdev;
vq->vq.vq_ops = &vring_vq_ops;
+ vq->vq.name = name;
vq->notify = notify;
vq->broken = false;
vq->last_used_idx = 0;
vq->num_added = 0;
+ list_add_tail(&vq->vq.list, &vdev->vqs);
#ifdef DEBUG
vq->in_use = false;
#endif
+ vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC);
+
/* No callback? Tell other side not to bother us. */
if (!callback)
vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
@@ -327,6 +408,7 @@ EXPORT_SYMBOL_GPL(vring_new_virtqueue);
void vring_del_virtqueue(struct virtqueue *vq)
{
+ list_del(&vq->list);
kfree(to_vvq(vq));
}
EXPORT_SYMBOL_GPL(vring_del_virtqueue);
@@ -338,6 +420,8 @@ void vring_transport_features(struct virtio_device *vdev)
for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) {
switch (i) {
+ case VIRTIO_RING_F_INDIRECT_DESC:
+ break;
default:
/* We don't understand this bit. */
clear_bit(i, vdev->features);
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 8ac9cddac575..cab100acf983 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -18,6 +18,16 @@ config XEN_SCRUB_PAGES
secure, but slightly less efficient.
If in doubt, say yes.
+config XEN_DEV_EVTCHN
+ tristate "Xen /dev/xen/evtchn device"
+ depends on XEN
+ default y
+ help
+ The evtchn driver allows a userspace process to triger event
+ channels and to receive notification of an event channel
+ firing.
+ If in doubt, say yes.
+
config XENFS
tristate "Xen filesystem"
depends on XEN
@@ -41,3 +51,13 @@ config XEN_COMPAT_XENFS
a xen platform.
If in doubt, say yes.
+config XEN_SYS_HYPERVISOR
+ bool "Create xen entries under /sys/hypervisor"
+ depends on XEN && SYSFS
+ select SYS_HYPERVISOR
+ default y
+ help
+ Create entries under /sys/hypervisor describing the Xen
+ hypervisor environment. When running native or in another
+ virtual environment, /sys/hypervisor will still be present,
+ but will have no xen contents. \ No newline at end of file
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index ff8accc9e103..ec2a39b1e26f 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -4,4 +4,6 @@ obj-y += xenbus/
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
-obj-$(CONFIG_XENFS) += xenfs/ \ No newline at end of file
+obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o
+obj-$(CONFIG_XENFS) += xenfs/
+obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o \ No newline at end of file
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index efa4b363ce72..f5bbd9e83416 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -513,7 +513,8 @@ static ssize_t show_target(struct sys_device *dev, struct sysdev_attribute *attr
char *buf)
{
return sprintf(buf, "%llu\n",
- (u64)balloon_stats.target_pages << PAGE_SHIFT);
+ (unsigned long long)balloon_stats.target_pages
+ << PAGE_SHIFT);
}
static ssize_t store_target(struct sys_device *dev,
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 30963af5dba0..891d2e90753a 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -151,6 +151,12 @@ static unsigned int evtchn_from_irq(unsigned irq)
return info_for_irq(irq)->evtchn;
}
+unsigned irq_from_evtchn(unsigned int evtchn)
+{
+ return evtchn_to_irq[evtchn];
+}
+EXPORT_SYMBOL_GPL(irq_from_evtchn);
+
static enum ipi_vector ipi_from_irq(unsigned irq)
{
struct irq_info *info = info_for_irq(irq);
@@ -335,7 +341,7 @@ static int find_unbound_irq(void)
if (irq == nr_irqs)
panic("No available IRQ to bind to: increase nr_irqs!\n");
- desc = irq_to_desc_alloc_cpu(irq, 0);
+ desc = irq_to_desc_alloc_node(irq, 0);
if (WARN_ON(desc == NULL))
return -1;
@@ -688,13 +694,13 @@ void rebind_evtchn_irq(int evtchn, int irq)
}
/* Rebind an evtchn so that it gets delivered to a specific cpu */
-static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
+static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
{
struct evtchn_bind_vcpu bind_vcpu;
int evtchn = evtchn_from_irq(irq);
if (!VALID_EVTCHN(evtchn))
- return;
+ return -1;
/* Send future instances of this interrupt to other vcpu. */
bind_vcpu.port = evtchn;
@@ -707,13 +713,15 @@ static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
*/
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0)
bind_evtchn_to_cpu(evtchn, tcpu);
-}
+ return 0;
+}
-static void set_affinity_irq(unsigned irq, const struct cpumask *dest)
+static int set_affinity_irq(unsigned irq, const struct cpumask *dest)
{
unsigned tcpu = cpumask_first(dest);
- rebind_irq_to_cpu(irq, tcpu);
+
+ return rebind_irq_to_cpu(irq, tcpu);
}
int resend_irq_on_evtchn(unsigned int irq)
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
new file mode 100644
index 000000000000..af031950f9b1
--- /dev/null
+++ b/drivers/xen/evtchn.c
@@ -0,0 +1,507 @@
+/******************************************************************************
+ * evtchn.c
+ *
+ * Driver for receiving and demuxing event-channel signals.
+ *
+ * Copyright (c) 2004-2005, K A Fraser
+ * Multi-process extensions Copyright (c) 2004, Steven Smith
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/poll.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <xen/events.h>
+#include <xen/evtchn.h>
+#include <asm/xen/hypervisor.h>
+
+struct per_user_data {
+ struct mutex bind_mutex; /* serialize bind/unbind operations */
+
+ /* Notification ring, accessed via /dev/xen/evtchn. */
+#define EVTCHN_RING_SIZE (PAGE_SIZE / sizeof(evtchn_port_t))
+#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
+ evtchn_port_t *ring;
+ unsigned int ring_cons, ring_prod, ring_overflow;
+ struct mutex ring_cons_mutex; /* protect against concurrent readers */
+
+ /* Processes wait on this queue when ring is empty. */
+ wait_queue_head_t evtchn_wait;
+ struct fasync_struct *evtchn_async_queue;
+ const char *name;
+};
+
+/* Who's bound to each port? */
+static struct per_user_data *port_user[NR_EVENT_CHANNELS];
+static DEFINE_SPINLOCK(port_user_lock); /* protects port_user[] and ring_prod */
+
+irqreturn_t evtchn_interrupt(int irq, void *data)
+{
+ unsigned int port = (unsigned long)data;
+ struct per_user_data *u;
+
+ spin_lock(&port_user_lock);
+
+ u = port_user[port];
+
+ disable_irq_nosync(irq);
+
+ if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
+ u->ring[EVTCHN_RING_MASK(u->ring_prod)] = port;
+ wmb(); /* Ensure ring contents visible */
+ if (u->ring_cons == u->ring_prod++) {
+ wake_up_interruptible(&u->evtchn_wait);
+ kill_fasync(&u->evtchn_async_queue,
+ SIGIO, POLL_IN);
+ }
+ } else {
+ u->ring_overflow = 1;
+ }
+
+ spin_unlock(&port_user_lock);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t evtchn_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc;
+ unsigned int c, p, bytes1 = 0, bytes2 = 0;
+ struct per_user_data *u = file->private_data;
+
+ /* Whole number of ports. */
+ count &= ~(sizeof(evtchn_port_t)-1);
+
+ if (count == 0)
+ return 0;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ for (;;) {
+ mutex_lock(&u->ring_cons_mutex);
+
+ rc = -EFBIG;
+ if (u->ring_overflow)
+ goto unlock_out;
+
+ c = u->ring_cons;
+ p = u->ring_prod;
+ if (c != p)
+ break;
+
+ mutex_unlock(&u->ring_cons_mutex);
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ rc = wait_event_interruptible(u->evtchn_wait,
+ u->ring_cons != u->ring_prod);
+ if (rc)
+ return rc;
+ }
+
+ /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
+ if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
+ bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
+ sizeof(evtchn_port_t);
+ bytes2 = EVTCHN_RING_MASK(p) * sizeof(evtchn_port_t);
+ } else {
+ bytes1 = (p - c) * sizeof(evtchn_port_t);
+ bytes2 = 0;
+ }
+
+ /* Truncate chunks according to caller's maximum byte count. */
+ if (bytes1 > count) {
+ bytes1 = count;
+ bytes2 = 0;
+ } else if ((bytes1 + bytes2) > count) {
+ bytes2 = count - bytes1;
+ }
+
+ rc = -EFAULT;
+ rmb(); /* Ensure that we see the port before we copy it. */
+ if (copy_to_user(buf, &u->ring[EVTCHN_RING_MASK(c)], bytes1) ||
+ ((bytes2 != 0) &&
+ copy_to_user(&buf[bytes1], &u->ring[0], bytes2)))
+ goto unlock_out;
+
+ u->ring_cons += (bytes1 + bytes2) / sizeof(evtchn_port_t);
+ rc = bytes1 + bytes2;
+
+ unlock_out:
+ mutex_unlock(&u->ring_cons_mutex);
+ return rc;
+}
+
+static ssize_t evtchn_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc, i;
+ evtchn_port_t *kbuf = (evtchn_port_t *)__get_free_page(GFP_KERNEL);
+ struct per_user_data *u = file->private_data;
+
+ if (kbuf == NULL)
+ return -ENOMEM;
+
+ /* Whole number of ports. */
+ count &= ~(sizeof(evtchn_port_t)-1);
+
+ rc = 0;
+ if (count == 0)
+ goto out;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ rc = -EFAULT;
+ if (copy_from_user(kbuf, buf, count) != 0)
+ goto out;
+
+ spin_lock_irq(&port_user_lock);
+ for (i = 0; i < (count/sizeof(evtchn_port_t)); i++)
+ if ((kbuf[i] < NR_EVENT_CHANNELS) && (port_user[kbuf[i]] == u))
+ enable_irq(irq_from_evtchn(kbuf[i]));
+ spin_unlock_irq(&port_user_lock);
+
+ rc = count;
+
+ out:
+ free_page((unsigned long)kbuf);
+ return rc;
+}
+
+static int evtchn_bind_to_user(struct per_user_data *u, int port)
+{
+ int rc = 0;
+
+ /*
+ * Ports are never reused, so every caller should pass in a
+ * unique port.
+ *
+ * (Locking not necessary because we haven't registered the
+ * interrupt handler yet, and our caller has already
+ * serialized bind operations.)
+ */
+ BUG_ON(port_user[port] != NULL);
+ port_user[port] = u;
+
+ rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED,
+ u->name, (void *)(unsigned long)port);
+ if (rc >= 0)
+ rc = 0;
+
+ return rc;
+}
+
+static void evtchn_unbind_from_user(struct per_user_data *u, int port)
+{
+ int irq = irq_from_evtchn(port);
+
+ unbind_from_irqhandler(irq, (void *)(unsigned long)port);
+
+ /* make sure we unbind the irq handler before clearing the port */
+ barrier();
+
+ port_user[port] = NULL;
+}
+
+static long evtchn_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ struct per_user_data *u = file->private_data;
+ void __user *uarg = (void __user *) arg;
+
+ /* Prevent bind from racing with unbind */
+ mutex_lock(&u->bind_mutex);
+
+ switch (cmd) {
+ case IOCTL_EVTCHN_BIND_VIRQ: {
+ struct ioctl_evtchn_bind_virq bind;
+ struct evtchn_bind_virq bind_virq;
+
+ rc = -EFAULT;
+ if (copy_from_user(&bind, uarg, sizeof(bind)))
+ break;
+
+ bind_virq.virq = bind.virq;
+ bind_virq.vcpu = 0;
+ rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
+ &bind_virq);
+ if (rc != 0)
+ break;
+
+ rc = evtchn_bind_to_user(u, bind_virq.port);
+ if (rc == 0)
+ rc = bind_virq.port;
+ break;
+ }
+
+ case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
+ struct ioctl_evtchn_bind_interdomain bind;
+ struct evtchn_bind_interdomain bind_interdomain;
+
+ rc = -EFAULT;
+ if (copy_from_user(&bind, uarg, sizeof(bind)))
+ break;
+
+ bind_interdomain.remote_dom = bind.remote_domain;
+ bind_interdomain.remote_port = bind.remote_port;
+ rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
+ &bind_interdomain);
+ if (rc != 0)
+ break;
+
+ rc = evtchn_bind_to_user(u, bind_interdomain.local_port);
+ if (rc == 0)
+ rc = bind_interdomain.local_port;
+ break;
+ }
+
+ case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
+ struct ioctl_evtchn_bind_unbound_port bind;
+ struct evtchn_alloc_unbound alloc_unbound;
+
+ rc = -EFAULT;
+ if (copy_from_user(&bind, uarg, sizeof(bind)))
+ break;
+
+ alloc_unbound.dom = DOMID_SELF;
+ alloc_unbound.remote_dom = bind.remote_domain;
+ rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
+ &alloc_unbound);
+ if (rc != 0)
+ break;
+
+ rc = evtchn_bind_to_user(u, alloc_unbound.port);
+ if (rc == 0)
+ rc = alloc_unbound.port;
+ break;
+ }
+
+ case IOCTL_EVTCHN_UNBIND: {
+ struct ioctl_evtchn_unbind unbind;
+
+ rc = -EFAULT;
+ if (copy_from_user(&unbind, uarg, sizeof(unbind)))
+ break;
+
+ rc = -EINVAL;
+ if (unbind.port >= NR_EVENT_CHANNELS)
+ break;
+
+ spin_lock_irq(&port_user_lock);
+
+ rc = -ENOTCONN;
+ if (port_user[unbind.port] != u) {
+ spin_unlock_irq(&port_user_lock);
+ break;
+ }
+
+ evtchn_unbind_from_user(u, unbind.port);
+
+ spin_unlock_irq(&port_user_lock);
+
+ rc = 0;
+ break;
+ }
+
+ case IOCTL_EVTCHN_NOTIFY: {
+ struct ioctl_evtchn_notify notify;
+
+ rc = -EFAULT;
+ if (copy_from_user(&notify, uarg, sizeof(notify)))
+ break;
+
+ if (notify.port >= NR_EVENT_CHANNELS) {
+ rc = -EINVAL;
+ } else if (port_user[notify.port] != u) {
+ rc = -ENOTCONN;
+ } else {
+ notify_remote_via_evtchn(notify.port);
+ rc = 0;
+ }
+ break;
+ }
+
+ case IOCTL_EVTCHN_RESET: {
+ /* Initialise the ring to empty. Clear errors. */
+ mutex_lock(&u->ring_cons_mutex);
+ spin_lock_irq(&port_user_lock);
+ u->ring_cons = u->ring_prod = u->ring_overflow = 0;
+ spin_unlock_irq(&port_user_lock);
+ mutex_unlock(&u->ring_cons_mutex);
+ rc = 0;
+ break;
+ }
+
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+ mutex_unlock(&u->bind_mutex);
+
+ return rc;
+}
+
+static unsigned int evtchn_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = POLLOUT | POLLWRNORM;
+ struct per_user_data *u = file->private_data;
+
+ poll_wait(file, &u->evtchn_wait, wait);
+ if (u->ring_cons != u->ring_prod)
+ mask |= POLLIN | POLLRDNORM;
+ if (u->ring_overflow)
+ mask = POLLERR;
+ return mask;
+}
+
+static int evtchn_fasync(int fd, struct file *filp, int on)
+{
+ struct per_user_data *u = filp->private_data;
+ return fasync_helper(fd, filp, on, &u->evtchn_async_queue);
+}
+
+static int evtchn_open(struct inode *inode, struct file *filp)
+{
+ struct per_user_data *u;
+
+ u = kzalloc(sizeof(*u), GFP_KERNEL);
+ if (u == NULL)
+ return -ENOMEM;
+
+ u->name = kasprintf(GFP_KERNEL, "evtchn:%s", current->comm);
+ if (u->name == NULL) {
+ kfree(u);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&u->evtchn_wait);
+
+ u->ring = (evtchn_port_t *)__get_free_page(GFP_KERNEL);
+ if (u->ring == NULL) {
+ kfree(u->name);
+ kfree(u);
+ return -ENOMEM;
+ }
+
+ mutex_init(&u->bind_mutex);
+ mutex_init(&u->ring_cons_mutex);
+
+ filp->private_data = u;
+
+ return 0;
+}
+
+static int evtchn_release(struct inode *inode, struct file *filp)
+{
+ int i;
+ struct per_user_data *u = filp->private_data;
+
+ spin_lock_irq(&port_user_lock);
+
+ free_page((unsigned long)u->ring);
+
+ for (i = 0; i < NR_EVENT_CHANNELS; i++) {
+ if (port_user[i] != u)
+ continue;
+
+ evtchn_unbind_from_user(port_user[i], i);
+ }
+
+ spin_unlock_irq(&port_user_lock);
+
+ kfree(u->name);
+ kfree(u);
+
+ return 0;
+}
+
+static const struct file_operations evtchn_fops = {
+ .owner = THIS_MODULE,
+ .read = evtchn_read,
+ .write = evtchn_write,
+ .unlocked_ioctl = evtchn_ioctl,
+ .poll = evtchn_poll,
+ .fasync = evtchn_fasync,
+ .open = evtchn_open,
+ .release = evtchn_release,
+};
+
+static struct miscdevice evtchn_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "evtchn",
+ .fops = &evtchn_fops,
+};
+static int __init evtchn_init(void)
+{
+ int err;
+
+ if (!xen_domain())
+ return -ENODEV;
+
+ spin_lock_init(&port_user_lock);
+ memset(port_user, 0, sizeof(port_user));
+
+ /* Create '/dev/misc/evtchn'. */
+ err = misc_register(&evtchn_miscdev);
+ if (err != 0) {
+ printk(KERN_ALERT "Could not register /dev/misc/evtchn\n");
+ return err;
+ }
+
+ printk(KERN_INFO "Event-channel device installed.\n");
+
+ return 0;
+}
+
+static void __exit evtchn_cleanup(void)
+{
+ misc_deregister(&evtchn_miscdev);
+}
+
+module_init(evtchn_init);
+module_exit(evtchn_cleanup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 4b5b84837ee1..10d03d7931c4 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -43,7 +43,7 @@ static int xen_suspend(void *data)
if (err) {
printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
err);
- device_power_up(PMSG_RESUME);
+ dpm_resume_noirq(PMSG_RESUME);
return err;
}
@@ -69,7 +69,7 @@ static int xen_suspend(void *data)
}
sysdev_resume();
- device_power_up(PMSG_RESUME);
+ dpm_resume_noirq(PMSG_RESUME);
return 0;
}
@@ -92,19 +92,18 @@ static void do_suspend(void)
}
#endif
- err = device_suspend(PMSG_SUSPEND);
+ err = dpm_suspend_start(PMSG_SUSPEND);
if (err) {
- printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
+ printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
goto out;
}
- printk("suspending xenbus...\n");
- /* XXX use normal device tree? */
- xenbus_suspend();
+ printk(KERN_DEBUG "suspending xenstore...\n");
+ xs_suspend();
- err = device_power_down(PMSG_SUSPEND);
+ err = dpm_suspend_noirq(PMSG_SUSPEND);
if (err) {
- printk(KERN_ERR "device_power_down failed: %d\n", err);
+ printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
goto resume_devices;
}
@@ -116,14 +115,14 @@ static void do_suspend(void)
if (!cancelled) {
xen_arch_resume();
- xenbus_resume();
+ xs_resume();
} else
- xenbus_suspend_cancel();
+ xs_suspend_cancel();
- device_power_up(PMSG_RESUME);
+ dpm_resume_noirq(PMSG_RESUME);
resume_devices:
- device_resume(PMSG_RESUME);
+ dpm_resume_end(PMSG_RESUME);
/* Make sure timer events get retriggered on all CPUs */
clock_was_set();
diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c
new file mode 100644
index 000000000000..88a60e03ccf0
--- /dev/null
+++ b/drivers/xen/sys-hypervisor.c
@@ -0,0 +1,445 @@
+/*
+ * copyright (c) 2006 IBM Corporation
+ * Authored by: Mike D. Day <ncmike@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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+
+#include <asm/xen/hypervisor.h>
+#include <asm/xen/hypercall.h>
+
+#include <xen/xenbus.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/version.h>
+
+#define HYPERVISOR_ATTR_RO(_name) \
+static struct hyp_sysfs_attr _name##_attr = __ATTR_RO(_name)
+
+#define HYPERVISOR_ATTR_RW(_name) \
+static struct hyp_sysfs_attr _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+struct hyp_sysfs_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct hyp_sysfs_attr *, char *);
+ ssize_t (*store)(struct hyp_sysfs_attr *, const char *, size_t);
+ void *hyp_attr_data;
+};
+
+static ssize_t type_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ return sprintf(buffer, "xen\n");
+}
+
+HYPERVISOR_ATTR_RO(type);
+
+static int __init xen_sysfs_type_init(void)
+{
+ return sysfs_create_file(hypervisor_kobj, &type_attr.attr);
+}
+
+static void xen_sysfs_type_destroy(void)
+{
+ sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
+}
+
+/* xen version attributes */
+static ssize_t major_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int version = HYPERVISOR_xen_version(XENVER_version, NULL);
+ if (version)
+ return sprintf(buffer, "%d\n", version >> 16);
+ return -ENODEV;
+}
+
+HYPERVISOR_ATTR_RO(major);
+
+static ssize_t minor_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int version = HYPERVISOR_xen_version(XENVER_version, NULL);
+ if (version)
+ return sprintf(buffer, "%d\n", version & 0xff);
+ return -ENODEV;
+}
+
+HYPERVISOR_ATTR_RO(minor);
+
+static ssize_t extra_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ char *extra;
+
+ extra = kmalloc(XEN_EXTRAVERSION_LEN, GFP_KERNEL);
+ if (extra) {
+ ret = HYPERVISOR_xen_version(XENVER_extraversion, extra);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", extra);
+ kfree(extra);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(extra);
+
+static struct attribute *version_attrs[] = {
+ &major_attr.attr,
+ &minor_attr.attr,
+ &extra_attr.attr,
+ NULL
+};
+
+static struct attribute_group version_group = {
+ .name = "version",
+ .attrs = version_attrs,
+};
+
+static int __init xen_sysfs_version_init(void)
+{
+ return sysfs_create_group(hypervisor_kobj, &version_group);
+}
+
+static void xen_sysfs_version_destroy(void)
+{
+ sysfs_remove_group(hypervisor_kobj, &version_group);
+}
+
+/* UUID */
+
+static ssize_t uuid_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ char *vm, *val;
+ int ret;
+ extern int xenstored_ready;
+
+ if (!xenstored_ready)
+ return -EBUSY;
+
+ vm = xenbus_read(XBT_NIL, "vm", "", NULL);
+ if (IS_ERR(vm))
+ return PTR_ERR(vm);
+ val = xenbus_read(XBT_NIL, vm, "uuid", NULL);
+ kfree(vm);
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+ ret = sprintf(buffer, "%s\n", val);
+ kfree(val);
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(uuid);
+
+static int __init xen_sysfs_uuid_init(void)
+{
+ return sysfs_create_file(hypervisor_kobj, &uuid_attr.attr);
+}
+
+static void xen_sysfs_uuid_destroy(void)
+{
+ sysfs_remove_file(hypervisor_kobj, &uuid_attr.attr);
+}
+
+/* xen compilation attributes */
+
+static ssize_t compiler_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ struct xen_compile_info *info;
+
+ info = kmalloc(sizeof(struct xen_compile_info), GFP_KERNEL);
+ if (info) {
+ ret = HYPERVISOR_xen_version(XENVER_compile_info, info);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", info->compiler);
+ kfree(info);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(compiler);
+
+static ssize_t compiled_by_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ struct xen_compile_info *info;
+
+ info = kmalloc(sizeof(struct xen_compile_info), GFP_KERNEL);
+ if (info) {
+ ret = HYPERVISOR_xen_version(XENVER_compile_info, info);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", info->compile_by);
+ kfree(info);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(compiled_by);
+
+static ssize_t compile_date_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ struct xen_compile_info *info;
+
+ info = kmalloc(sizeof(struct xen_compile_info), GFP_KERNEL);
+ if (info) {
+ ret = HYPERVISOR_xen_version(XENVER_compile_info, info);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", info->compile_date);
+ kfree(info);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(compile_date);
+
+static struct attribute *xen_compile_attrs[] = {
+ &compiler_attr.attr,
+ &compiled_by_attr.attr,
+ &compile_date_attr.attr,
+ NULL
+};
+
+static struct attribute_group xen_compilation_group = {
+ .name = "compilation",
+ .attrs = xen_compile_attrs,
+};
+
+int __init static xen_compilation_init(void)
+{
+ return sysfs_create_group(hypervisor_kobj, &xen_compilation_group);
+}
+
+static void xen_compilation_destroy(void)
+{
+ sysfs_remove_group(hypervisor_kobj, &xen_compilation_group);
+}
+
+/* xen properties info */
+
+static ssize_t capabilities_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ char *caps;
+
+ caps = kmalloc(XEN_CAPABILITIES_INFO_LEN, GFP_KERNEL);
+ if (caps) {
+ ret = HYPERVISOR_xen_version(XENVER_capabilities, caps);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", caps);
+ kfree(caps);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(capabilities);
+
+static ssize_t changeset_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ char *cset;
+
+ cset = kmalloc(XEN_CHANGESET_INFO_LEN, GFP_KERNEL);
+ if (cset) {
+ ret = HYPERVISOR_xen_version(XENVER_changeset, cset);
+ if (!ret)
+ ret = sprintf(buffer, "%s\n", cset);
+ kfree(cset);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(changeset);
+
+static ssize_t virtual_start_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret = -ENOMEM;
+ struct xen_platform_parameters *parms;
+
+ parms = kmalloc(sizeof(struct xen_platform_parameters), GFP_KERNEL);
+ if (parms) {
+ ret = HYPERVISOR_xen_version(XENVER_platform_parameters,
+ parms);
+ if (!ret)
+ ret = sprintf(buffer, "%lx\n", parms->virt_start);
+ kfree(parms);
+ }
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(virtual_start);
+
+static ssize_t pagesize_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret;
+
+ ret = HYPERVISOR_xen_version(XENVER_pagesize, NULL);
+ if (ret > 0)
+ ret = sprintf(buffer, "%x\n", ret);
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(pagesize);
+
+static ssize_t xen_feature_show(int index, char *buffer)
+{
+ ssize_t ret;
+ struct xen_feature_info info;
+
+ info.submap_idx = index;
+ ret = HYPERVISOR_xen_version(XENVER_get_features, &info);
+ if (!ret)
+ ret = sprintf(buffer, "%08x", info.submap);
+
+ return ret;
+}
+
+static ssize_t features_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ ssize_t len;
+ int i;
+
+ len = 0;
+ for (i = XENFEAT_NR_SUBMAPS-1; i >= 0; i--) {
+ int ret = xen_feature_show(i, buffer + len);
+ if (ret < 0) {
+ if (len == 0)
+ len = ret;
+ break;
+ }
+ len += ret;
+ }
+ if (len > 0)
+ buffer[len++] = '\n';
+
+ return len;
+}
+
+HYPERVISOR_ATTR_RO(features);
+
+static struct attribute *xen_properties_attrs[] = {
+ &capabilities_attr.attr,
+ &changeset_attr.attr,
+ &virtual_start_attr.attr,
+ &pagesize_attr.attr,
+ &features_attr.attr,
+ NULL
+};
+
+static struct attribute_group xen_properties_group = {
+ .name = "properties",
+ .attrs = xen_properties_attrs,
+};
+
+static int __init xen_properties_init(void)
+{
+ return sysfs_create_group(hypervisor_kobj, &xen_properties_group);
+}
+
+static void xen_properties_destroy(void)
+{
+ sysfs_remove_group(hypervisor_kobj, &xen_properties_group);
+}
+
+static int __init hyper_sysfs_init(void)
+{
+ int ret;
+
+ if (!xen_domain())
+ return -ENODEV;
+
+ ret = xen_sysfs_type_init();
+ if (ret)
+ goto out;
+ ret = xen_sysfs_version_init();
+ if (ret)
+ goto version_out;
+ ret = xen_compilation_init();
+ if (ret)
+ goto comp_out;
+ ret = xen_sysfs_uuid_init();
+ if (ret)
+ goto uuid_out;
+ ret = xen_properties_init();
+ if (ret)
+ goto prop_out;
+
+ goto out;
+
+prop_out:
+ xen_sysfs_uuid_destroy();
+uuid_out:
+ xen_compilation_destroy();
+comp_out:
+ xen_sysfs_version_destroy();
+version_out:
+ xen_sysfs_type_destroy();
+out:
+ return ret;
+}
+
+static void __exit hyper_sysfs_exit(void)
+{
+ xen_properties_destroy();
+ xen_compilation_destroy();
+ xen_sysfs_uuid_destroy();
+ xen_sysfs_version_destroy();
+ xen_sysfs_type_destroy();
+
+}
+module_init(hyper_sysfs_init);
+module_exit(hyper_sysfs_exit);
+
+static ssize_t hyp_sysfs_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buffer)
+{
+ struct hyp_sysfs_attr *hyp_attr;
+ hyp_attr = container_of(attr, struct hyp_sysfs_attr, attr);
+ if (hyp_attr->show)
+ return hyp_attr->show(hyp_attr, buffer);
+ return 0;
+}
+
+static ssize_t hyp_sysfs_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer,
+ size_t len)
+{
+ struct hyp_sysfs_attr *hyp_attr;
+ hyp_attr = container_of(attr, struct hyp_sysfs_attr, attr);
+ if (hyp_attr->store)
+ return hyp_attr->store(hyp_attr, buffer, len);
+ return 0;
+}
+
+static struct sysfs_ops hyp_sysfs_ops = {
+ .show = hyp_sysfs_show,
+ .store = hyp_sysfs_store,
+};
+
+static struct kobj_type hyp_sysfs_kobj_type = {
+ .sysfs_ops = &hyp_sysfs_ops,
+};
+
+static int __init hypervisor_subsys_init(void)
+{
+ if (!xen_domain())
+ return -ENODEV;
+
+ hypervisor_kobj->ktype = &hyp_sysfs_kobj_type;
+ return 0;
+}
+device_initcall(hypervisor_subsys_init);
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 773d1cf23283..d42e25d5968d 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -71,6 +71,9 @@ static int xenbus_probe_frontend(const char *type, const char *name);
static void xenbus_dev_shutdown(struct device *_dev);
+static int xenbus_dev_suspend(struct device *dev, pm_message_t state);
+static int xenbus_dev_resume(struct device *dev);
+
/* If something in array of ids matches this device, return it. */
static const struct xenbus_device_id *
match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev)
@@ -188,6 +191,9 @@ static struct xen_bus_type xenbus_frontend = {
.remove = xenbus_dev_remove,
.shutdown = xenbus_dev_shutdown,
.dev_attrs = xenbus_dev_attrs,
+
+ .suspend = xenbus_dev_suspend,
+ .resume = xenbus_dev_resume,
},
};
@@ -654,6 +660,7 @@ void xenbus_dev_changed(const char *node, struct xen_bus_type *bus)
kfree(root);
}
+EXPORT_SYMBOL_GPL(xenbus_dev_changed);
static void frontend_changed(struct xenbus_watch *watch,
const char **vec, unsigned int len)
@@ -669,7 +676,7 @@ static struct xenbus_watch fe_watch = {
.callback = frontend_changed,
};
-static int suspend_dev(struct device *dev, void *data)
+static int xenbus_dev_suspend(struct device *dev, pm_message_t state)
{
int err = 0;
struct xenbus_driver *drv;
@@ -682,35 +689,14 @@ static int suspend_dev(struct device *dev, void *data)
drv = to_xenbus_driver(dev->driver);
xdev = container_of(dev, struct xenbus_device, dev);
if (drv->suspend)
- err = drv->suspend(xdev);
+ err = drv->suspend(xdev, state);
if (err)
printk(KERN_WARNING
"xenbus: suspend %s failed: %i\n", dev_name(dev), err);
return 0;
}
-static int suspend_cancel_dev(struct device *dev, void *data)
-{
- int err = 0;
- struct xenbus_driver *drv;
- struct xenbus_device *xdev;
-
- DPRINTK("");
-
- if (dev->driver == NULL)
- return 0;
- drv = to_xenbus_driver(dev->driver);
- xdev = container_of(dev, struct xenbus_device, dev);
- if (drv->suspend_cancel)
- err = drv->suspend_cancel(xdev);
- if (err)
- printk(KERN_WARNING
- "xenbus: suspend_cancel %s failed: %i\n",
- dev_name(dev), err);
- return 0;
-}
-
-static int resume_dev(struct device *dev, void *data)
+static int xenbus_dev_resume(struct device *dev)
{
int err;
struct xenbus_driver *drv;
@@ -755,33 +741,6 @@ static int resume_dev(struct device *dev, void *data)
return 0;
}
-void xenbus_suspend(void)
-{
- DPRINTK("");
-
- bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev);
- xenbus_backend_suspend(suspend_dev);
- xs_suspend();
-}
-EXPORT_SYMBOL_GPL(xenbus_suspend);
-
-void xenbus_resume(void)
-{
- xb_init_comms();
- xs_resume();
- bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev);
- xenbus_backend_resume(resume_dev);
-}
-EXPORT_SYMBOL_GPL(xenbus_resume);
-
-void xenbus_suspend_cancel(void)
-{
- xs_suspend_cancel();
- bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_cancel_dev);
- xenbus_backend_resume(suspend_cancel_dev);
-}
-EXPORT_SYMBOL_GPL(xenbus_suspend_cancel);
-
/* A flag to determine if xenstored is 'ready' (i.e. has started) */
int xenstored_ready = 0;
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index e325eab4724d..eab33f1dbdf7 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -673,6 +673,8 @@ void xs_resume(void)
struct xenbus_watch *watch;
char token[sizeof(watch) * 2 + 1];
+ xb_init_comms();
+
mutex_unlock(&xs_state.response_mutex);
mutex_unlock(&xs_state.request_mutex);
up_write(&xs_state.transaction_mutex);
diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c
index 515741a8e6b8..6559e0c752ce 100644
--- a/drivers/xen/xenfs/super.c
+++ b/drivers/xen/xenfs/super.c
@@ -20,10 +20,27 @@
MODULE_DESCRIPTION("Xen filesystem");
MODULE_LICENSE("GPL");
+static ssize_t capabilities_read(struct file *file, char __user *buf,
+ size_t size, loff_t *off)
+{
+ char *tmp = "";
+
+ if (xen_initial_domain())
+ tmp = "control_d\n";
+
+ return simple_read_from_buffer(buf, size, off, tmp, strlen(tmp));
+}
+
+static const struct file_operations capabilities_file_ops = {
+ .read = capabilities_read,
+};
+
static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr xenfs_files[] = {
- [2] = {"xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR},
+ [1] = {},
+ { "xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR },
+ { "capabilities", &capabilities_file_ops, S_IRUGO },
{""},
};