aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2009-06-15 03:02:23 -0700
committerDavid S. Miller <davem@davemloft.net>2009-06-15 03:02:23 -0700
commit9cbc1cb8cd46ce1f7645b9de249b2ce8460129bb (patch)
tree8d104ec2a459346b99413b0b77421ca7b9936c1a /drivers
parentpkt_sched: Rename PSCHED_US2NS and PSCHED_NS2US (diff)
parentMerge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild-next (diff)
downloadlinux-dev-9cbc1cb8cd46ce1f7645b9de249b2ce8460129bb.tar.xz
linux-dev-9cbc1cb8cd46ce1f7645b9de249b2ce8460129bb.zip
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: Documentation/feature-removal-schedule.txt drivers/scsi/fcoe/fcoe.c net/core/drop_monitor.c net/core/net-traces.c
Diffstat (limited to 'drivers')
-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.c8
-rw-r--r--drivers/acpi/processor_perflib.c12
-rw-r--r--drivers/acpi/processor_throttling.c2
-rw-r--r--drivers/acpi/video.c18
-rw-r--r--drivers/ata/Kconfig9
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci.c159
-rw-r--r--drivers/ata/ata_piix.c20
-rw-r--r--drivers/ata/libata-acpi.c4
-rw-r--r--drivers/ata/libata-core.c11
-rw-r--r--drivers/ata/libata-eh.c2
-rw-r--r--drivers/ata/libata-scsi.c2
-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_palmld.c150
-rw-r--r--drivers/ata/sata_nv.c131
-rw-r--r--drivers/ata/sata_sil.c2
-rw-r--r--drivers/ata/sata_sx4.c11
-rw-r--r--drivers/base/bus.c6
-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/platform.c36
-rw-r--r--drivers/base/power/main.c98
-rw-r--r--drivers/base/sys.c16
-rw-r--r--drivers/block/DAC960.c10
-rw-r--r--drivers/block/Kconfig6
-rw-r--r--drivers/block/amiflop.c54
-rw-r--r--drivers/block/ataflop.c66
-rw-r--r--drivers/block/brd.c7
-rw-r--r--drivers/block/cciss.c927
-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.c20
-rw-r--r--drivers/block/floppy.c85
-rw-r--r--drivers/block/hd.c106
-rw-r--r--drivers/block/loop.c37
-rw-r--r--drivers/block/mg_disk.c537
-rw-r--r--drivers/block/nbd.c23
-rw-r--r--drivers/block/paride/pcd.c29
-rw-r--r--drivers/block/paride/pd.c22
-rw-r--r--drivers/block/paride/pf.c47
-rw-r--r--drivers/block/pktcdvd.c8
-rw-r--r--drivers/block/ps3disk.c24
-rw-r--r--drivers/block/sunvdc.c14
-rw-r--r--drivers/block/swim.c48
-rw-r--r--drivers/block/swim3.c107
-rw-r--r--drivers/block/sx8.c17
-rw-r--r--drivers/block/ub.c54
-rw-r--r--drivers/block/viodasd.c12
-rw-r--r--drivers/block/virtio_blk.c120
-rw-r--r--drivers/block/xd.c41
-rw-r--r--drivers/block/xen-blkfront.c44
-rw-r--r--drivers/block/xsysace.c46
-rw-r--r--drivers/block/z2ram.c19
-rw-r--r--drivers/bluetooth/hci_ldisc.c5
-rw-r--r--drivers/cdrom/cdrom.c4
-rw-r--r--drivers/cdrom/gdrom.c36
-rw-r--r--drivers/cdrom/viocd.c35
-rw-r--r--drivers/char/Kconfig17
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/agp/intel-agp.c16
-rw-r--r--drivers/char/amiserial.c2
-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/Kconfig14
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/mxc-rnga.c247
-rw-r--r--drivers/char/hw_random/omap-rng.c2
-rw-r--r--drivers/char/hw_random/timeriomem-rng.c26
-rw-r--r--drivers/char/hw_random/via-rng.c15
-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.c2
-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.c2
-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/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/connector/Kconfig2
-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/Kconfig8
-rw-r--r--drivers/crypto/hifn_795x.c8
-rw-r--r--drivers/crypto/ixp4xx_crypto.c33
-rw-r--r--drivers/crypto/padlock-aes.c15
-rw-r--r--drivers/crypto/talitos.c713
-rw-r--r--drivers/dma/fsldma.c71
-rw-r--r--drivers/dma/ioat_dma.c2
-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/e752x_edac.c2
-rw-r--r--drivers/edac/edac_core.h9
-rw-r--r--drivers/firmware/dmi_scan.c1
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpu/drm/Kconfig14
-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.c109
-rw-r--r--drivers/gpu/drm/drm_drv.c2
-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.c17
-rw-r--r--drivers/gpu/drm/drm_sysfs.c7
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c79
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h51
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c221
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c166
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c190
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h636
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c20
-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.c231
-rw-r--r--drivers/gpu/drm/i915/intel_display.c671
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c1
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c26
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c34
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c163
-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/radeon/r600_cp.c42
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h6
-rw-r--r--drivers/gpu/drm/via/via_dmablit.c6
-rw-r--r--drivers/hid/Kconfig92
-rw-r--r--drivers/hid/Makefile10
-rw-r--r--drivers/hid/hid-apple.c4
-rw-r--r--drivers/hid/hid-core.c2
-rw-r--r--drivers/hid/hid-debug.c23
-rw-r--r--drivers/hid/hid-drff.c8
-rw-r--r--drivers/hid/hid-gaff.c8
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/hid/hid-lgff.c10
-rw-r--r--drivers/hid/hid-ntrig.c222
-rw-r--r--drivers/hid/hid-sjoy.c180
-rw-r--r--drivers/hid/hid-tmff.c17
-rw-r--r--drivers/hid/hid-wacom.c259
-rw-r--r--drivers/hid/hid-zpff.c7
-rw-r--r--drivers/hid/hidraw.c5
-rw-r--r--drivers/hid/usbhid/hid-core.c7
-rw-r--r--drivers/hwmon/lm78.c2
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c59
-rw-r--r--drivers/i2c/busses/i2c-ocores.c5
-rw-r--r--drivers/i2c/busses/i2c-omap.c39
-rw-r--r--drivers/i2c/busses/i2c-pxa.c22
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c48
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c2
-rw-r--r--drivers/ide/Kconfig2
-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.c187
-rw-r--r--drivers/ide/ide-cd.c152
-rw-r--r--drivers/ide/ide-cd.h4
-rw-r--r--drivers/ide/ide-cs.c6
-rw-r--r--drivers/ide/ide-disk.c86
-rw-r--r--drivers/ide/ide-dma.c23
-rw-r--r--drivers/ide/ide-eh.c14
-rw-r--r--drivers/ide/ide-floppy.c32
-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.c138
-rw-r--r--drivers/ide/ide-ioctls.c1
-rw-r--r--drivers/ide/ide-iops.c47
-rw-r--r--drivers/ide/ide-legacy.c7
-rw-r--r--drivers/ide/ide-lib.c29
-rw-r--r--drivers/ide/ide-park.c7
-rw-r--r--drivers/ide/ide-pci-generic.c11
-rw-r--r--drivers/ide/ide-pm.c38
-rw-r--r--drivers/ide/ide-pnp.c6
-rw-r--r--drivers/ide/ide-probe.c104
-rw-r--r--drivers/ide/ide-tape.c822
-rw-r--r--drivers/ide/ide-taskfile.c23
-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.c106
-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/tc86c001.c2
-rw-r--r--drivers/ide/tx4938ide.c5
-rw-r--r--drivers/ide/tx4939ide.c7
-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/amso1100/c2_cq.c4
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_wr.h2
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c32
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes_pSeries.h28
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c9
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_qp.c112
-rw-r--r--drivers/infiniband/hw/ehca/hcp_if.c6
-rw-r--r--drivers/infiniband/hw/ehca/hcp_if.h2
-rw-r--r--drivers/infiniband/hw/ehca/hcp_phyp.c11
-rw-r--r--drivers/infiniband/hw/ehca/hcp_phyp.h2
-rw-r--r--drivers/infiniband/hw/ehca/ipz_pt_fn.c19
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_dev.h1
-rw-r--r--drivers/infiniband/hw/mthca/mthca_eq.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c17
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c16
-rw-r--r--drivers/infiniband/hw/mthca/mthca_profile.c4
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c14
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c10
-rw-r--r--drivers/input/input.c1
-rw-r--r--drivers/input/misc/Kconfig2
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/serio/ambakmi.c4
-rw-r--r--drivers/input/serio/libps2.c2
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c2
-rw-r--r--drivers/isdn/divert/isdn_divert.c2
-rw-r--r--drivers/isdn/mISDN/dsp_core.c2
-rw-r--r--drivers/leds/leds-h1940.c2
-rw-r--r--drivers/leds/leds-s3c24xx.c1
-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.c17
-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.c33
-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.c8
-rw-r--r--drivers/md/raid5.c38
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig2
-rw-r--r--drivers/media/video/Kconfig4
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c3
-rw-r--r--drivers/memstick/core/mspro_block.c19
-rw-r--r--drivers/message/fusion/lsi/mpi_history.txt6
-rw-r--r--drivers/message/fusion/mptbase.c1570
-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.c3136
-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.c43
-rw-r--r--drivers/mfd/pcf50633-core.c2
-rw-r--r--drivers/mfd/t7l66xb.c5
-rw-r--r--drivers/mfd/tc6387xb.c5
-rw-r--r--drivers/mfd/tc6393xb.c5
-rw-r--r--drivers/mfd/wm8350-core.c8
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/cb710/Kconfig25
-rw-r--r--drivers/misc/cb710/Makefile8
-rw-r--r--drivers/misc/cb710/core.c357
-rw-r--r--drivers/misc/cb710/debug.c119
-rw-r--r--drivers/misc/cb710/sgbuf2.c150
-rw-r--r--drivers/misc/enclosure.c6
-rw-r--r--drivers/mmc/card/block.c28
-rw-r--r--drivers/mmc/card/queue.c11
-rw-r--r--drivers/mmc/core/core.c107
-rw-r--r--drivers/mmc/host/Kconfig30
-rw-r--r--drivers/mmc/host/Makefile5
-rw-r--r--drivers/mmc/host/atmel-mci-regs.h33
-rw-r--r--drivers/mmc/host/atmel-mci.c12
-rw-r--r--drivers/mmc/host/cb710-mmc.c804
-rw-r--r--drivers/mmc/host/cb710-mmc.h104
-rw-r--r--drivers/mmc/host/mmc_spi.c23
-rw-r--r--drivers/mmc/host/mmci.c4
-rw-r--r--drivers/mmc/host/mvsdio.c35
-rw-r--r--drivers/mmc/host/mxcmmc.c45
-rw-r--r--drivers/mmc/host/omap.c5
-rw-r--r--drivers/mmc/host/omap_hsmmc.c8
-rw-r--r--drivers/mmc/host/pxamci.c46
-rw-r--r--drivers/mmc/host/s3cmci.c5
-rw-r--r--drivers/mmc/host/sdhci-of.c9
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c168
-rw-r--r--drivers/mmc/host/sdhci.c58
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mmc/host/tmio_mmc.c180
-rw-r--r--drivers/mmc/host/tmio_mmc.h77
-rw-r--r--drivers/mtd/Kconfig3
-rw-r--r--drivers/mtd/devices/Kconfig2
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c43
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/davinci_nand.c7
-rw-r--r--drivers/mtd/nand/mxc_nand.c43
-rw-r--r--drivers/mtd/onenand/omap2.c1
-rw-r--r--drivers/net/Kconfig15
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/appletalk/ltpc.c2
-rw-r--r--drivers/net/arm/ixp4xx_eth.c26
-rw-r--r--drivers/net/b44.h2
-rw-r--r--drivers/net/bnx2.c193
-rw-r--r--drivers/net/bnx2.h18
-rw-r--r--drivers/net/cnic.c2717
-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/e100.c2
-rw-r--r--drivers/net/e1000e/e1000.h2
-rw-r--r--drivers/net/ehea/ehea.h2
-rw-r--r--drivers/net/igbvf/igbvf.h2
-rw-r--r--drivers/net/ipg.h2
-rw-r--r--drivers/net/mlx4/en_netdev.c2
-rw-r--r--drivers/net/mlx4/eq.c4
-rw-r--r--drivers/net/mlx4/main.c14
-rw-r--r--drivers/net/mlx4/mr.c6
-rw-r--r--drivers/net/mlx4/profile.c2
-rw-r--r--drivers/net/niu.h4
-rw-r--r--drivers/net/qlge/qlge_main.c2
-rw-r--r--drivers/net/qlge/qlge_mpi.c6
-rw-r--r--drivers/net/r8169.c11
-rw-r--r--drivers/net/skfp/h/smt.h2
-rw-r--r--drivers/net/smc91x.h5
-rw-r--r--drivers/net/tokenring/3c359.c2
-rw-r--r--drivers/net/tokenring/lanstreamer.c2
-rw-r--r--drivers/net/tokenring/olympic.c2
-rw-r--r--drivers/net/ucc_geth_ethtool.c2
-rw-r--r--drivers/net/usb/usbnet.c2
-rw-r--r--drivers/net/virtio_net.c45
-rw-r--r--drivers/net/wan/ixp4xx_hss.c11
-rw-r--r--drivers/net/wireless/Kconfig2
-rw-r--r--drivers/net/wireless/hostap/Kconfig8
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig4
-rw-r--r--drivers/net/wireless/rndis_wlan.c2
-rw-r--r--drivers/net/wireless/rt2x00/Kconfig14
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h2
-rw-r--r--drivers/net/wireless/wavelan_cs.c2
-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/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/hotplug/sgi_hotplug.c4
-rw-r--r--drivers/pci/htirq.c4
-rw-r--r--drivers/pci/intel-iommu.c9
-rw-r--r--drivers/pci/intr_remapping.c54
-rw-r--r--drivers/pci/pci.c3
-rw-r--r--drivers/pci/probe.c2
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/pxa2xx_stargate2.c174
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c2
-rw-r--r--drivers/pnp/resource.c18
-rw-r--r--drivers/regulator/da903x.c2
-rw-r--r--drivers/rtc/Kconfig2
-rw-r--r--drivers/rtc/rtc-ep93xx.c149
-rw-r--r--drivers/rtc/rtc-pl030.c4
-rw-r--r--drivers/rtc/rtc-pl031.c5
-rw-r--r--drivers/s390/block/dasd.c71
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/block/dasd_eckd.c50
-rw-r--r--drivers/s390/block/dasd_fba.c29
-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_34xx.c2
-rw-r--r--drivers/s390/char/tape_3590.c2
-rw-r--r--drivers/s390/char/tape_block.c26
-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/net/Kconfig14
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c30
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c10
-rw-r--r--drivers/s390/scsi/zfcp_def.h7
-rw-r--r--drivers/s390/scsi/zfcp_erp.c8
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
-rw-r--r--drivers/s390/scsi/zfcp_fc.c9
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c29
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c13
-rw-r--r--drivers/sbus/char/jsflash.c26
-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.h1
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c26
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.c23
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.h3
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c6
-rw-r--r--drivers/scsi/dpt/osd_util.h2
-rw-r--r--drivers/scsi/eata.c24
-rw-r--r--drivers/scsi/fcoe/fcoe.c95
-rw-r--r--drivers/scsi/fcoe/fcoe.h1
-rw-r--r--drivers/scsi/fcoe/libfcoe.c21
-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.c5
-rw-r--r--drivers/scsi/libfc/fc_exch.c4
-rw-r--r--drivers/scsi/libfc/fc_fcp.c2
-rw-r--r--drivers/scsi/libfc/fc_rport.c6
-rw-r--r--drivers/scsi/libiscsi.c468
-rw-r--r--drivers/scsi/libiscsi_tcp.c18
-rw-r--r--drivers/scsi/libsas/sas_expander.c16
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c49
-rw-r--r--drivers/scsi/lpfc/lpfc.h123
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c250
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h63
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c15
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c21
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c275
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c1365
-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.c5626
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h54
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c674
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c206
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c51
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c956
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c6683
-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.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c62
-rw-r--r--drivers/scsi/megaraid.h2
-rw-r--r--drivers/scsi/megaraid/mbox_defs.h2
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h7
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c32
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c363
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c59
-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.c155
-rw-r--r--drivers/scsi/osd/osd_uld.c66
-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.c101
-rw-r--r--drivers/scsi/scsi_scan.c5
-rw-r--r--drivers/scsi/scsi_tgt_lib.c2
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c175
-rw-r--r--drivers/scsi/scsi_transport_sas.c4
-rw-r--r--drivers/scsi/sd.c71
-rw-r--r--drivers/scsi/sd_dif.c2
-rw-r--r--drivers/scsi/sg.c18
-rw-r--r--drivers/scsi/sr.c17
-rw-r--r--drivers/scsi/st.c8
-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/scsi/u14-34f.c22
-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/Kconfig10
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/amba-pl010.c4
-rw-r--r--drivers/serial/amba-pl011.c40
-rw-r--r--drivers/serial/bfin_5xx.c77
-rw-r--r--drivers/serial/bfin_sport_uart.c58
-rw-r--r--drivers/serial/icom.c22
-rw-r--r--drivers/serial/imx.c309
-rw-r--r--drivers/serial/jsm/jsm.h1
-rw-r--r--drivers/serial/jsm/jsm_tty.c14
-rw-r--r--drivers/serial/mpc52xx_uart.c2
-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/Kconfig13
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/amba-pl022.c1866
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c1
-rw-r--r--drivers/ssb/embedded.c1
-rw-r--r--drivers/staging/go7007/go7007.txt4
-rw-r--r--drivers/staging/panel/lcd-panel-cgram.txt2
-rw-r--r--drivers/staging/rt2860/common/mlme.c2
-rw-r--r--drivers/staging/rt2870/common/mlme.c2
-rw-r--r--drivers/staging/rt3070/common/mlme.c2
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c2
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/Makefile1
-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/host/isp1760-hcd.c24
-rw-r--r--drivers/usb/host/ohci-ep93xx.c13
-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.c158
-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.c5
-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/video/Kconfig14
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/amba-clcd.c4
-rw-r--r--drivers/video/atmel_lcdfb.c10
-rw-r--r--drivers/video/aty/aty128fb.c2
-rw-r--r--drivers/video/console/vgacon.c5
-rw-r--r--drivers/video/cyber2000fb.c9
-rw-r--r--drivers/video/hitfb.c4
-rw-r--r--drivers/video/mx3fb.c4
-rw-r--r--drivers/video/omap/dispc.c14
-rw-r--r--drivers/video/omap/hwa742.c26
-rw-r--r--drivers/video/omap/rfbi.c8
-rw-r--r--drivers/video/pxa168fb.c803
-rw-r--r--drivers/video/pxa168fb.h558
-rw-r--r--drivers/video/s3c-fb.c12
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c7
-rw-r--r--drivers/video/uvesafb.c10
-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/w1/Kconfig2
-rw-r--r--drivers/w1/masters/Kconfig6
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/iop_wdt.c2
-rw-r--r--drivers/watchdog/orion_wdt.c (renamed from drivers/watchdog/orion5x_wdt.c)120
-rw-r--r--drivers/xen/Kconfig20
-rw-r--r--drivers/xen/Makefile4
-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
725 files changed, 79188 insertions, 17371 deletions
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 72069ba5f1ed..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
@@ -611,6 +614,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
switch (cx->type) {
case ACPI_STATE_C1:
cx->valid = 1;
+ acpi_timer_check_state(i, pr, cx);
break;
case ACPI_STATE_C2:
@@ -830,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();
@@ -842,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 7f16f5f8e7d3..227543789ba9 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -840,7 +840,7 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
state = acpi_get_throttling_state(pr, value);
if (state == -1) {
ACPI_WARNING((AE_INFO,
- "Invalid throttling state, reset\n"));
+ "Invalid throttling state, reset"));
state = 0;
ret = acpi_processor_set_throttling(pr, state);
if (ret)
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 810cca90ca7f..1bdfb37377e3 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -570,6 +570,22 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
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"),
+ },
+ },
{}
};
@@ -2334,7 +2350,7 @@ static int __init acpi_video_init(void)
return acpi_video_register();
}
-void __exit acpi_video_exit(void)
+void acpi_video_exit(void)
{
acpi_bus_unregister_driver(&acpi_video_bus);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 9120717c0701..2aa1908e5ce0 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -535,6 +535,15 @@ config PATA_OPTIDMA
If unsure, say N.
+config PATA_PALMLD
+ tristate "Palm LifeDrive PATA support"
+ depends on MACH_PALMLD
+ help
+ This option enables support for Palm LifeDrive's internal ATA
+ port via the new ATA layer.
+
+ If unsure, say N.
+
config PATA_PCMCIA
tristate "PCMCIA PATA support"
depends on PCMCIA
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 7f1ecf99528c..1558059874f0 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_PATA_MPC52xx) += pata_mpc52xx.o
obj-$(CONFIG_PATA_MARVELL) += pata_marvell.o
obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
obj-$(CONFIG_PATA_OLDPIIX) += pata_oldpiix.o
+obj-$(CONFIG_PATA_PALMLD) += pata_palmld.o
obj-$(CONFIG_PATA_PCMCIA) += pata_pcmcia.o
obj-$(CONFIG_PATA_PDC2027X) += pata_pdc2027x.o
obj-$(CONFIG_PATA_PDC_OLD) += pata_pdc202xx_old.o
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 d51a17c0f59b..d0a14cf2bd74 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -223,10 +223,8 @@ static const struct pci_device_id piix_pci_tbl[] = {
/* 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) */
@@ -1455,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 */
};
@@ -1500,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)) {
@@ -1582,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-acpi.c b/drivers/ata/libata-acpi.c
index 6273d98d00eb..ac176da1f94e 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -748,9 +748,9 @@ static int ata_acpi_run_tf(struct ata_device *dev,
/**
* ata_acpi_exec_tfs - get then write drive taskfile settings
* @dev: target ATA device
- * @nr_executed: out paramter for the number of executed commands
+ * @nr_executed: out parameter for the number of executed commands
*
- * Evaluate _GTF and excute returned taskfiles.
+ * Evaluate _GTF and execute returned taskfiles.
*
* LOCKING:
* EH context.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c9242301cfa1..ca4d208ddf3b 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5031,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;
@@ -5041,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 94919ad03df1..fa22f94ca415 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2864,7 +2864,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
/**
* ata_set_mode - Program timings and issue SET FEATURES - XFER
* @link: link on which timings will be programmed
- * @r_failed_dev: out paramter for failed device
+ * @r_failed_dev: out parameter for failed device
*
* Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
* ata_set_mode() fails, pointer to the failing device is
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 342316064e9f..d0dfeef55db5 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1084,7 +1084,7 @@ static int atapi_drain_needed(struct request *rq)
if (likely(!blk_pc_request(rq)))
return 0;
- if (!rq->data_len || (rq->cmd_flags & REQ_RW))
+ if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_RW))
return 0;
return atapi_cmd_type(rq->cmd[0]) == ATAPI_MISC;
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_palmld.c b/drivers/ata/pata_palmld.c
new file mode 100644
index 000000000000..11fb4ccc74b4
--- /dev/null
+++ b/drivers/ata/pata_palmld.c
@@ -0,0 +1,150 @@
+/*
+ * drivers/ata/pata_palmld.c
+ *
+ * Driver for IDE channel in Palm LifeDrive
+ *
+ * Based on research of:
+ * Alex Osborne <ato@meshy.org>
+ *
+ * Rewrite for mainline:
+ * Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Rewritten version based on pata_ixp4xx_cf.c:
+ * ixp4xx PATA/Compact Flash driver
+ * Copyright (C) 2006-07 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can 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/libata.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <scsi/scsi_host.h>
+#include <mach/palmld.h>
+
+#define DRV_NAME "pata_palmld"
+
+static struct scsi_host_template palmld_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations palmld_port_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .cable_detect = ata_cable_40wire,
+};
+
+static __devinit int palmld_pata_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ void __iomem *mem;
+ int ret;
+
+ /* allocate host */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ /* remap drive's physical memory address */
+ mem = devm_ioremap(&pdev->dev, PALMLD_IDE_PHYS, 0x1000);
+ if (!mem)
+ return -ENOMEM;
+
+ /* request and activate power GPIO, IRQ GPIO */
+ ret = gpio_request(GPIO_NR_PALMLD_IDE_PWEN, "HDD PWR");
+ if (ret)
+ goto err1;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_IDE_PWEN, 1);
+ if (ret)
+ goto err2;
+
+ ret = gpio_request(GPIO_NR_PALMLD_IDE_RESET, "HDD RST");
+ if (ret)
+ goto err2;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_IDE_RESET, 0);
+ if (ret)
+ goto err3;
+
+ /* reset the drive */
+ gpio_set_value(GPIO_NR_PALMLD_IDE_RESET, 0);
+ msleep(30);
+ gpio_set_value(GPIO_NR_PALMLD_IDE_RESET, 1);
+ msleep(30);
+
+ /* setup the ata port */
+ ap = host->ports[0];
+ ap->ops = &palmld_port_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING;
+
+ /* memory mapping voodoo */
+ ap->ioaddr.cmd_addr = mem + 0x10;
+ ap->ioaddr.altstatus_addr = mem + 0xe;
+ ap->ioaddr.ctl_addr = mem + 0xe;
+
+ /* start the port */
+ ata_sff_std_ports(&ap->ioaddr);
+
+ /* activate host */
+ return ata_host_activate(host, 0, NULL, IRQF_TRIGGER_RISING,
+ &palmld_sht);
+
+err3:
+ gpio_free(GPIO_NR_PALMLD_IDE_RESET);
+err2:
+ gpio_free(GPIO_NR_PALMLD_IDE_PWEN);
+err1:
+ return ret;
+}
+
+static __devexit int palmld_pata_remove(struct platform_device *dev)
+{
+ struct ata_host *host = platform_get_drvdata(dev);
+
+ ata_host_detach(host);
+
+ /* power down the HDD */
+ gpio_set_value(GPIO_NR_PALMLD_IDE_PWEN, 0);
+
+ gpio_free(GPIO_NR_PALMLD_IDE_RESET);
+ gpio_free(GPIO_NR_PALMLD_IDE_PWEN);
+
+ return 0;
+}
+
+static struct platform_driver palmld_pata_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = palmld_pata_probe,
+ .remove = __devexit_p(palmld_pata_remove),
+};
+
+static int __init palmld_pata_init(void)
+{
+ return platform_driver_register(&palmld_pata_platform_driver);
+}
+
+static void __exit palmld_pata_exit(void)
+{
+ platform_driver_unregister(&palmld_pata_platform_driver);
+}
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("PalmLD PATA driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+module_init(palmld_pata_init);
+module_exit(palmld_pata_exit);
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 eb05a3c82a9e..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];
@@ -1280,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;
@@ -1395,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..4b04a15146d7 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -279,7 +279,7 @@ static struct device *next_device(struct klist_iter *i)
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
- * to retain this data, it should do, and increment the reference
+ * to retain this data, it should do so, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
@@ -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/platform.c b/drivers/base/platform.c
index 8b4708e06244..ead3f64c41d0 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -469,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
@@ -498,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);
@@ -633,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;
}
@@ -667,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;
}
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/DAC960.c b/drivers/block/DAC960.c
index f22ed6cc69f2..668dc234b8e2 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -3321,7 +3321,7 @@ static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_
DAC960_Command_T *Command;
while(1) {
- Request = elv_next_request(req_q);
+ Request = blk_peek_request(req_q);
if (!Request)
return 1;
@@ -3338,10 +3338,10 @@ static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_
}
Command->Completion = Request->end_io_data;
Command->LogicalDriveNumber = (long)Request->rq_disk->private_data;
- Command->BlockNumber = Request->sector;
- Command->BlockCount = Request->nr_sectors;
+ Command->BlockNumber = blk_rq_pos(Request);
+ Command->BlockCount = blk_rq_sectors(Request);
Command->Request = Request;
- blkdev_dequeue_request(Request);
+ blk_start_request(Request);
Command->SegmentCount = blk_rq_map_sg(req_q,
Command->Request, Command->cmd_sglist);
/* pci_map_sg MAY change the value of SegCount */
@@ -3431,7 +3431,7 @@ static void DAC960_queue_partial_rw(DAC960_Command_T *Command)
* successfully as possible.
*/
Command->SegmentCount = 1;
- Command->BlockNumber = Request->sector;
+ Command->BlockNumber = blk_rq_pos(Request);
Command->BlockCount = 1;
DAC960_QueueReadWriteCommand(Command);
return;
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index ddea8e485cc9..bb72ada9f074 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -112,7 +112,7 @@ config GDROM
with up to 1 GB of data. This drive will also read standard CD ROM
disks. Select this option to access any disks in your GD ROM drive.
Most users will want to say "Y" here.
- You can also build this as a module which will be called gdrom.ko
+ You can also build this as a module which will be called gdrom.
source "drivers/block/paride/Kconfig"
@@ -412,7 +412,7 @@ config ATA_OVER_ETH
config MG_DISK
tristate "mGine mflash, gflash support"
- depends on ARM && ATA && GPIOLIB
+ depends on ARM && GPIOLIB
help
mGine mFlash(gFlash) block device driver
@@ -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/amiflop.c b/drivers/block/amiflop.c
index 8df436ff7068..9c6e5b0fe894 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -112,8 +112,6 @@ module_param(fd_def_df0, ulong, 0);
MODULE_LICENSE("GPL");
static struct request_queue *floppy_queue;
-#define QUEUE (floppy_queue)
-#define CURRENT elv_next_request(floppy_queue)
/*
* Macros
@@ -1335,64 +1333,60 @@ static int get_track(int drive, int track)
static void redo_fd_request(void)
{
+ struct request *rq;
unsigned int cnt, block, track, sector;
int drive;
struct amiga_floppy_struct *floppy;
char *data;
unsigned long flags;
+ int err;
- repeat:
- if (!CURRENT) {
+next_req:
+ rq = blk_fetch_request(floppy_queue);
+ if (!rq) {
/* Nothing left to do */
return;
}
- floppy = CURRENT->rq_disk->private_data;
+ floppy = rq->rq_disk->private_data;
drive = floppy - unit;
+next_segment:
/* Here someone could investigate to be more efficient */
- for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) {
+ for (cnt = 0, err = 0; cnt < blk_rq_cur_sectors(rq); cnt++) {
#ifdef DEBUG
printk("fd: sector %ld + %d requested for %s\n",
- CURRENT->sector,cnt,
- (rq_data_dir(CURRENT) == READ) ? "read" : "write");
+ blk_rq_pos(rq), cnt,
+ (rq_data_dir(rq) == READ) ? "read" : "write");
#endif
- block = CURRENT->sector + cnt;
+ block = blk_rq_pos(rq) + cnt;
if ((int)block > floppy->blocks) {
- end_request(CURRENT, 0);
- goto repeat;
+ err = -EIO;
+ break;
}
track = block / (floppy->dtype->sects * floppy->type->sect_mult);
sector = block % (floppy->dtype->sects * floppy->type->sect_mult);
- data = CURRENT->buffer + 512 * cnt;
+ data = rq->buffer + 512 * cnt;
#ifdef DEBUG
printk("access to track %d, sector %d, with buffer at "
"0x%08lx\n", track, sector, data);
#endif
- if ((rq_data_dir(CURRENT) != READ) && (rq_data_dir(CURRENT) != WRITE)) {
- printk(KERN_WARNING "do_fd_request: unknown command\n");
- end_request(CURRENT, 0);
- goto repeat;
- }
if (get_track(drive, track) == -1) {
- end_request(CURRENT, 0);
- goto repeat;
+ err = -EIO;
+ break;
}
- switch (rq_data_dir(CURRENT)) {
- case READ:
+ if (rq_data_dir(rq) == READ) {
memcpy(data, floppy->trackbuf + sector * 512, 512);
- break;
-
- case WRITE:
+ } else {
memcpy(floppy->trackbuf + sector * 512, data, 512);
/* keep the drive spinning while writes are scheduled */
if (!fd_motor_on(drive)) {
- end_request(CURRENT, 0);
- goto repeat;
+ err = -EIO;
+ break;
}
/*
* setup a callback to write the track buffer
@@ -1404,14 +1398,12 @@ static void redo_fd_request(void)
/* reset the timer */
mod_timer (flush_track_timer + drive, jiffies + 1);
local_irq_restore(flags);
- break;
}
}
- CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- CURRENT->sector += CURRENT->current_nr_sectors;
- end_request(CURRENT, 1);
- goto repeat;
+ if (__blk_end_request_cur(rq, err))
+ goto next_segment;
+ goto next_req;
}
static void do_fd_request(struct request_queue * q)
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 4234c11c1e4c..f5e7180d7f47 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -79,9 +79,7 @@
#undef DEBUG
static struct request_queue *floppy_queue;
-
-#define QUEUE (floppy_queue)
-#define CURRENT elv_next_request(floppy_queue)
+static struct request *fd_request;
/* Disk types: DD, HD, ED */
static struct atari_disk_type {
@@ -376,6 +374,12 @@ static DEFINE_TIMER(readtrack_timer, fd_readtrack_check, 0, 0);
static DEFINE_TIMER(timeout_timer, fd_times_out, 0, 0);
static DEFINE_TIMER(fd_timer, check_change, 0, 0);
+static void fd_end_request_cur(int err)
+{
+ if (!__blk_end_request_cur(fd_request, err))
+ fd_request = NULL;
+}
+
static inline void start_motor_off_timer(void)
{
mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY);
@@ -606,15 +610,15 @@ static void fd_error( void )
return;
}
- if (!CURRENT)
+ if (!fd_request)
return;
- CURRENT->errors++;
- if (CURRENT->errors >= MAX_ERRORS) {
+ fd_request->errors++;
+ if (fd_request->errors >= MAX_ERRORS) {
printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive );
- end_request(CURRENT, 0);
+ fd_end_request_cur(-EIO);
}
- else if (CURRENT->errors == RECALIBRATE_ERRORS) {
+ else if (fd_request->errors == RECALIBRATE_ERRORS) {
printk(KERN_WARNING "fd%d: recalibrating\n", SelectedDrive );
if (SelectedDrive != -1)
SUD.track = -1;
@@ -725,16 +729,14 @@ static void do_fd_action( int drive )
if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
if (ReqCmd == READ) {
copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
- if (++ReqCnt < CURRENT->current_nr_sectors) {
+ if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
/* read next sector */
setup_req_params( drive );
goto repeat;
}
else {
/* all sectors finished */
- CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- CURRENT->sector += CURRENT->current_nr_sectors;
- end_request(CURRENT, 1);
+ fd_end_request_cur(0);
redo_fd_request();
return;
}
@@ -1132,16 +1134,14 @@ static void fd_rwsec_done1(int status)
}
}
- if (++ReqCnt < CURRENT->current_nr_sectors) {
+ if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
/* read next sector */
setup_req_params( SelectedDrive );
do_fd_action( SelectedDrive );
}
else {
/* all sectors finished */
- CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- CURRENT->sector += CURRENT->current_nr_sectors;
- end_request(CURRENT, 1);
+ fd_end_request_cur(0);
redo_fd_request();
}
return;
@@ -1382,7 +1382,7 @@ static void setup_req_params( int drive )
ReqData = ReqBuffer + 512 * ReqCnt;
if (UseTrackbuffer)
- read_track = (ReqCmd == READ && CURRENT->errors == 0);
+ read_track = (ReqCmd == READ && fd_request->errors == 0);
else
read_track = 0;
@@ -1396,25 +1396,27 @@ static void redo_fd_request(void)
int drive, type;
struct atari_floppy_struct *floppy;
- DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n",
- CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "",
- CURRENT ? CURRENT->sector : 0 ));
+ DPRINT(("redo_fd_request: fd_request=%p dev=%s fd_request->sector=%ld\n",
+ fd_request, fd_request ? fd_request->rq_disk->disk_name : "",
+ fd_request ? blk_rq_pos(fd_request) : 0 ));
IsFormatting = 0;
repeat:
+ if (!fd_request) {
+ fd_request = blk_fetch_request(floppy_queue);
+ if (!fd_request)
+ goto the_end;
+ }
- if (!CURRENT)
- goto the_end;
-
- floppy = CURRENT->rq_disk->private_data;
+ floppy = fd_request->rq_disk->private_data;
drive = floppy - unit;
type = floppy->type;
if (!UD.connected) {
/* drive not connected */
printk(KERN_ERR "Unknown Device: fd%d\n", drive );
- end_request(CURRENT, 0);
+ fd_end_request_cur(-EIO);
goto repeat;
}
@@ -1430,12 +1432,12 @@ repeat:
/* user supplied disk type */
if (--type >= NUM_DISK_MINORS) {
printk(KERN_WARNING "fd%d: invalid disk format", drive );
- end_request(CURRENT, 0);
+ fd_end_request_cur(-EIO);
goto repeat;
}
if (minor2disktype[type].drive_types > DriveType) {
printk(KERN_WARNING "fd%d: unsupported disk format", drive );
- end_request(CURRENT, 0);
+ fd_end_request_cur(-EIO);
goto repeat;
}
type = minor2disktype[type].index;
@@ -1444,8 +1446,8 @@ repeat:
UD.autoprobe = 0;
}
- if (CURRENT->sector + 1 > UDT->blocks) {
- end_request(CURRENT, 0);
+ if (blk_rq_pos(fd_request) + 1 > UDT->blocks) {
+ fd_end_request_cur(-EIO);
goto repeat;
}
@@ -1453,9 +1455,9 @@ repeat:
del_timer( &motor_off_timer );
ReqCnt = 0;
- ReqCmd = rq_data_dir(CURRENT);
- ReqBlock = CURRENT->sector;
- ReqBuffer = CURRENT->buffer;
+ ReqCmd = rq_data_dir(fd_request);
+ ReqBlock = blk_rq_pos(fd_request);
+ ReqBuffer = fd_request->buffer;
setup_req_params( drive );
do_fd_action( drive );
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 4d4d5e0d3fa6..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
@@ -1299,7 +1489,6 @@ static void cciss_softirq_done(struct request *rq)
{
CommandList_struct *cmd = rq->completion_data;
ctlr_info_t *h = hba[cmd->ctlr];
- unsigned int nr_bytes;
unsigned long flags;
u64bit temp64;
int i, ddir;
@@ -1321,15 +1510,11 @@ static void cciss_softirq_done(struct request *rq)
printk("Done with %p\n", rq);
#endif /* CCISS_DEBUG */
- /*
- * Store the full size and set the residual count for pc requests
- */
- nr_bytes = blk_rq_bytes(rq);
+ /* set the residual count for pc requests */
if (blk_pc_request(rq))
- rq->data_len = cmd->err_info->ResidualCnt;
+ rq->resid_len = cmd->err_info->ResidualCnt;
- if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes))
- BUG();
+ blk_end_request_all(rq, (rq->errors == 0) ? 0 : -EIO);
spin_lock_irqsave(&h->lock, flags);
cmd_free(h, cmd, 1);
@@ -1337,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
@@ -1348,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;
@@ -1356,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);
@@ -1377,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);
@@ -1394,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 */
@@ -1468,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));
@@ -1517,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];
@@ -1591,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 */
@@ -1598,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
@@ -1668,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);
@@ -1718,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;
}
}
@@ -1877,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;
@@ -1897,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;
@@ -1947,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;
@@ -1956,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;
@@ -1979,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);
@@ -1997,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;
@@ -2035,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;
}
@@ -2155,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) {
@@ -2204,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) {
@@ -2211,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);
@@ -2238,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) {
@@ -2245,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);
@@ -2303,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);
@@ -2333,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 */
@@ -2420,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");
@@ -2443,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;
}
@@ -2691,7 +2904,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
printk(KERN_WARNING "cciss: cmd %p has"
" completed with data underrun "
"reported\n", cmd);
- cmd->rq->data_len = cmd->err_info->ResidualCnt;
+ cmd->rq->resid_len = cmd->err_info->ResidualCnt;
}
break;
case CMD_DATA_OVERRUN:
@@ -2806,7 +3019,7 @@ static void do_cciss_request(struct request_queue *q)
goto startio;
queue:
- creq = elv_next_request(q);
+ creq = blk_peek_request(q);
if (!creq)
goto startio;
@@ -2815,7 +3028,7 @@ static void do_cciss_request(struct request_queue *q)
if ((c = cmd_alloc(h, 1)) == NULL)
goto full;
- blkdev_dequeue_request(creq);
+ blk_start_request(creq);
spin_unlock_irq(q->queue_lock);
@@ -2840,10 +3053,10 @@ static void do_cciss_request(struct request_queue *q)
c->Request.Timeout = 0; // Don't time out
c->Request.CDB[0] =
(rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
- start_blk = creq->sector;
+ start_blk = blk_rq_pos(creq);
#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n", (int)creq->sector,
- (int)creq->nr_sectors);
+ printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",
+ (int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
#endif /* CCISS_DEBUG */
sg_init_table(tmp_sg, MAXSGENTRIES);
@@ -2869,8 +3082,8 @@ static void do_cciss_request(struct request_queue *q)
h->maxSG = seg;
#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: Submitting %lu sectors in %d segments\n",
- creq->nr_sectors, seg);
+ printk(KERN_DEBUG "cciss: Submitting %u sectors in %d segments\n",
+ blk_rq_sectors(creq), seg);
#endif /* CCISS_DEBUG */
c->Header.SGList = c->Header.SGTotal = seg;
@@ -2882,8 +3095,8 @@ static void do_cciss_request(struct request_queue *q)
c->Request.CDB[4] = (start_blk >> 8) & 0xff;
c->Request.CDB[5] = start_blk & 0xff;
c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
- c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
- c->Request.CDB[8] = creq->nr_sectors & 0xff;
+ c->Request.CDB[7] = (blk_rq_sectors(creq) >> 8) & 0xff;
+ c->Request.CDB[8] = blk_rq_sectors(creq) & 0xff;
c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
} else {
u32 upper32 = upper_32_bits(start_blk);
@@ -2898,10 +3111,10 @@ static void do_cciss_request(struct request_queue *q)
c->Request.CDB[7]= (start_blk >> 16) & 0xff;
c->Request.CDB[8]= (start_blk >> 8) & 0xff;
c->Request.CDB[9]= start_blk & 0xff;
- c->Request.CDB[10]= (creq->nr_sectors >> 24) & 0xff;
- c->Request.CDB[11]= (creq->nr_sectors >> 16) & 0xff;
- c->Request.CDB[12]= (creq->nr_sectors >> 8) & 0xff;
- c->Request.CDB[13]= creq->nr_sectors & 0xff;
+ c->Request.CDB[10]= (blk_rq_sectors(creq) >> 24) & 0xff;
+ c->Request.CDB[11]= (blk_rq_sectors(creq) >> 16) & 0xff;
+ c->Request.CDB[12]= (blk_rq_sectors(creq) >> 8) & 0xff;
+ c->Request.CDB[13]= blk_rq_sectors(creq) & 0xff;
c->Request.CDB[14] = c->Request.CDB[15] = 0;
}
} else if (blk_pc_request(creq)) {
@@ -2931,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)
@@ -3723,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;
@@ -3787,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.
@@ -3828,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];
@@ -3855,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,
@@ -3872,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++){
@@ -3907,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 {
@@ -3973,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);
}
@@ -3999,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
@@ -4008,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)
@@ -4026,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 ca268ca11159..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;
@@ -903,7 +903,7 @@ static void do_ida_request(struct request_queue *q)
goto startio;
queue_next:
- creq = elv_next_request(q);
+ creq = blk_peek_request(q);
if (!creq)
goto startio;
@@ -912,17 +912,18 @@ queue_next:
if ((c = cmd_alloc(h,1)) == NULL)
goto startio;
- blkdev_dequeue_request(creq);
+ blk_start_request(creq);
c->ctlr = h->ctlr;
c->hdr.unit = (drv_info_t *)(creq->rq_disk->private_data) - h->drv;
c->hdr.size = sizeof(rblk_t) >> 2;
c->size += sizeof(rblk_t);
- c->req.hdr.blk = creq->sector;
+ c->req.hdr.blk = blk_rq_pos(creq);
c->rq = creq;
DBGPX(
- printk("sector=%d, nr_sectors=%d\n", creq->sector, creq->nr_sectors);
+ printk("sector=%d, nr_sectors=%u\n",
+ blk_rq_pos(creq), blk_rq_sectors(creq));
);
sg_init_table(tmp_sg, SG_MAX);
seg = blk_rq_map_sg(q, creq, tmp_sg);
@@ -940,9 +941,9 @@ DBGPX(
tmp_sg[i].offset,
tmp_sg[i].length, dir);
}
-DBGPX( printk("Submitting %d sectors in %d segments\n", creq->nr_sectors, seg); );
+DBGPX( printk("Submitting %u sectors in %d segments\n", blk_rq_sectors(creq), seg); );
c->req.hdr.sg_cnt = seg;
- c->req.hdr.blk_cnt = creq->nr_sectors;
+ c->req.hdr.blk_cnt = blk_rq_sectors(creq);
c->req.hdr.cmd = (rq_data_dir(creq) == READ) ? IDA_READ : IDA_WRITE;
c->type = CMD_RWREQ;
@@ -1024,8 +1025,7 @@ static inline void complete_command(cmdlist_t *cmd, int timeout)
cmd->req.sg[i].size, ddir);
DBGPX(printk("Done with %p\n", rq););
- if (__blk_end_request(rq, error, blk_rq_bytes(rq)))
- BUG();
+ __blk_end_request_all(rq, error);
}
/*
@@ -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 1300df6f1642..862b40c90181 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -931,7 +931,7 @@ static inline void unlock_fdc(void)
del_timer(&fd_timeout);
cont = NULL;
clear_bit(0, &fdc_busy);
- if (elv_next_request(floppy_queue))
+ if (current_req || blk_peek_request(floppy_queue))
do_fd_request(floppy_queue);
spin_unlock_irqrestore(&floppy_lock, flags);
wake_up(&fdc_wait);
@@ -2303,7 +2303,7 @@ static void floppy_end_request(struct request *req, int error)
/* current_count_sectors can be zero if transfer failed */
if (error)
- nr_sectors = req->current_nr_sectors;
+ nr_sectors = blk_rq_cur_sectors(req);
if (__blk_end_request(req, error, nr_sectors << 9))
return;
@@ -2332,7 +2332,7 @@ static void request_done(int uptodate)
if (uptodate) {
/* maintain values for invalidation on geometry
* change */
- block = current_count_sectors + req->sector;
+ block = current_count_sectors + blk_rq_pos(req);
INFBOUND(DRS->maxblock, block);
if (block > _floppy->sect)
DRS->maxtrack = 1;
@@ -2346,10 +2346,10 @@ static void request_done(int uptodate)
/* record write error information */
DRWE->write_errors++;
if (DRWE->write_errors == 1) {
- DRWE->first_error_sector = req->sector;
+ DRWE->first_error_sector = blk_rq_pos(req);
DRWE->first_error_generation = DRS->generation;
}
- DRWE->last_error_sector = req->sector;
+ DRWE->last_error_sector = blk_rq_pos(req);
DRWE->last_error_generation = DRS->generation;
}
spin_lock_irqsave(q->queue_lock, flags);
@@ -2503,24 +2503,23 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
max_sector = transfer_size(ssize,
min(max_sector, max_sector_2),
- current_req->nr_sectors);
+ blk_rq_sectors(current_req));
if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
- buffer_max > fsector_t + current_req->nr_sectors)
+ buffer_max > fsector_t + blk_rq_sectors(current_req))
current_count_sectors = min_t(int, buffer_max - fsector_t,
- current_req->nr_sectors);
+ blk_rq_sectors(current_req));
remaining = current_count_sectors << 9;
#ifdef FLOPPY_SANITY_CHECK
- if ((remaining >> 9) > current_req->nr_sectors &&
- CT(COMMAND) == FD_WRITE) {
+ if (remaining > blk_rq_bytes(current_req) && CT(COMMAND) == FD_WRITE) {
DPRINT("in copy buffer\n");
printk("current_count_sectors=%ld\n", current_count_sectors);
printk("remaining=%d\n", remaining >> 9);
- printk("current_req->nr_sectors=%ld\n",
- current_req->nr_sectors);
+ printk("current_req->nr_sectors=%u\n",
+ blk_rq_sectors(current_req));
printk("current_req->current_nr_sectors=%u\n",
- current_req->current_nr_sectors);
+ blk_rq_cur_sectors(current_req));
printk("max_sector=%d\n", max_sector);
printk("ssize=%d\n", ssize);
}
@@ -2530,7 +2529,7 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9);
- size = current_req->current_nr_sectors << 9;
+ size = blk_rq_cur_bytes(current_req);
rq_for_each_segment(bv, current_req, iter) {
if (!remaining)
@@ -2648,10 +2647,10 @@ static int make_raw_rw_request(void)
max_sector = _floppy->sect * _floppy->head;
- TRACK = (int)current_req->sector / max_sector;
- fsector_t = (int)current_req->sector % max_sector;
+ TRACK = (int)blk_rq_pos(current_req) / max_sector;
+ fsector_t = (int)blk_rq_pos(current_req) % max_sector;
if (_floppy->track && TRACK >= _floppy->track) {
- if (current_req->current_nr_sectors & 1) {
+ if (blk_rq_cur_sectors(current_req) & 1) {
current_count_sectors = 1;
return 1;
} else
@@ -2669,7 +2668,7 @@ static int make_raw_rw_request(void)
if (fsector_t >= max_sector) {
current_count_sectors =
min_t(int, _floppy->sect - fsector_t,
- current_req->nr_sectors);
+ blk_rq_sectors(current_req));
return 1;
}
SIZECODE = 2;
@@ -2720,7 +2719,7 @@ static int make_raw_rw_request(void)
in_sector_offset = (fsector_t % _floppy->sect) % ssize;
aligned_sector_t = fsector_t - in_sector_offset;
- max_size = current_req->nr_sectors;
+ max_size = blk_rq_sectors(current_req);
if ((raw_cmd->track == buffer_track) &&
(current_drive == buffer_drive) &&
(fsector_t >= buffer_min) && (fsector_t < buffer_max)) {
@@ -2729,10 +2728,10 @@ static int make_raw_rw_request(void)
copy_buffer(1, max_sector, buffer_max);
return 1;
}
- } else if (in_sector_offset || current_req->nr_sectors < ssize) {
+ } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) {
if (CT(COMMAND) == FD_WRITE) {
- if (fsector_t + current_req->nr_sectors > ssize &&
- fsector_t + current_req->nr_sectors < ssize + ssize)
+ if (fsector_t + blk_rq_sectors(current_req) > ssize &&
+ fsector_t + blk_rq_sectors(current_req) < ssize + ssize)
max_size = ssize + ssize;
else
max_size = ssize;
@@ -2776,7 +2775,7 @@ static int make_raw_rw_request(void)
(indirect * 2 > direct * 3 &&
*errors < DP->max_errors.read_track && ((!probing
|| (DP->read_track & (1 << DRS->probed_format)))))) {
- max_size = current_req->nr_sectors;
+ max_size = blk_rq_sectors(current_req);
} else {
raw_cmd->kernel_data = current_req->buffer;
raw_cmd->length = current_count_sectors << 9;
@@ -2801,7 +2800,7 @@ static int make_raw_rw_request(void)
fsector_t > buffer_max ||
fsector_t < buffer_min ||
((CT(COMMAND) == FD_READ ||
- (!in_sector_offset && current_req->nr_sectors >= ssize)) &&
+ (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) &&
max_sector > 2 * max_buffer_sectors + buffer_min &&
max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)
/* not enough space */
@@ -2879,8 +2878,8 @@ static int make_raw_rw_request(void)
printk("write\n");
return 0;
}
- } else if (raw_cmd->length > current_req->nr_sectors << 9 ||
- current_count_sectors > current_req->nr_sectors) {
+ } else if (raw_cmd->length > blk_rq_bytes(current_req) ||
+ current_count_sectors > blk_rq_sectors(current_req)) {
DPRINT("buffer overrun in direct transfer\n");
return 0;
} else if (raw_cmd->length < current_count_sectors << 9) {
@@ -2913,7 +2912,7 @@ static void redo_fd_request(void)
struct request *req;
spin_lock_irq(floppy_queue->queue_lock);
- req = elv_next_request(floppy_queue);
+ req = blk_fetch_request(floppy_queue);
spin_unlock_irq(floppy_queue->queue_lock);
if (!req) {
do_floppy = NULL;
@@ -2990,8 +2989,9 @@ static void do_fd_request(struct request_queue * q)
if (usage_count == 0) {
printk("warning: usage count=0, current_req=%p exiting\n",
current_req);
- printk("sect=%ld type=%x flags=%x\n", (long)current_req->sector,
- current_req->cmd_type, current_req->cmd_flags);
+ printk("sect=%ld type=%x flags=%x\n",
+ (long)blk_rq_pos(current_req), current_req->cmd_type,
+ current_req->cmd_flags);
return;
}
if (test_bit(0, &fdc_busy)) {
@@ -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 baaa9e486e50..f65b3f369eb0 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -98,10 +98,9 @@
static DEFINE_SPINLOCK(hd_lock);
static struct request_queue *hd_queue;
+static struct request *hd_req;
#define MAJOR_NR HD_MAJOR
-#define QUEUE (hd_queue)
-#define CURRENT elv_next_request(hd_queue)
#define TIMEOUT_VALUE (6*HZ)
#define HD_DELAY 0
@@ -195,11 +194,24 @@ static void __init hd_setup(char *str, int *ints)
NR_HD = hdind+1;
}
+static bool hd_end_request(int err, unsigned int bytes)
+{
+ if (__blk_end_request(hd_req, err, bytes))
+ return true;
+ hd_req = NULL;
+ return false;
+}
+
+static bool hd_end_request_cur(int err)
+{
+ return hd_end_request(err, blk_rq_cur_bytes(hd_req));
+}
+
static void dump_status(const char *msg, unsigned int stat)
{
char *name = "hd?";
- if (CURRENT)
- name = CURRENT->rq_disk->disk_name;
+ if (hd_req)
+ name = hd_req->rq_disk->disk_name;
#ifdef VERBOSE_ERRORS
printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
@@ -227,8 +239,8 @@ static void dump_status(const char *msg, unsigned int stat)
if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
- if (CURRENT)
- printk(", sector=%ld", CURRENT->sector);
+ if (hd_req)
+ printk(", sector=%ld", blk_rq_pos(hd_req));
}
printk("\n");
}
@@ -406,11 +418,12 @@ static void unexpected_hd_interrupt(void)
*/
static void bad_rw_intr(void)
{
- struct request *req = CURRENT;
+ struct request *req = hd_req;
+
if (req != NULL) {
struct hd_i_struct *disk = req->rq_disk->private_data;
if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
- end_request(req, 0);
+ hd_end_request_cur(-EIO);
disk->special_op = disk->recalibrate = 1;
} else if (req->errors % RESET_FREQ == 0)
reset = 1;
@@ -452,37 +465,30 @@ static void read_intr(void)
bad_rw_intr();
hd_request();
return;
+
ok_to_read:
- req = CURRENT;
+ req = hd_req;
insw(HD_DATA, req->buffer, 256);
- req->sector++;
- req->buffer += 512;
- req->errors = 0;
- i = --req->nr_sectors;
- --req->current_nr_sectors;
#ifdef DEBUG
- printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n",
- req->rq_disk->disk_name, req->sector, req->nr_sectors,
- req->buffer+512);
+ printk("%s: read: sector %ld, remaining = %u, buffer=%p\n",
+ req->rq_disk->disk_name, blk_rq_pos(req) + 1,
+ blk_rq_sectors(req) - 1, req->buffer+512);
#endif
- if (req->current_nr_sectors <= 0)
- end_request(req, 1);
- if (i > 0) {
+ if (hd_end_request(0, 512)) {
SET_HANDLER(&read_intr);
return;
}
+
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
- if (elv_next_request(QUEUE))
- hd_request();
- return;
+ hd_request();
}
static void write_intr(void)
{
- struct request *req = CURRENT;
+ struct request *req = hd_req;
int i;
int retries = 100000;
@@ -492,30 +498,25 @@ static void write_intr(void)
continue;
if (!OK_STATUS(i))
break;
- if ((req->nr_sectors <= 1) || (i & DRQ_STAT))
+ if ((blk_rq_sectors(req) <= 1) || (i & DRQ_STAT))
goto ok_to_write;
} while (--retries > 0);
dump_status("write_intr", i);
bad_rw_intr();
hd_request();
return;
+
ok_to_write:
- req->sector++;
- i = --req->nr_sectors;
- --req->current_nr_sectors;
- req->buffer += 512;
- if (!i || (req->bio && req->current_nr_sectors <= 0))
- end_request(req, 1);
- if (i > 0) {
+ if (hd_end_request(0, 512)) {
SET_HANDLER(&write_intr);
outsw(HD_DATA, req->buffer, 256);
- } else {
+ return;
+ }
+
#if (HD_DELAY > 0)
- last_req = read_timer();
+ last_req = read_timer();
#endif
- hd_request();
- }
- return;
+ hd_request();
}
static void recal_intr(void)
@@ -537,18 +538,18 @@ static void hd_times_out(unsigned long dummy)
do_hd = NULL;
- if (!CURRENT)
+ if (!hd_req)
return;
spin_lock_irq(hd_queue->queue_lock);
reset = 1;
- name = CURRENT->rq_disk->disk_name;
+ name = hd_req->rq_disk->disk_name;
printk("%s: timeout\n", name);
- if (++CURRENT->errors >= MAX_ERRORS) {
+ if (++hd_req->errors >= MAX_ERRORS) {
#ifdef DEBUG
printk("%s: too many errors\n", name);
#endif
- end_request(CURRENT, 0);
+ hd_end_request_cur(-EIO);
}
hd_request();
spin_unlock_irq(hd_queue->queue_lock);
@@ -563,7 +564,7 @@ static int do_special_op(struct hd_i_struct *disk, struct request *req)
}
if (disk->head > 16) {
printk("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name);
- end_request(req, 0);
+ hd_end_request_cur(-EIO);
}
disk->special_op = 0;
return 1;
@@ -590,24 +591,27 @@ static void hd_request(void)
repeat:
del_timer(&device_timer);
- req = CURRENT;
- if (!req) {
- do_hd = NULL;
- return;
+ if (!hd_req) {
+ hd_req = blk_fetch_request(hd_queue);
+ if (!hd_req) {
+ do_hd = NULL;
+ return;
+ }
}
+ req = hd_req;
if (reset) {
reset_hd();
return;
}
disk = req->rq_disk->private_data;
- block = req->sector;
- nsect = req->nr_sectors;
+ block = blk_rq_pos(req);
+ nsect = blk_rq_sectors(req);
if (block >= get_capacity(req->rq_disk) ||
((block+nsect) > get_capacity(req->rq_disk))) {
printk("%s: bad access: block=%d, count=%d\n",
req->rq_disk->disk_name, block, nsect);
- end_request(req, 0);
+ hd_end_request_cur(-EIO);
goto repeat;
}
@@ -647,7 +651,7 @@ repeat:
break;
default:
printk("unknown hd-command\n");
- end_request(req, 0);
+ hd_end_request_cur(-EIO);
break;
}
}
@@ -720,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/loop.c b/drivers/block/loop.c
index ddae80825899..801f4ab83302 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -511,11 +511,7 @@ out:
*/
static void loop_add_bio(struct loop_device *lo, struct bio *bio)
{
- if (lo->lo_biotail) {
- lo->lo_biotail->bi_next = bio;
- lo->lo_biotail = bio;
- } else
- lo->lo_bio = lo->lo_biotail = bio;
+ bio_list_add(&lo->lo_bio_list, bio);
}
/*
@@ -523,16 +519,7 @@ static void loop_add_bio(struct loop_device *lo, struct bio *bio)
*/
static struct bio *loop_get_bio(struct loop_device *lo)
{
- struct bio *bio;
-
- if ((bio = lo->lo_bio)) {
- if (bio == lo->lo_biotail)
- lo->lo_biotail = NULL;
- lo->lo_bio = bio->bi_next;
- bio->bi_next = NULL;
- }
-
- return bio;
+ return bio_list_pop(&lo->lo_bio_list);
}
static int loop_make_request(struct request_queue *q, struct bio *old_bio)
@@ -609,12 +596,13 @@ static int loop_thread(void *data)
set_user_nice(current, -20);
- while (!kthread_should_stop() || lo->lo_bio) {
+ while (!kthread_should_stop() || !bio_list_empty(&lo->lo_bio_list)) {
wait_event_interruptible(lo->lo_event,
- lo->lo_bio || kthread_should_stop());
+ !bio_list_empty(&lo->lo_bio_list) ||
+ kthread_should_stop());
- if (!lo->lo_bio)
+ if (bio_list_empty(&lo->lo_bio_list))
continue;
spin_lock_irq(&lo->lo_lock);
bio = loop_get_bio(lo);
@@ -721,10 +709,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
goto out_putf;
- /* new backing store needs to support loop (eg splice_read) */
- if (!inode->i_fop->splice_read)
- goto out_putf;
-
/* size of the new backing store needs to be the same */
if (get_loop_size(lo, file) != get_loop_size(lo, old_file))
goto out_putf;
@@ -800,12 +784,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
error = -EINVAL;
if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
const struct address_space_operations *aops = mapping->a_ops;
- /*
- * If we can't read - sorry. If we only can't write - well,
- * it's going to be read-only.
- */
- if (!file->f_op->splice_read)
- goto out_putf;
+
if (aops->write_begin)
lo_flags |= LO_FLAGS_USE_AOPS;
if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
@@ -841,7 +820,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
lo->old_gfp_mask = mapping_gfp_mask(mapping);
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
- lo->lo_bio = lo->lo_biotail = NULL;
+ bio_list_init(&lo->lo_bio_list);
/*
* set queue make_request_fn, and add limits based on lower level
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index f3898353d0a8..60de5a01e71e 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -17,71 +17,220 @@
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
-#include <linux/libata.h>
+#include <linux/ata.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
-#include <linux/mg_disk.h>
#define MG_RES_SEC (CONFIG_MG_DISK_RES << 1)
+/* name for block device */
+#define MG_DISK_NAME "mgd"
+/* name for platform device */
+#define MG_DEV_NAME "mg_disk"
+
+#define MG_DISK_MAJ 0
+#define MG_DISK_MAX_PART 16
+#define MG_SECTOR_SIZE 512
+#define MG_MAX_SECTS 256
+
+/* Register offsets */
+#define MG_BUFF_OFFSET 0x8000
+#define MG_STORAGE_BUFFER_SIZE 0x200
+#define MG_REG_OFFSET 0xC000
+#define MG_REG_FEATURE (MG_REG_OFFSET + 2) /* write case */
+#define MG_REG_ERROR (MG_REG_OFFSET + 2) /* read case */
+#define MG_REG_SECT_CNT (MG_REG_OFFSET + 4)
+#define MG_REG_SECT_NUM (MG_REG_OFFSET + 6)
+#define MG_REG_CYL_LOW (MG_REG_OFFSET + 8)
+#define MG_REG_CYL_HIGH (MG_REG_OFFSET + 0xA)
+#define MG_REG_DRV_HEAD (MG_REG_OFFSET + 0xC)
+#define MG_REG_COMMAND (MG_REG_OFFSET + 0xE) /* write case */
+#define MG_REG_STATUS (MG_REG_OFFSET + 0xE) /* read case */
+#define MG_REG_DRV_CTRL (MG_REG_OFFSET + 0x10)
+#define MG_REG_BURST_CTRL (MG_REG_OFFSET + 0x12)
+
+/* handy status */
+#define MG_STAT_READY (ATA_DRDY | ATA_DSC)
+#define MG_READY_OK(s) (((s) & (MG_STAT_READY | (ATA_BUSY | ATA_DF | \
+ ATA_ERR))) == MG_STAT_READY)
+
+/* error code for others */
+#define MG_ERR_NONE 0
+#define MG_ERR_TIMEOUT 0x100
+#define MG_ERR_INIT_STAT 0x101
+#define MG_ERR_TRANSLATION 0x102
+#define MG_ERR_CTRL_RST 0x103
+#define MG_ERR_INV_STAT 0x104
+#define MG_ERR_RSTOUT 0x105
+
+#define MG_MAX_ERRORS 6 /* Max read/write errors */
+
+/* command */
+#define MG_CMD_RD 0x20
+#define MG_CMD_WR 0x30
+#define MG_CMD_SLEEP 0x99
+#define MG_CMD_WAKEUP 0xC3
+#define MG_CMD_ID 0xEC
+#define MG_CMD_WR_CONF 0x3C
+#define MG_CMD_RD_CONF 0x40
+
+/* operation mode */
+#define MG_OP_CASCADE (1 << 0)
+#define MG_OP_CASCADE_SYNC_RD (1 << 1)
+#define MG_OP_CASCADE_SYNC_WR (1 << 2)
+#define MG_OP_INTERLEAVE (1 << 3)
+
+/* synchronous */
+#define MG_BURST_LAT_4 (3 << 4)
+#define MG_BURST_LAT_5 (4 << 4)
+#define MG_BURST_LAT_6 (5 << 4)
+#define MG_BURST_LAT_7 (6 << 4)
+#define MG_BURST_LAT_8 (7 << 4)
+#define MG_BURST_LEN_4 (1 << 1)
+#define MG_BURST_LEN_8 (2 << 1)
+#define MG_BURST_LEN_16 (3 << 1)
+#define MG_BURST_LEN_32 (4 << 1)
+#define MG_BURST_LEN_CONT (0 << 1)
+
+/* timeout value (unit: ms) */
+#define MG_TMAX_CONF_TO_CMD 1
+#define MG_TMAX_WAIT_RD_DRQ 10
+#define MG_TMAX_WAIT_WR_DRQ 500
+#define MG_TMAX_RST_TO_BUSY 10
+#define MG_TMAX_HDRST_TO_RDY 500
+#define MG_TMAX_SWRST_TO_RDY 500
+#define MG_TMAX_RSTOUT 3000
+
+/* device attribution */
+/* use mflash as boot device */
+#define MG_BOOT_DEV (1 << 0)
+/* use mflash as storage device */
+#define MG_STORAGE_DEV (1 << 1)
+/* same as MG_STORAGE_DEV, but bootloader already done reset sequence */
+#define MG_STORAGE_DEV_SKIP_RST (1 << 2)
+
+#define MG_DEV_MASK (MG_BOOT_DEV | MG_STORAGE_DEV | MG_STORAGE_DEV_SKIP_RST)
+
+/* names of GPIO resource */
+#define MG_RST_PIN "mg_rst"
+/* except MG_BOOT_DEV, reset-out pin should be assigned */
+#define MG_RSTOUT_PIN "mg_rstout"
+
+/* private driver data */
+struct mg_drv_data {
+ /* disk resource */
+ u32 use_polling;
+
+ /* device attribution */
+ u32 dev_attr;
+
+ /* internally used */
+ struct mg_host *host;
+};
+
+/* main structure for mflash driver */
+struct mg_host {
+ struct device *dev;
+
+ struct request_queue *breq;
+ struct request *req;
+ spinlock_t lock;
+ struct gendisk *gd;
+
+ struct timer_list timer;
+ void (*mg_do_intr) (struct mg_host *);
+
+ u16 id[ATA_ID_WORDS];
+
+ u16 cyls;
+ u16 heads;
+ u16 sectors;
+ u32 n_sectors;
+ u32 nres_sectors;
+
+ void __iomem *dev_base;
+ unsigned int irq;
+ unsigned int rst;
+ unsigned int rstout;
+
+ u32 major;
+ u32 error;
+};
+
+/*
+ * Debugging macro and defines
+ */
+#undef DO_MG_DEBUG
+#ifdef DO_MG_DEBUG
+# define MG_DBG(fmt, args...) \
+ printk(KERN_DEBUG "%s:%d "fmt, __func__, __LINE__, ##args)
+#else /* CONFIG_MG_DEBUG */
+# define MG_DBG(fmt, args...) do { } while (0)
+#endif /* CONFIG_MG_DEBUG */
+
static void mg_request(struct request_queue *);
+static bool mg_end_request(struct mg_host *host, int err, unsigned int nr_bytes)
+{
+ if (__blk_end_request(host->req, err, nr_bytes))
+ return true;
+
+ host->req = NULL;
+ return false;
+}
+
+static bool mg_end_request_cur(struct mg_host *host, int err)
+{
+ return mg_end_request(host, err, blk_rq_cur_bytes(host->req));
+}
+
static void mg_dump_status(const char *msg, unsigned int stat,
struct mg_host *host)
{
char *name = MG_DISK_NAME;
- struct request *req;
- if (host->breq) {
- req = elv_next_request(host->breq);
- if (req)
- name = req->rq_disk->disk_name;
- }
+ if (host->req)
+ name = host->req->rq_disk->disk_name;
printk(KERN_ERR "%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
- if (stat & MG_REG_STATUS_BIT_BUSY)
+ if (stat & ATA_BUSY)
printk("Busy ");
- if (stat & MG_REG_STATUS_BIT_READY)
+ if (stat & ATA_DRDY)
printk("DriveReady ");
- if (stat & MG_REG_STATUS_BIT_WRITE_FAULT)
+ if (stat & ATA_DF)
printk("WriteFault ");
- if (stat & MG_REG_STATUS_BIT_SEEK_DONE)
+ if (stat & ATA_DSC)
printk("SeekComplete ");
- if (stat & MG_REG_STATUS_BIT_DATA_REQ)
+ if (stat & ATA_DRQ)
printk("DataRequest ");
- if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR)
+ if (stat & ATA_CORR)
printk("CorrectedError ");
- if (stat & MG_REG_STATUS_BIT_ERROR)
+ if (stat & ATA_ERR)
printk("Error ");
printk("}\n");
- if ((stat & MG_REG_STATUS_BIT_ERROR) == 0) {
+ if ((stat & ATA_ERR) == 0) {
host->error = 0;
} else {
host->error = inb((unsigned long)host->dev_base + MG_REG_ERROR);
printk(KERN_ERR "%s: %s: error=0x%02x { ", name, msg,
host->error & 0xff);
- if (host->error & MG_REG_ERR_BBK)
+ if (host->error & ATA_BBK)
printk("BadSector ");
- if (host->error & MG_REG_ERR_UNC)
+ if (host->error & ATA_UNC)
printk("UncorrectableError ");
- if (host->error & MG_REG_ERR_IDNF)
+ if (host->error & ATA_IDNF)
printk("SectorIdNotFound ");
- if (host->error & MG_REG_ERR_ABRT)
+ if (host->error & ATA_ABORTED)
printk("DriveStatusError ");
- if (host->error & MG_REG_ERR_AMNF)
+ if (host->error & ATA_AMNF)
printk("AddrMarkNotFound ");
printk("}");
- if (host->error &
- (MG_REG_ERR_BBK | MG_REG_ERR_UNC |
- MG_REG_ERR_IDNF | MG_REG_ERR_AMNF)) {
- if (host->breq) {
- req = elv_next_request(host->breq);
- if (req)
- printk(", sector=%u", (u32)req->sector);
- }
-
+ if (host->error & (ATA_BBK | ATA_UNC | ATA_IDNF | ATA_AMNF)) {
+ if (host->req)
+ printk(", sector=%u",
+ (unsigned int)blk_rq_pos(host->req));
}
printk("\n");
}
@@ -100,12 +249,12 @@ static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec)
do {
cur_jiffies = jiffies;
- if (status & MG_REG_STATUS_BIT_BUSY) {
- if (expect == MG_REG_STATUS_BIT_BUSY)
+ if (status & ATA_BUSY) {
+ if (expect == ATA_BUSY)
break;
} else {
/* Check the error condition! */
- if (status & MG_REG_STATUS_BIT_ERROR) {
+ if (status & ATA_ERR) {
mg_dump_status("mg_wait", status, host);
break;
}
@@ -114,8 +263,8 @@ static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec)
if (MG_READY_OK(status))
break;
- if (expect == MG_REG_STATUS_BIT_DATA_REQ)
- if (status & MG_REG_STATUS_BIT_DATA_REQ)
+ if (expect == ATA_DRQ)
+ if (status & ATA_DRQ)
break;
}
if (!msec) {
@@ -173,6 +322,42 @@ static irqreturn_t mg_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/* local copy of ata_id_string() */
+static void mg_id_string(const u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned int c;
+
+ BUG_ON(len & 1);
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+/* local copy of ata_id_c_string() */
+static void mg_id_c_string(const u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned char *p;
+
+ mg_id_string(id, s, ofs, len - 1);
+
+ p = s + strnlen(s, len - 1);
+ while (p > s && p[-1] == ' ')
+ p--;
+ *p = '\0';
+}
+
static int mg_get_disk_id(struct mg_host *host)
{
u32 i;
@@ -184,12 +369,10 @@ static int mg_get_disk_id(struct mg_host *host)
char serial[ATA_ID_SERNO_LEN + 1];
if (!prv_data->use_polling)
- outb(MG_REG_CTRL_INTR_DISABLE,
- (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(ATA_NIEN, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
outb(MG_CMD_ID, (unsigned long)host->dev_base + MG_REG_COMMAND);
- err = mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_RD_DRQ);
+ err = mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_RD_DRQ);
if (err)
return err;
@@ -219,9 +402,9 @@ static int mg_get_disk_id(struct mg_host *host)
host->n_sectors -= host->nres_sectors;
}
- ata_id_c_string(id, fwrev, ATA_ID_FW_REV, sizeof(fwrev));
- ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
- ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ mg_id_c_string(id, fwrev, ATA_ID_FW_REV, sizeof(fwrev));
+ mg_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
+ mg_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
printk(KERN_INFO "mg_disk: model: %s\n", model);
printk(KERN_INFO "mg_disk: firm: %.8s\n", fwrev);
printk(KERN_INFO "mg_disk: serial: %s\n", serial);
@@ -229,8 +412,7 @@ static int mg_get_disk_id(struct mg_host *host)
host->n_sectors, host->nres_sectors);
if (!prv_data->use_polling)
- outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(0, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
return err;
}
@@ -244,7 +426,7 @@ static int mg_disk_init(struct mg_host *host)
/* hdd rst low */
gpio_set_value(host->rst, 0);
- err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY);
+ err = mg_wait(host, ATA_BUSY, MG_TMAX_RST_TO_BUSY);
if (err)
return err;
@@ -255,17 +437,14 @@ static int mg_disk_init(struct mg_host *host)
return err;
/* soft reset on */
- outb(MG_REG_CTRL_RESET |
- (prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE :
- MG_REG_CTRL_INTR_ENABLE),
+ outb(ATA_SRST | (prv_data->use_polling ? ATA_NIEN : 0),
(unsigned long)host->dev_base + MG_REG_DRV_CTRL);
- err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY);
+ err = mg_wait(host, ATA_BUSY, MG_TMAX_RST_TO_BUSY);
if (err)
return err;
/* soft reset off */
- outb(prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE :
- MG_REG_CTRL_INTR_ENABLE,
+ outb(prv_data->use_polling ? ATA_NIEN : 0,
(unsigned long)host->dev_base + MG_REG_DRV_CTRL);
err = mg_wait(host, MG_STAT_READY, MG_TMAX_SWRST_TO_RDY);
if (err)
@@ -281,11 +460,10 @@ static int mg_disk_init(struct mg_host *host)
static void mg_bad_rw_intr(struct mg_host *host)
{
- struct request *req = elv_next_request(host->breq);
- if (req != NULL)
- if (++req->errors >= MG_MAX_ERRORS ||
- host->error == MG_ERR_TIMEOUT)
- end_request(req, 0);
+ if (host->req)
+ if (++host->req->errors >= MG_MAX_ERRORS ||
+ host->error == MG_ERR_TIMEOUT)
+ mg_end_request_cur(host, -EIO);
}
static unsigned int mg_out(struct mg_host *host,
@@ -311,7 +489,7 @@ static unsigned int mg_out(struct mg_host *host,
MG_REG_CYL_LOW);
outb((u8)(sect_num >> 16), (unsigned long)host->dev_base +
MG_REG_CYL_HIGH);
- outb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE),
+ outb((u8)((sect_num >> 24) | ATA_LBA | ATA_DEVICE_OBS),
(unsigned long)host->dev_base + MG_REG_DRV_HEAD);
outb(cmd, (unsigned long)host->dev_base + MG_REG_COMMAND);
return MG_ERR_NONE;
@@ -319,105 +497,77 @@ static unsigned int mg_out(struct mg_host *host,
static void mg_read(struct request *req)
{
- u32 remains, j;
+ u32 j;
struct mg_host *host = req->rq_disk->private_data;
- remains = req->nr_sectors;
-
- if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_RD, NULL) !=
- MG_ERR_NONE)
+ if (mg_out(host, blk_rq_pos(req), blk_rq_sectors(req),
+ MG_CMD_RD, NULL) != MG_ERR_NONE)
mg_bad_rw_intr(host);
MG_DBG("requested %d sects (from %ld), buffer=0x%p\n",
- remains, req->sector, req->buffer);
+ blk_rq_sectors(req), blk_rq_pos(req), req->buffer);
+
+ do {
+ u16 *buff = (u16 *)req->buffer;
- while (remains) {
- if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ,
- MG_TMAX_WAIT_RD_DRQ) != MG_ERR_NONE) {
+ if (mg_wait(host, ATA_DRQ,
+ MG_TMAX_WAIT_RD_DRQ) != MG_ERR_NONE) {
mg_bad_rw_intr(host);
return;
}
- for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
- *(u16 *)req->buffer =
- inw((unsigned long)host->dev_base +
- MG_BUFF_OFFSET + (j << 1));
- req->buffer += 2;
- }
-
- req->sector++;
- req->errors = 0;
- remains = --req->nr_sectors;
- --req->current_nr_sectors;
-
- if (req->current_nr_sectors <= 0) {
- MG_DBG("remain : %d sects\n", remains);
- end_request(req, 1);
- if (remains > 0)
- req = elv_next_request(host->breq);
- }
+ for (j = 0; j < MG_SECTOR_SIZE >> 1; j++)
+ *buff++ = inw((unsigned long)host->dev_base +
+ MG_BUFF_OFFSET + (j << 1));
outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base +
MG_REG_COMMAND);
- }
+ } while (mg_end_request(host, 0, MG_SECTOR_SIZE));
}
static void mg_write(struct request *req)
{
- u32 remains, j;
+ u32 j;
struct mg_host *host = req->rq_disk->private_data;
- remains = req->nr_sectors;
-
- if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_WR, NULL) !=
- MG_ERR_NONE) {
+ if (mg_out(host, blk_rq_pos(req), blk_rq_sectors(req),
+ MG_CMD_WR, NULL) != MG_ERR_NONE) {
mg_bad_rw_intr(host);
return;
}
-
MG_DBG("requested %d sects (from %ld), buffer=0x%p\n",
- remains, req->sector, req->buffer);
- while (remains) {
- if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ,
- MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) {
+ blk_rq_sectors(req), blk_rq_pos(req), req->buffer);
+
+ do {
+ u16 *buff = (u16 *)req->buffer;
+
+ if (mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) {
mg_bad_rw_intr(host);
return;
}
- for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
- outw(*(u16 *)req->buffer,
- (unsigned long)host->dev_base +
- MG_BUFF_OFFSET + (j << 1));
- req->buffer += 2;
- }
- req->sector++;
- remains = --req->nr_sectors;
- --req->current_nr_sectors;
-
- if (req->current_nr_sectors <= 0) {
- MG_DBG("remain : %d sects\n", remains);
- end_request(req, 1);
- if (remains > 0)
- req = elv_next_request(host->breq);
- }
+ for (j = 0; j < MG_SECTOR_SIZE >> 1; j++)
+ outw(*buff++, (unsigned long)host->dev_base +
+ MG_BUFF_OFFSET + (j << 1));
outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base +
MG_REG_COMMAND);
- }
+ } while (mg_end_request(host, 0, MG_SECTOR_SIZE));
}
static void mg_read_intr(struct mg_host *host)
{
+ struct request *req = host->req;
u32 i;
- struct request *req;
+ u16 *buff;
/* check status */
do {
i = inb((unsigned long)host->dev_base + MG_REG_STATUS);
- if (i & MG_REG_STATUS_BIT_BUSY)
+ if (i & ATA_BUSY)
break;
if (!MG_READY_OK(i))
break;
- if (i & MG_REG_STATUS_BIT_DATA_REQ)
+ if (i & ATA_DRQ)
goto ok_to_read;
} while (0);
mg_dump_status("mg_read_intr", i, host);
@@ -427,60 +577,42 @@ static void mg_read_intr(struct mg_host *host)
ok_to_read:
/* get current segment of request */
- req = elv_next_request(host->breq);
+ buff = (u16 *)req->buffer;
/* read 1 sector */
- for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) {
- *(u16 *)req->buffer =
- inw((unsigned long)host->dev_base + MG_BUFF_OFFSET +
- (i << 1));
- req->buffer += 2;
- }
+ for (i = 0; i < MG_SECTOR_SIZE >> 1; i++)
+ *buff++ = inw((unsigned long)host->dev_base + MG_BUFF_OFFSET +
+ (i << 1));
- /* manipulate request */
MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n",
- req->sector, req->nr_sectors - 1, req->buffer);
-
- req->sector++;
- req->errors = 0;
- i = --req->nr_sectors;
- --req->current_nr_sectors;
-
- /* let know if current segment done */
- if (req->current_nr_sectors <= 0)
- end_request(req, 1);
-
- /* set handler if read remains */
- if (i > 0) {
- host->mg_do_intr = mg_read_intr;
- mod_timer(&host->timer, jiffies + 3 * HZ);
- }
+ blk_rq_pos(req), blk_rq_sectors(req) - 1, req->buffer);
/* send read confirm */
outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND);
- /* goto next request */
- if (!i)
+ if (mg_end_request(host, 0, MG_SECTOR_SIZE)) {
+ /* set handler if read remains */
+ host->mg_do_intr = mg_read_intr;
+ mod_timer(&host->timer, jiffies + 3 * HZ);
+ } else /* goto next request */
mg_request(host->breq);
}
static void mg_write_intr(struct mg_host *host)
{
+ struct request *req = host->req;
u32 i, j;
u16 *buff;
- struct request *req;
-
- /* get current segment of request */
- req = elv_next_request(host->breq);
+ bool rem;
/* check status */
do {
i = inb((unsigned long)host->dev_base + MG_REG_STATUS);
- if (i & MG_REG_STATUS_BIT_BUSY)
+ if (i & ATA_BUSY)
break;
if (!MG_READY_OK(i))
break;
- if ((req->nr_sectors <= 1) || (i & MG_REG_STATUS_BIT_DATA_REQ))
+ if ((blk_rq_sectors(req) <= 1) || (i & ATA_DRQ))
goto ok_to_write;
} while (0);
mg_dump_status("mg_write_intr", i, host);
@@ -489,18 +621,8 @@ static void mg_write_intr(struct mg_host *host)
return;
ok_to_write:
- /* manipulate request */
- req->sector++;
- i = --req->nr_sectors;
- --req->current_nr_sectors;
- req->buffer += MG_SECTOR_SIZE;
-
- /* let know if current segment or all done */
- if (!i || (req->bio && req->current_nr_sectors <= 0))
- end_request(req, 1);
-
- /* write 1 sector and set handler if remains */
- if (i > 0) {
+ if ((rem = mg_end_request(host, 0, MG_SECTOR_SIZE))) {
+ /* write 1 sector and set handler if remains */
buff = (u16 *)req->buffer;
for (j = 0; j < MG_STORAGE_BUFFER_SIZE >> 1; j++) {
outw(*buff, (unsigned long)host->dev_base +
@@ -508,7 +630,7 @@ ok_to_write:
buff++;
}
MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n",
- req->sector, req->nr_sectors, req->buffer);
+ blk_rq_pos(req), blk_rq_sectors(req), req->buffer);
host->mg_do_intr = mg_write_intr;
mod_timer(&host->timer, jiffies + 3 * HZ);
}
@@ -516,7 +638,7 @@ ok_to_write:
/* send write confirm */
outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND);
- if (!i)
+ if (!rem)
mg_request(host->breq);
}
@@ -524,49 +646,45 @@ void mg_times_out(unsigned long data)
{
struct mg_host *host = (struct mg_host *)data;
char *name;
- struct request *req;
spin_lock_irq(&host->lock);
- req = elv_next_request(host->breq);
- if (!req)
+ if (!host->req)
goto out_unlock;
host->mg_do_intr = NULL;
- name = req->rq_disk->disk_name;
+ name = host->req->rq_disk->disk_name;
printk(KERN_DEBUG "%s: timeout\n", name);
host->error = MG_ERR_TIMEOUT;
mg_bad_rw_intr(host);
- mg_request(host->breq);
out_unlock:
+ mg_request(host->breq);
spin_unlock_irq(&host->lock);
}
static void mg_request_poll(struct request_queue *q)
{
- struct request *req;
- struct mg_host *host;
+ struct mg_host *host = q->queuedata;
- while ((req = elv_next_request(q)) != NULL) {
- host = req->rq_disk->private_data;
- if (blk_fs_request(req)) {
- switch (rq_data_dir(req)) {
- case READ:
- mg_read(req);
- break;
- case WRITE:
- mg_write(req);
- break;
- default:
- printk(KERN_WARNING "%s:%d unknown command\n",
- __func__, __LINE__);
- end_request(req, 0);
+ while (1) {
+ if (!host->req) {
+ host->req = blk_fetch_request(q);
+ if (!host->req)
break;
- }
}
+
+ if (unlikely(!blk_fs_request(host->req))) {
+ mg_end_request_cur(host, -EIO);
+ continue;
+ }
+
+ if (rq_data_dir(host->req) == READ)
+ mg_read(host->req);
+ else
+ mg_write(host->req);
}
}
@@ -588,18 +706,15 @@ static unsigned int mg_issue_req(struct request *req,
break;
case WRITE:
/* TODO : handler */
- outb(MG_REG_CTRL_INTR_DISABLE,
- (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(ATA_NIEN, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
if (mg_out(host, sect_num, sect_cnt, MG_CMD_WR, &mg_write_intr)
!= MG_ERR_NONE) {
mg_bad_rw_intr(host);
return host->error;
}
del_timer(&host->timer);
- mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_WR_DRQ);
- outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_WR_DRQ);
+ outb(0, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
if (host->error) {
mg_bad_rw_intr(host);
return host->error;
@@ -614,11 +729,6 @@ static unsigned int mg_issue_req(struct request *req,
outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base +
MG_REG_COMMAND);
break;
- default:
- printk(KERN_WARNING "%s:%d unknown command\n",
- __func__, __LINE__);
- end_request(req, 0);
- break;
}
return MG_ERR_NONE;
}
@@ -626,16 +736,17 @@ static unsigned int mg_issue_req(struct request *req,
/* This function also called from IRQ context */
static void mg_request(struct request_queue *q)
{
+ struct mg_host *host = q->queuedata;
struct request *req;
- struct mg_host *host;
u32 sect_num, sect_cnt;
while (1) {
- req = elv_next_request(q);
- if (!req)
- return;
-
- host = req->rq_disk->private_data;
+ if (!host->req) {
+ host->req = blk_fetch_request(q);
+ if (!host->req)
+ break;
+ }
+ req = host->req;
/* check unwanted request call */
if (host->mg_do_intr)
@@ -643,9 +754,9 @@ static void mg_request(struct request_queue *q)
del_timer(&host->timer);
- sect_num = req->sector;
+ sect_num = blk_rq_pos(req);
/* deal whole segments */
- sect_cnt = req->nr_sectors;
+ sect_cnt = blk_rq_sectors(req);
/* sanity check */
if (sect_num >= get_capacity(req->rq_disk) ||
@@ -655,12 +766,14 @@ static void mg_request(struct request_queue *q)
"%s: bad access: sector=%d, count=%d\n",
req->rq_disk->disk_name,
sect_num, sect_cnt);
- end_request(req, 0);
+ mg_end_request_cur(host, -EIO);
continue;
}
- if (!blk_fs_request(req))
- return;
+ if (unlikely(!blk_fs_request(req))) {
+ mg_end_request_cur(host, -EIO);
+ continue;
+ }
if (!mg_issue_req(req, host, sect_num, sect_cnt))
return;
@@ -690,9 +803,7 @@ static int mg_suspend(struct platform_device *plat_dev, pm_message_t state)
return -EIO;
if (!prv_data->use_polling)
- outb(MG_REG_CTRL_INTR_DISABLE,
- (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(ATA_NIEN, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
outb(MG_CMD_SLEEP, (unsigned long)host->dev_base + MG_REG_COMMAND);
/* wait until mflash deep sleep */
@@ -700,9 +811,7 @@ static int mg_suspend(struct platform_device *plat_dev, pm_message_t state)
if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) {
if (!prv_data->use_polling)
- outb(MG_REG_CTRL_INTR_ENABLE,
- (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(0, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
return -EIO;
}
@@ -725,8 +834,7 @@ static int mg_resume(struct platform_device *plat_dev)
return -EIO;
if (!prv_data->use_polling)
- outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base +
- MG_REG_DRV_CTRL);
+ outb(0, (unsigned long)host->dev_base + MG_REG_DRV_CTRL);
return 0;
}
@@ -877,6 +985,7 @@ static int mg_probe(struct platform_device *plat_dev)
__func__, __LINE__);
goto probe_err_5;
}
+ host->breq->queuedata = host;
/* mflash is random device, thanx for the noop */
elevator_exit(host->breq->elevator);
@@ -887,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/nbd.c b/drivers/block/nbd.c
index 4d6de4f15ccb..5d23ffad7c77 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -110,7 +110,7 @@ static void nbd_end_request(struct request *req)
req, error ? "failed" : "done");
spin_lock_irqsave(q->queue_lock, flags);
- __blk_end_request(req, error, req->nr_sectors << 9);
+ __blk_end_request_all(req, error);
spin_unlock_irqrestore(q->queue_lock, flags);
}
@@ -231,19 +231,19 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
{
int result, flags;
struct nbd_request request;
- unsigned long size = req->nr_sectors << 9;
+ unsigned long size = blk_rq_bytes(req);
request.magic = htonl(NBD_REQUEST_MAGIC);
request.type = htonl(nbd_cmd(req));
- request.from = cpu_to_be64((u64) req->sector << 9);
+ request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9);
request.len = htonl(size);
memcpy(request.handle, &req, sizeof(req));
- dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n",
+ dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%uB)\n",
lo->disk->disk_name, req,
nbdcmd_to_ascii(nbd_cmd(req)),
- (unsigned long long)req->sector << 9,
- req->nr_sectors << 9);
+ (unsigned long long)blk_rq_pos(req) << 9,
+ blk_rq_bytes(req));
result = sock_xmit(lo, 1, &request, sizeof(request),
(nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0);
if (result <= 0) {
@@ -533,11 +533,9 @@ static void do_nbd_request(struct request_queue *q)
{
struct request *req;
- while ((req = elv_next_request(q)) != NULL) {
+ while ((req = blk_fetch_request(q)) != NULL) {
struct nbd_device *lo;
- blkdev_dequeue_request(req);
-
spin_unlock_irq(q->queue_lock);
dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n",
@@ -580,13 +578,6 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_SPECIAL;
nbd_cmd(&sreq) = NBD_CMD_DISC;
- /*
- * Set these to sane values in case server implementation
- * fails to check the request type first and also to keep
- * debugging output cleaner.
- */
- sreq.sector = 0;
- sreq.nr_sectors = 0;
if (!lo->sock)
return -EINVAL;
nbd_send_req(lo, &sreq);
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index e91d4b4b014f..911dfd98d813 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -719,32 +719,37 @@ static void do_pcd_request(struct request_queue * q)
if (pcd_busy)
return;
while (1) {
- pcd_req = elv_next_request(q);
- if (!pcd_req)
- return;
+ if (!pcd_req) {
+ pcd_req = blk_fetch_request(q);
+ if (!pcd_req)
+ return;
+ }
if (rq_data_dir(pcd_req) == READ) {
struct pcd_unit *cd = pcd_req->rq_disk->private_data;
if (cd != pcd_current)
pcd_bufblk = -1;
pcd_current = cd;
- pcd_sector = pcd_req->sector;
- pcd_count = pcd_req->current_nr_sectors;
+ pcd_sector = blk_rq_pos(pcd_req);
+ pcd_count = blk_rq_cur_sectors(pcd_req);
pcd_buf = pcd_req->buffer;
pcd_busy = 1;
ps_set_intr(do_pcd_read, NULL, 0, nice);
return;
- } else
- end_request(pcd_req, 0);
+ } else {
+ __blk_end_request_all(pcd_req, -EIO);
+ pcd_req = NULL;
+ }
}
}
-static inline void next_request(int success)
+static inline void next_request(int err)
{
unsigned long saved_flags;
spin_lock_irqsave(&pcd_lock, saved_flags);
- end_request(pcd_req, success);
+ if (!__blk_end_request_cur(pcd_req, err))
+ pcd_req = NULL;
pcd_busy = 0;
do_pcd_request(pcd_queue);
spin_unlock_irqrestore(&pcd_lock, saved_flags);
@@ -781,7 +786,7 @@ static void pcd_start(void)
if (pcd_command(pcd_current, rd_cmd, 2048, "read block")) {
pcd_bufblk = -1;
- next_request(0);
+ next_request(-EIO);
return;
}
@@ -796,7 +801,7 @@ static void do_pcd_read(void)
pcd_retries = 0;
pcd_transfer();
if (!pcd_count) {
- next_request(1);
+ next_request(0);
return;
}
@@ -815,7 +820,7 @@ static void do_pcd_read_drq(void)
return;
}
pcd_bufblk = -1;
- next_request(0);
+ next_request(-EIO);
return;
}
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 9299455b0af6..bf5955b3d873 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -410,10 +410,12 @@ static void run_fsm(void)
pd_claimed = 0;
phase = NULL;
spin_lock_irqsave(&pd_lock, saved_flags);
- end_request(pd_req, res);
- pd_req = elv_next_request(pd_queue);
- if (!pd_req)
- stop = 1;
+ if (!__blk_end_request_cur(pd_req,
+ res == Ok ? 0 : -EIO)) {
+ pd_req = blk_fetch_request(pd_queue);
+ if (!pd_req)
+ stop = 1;
+ }
spin_unlock_irqrestore(&pd_lock, saved_flags);
if (stop)
return;
@@ -443,11 +445,11 @@ static enum action do_pd_io_start(void)
pd_cmd = rq_data_dir(pd_req);
if (pd_cmd == READ || pd_cmd == WRITE) {
- pd_block = pd_req->sector;
- pd_count = pd_req->current_nr_sectors;
+ pd_block = blk_rq_pos(pd_req);
+ pd_count = blk_rq_cur_sectors(pd_req);
if (pd_block + pd_count > get_capacity(pd_req->rq_disk))
return Fail;
- pd_run = pd_req->nr_sectors;
+ pd_run = blk_rq_sectors(pd_req);
pd_buf = pd_req->buffer;
pd_retries = 0;
if (pd_cmd == READ)
@@ -477,8 +479,8 @@ static int pd_next_buf(void)
if (pd_count)
return 0;
spin_lock_irqsave(&pd_lock, saved_flags);
- end_request(pd_req, 1);
- pd_count = pd_req->current_nr_sectors;
+ __blk_end_request_cur(pd_req, 0);
+ pd_count = blk_rq_cur_sectors(pd_req);
pd_buf = pd_req->buffer;
spin_unlock_irqrestore(&pd_lock, saved_flags);
return 0;
@@ -702,7 +704,7 @@ static void do_pd_request(struct request_queue * q)
{
if (pd_req)
return;
- pd_req = elv_next_request(q);
+ pd_req = blk_fetch_request(q);
if (!pd_req)
return;
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index bef3b997ba3e..68a90834e993 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -750,12 +750,10 @@ static int pf_ready(void)
static struct request_queue *pf_queue;
-static void pf_end_request(int uptodate)
+static void pf_end_request(int err)
{
- if (pf_req) {
- end_request(pf_req, uptodate);
+ if (pf_req && !__blk_end_request_cur(pf_req, err))
pf_req = NULL;
- }
}
static void do_pf_request(struct request_queue * q)
@@ -763,17 +761,19 @@ static void do_pf_request(struct request_queue * q)
if (pf_busy)
return;
repeat:
- pf_req = elv_next_request(q);
- if (!pf_req)
- return;
+ if (!pf_req) {
+ pf_req = blk_fetch_request(q);
+ if (!pf_req)
+ return;
+ }
pf_current = pf_req->rq_disk->private_data;
- pf_block = pf_req->sector;
- pf_run = pf_req->nr_sectors;
- pf_count = pf_req->current_nr_sectors;
+ pf_block = blk_rq_pos(pf_req);
+ pf_run = blk_rq_sectors(pf_req);
+ pf_count = blk_rq_cur_sectors(pf_req);
if (pf_block + pf_count > get_capacity(pf_req->rq_disk)) {
- pf_end_request(0);
+ pf_end_request(-EIO);
goto repeat;
}
@@ -788,7 +788,7 @@ repeat:
pi_do_claimed(pf_current->pi, do_pf_write);
else {
pf_busy = 0;
- pf_end_request(0);
+ pf_end_request(-EIO);
goto repeat;
}
}
@@ -805,23 +805,22 @@ static int pf_next_buf(void)
return 1;
if (!pf_count) {
spin_lock_irqsave(&pf_spin_lock, saved_flags);
- pf_end_request(1);
- pf_req = elv_next_request(pf_queue);
+ pf_end_request(0);
spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
if (!pf_req)
return 1;
- pf_count = pf_req->current_nr_sectors;
+ pf_count = blk_rq_cur_sectors(pf_req);
pf_buf = pf_req->buffer;
}
return 0;
}
-static inline void next_request(int success)
+static inline void next_request(int err)
{
unsigned long saved_flags;
spin_lock_irqsave(&pf_spin_lock, saved_flags);
- pf_end_request(success);
+ pf_end_request(err);
pf_busy = 0;
do_pf_request(pf_queue);
spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
@@ -844,7 +843,7 @@ static void do_pf_read_start(void)
pi_do_claimed(pf_current->pi, do_pf_read_start);
return;
}
- next_request(0);
+ next_request(-EIO);
return;
}
pf_mask = STAT_DRQ;
@@ -863,7 +862,7 @@ static void do_pf_read_drq(void)
pi_do_claimed(pf_current->pi, do_pf_read_start);
return;
}
- next_request(0);
+ next_request(-EIO);
return;
}
pi_read_block(pf_current->pi, pf_buf, 512);
@@ -871,7 +870,7 @@ static void do_pf_read_drq(void)
break;
}
pi_disconnect(pf_current->pi);
- next_request(1);
+ next_request(0);
}
static void do_pf_write(void)
@@ -890,7 +889,7 @@ static void do_pf_write_start(void)
pi_do_claimed(pf_current->pi, do_pf_write_start);
return;
}
- next_request(0);
+ next_request(-EIO);
return;
}
@@ -903,7 +902,7 @@ static void do_pf_write_start(void)
pi_do_claimed(pf_current->pi, do_pf_write_start);
return;
}
- next_request(0);
+ next_request(-EIO);
return;
}
pi_write_block(pf_current->pi, pf_buf, 512);
@@ -923,11 +922,11 @@ static void do_pf_write_done(void)
pi_do_claimed(pf_current->pi, do_pf_write_start);
return;
}
- next_request(0);
+ next_request(-EIO);
return;
}
pi_disconnect(pf_current->pi);
- next_request(1);
+ next_request(0);
}
static int __init pf_init(void)
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 bccc42bb9212..aaeeb544228a 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -134,13 +134,12 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
rq_for_each_segment(bv, req, iter)
n++;
dev_dbg(&dev->sbd.core,
- "%s:%u: %s req has %u bvecs for %lu sectors %lu hard sectors\n",
- __func__, __LINE__, op, n, req->nr_sectors,
- req->hard_nr_sectors);
+ "%s:%u: %s req has %u bvecs for %u sectors\n",
+ __func__, __LINE__, op, n, blk_rq_sectors(req));
#endif
- start_sector = req->sector * priv->blocking_factor;
- sectors = req->nr_sectors * priv->blocking_factor;
+ start_sector = blk_rq_pos(req) * priv->blocking_factor;
+ sectors = blk_rq_sectors(req) * priv->blocking_factor;
dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
__func__, __LINE__, op, sectors, start_sector);
@@ -158,7 +157,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
if (res) {
dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
__LINE__, op, res);
- end_request(req, 0);
+ __blk_end_request_all(req, -EIO);
return 0;
}
@@ -180,7 +179,7 @@ static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
if (res) {
dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
__func__, __LINE__, res);
- end_request(req, 0);
+ __blk_end_request_all(req, -EIO);
return 0;
}
@@ -195,7 +194,7 @@ static void ps3disk_do_request(struct ps3_storage_device *dev,
dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
- while ((req = elv_next_request(q))) {
+ while ((req = blk_fetch_request(q))) {
if (blk_fs_request(req)) {
if (ps3disk_submit_request_sg(dev, req))
break;
@@ -205,7 +204,7 @@ static void ps3disk_do_request(struct ps3_storage_device *dev,
break;
} else {
blk_dump_rq_flags(req, DEVICE_NAME " bad request");
- end_request(req, 0);
+ __blk_end_request_all(req, -EIO);
continue;
}
}
@@ -231,7 +230,6 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
struct request *req;
int res, read, error;
u64 tag, status;
- unsigned long num_sectors;
const char *op;
res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
@@ -261,11 +259,9 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
req->cmd[0] == REQ_LB_OP_FLUSH) {
read = 0;
- num_sectors = req->hard_cur_sectors;
op = "flush";
} else {
read = !rq_data_dir(req);
- num_sectors = req->nr_sectors;
op = read ? "read" : "write";
}
if (status) {
@@ -281,7 +277,7 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
}
spin_lock(&priv->lock);
- __blk_end_request(req, error, num_sectors << 9);
+ __blk_end_request_all(req, error);
priv->req = NULL;
ps3disk_do_request(dev, priv->queue);
spin_unlock(&priv->lock);
@@ -481,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/sunvdc.c b/drivers/block/sunvdc.c
index 5861e33efe63..cbfd9c0aef03 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -212,11 +212,6 @@ static void vdc_end_special(struct vdc_port *port, struct vio_disk_desc *desc)
vdc_finish(&port->vio, -err, WAITING_FOR_GEN_CMD);
}
-static void vdc_end_request(struct request *req, int error, int num_sectors)
-{
- __blk_end_request(req, error, num_sectors << 9);
-}
-
static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
unsigned int index)
{
@@ -239,7 +234,7 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
rqe->req = NULL;
- vdc_end_request(req, (desc->status ? -EIO : 0), desc->size >> 9);
+ __blk_end_request(req, (desc->status ? -EIO : 0), desc->size);
if (blk_queue_stopped(port->disk->queue))
blk_start_queue(port->disk->queue);
@@ -421,7 +416,7 @@ static int __send_request(struct request *req)
desc->slice = 0;
}
desc->status = ~0;
- desc->offset = (req->sector << 9) / port->vdisk_block_size;
+ desc->offset = (blk_rq_pos(req) << 9) / port->vdisk_block_size;
desc->size = len;
desc->ncookies = err;
@@ -446,14 +441,13 @@ out:
static void do_vdc_request(struct request_queue *q)
{
while (1) {
- struct request *req = elv_next_request(q);
+ struct request *req = blk_fetch_request(q);
if (!req)
break;
- blkdev_dequeue_request(req);
if (__send_request(req) < 0)
- vdc_end_request(req, -EIO, req->hard_nr_sectors);
+ __blk_end_request_all(req, -EIO);
}
}
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index d22cc3856937..cf7877fb8a7d 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -514,7 +514,7 @@ static int floppy_read_sectors(struct floppy_state *fs,
ret = swim_read_sector(fs, side, track, sector,
buffer);
if (try-- == 0)
- return -1;
+ return -EIO;
} while (ret != 512);
buffer += ret;
@@ -528,45 +528,31 @@ static void redo_fd_request(struct request_queue *q)
struct request *req;
struct floppy_state *fs;
- while ((req = elv_next_request(q))) {
+ req = blk_fetch_request(q);
+ while (req) {
+ int err = -EIO;
fs = req->rq_disk->private_data;
- if (req->sector < 0 || req->sector >= fs->total_secs) {
- end_request(req, 0);
- continue;
- }
- if (req->current_nr_sectors == 0) {
- end_request(req, 1);
- continue;
- }
- if (!fs->disk_in) {
- end_request(req, 0);
- continue;
- }
- if (rq_data_dir(req) == WRITE) {
- if (fs->write_protected) {
- end_request(req, 0);
- continue;
- }
- }
+ if (blk_rq_pos(req) >= fs->total_secs)
+ goto done;
+ if (!fs->disk_in)
+ goto done;
+ if (rq_data_dir(req) == WRITE && fs->write_protected)
+ goto done;
+
switch (rq_data_dir(req)) {
case WRITE:
/* NOT IMPLEMENTED */
- end_request(req, 0);
break;
case READ:
- if (floppy_read_sectors(fs, req->sector,
- req->current_nr_sectors,
- req->buffer)) {
- end_request(req, 0);
- continue;
- }
- req->nr_sectors -= req->current_nr_sectors;
- req->sector += req->current_nr_sectors;
- req->buffer += req->current_nr_sectors * 512;
- end_request(req, 1);
+ err = floppy_read_sectors(fs, blk_rq_pos(req),
+ blk_rq_cur_sectors(req),
+ req->buffer);
break;
}
+ done:
+ if (!__blk_end_request_cur(req, err))
+ req = blk_fetch_request(q);
}
}
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 612965307ba0..80df93e3cdd0 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -251,6 +251,20 @@ static int floppy_release(struct gendisk *disk, fmode_t mode);
static int floppy_check_change(struct gendisk *disk);
static int floppy_revalidate(struct gendisk *disk);
+static bool swim3_end_request(int err, unsigned int nr_bytes)
+{
+ if (__blk_end_request(fd_req, err, nr_bytes))
+ return true;
+
+ fd_req = NULL;
+ return false;
+}
+
+static bool swim3_end_request_cur(int err)
+{
+ return swim3_end_request(err, blk_rq_cur_bytes(fd_req));
+}
+
static void swim3_select(struct floppy_state *fs, int sel)
{
struct swim3 __iomem *sw = fs->swim3;
@@ -310,25 +324,27 @@ static void start_request(struct floppy_state *fs)
wake_up(&fs->wait);
return;
}
- while (fs->state == idle && (req = elv_next_request(swim3_queue))) {
+ while (fs->state == idle) {
+ if (!fd_req) {
+ fd_req = blk_fetch_request(swim3_queue);
+ if (!fd_req)
+ break;
+ }
+ req = fd_req;
#if 0
- printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
+ printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%u buf=%p\n",
req->rq_disk->disk_name, req->cmd,
- (long)req->sector, req->nr_sectors, req->buffer);
- printk(" errors=%d current_nr_sectors=%ld\n",
- req->errors, req->current_nr_sectors);
+ (long)blk_rq_pos(req), blk_rq_sectors(req), req->buffer);
+ printk(" errors=%d current_nr_sectors=%u\n",
+ req->errors, blk_rq_cur_sectors(req));
#endif
- if (req->sector < 0 || req->sector >= fs->total_secs) {
- end_request(req, 0);
- continue;
- }
- if (req->current_nr_sectors == 0) {
- end_request(req, 1);
+ if (blk_rq_pos(req) >= fs->total_secs) {
+ swim3_end_request_cur(-EIO);
continue;
}
if (fs->ejected) {
- end_request(req, 0);
+ swim3_end_request_cur(-EIO);
continue;
}
@@ -336,18 +352,19 @@ static void start_request(struct floppy_state *fs)
if (fs->write_prot < 0)
fs->write_prot = swim3_readbit(fs, WRITE_PROT);
if (fs->write_prot) {
- end_request(req, 0);
+ swim3_end_request_cur(-EIO);
continue;
}
}
- /* Do not remove the cast. req->sector is now a sector_t and
- * can be 64 bits, but it will never go past 32 bits for this
- * driver anyway, so we can safely cast it down and not have
- * to do a 64/32 division
+ /* Do not remove the cast. blk_rq_pos(req) is now a
+ * sector_t and can be 64 bits, but it will never go
+ * past 32 bits for this driver anyway, so we can
+ * safely cast it down and not have to do a 64/32
+ * division
*/
- fs->req_cyl = ((long)req->sector) / fs->secpercyl;
- x = ((long)req->sector) % fs->secpercyl;
+ fs->req_cyl = ((long)blk_rq_pos(req)) / fs->secpercyl;
+ x = ((long)blk_rq_pos(req)) % fs->secpercyl;
fs->head = x / fs->secpertrack;
fs->req_sector = x % fs->secpertrack + 1;
fd_req = req;
@@ -424,7 +441,7 @@ static inline void setup_transfer(struct floppy_state *fs)
struct dbdma_cmd *cp = fs->dma_cmd;
struct dbdma_regs __iomem *dr = fs->dma;
- if (fd_req->current_nr_sectors <= 0) {
+ if (blk_rq_cur_sectors(fd_req) <= 0) {
printk(KERN_ERR "swim3: transfer 0 sectors?\n");
return;
}
@@ -432,8 +449,8 @@ static inline void setup_transfer(struct floppy_state *fs)
n = 1;
else {
n = fs->secpertrack - fs->req_sector + 1;
- if (n > fd_req->current_nr_sectors)
- n = fd_req->current_nr_sectors;
+ if (n > blk_rq_cur_sectors(fd_req))
+ n = blk_rq_cur_sectors(fd_req);
}
fs->scount = n;
swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0);
@@ -508,7 +525,7 @@ static void act(struct floppy_state *fs)
case do_transfer:
if (fs->cur_cyl != fs->req_cyl) {
if (fs->retries > 5) {
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
return;
}
@@ -540,7 +557,7 @@ static void scan_timeout(unsigned long data)
out_8(&sw->intr_enable, 0);
fs->cur_cyl = -1;
if (fs->retries > 5) {
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
} else {
@@ -559,7 +576,7 @@ static void seek_timeout(unsigned long data)
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
printk(KERN_ERR "swim3: seek timeout\n");
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
}
@@ -583,7 +600,7 @@ static void settle_timeout(unsigned long data)
return;
}
printk(KERN_ERR "swim3: seek settle timeout\n");
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
}
@@ -593,8 +610,6 @@ static void xfer_timeout(unsigned long data)
struct floppy_state *fs = (struct floppy_state *) data;
struct swim3 __iomem *sw = fs->swim3;
struct dbdma_regs __iomem *dr = fs->dma;
- struct dbdma_cmd *cp = fs->dma_cmd;
- unsigned long s;
int n;
fs->timeout_pending = 0;
@@ -605,17 +620,10 @@ static void xfer_timeout(unsigned long data)
out_8(&sw->intr_enable, 0);
out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
out_8(&sw->select, RELAX);
- if (rq_data_dir(fd_req) == WRITE)
- ++cp;
- if (ld_le16(&cp->xfer_status) != 0)
- s = fs->scount - ((ld_le16(&cp->res_count) + 511) >> 9);
- else
- s = 0;
- fd_req->sector += s;
- fd_req->current_nr_sectors -= s;
printk(KERN_ERR "swim3: timeout %sing sector %ld\n",
- (rq_data_dir(fd_req)==WRITE? "writ": "read"), (long)fd_req->sector);
- end_request(fd_req, 0);
+ (rq_data_dir(fd_req)==WRITE? "writ": "read"),
+ (long)blk_rq_pos(fd_req));
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
}
@@ -646,7 +654,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
printk(KERN_ERR "swim3: seen sector but cyl=ff?\n");
fs->cur_cyl = -1;
if (fs->retries > 5) {
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
} else {
@@ -719,9 +727,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
if (intr & ERROR_INTR) {
n = fs->scount - 1 - resid / 512;
if (n > 0) {
- fd_req->sector += n;
- fd_req->current_nr_sectors -= n;
- fd_req->buffer += n * 512;
+ blk_update_request(fd_req, 0, n << 9);
fs->req_sector += n;
}
if (fs->retries < 5) {
@@ -730,8 +736,8 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
} else {
printk("swim3: error %sing block %ld (err=%x)\n",
rq_data_dir(fd_req) == WRITE? "writ": "read",
- (long)fd_req->sector, err);
- end_request(fd_req, 0);
+ (long)blk_rq_pos(fd_req), err);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
}
} else {
@@ -740,18 +746,12 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
printk(KERN_ERR "swim3: fd dma: stat=%x resid=%d\n", stat, resid);
printk(KERN_ERR " state=%d, dir=%x, intr=%x, err=%x\n",
fs->state, rq_data_dir(fd_req), intr, err);
- end_request(fd_req, 0);
+ swim3_end_request_cur(-EIO);
fs->state = idle;
start_request(fs);
break;
}
- fd_req->sector += fs->scount;
- fd_req->current_nr_sectors -= fs->scount;
- fd_req->buffer += fs->scount * 512;
- if (fd_req->current_nr_sectors <= 0) {
- end_request(fd_req, 1);
- fs->state = idle;
- } else {
+ if (swim3_end_request(0, fs->scount << 9)) {
fs->req_sector += fs->scount;
if (fs->req_sector > fs->secpertrack) {
fs->req_sector -= fs->secpertrack;
@@ -761,7 +761,8 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
}
}
act(fs);
- }
+ } else
+ fs->state = idle;
}
if (fs->state == idle)
start_request(fs);
diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
index ff0448e4bf03..da403b6a7f43 100644
--- a/drivers/block/sx8.c
+++ b/drivers/block/sx8.c
@@ -749,8 +749,7 @@ static inline void carm_end_request_queued(struct carm_host *host,
struct request *req = crq->rq;
int rc;
- rc = __blk_end_request(req, error, blk_rq_bytes(req));
- assert(rc == 0);
+ __blk_end_request_all(req, error);
rc = carm_put_request(host, crq);
assert(rc == 0);
@@ -811,12 +810,10 @@ static void carm_oob_rq_fn(struct request_queue *q)
while (1) {
DPRINTK("get req\n");
- rq = elv_next_request(q);
+ rq = blk_fetch_request(q);
if (!rq)
break;
- blkdev_dequeue_request(rq);
-
crq = rq->special;
assert(crq != NULL);
assert(crq->rq == rq);
@@ -847,7 +844,7 @@ static void carm_rq_fn(struct request_queue *q)
queue_one_request:
VPRINTK("get req\n");
- rq = elv_next_request(q);
+ rq = blk_peek_request(q);
if (!rq)
return;
@@ -858,7 +855,7 @@ queue_one_request:
}
crq->rq = rq;
- blkdev_dequeue_request(rq);
+ blk_start_request(rq);
if (rq_data_dir(rq) == WRITE) {
writing = 1;
@@ -904,10 +901,10 @@ queue_one_request:
msg->sg_count = n_elem;
msg->sg_type = SGT_32BIT;
msg->handle = cpu_to_le32(TAG_ENCODE(crq->tag));
- msg->lba = cpu_to_le32(rq->sector & 0xffffffff);
- tmp = (rq->sector >> 16) >> 16;
+ msg->lba = cpu_to_le32(blk_rq_pos(rq) & 0xffffffff);
+ tmp = (blk_rq_pos(rq) >> 16) >> 16;
msg->lba_high = cpu_to_le16( (u16) tmp );
- msg->lba_count = cpu_to_le16(rq->nr_sectors);
+ msg->lba_count = cpu_to_le16(blk_rq_sectors(rq));
msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg);
for (i = 0; i < n_elem; i++) {
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 689cd27ac890..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);
@@ -627,7 +626,7 @@ static void ub_request_fn(struct request_queue *q)
struct ub_lun *lun = q->queuedata;
struct request *rq;
- while ((rq = elv_next_request(q)) != NULL) {
+ while ((rq = blk_peek_request(q)) != NULL) {
if (ub_request_fn_1(lun, rq) != 0) {
blk_stop_queue(q);
break;
@@ -643,14 +642,14 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
int n_elem;
if (atomic_read(&sc->poison)) {
- blkdev_dequeue_request(rq);
- ub_end_rq(rq, DID_NO_CONNECT << 16, blk_rq_bytes(rq));
+ blk_start_request(rq);
+ ub_end_rq(rq, DID_NO_CONNECT << 16);
return 0;
}
if (lun->changed && !blk_pc_request(rq)) {
- blkdev_dequeue_request(rq);
- ub_end_rq(rq, SAM_STAT_CHECK_CONDITION, blk_rq_bytes(rq));
+ blk_start_request(rq);
+ ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
}
@@ -660,7 +659,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
return -1;
memset(cmd, 0, sizeof(struct ub_scsi_cmd));
- blkdev_dequeue_request(rq);
+ blk_start_request(rq);
urq = &lun->urq;
memset(urq, 0, sizeof(struct ub_request));
@@ -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,11 +722,11 @@ 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 = rq->sector >> lun->capacity.bshift;
- nblks = rq->nr_sectors >> lun->capacity.bshift;
+ block = blk_rq_pos(rq) >> lun->capacity.bshift;
+ nblks = blk_rq_sectors(rq) >> lun->capacity.bshift;
cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10;
/* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
@@ -739,7 +738,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
cmd->cdb[8] = nblks;
cmd->cdb_len = 10;
- cmd->len = rq->nr_sectors * 512;
+ cmd->len = blk_rq_bytes(rq);
}
static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
@@ -747,7 +746,7 @@ static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
{
struct request *rq = urq->rq;
- if (rq->data_len == 0) {
+ if (blk_rq_bytes(rq) == 0) {
cmd->dir = UB_DIR_NONE;
} else {
if (rq_data_dir(rq) == WRITE)
@@ -762,7 +761,7 @@ static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
cmd->cdb_len = rq->cmd_len;
- cmd->len = rq->data_len;
+ cmd->len = blk_rq_bytes(rq);
/*
* To reapply this to every URB is not as incorrect as it looks.
@@ -777,16 +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 >= rq->data_len)
- rq->data_len = 0;
+ if (cmd->act_len >= rq->resid_len)
+ rq->resid_len = 0;
else
- rq->data_len -= cmd->act_len;
+ rq->resid_len -= cmd->act_len;
scsi_status = 0;
} else {
if (cmd->act_len != cmd->len) {
@@ -818,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;
@@ -836,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,
@@ -1759,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);
@@ -2334,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/viodasd.c b/drivers/block/viodasd.c
index ecccf65dce2f..390d69bb7c48 100644
--- a/drivers/block/viodasd.c
+++ b/drivers/block/viodasd.c
@@ -252,7 +252,7 @@ static int send_request(struct request *req)
struct viodasd_device *d;
unsigned long flags;
- start = (u64)req->sector << 9;
+ start = (u64)blk_rq_pos(req) << 9;
if (rq_data_dir(req) == READ) {
direction = DMA_FROM_DEVICE;
@@ -361,19 +361,17 @@ static void do_viodasd_request(struct request_queue *q)
* back later.
*/
while (num_req_outstanding < VIOMAXREQ) {
- req = elv_next_request(q);
+ req = blk_fetch_request(q);
if (req == NULL)
return;
- /* dequeue the current request from the queue */
- blkdev_dequeue_request(req);
/* check that request contains a valid command */
if (!blk_fs_request(req)) {
- viodasd_end_request(req, -EIO, req->hard_nr_sectors);
+ viodasd_end_request(req, -EIO, blk_rq_sectors(req));
continue;
}
/* Try sending the request */
if (send_request(req) != 0)
- viodasd_end_request(req, -EIO, req->hard_nr_sectors);
+ viodasd_end_request(req, -EIO, blk_rq_sectors(req));
}
}
@@ -590,7 +588,7 @@ static int viodasd_handle_read_write(struct vioblocklpevent *bevent)
err = vio_lookup_rc(viodasd_err_table, bevent->sub_result);
printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n",
event->xRc, bevent->sub_result, err->msg);
- num_sect = req->hard_nr_sectors;
+ num_sect = blk_rq_sectors(req);
}
qlock = req->q->queue_lock;
spin_lock_irqsave(qlock, irq_flags);
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 5d34764c8a87..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,7 +64,13 @@ static void blk_done(struct virtqueue *vq)
break;
}
- __blk_end_request(vbr->req, error, blk_rq_bytes(vbr->req));
+ 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);
@@ -85,7 +93,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
vbr->req = req;
if (blk_fs_request(vbr->req)) {
vbr->out_hdr.type = 0;
- vbr->out_hdr.sector = vbr->req->sector;
+ vbr->out_hdr.sector = blk_rq_pos(vbr->req);
vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
} else if (blk_pc_request(vbr->req)) {
vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
@@ -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 = elv_next_request(q)) != NULL) {
- vblk = req->rq_disk->private_data;
+ while ((req = blk_peek_request(q)) != NULL) {
BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
/* If this request fails, stop queue and wait for something to
@@ -138,7 +163,7 @@ static void do_virtblk_request(struct request_queue *q)
blk_stop_queue(q);
break;
}
- blkdev_dequeue_request(req);
+ blk_start_request(req);
issued++;
}
@@ -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/xd.c b/drivers/block/xd.c
index 64b496fce98b..ce2429219925 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -305,30 +305,25 @@ static void do_xd_request (struct request_queue * q)
if (xdc_busy)
return;
- while ((req = elv_next_request(q)) != NULL) {
- unsigned block = req->sector;
- unsigned count = req->nr_sectors;
- int rw = rq_data_dir(req);
+ req = blk_fetch_request(q);
+ while (req) {
+ unsigned block = blk_rq_pos(req);
+ unsigned count = blk_rq_cur_sectors(req);
XD_INFO *disk = req->rq_disk->private_data;
- int res = 0;
+ int res = -EIO;
int retry;
- if (!blk_fs_request(req)) {
- end_request(req, 0);
- continue;
- }
- if (block + count > get_capacity(req->rq_disk)) {
- end_request(req, 0);
- continue;
- }
- if (rw != READ && rw != WRITE) {
- printk("do_xd_request: unknown request\n");
- end_request(req, 0);
- continue;
- }
+ if (!blk_fs_request(req))
+ goto done;
+ if (block + count > get_capacity(req->rq_disk))
+ goto done;
for (retry = 0; (retry < XD_RETRIES) && !res; retry++)
- res = xd_readwrite(rw, disk, req->buffer, block, count);
- end_request(req, res); /* wrap up, 0 = fail, 1 = success */
+ res = xd_readwrite(rq_data_dir(req), disk, req->buffer,
+ block, count);
+ done:
+ /* wrap up, 0 = success, -errno = fail */
+ if (!__blk_end_request_cur(req, res))
+ req = blk_fetch_request(q);
}
}
@@ -418,7 +413,7 @@ static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_
printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write"));
xd_recalibrate(drive);
spin_lock_irq(&xd_lock);
- return (0);
+ return -EIO;
case 2:
if (sense[0] & 0x30) {
printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing"));
@@ -439,7 +434,7 @@ static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_
else
printk(" - no valid disk address\n");
spin_lock_irq(&xd_lock);
- return (0);
+ return -EIO;
}
if (xd_dma_buffer)
for (i=0; i < (temp * 0x200); i++)
@@ -448,7 +443,7 @@ static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_
count -= temp, buffer += temp * 0x200, block += temp;
}
spin_lock_irq(&xd_lock);
- return (1);
+ return 0;
}
/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 8f905089b72b..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;
@@ -231,7 +231,7 @@ static int blkif_queue_request(struct request *req)
info->shadow[id].request = (unsigned long)req;
ring_req->id = id;
- ring_req->sector_number = (blkif_sector_t)req->sector;
+ ring_req->sector_number = (blkif_sector_t)blk_rq_pos(req);
ring_req->handle = info->handle;
ring_req->operation = rq_data_dir(req) ?
@@ -299,25 +299,25 @@ static void do_blkif_request(struct request_queue *rq)
queued = 0;
- while ((req = elv_next_request(rq)) != NULL) {
+ while ((req = blk_peek_request(rq)) != NULL) {
info = req->rq_disk->private_data;
- if (!blk_fs_request(req)) {
- end_request(req, 0);
- continue;
- }
if (RING_FULL(&info->ring))
goto wait;
- pr_debug("do_blk_req %p: cmd %p, sec %lx, "
- "(%u/%li) buffer:%p [%s]\n",
- req, req->cmd, (unsigned long)req->sector,
- req->current_nr_sectors,
- req->nr_sectors, req->buffer,
- rq_data_dir(req) ? "write" : "read");
+ blk_start_request(req);
+ if (!blk_fs_request(req)) {
+ __blk_end_request_all(req, -EIO);
+ continue;
+ }
+
+ pr_debug("do_blk_req %p: cmd %p, sec %lx, "
+ "(%u/%u) buffer:%p [%s]\n",
+ req, req->cmd, (unsigned long)blk_rq_pos(req),
+ blk_rq_cur_sectors(req), blk_rq_sectors(req),
+ req->buffer, rq_data_dir(req) ? "write" : "read");
- blkdev_dequeue_request(req);
if (blkif_queue_request(req)) {
blk_requeue_request(rq, req);
wait:
@@ -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. */
@@ -551,7 +551,6 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
for (i = info->ring.rsp_cons; i != rp; i++) {
unsigned long id;
- int ret;
bret = RING_GET_RESPONSE(&info->ring, i);
id = bret->id;
@@ -578,8 +577,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
dev_dbg(&info->xbdev->dev, "Bad return from blkdev data "
"request: %x\n", bret->status);
- ret = __blk_end_request(req, error, blk_rq_bytes(req));
- BUG_ON(ret);
+ __blk_end_request_all(req, error);
break;
default:
BUG();
@@ -934,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);
@@ -949,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);
}
@@ -977,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 4aecf5dc6a93..f08491a3a813 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -463,10 +463,11 @@ struct request *ace_get_next_request(struct request_queue * q)
{
struct request *req;
- while ((req = elv_next_request(q)) != NULL) {
+ while ((req = blk_peek_request(q)) != NULL) {
if (blk_fs_request(req))
break;
- end_request(req, 0);
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
}
return req;
}
@@ -492,9 +493,13 @@ static void ace_fsm_dostate(struct ace_device *ace)
set_capacity(ace->gd, 0);
dev_info(ace->dev, "No CF in slot\n");
- /* Drop all pending requests */
- while ((req = elv_next_request(ace->queue)) != NULL)
- end_request(req, 0);
+ /* Drop all in-flight and pending requests */
+ if (ace->req) {
+ __blk_end_request_all(ace->req, -EIO);
+ ace->req = NULL;
+ }
+ while ((req = blk_fetch_request(ace->queue)) != NULL)
+ __blk_end_request_all(req, -EIO);
/* Drop back to IDLE state and notify waiters */
ace->fsm_state = ACE_FSM_STATE_IDLE;
@@ -642,19 +647,21 @@ static void ace_fsm_dostate(struct ace_device *ace)
ace->fsm_state = ACE_FSM_STATE_IDLE;
break;
}
+ blk_start_request(req);
/* Okay, it's a data request, set it up for transfer */
dev_dbg(ace->dev,
- "request: sec=%llx hcnt=%lx, ccnt=%x, dir=%i\n",
- (unsigned long long) req->sector, req->hard_nr_sectors,
- req->current_nr_sectors, rq_data_dir(req));
+ "request: sec=%llx hcnt=%x, ccnt=%x, dir=%i\n",
+ (unsigned long long)blk_rq_pos(req),
+ blk_rq_sectors(req), blk_rq_cur_sectors(req),
+ rq_data_dir(req));
ace->req = req;
ace->data_ptr = req->buffer;
- ace->data_count = req->current_nr_sectors * ACE_BUF_PER_SECTOR;
- ace_out32(ace, ACE_MPULBA, req->sector & 0x0FFFFFFF);
+ ace->data_count = blk_rq_cur_sectors(req) * ACE_BUF_PER_SECTOR;
+ ace_out32(ace, ACE_MPULBA, blk_rq_pos(req) & 0x0FFFFFFF);
- count = req->hard_nr_sectors;
+ count = blk_rq_sectors(req);
if (rq_data_dir(req)) {
/* Kick off write request */
dev_dbg(ace->dev, "write data\n");
@@ -688,7 +695,7 @@ static void ace_fsm_dostate(struct ace_device *ace)
dev_dbg(ace->dev,
"CFBSY set; t=%i iter=%i c=%i dc=%i irq=%i\n",
ace->fsm_task, ace->fsm_iter_num,
- ace->req->current_nr_sectors * 16,
+ blk_rq_cur_sectors(ace->req) * 16,
ace->data_count, ace->in_irq);
ace_fsm_yield(ace); /* need to poll CFBSY bit */
break;
@@ -697,7 +704,7 @@ static void ace_fsm_dostate(struct ace_device *ace)
dev_dbg(ace->dev,
"DATABUF not set; t=%i iter=%i c=%i dc=%i irq=%i\n",
ace->fsm_task, ace->fsm_iter_num,
- ace->req->current_nr_sectors * 16,
+ blk_rq_cur_sectors(ace->req) * 16,
ace->data_count, ace->in_irq);
ace_fsm_yieldirq(ace);
break;
@@ -717,14 +724,13 @@ static void ace_fsm_dostate(struct ace_device *ace)
}
/* bio finished; is there another one? */
- if (__blk_end_request(ace->req, 0,
- blk_rq_cur_bytes(ace->req))) {
- /* dev_dbg(ace->dev, "next block; h=%li c=%i\n",
- * ace->req->hard_nr_sectors,
- * ace->req->current_nr_sectors);
+ if (__blk_end_request_cur(ace->req, 0)) {
+ /* dev_dbg(ace->dev, "next block; h=%u c=%u\n",
+ * blk_rq_sectors(ace->req),
+ * blk_rq_cur_sectors(ace->req));
*/
ace->data_ptr = ace->req->buffer;
- ace->data_count = ace->req->current_nr_sectors * 16;
+ ace->data_count = blk_rq_cur_sectors(ace->req) * 16;
ace_fsm_yieldirq(ace);
break;
}
@@ -978,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/block/z2ram.c b/drivers/block/z2ram.c
index 80754cdd3119..4575171e5beb 100644
--- a/drivers/block/z2ram.c
+++ b/drivers/block/z2ram.c
@@ -70,15 +70,18 @@ static struct gendisk *z2ram_gendisk;
static void do_z2_request(struct request_queue *q)
{
struct request *req;
- while ((req = elv_next_request(q)) != NULL) {
- unsigned long start = req->sector << 9;
- unsigned long len = req->current_nr_sectors << 9;
+
+ req = blk_fetch_request(q);
+ while (req) {
+ unsigned long start = blk_rq_pos(req) << 9;
+ unsigned long len = blk_rq_cur_bytes(req);
+ int err = 0;
if (start + len > z2ram_size) {
printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n",
- req->sector, req->current_nr_sectors);
- end_request(req, 0);
- continue;
+ blk_rq_pos(req), blk_rq_cur_sectors(req));
+ err = -EIO;
+ goto done;
}
while (len) {
unsigned long addr = start & Z2RAM_CHUNKMASK;
@@ -93,7 +96,9 @@ static void do_z2_request(struct request_queue *q)
start += size;
len -= size;
}
- end_request(req, 1);
+ done:
+ if (!__blk_end_request_cur(req, err))
+ req = blk_fetch_request(q);
}
}
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 2eecb779437b..b5621f27c4be 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -584,8 +584,8 @@ static void gdrom_readdisk_dma(struct work_struct *work)
list_for_each_safe(elem, next, &gdrom_deferred) {
req = list_entry(elem, struct request, queuelist);
spin_unlock(&gdrom_lock);
- block = req->sector/GD_TO_BLK + GD_SESSION_OFFSET;
- block_cnt = req->nr_sectors/GD_TO_BLK;
+ block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET;
+ block_cnt = blk_rq_sectors(req)/GD_TO_BLK;
ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG);
ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG);
ctrl_outl(1, GDROM_DMA_DIRECTION_REG);
@@ -632,39 +632,35 @@ static void gdrom_readdisk_dma(struct work_struct *work)
* before handling ending the request */
spin_lock(&gdrom_lock);
list_del_init(&req->queuelist);
- __blk_end_request(req, err, blk_rq_bytes(req));
+ __blk_end_request_all(req, err);
}
spin_unlock(&gdrom_lock);
kfree(read_command);
}
-static void gdrom_request_handler_dma(struct request *req)
-{
- /* dequeue, add to list of deferred work
- * and then schedule workqueue */
- blkdev_dequeue_request(req);
- list_add_tail(&req->queuelist, &gdrom_deferred);
- schedule_work(&work);
-}
-
static void gdrom_request(struct request_queue *rq)
{
struct request *req;
- while ((req = elv_next_request(rq)) != NULL) {
+ while ((req = blk_fetch_request(rq)) != NULL) {
if (!blk_fs_request(req)) {
printk(KERN_DEBUG "GDROM: Non-fs request ignored\n");
- end_request(req, 0);
+ __blk_end_request_all(req, -EIO);
+ continue;
}
if (rq_data_dir(req) != READ) {
printk(KERN_NOTICE "GDROM: Read only device -");
printk(" write request ignored\n");
- end_request(req, 0);
+ __blk_end_request_all(req, -EIO);
+ continue;
}
- if (req->nr_sectors)
- gdrom_request_handler_dma(req);
- else
- end_request(req, 0);
+
+ /*
+ * Add to list of deferred work and then schedule
+ * workqueue.
+ */
+ list_add_tail(&req->queuelist, &gdrom_deferred);
+ schedule_work(&work);
}
}
@@ -743,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 13929356135c..0fff646cc2f0 100644
--- a/drivers/cdrom/viocd.c
+++ b/drivers/cdrom/viocd.c
@@ -282,7 +282,7 @@ static int send_request(struct request *req)
viopath_targetinst(viopath_hostLp),
(u64)req, VIOVERSION << 16,
((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr,
- (u64)req->sector * 512, len, 0);
+ (u64)blk_rq_pos(req) * 512, len, 0);
if (hvrc != HvLpEvent_Rc_Good) {
printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc);
return -1;
@@ -291,36 +291,19 @@ static int send_request(struct request *req)
return 0;
}
-static void viocd_end_request(struct request *req, int error)
-{
- int nsectors = req->hard_nr_sectors;
-
- /*
- * Make sure it's fully ended, and ensure that we process
- * at least one sector.
- */
- if (blk_pc_request(req))
- nsectors = (req->data_len + 511) >> 9;
- if (!nsectors)
- nsectors = 1;
-
- if (__blk_end_request(req, error, nsectors << 9))
- BUG();
-}
-
static int rwreq;
static void do_viocd_request(struct request_queue *q)
{
struct request *req;
- while ((rwreq == 0) && ((req = elv_next_request(q)) != NULL)) {
+ while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) {
if (!blk_fs_request(req))
- viocd_end_request(req, -EIO);
+ __blk_end_request_all(req, -EIO);
else if (send_request(req) < 0) {
printk(VIOCD_KERN_WARNING
"unable to send message to OS/400!");
- viocd_end_request(req, -EIO);
+ __blk_end_request_all(req, -EIO);
} else
rwreq++;
}
@@ -486,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);
@@ -531,9 +514,9 @@ return_complete:
"with rc %d:0x%04X: %s\n",
req, event->xRc,
bevent->sub_result, err->msg);
- viocd_end_request(req, -EIO);
+ __blk_end_request_all(req, -EIO);
} else
- viocd_end_request(req, 0);
+ __blk_end_request_all(req, 0);
/* restart handling of incoming requests */
spin_unlock_irqrestore(&viocd_reqlock, flags);
@@ -587,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..30bae6de6a0d 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
@@ -679,7 +692,7 @@ config HVCS
this driver.
To compile this driver as a module, choose M here: the
- module will be called hvcs.ko. Additionally, this module
+ module will be called hvcs. Additionally, this module
will depend on arch specific APIs exported from hvcserver.ko
which will also be compiled when this driver is built as a
module.
@@ -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/amiserial.c b/drivers/char/amiserial.c
index fd3ebd1be570..72429b6b2fa8 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -779,7 +779,7 @@ static void change_speed(struct async_struct *info,
info->IER |= UART_IER_MSI;
}
/* TBD:
- * Does clearing IER_MSI imply that we should disbale the VBL interrupt ?
+ * Does clearing IER_MSI imply that we should disable the VBL interrupt ?
*/
/*
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/Kconfig b/drivers/char/hw_random/Kconfig
index 5fab6470f4b2..f4b3f7293feb 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -88,7 +88,7 @@ config HW_RANDOM_N2RNG
config HW_RANDOM_VIA
tristate "VIA HW Random Number Generator support"
- depends on HW_RANDOM && X86_32
+ depends on HW_RANDOM && X86
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -148,3 +148,15 @@ config HW_RANDOM_VIRTIO
To compile this driver as a module, choose M here: the
module will be called virtio-rng. If unsure, say N.
+
+config HW_RANDOM_MXC_RNGA
+ tristate "Freescale i.MX RNGA Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGA
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxc-rnga.
+
+ If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index e81d21a5f28f..fd1ecd2f6731 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o
obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
+obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
new file mode 100644
index 000000000000..187c6be80f43
--- /dev/null
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -0,0 +1,247 @@
+/*
+ * RNG driver for Freescale RNGA
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Author: Alan Carvalho de Assis <acassis@gmail.com>
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * This driver is based on other RNG drivers.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+
+/* RNGA Registers */
+#define RNGA_CONTROL 0x00
+#define RNGA_STATUS 0x04
+#define RNGA_ENTROPY 0x08
+#define RNGA_OUTPUT_FIFO 0x0c
+#define RNGA_MODE 0x10
+#define RNGA_VERIFICATION_CONTROL 0x14
+#define RNGA_OSC_CONTROL_COUNTER 0x18
+#define RNGA_OSC1_COUNTER 0x1c
+#define RNGA_OSC2_COUNTER 0x20
+#define RNGA_OSC_COUNTER_STATUS 0x24
+
+/* RNGA Registers Range */
+#define RNG_ADDR_RANGE 0x28
+
+/* RNGA Control Register */
+#define RNGA_CONTROL_SLEEP 0x00000010
+#define RNGA_CONTROL_CLEAR_INT 0x00000008
+#define RNGA_CONTROL_MASK_INTS 0x00000004
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+#define RNGA_CONTROL_GO 0x00000001
+
+#define RNGA_STATUS_LEVEL_MASK 0x0000ff00
+
+/* RNGA Status Register */
+#define RNGA_STATUS_OSC_DEAD 0x80000000
+#define RNGA_STATUS_SLEEP 0x00000010
+#define RNGA_STATUS_ERROR_INT 0x00000008
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+
+static struct platform_device *rng_dev;
+
+static int mxc_rnga_data_present(struct hwrng *rng)
+{
+ int level;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* how many random numbers is in FIFO? [0-16] */
+ level = ((__raw_readl(rng_base + RNGA_STATUS) &
+ RNGA_STATUS_LEVEL_MASK) >> 8);
+
+ return level > 0 ? 1 : 0;
+}
+
+static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 ctrl;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
+
+ /* some error while reading this random number? */
+ err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
+
+ /* if error: clear error interrupt, but doesn't return random number */
+ if (err) {
+ dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
+ rng_base + RNGA_CONTROL);
+ return 0;
+ } else
+ return 4;
+}
+
+static int mxc_rnga_init(struct hwrng *rng)
+{
+ u32 ctrl, osc;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* wake up */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rng_base + RNGA_STATUS);
+ if (osc & RNGA_STATUS_OSC_DEAD) {
+ dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
+ return -ENODEV;
+ }
+
+ /* go running */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+
+ return 0;
+}
+
+static void mxc_rnga_cleanup(struct hwrng *rng)
+{
+ u32 ctrl;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+
+ /* stop rnga */
+ __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+}
+
+static struct hwrng mxc_rnga = {
+ .name = "mxc-rnga",
+ .init = mxc_rnga_init,
+ .cleanup = mxc_rnga_cleanup,
+ .data_present = mxc_rnga_data_present,
+ .data_read = mxc_rnga_data_read
+};
+
+static int __init mxc_rnga_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rng_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(&pdev->dev, "rng");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get rng_clk!\n");
+ err = PTR_ERR(clk);
+ goto out;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENOENT;
+ goto err_region;
+ }
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (mem == NULL) {
+ err = -EBUSY;
+ goto err_region;
+ }
+
+ rng_base = ioremap(res->start, resource_size(res));
+ if (!rng_base) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ mxc_rnga.priv = (unsigned long)rng_base;
+
+ err = hwrng_register(&mxc_rnga);
+ if (err) {
+ dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
+ goto err_register;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "MXC RNGA Registered.\n");
+
+ return 0;
+
+err_register:
+ iounmap(rng_base);
+ rng_base = NULL;
+
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+
+err_region:
+ clk_disable(clk);
+ clk_put(clk);
+
+out:
+ return err;
+}
+
+static int __exit mxc_rnga_remove(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ void __iomem *rng_base = (void __iomem *)mxc_rnga.priv;
+ struct clk *clk = clk_get(&pdev->dev, "rng");
+
+ hwrng_unregister(&mxc_rnga);
+
+ iounmap(rng_base);
+
+ release_mem_region(res->start, resource_size(res));
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return 0;
+}
+
+static struct platform_driver mxc_rnga_driver = {
+ .driver = {
+ .name = "mxc_rnga",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(mxc_rnga_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&mxc_rnga_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 538313f9e7ac..00dd3de1be51 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -89,7 +89,7 @@ static struct hwrng omap_rng_ops = {
.data_read = omap_rng_data_read,
};
-static int __init omap_rng_probe(struct platform_device *pdev)
+static int __devinit omap_rng_probe(struct platform_device *pdev)
{
struct resource *res, *mem;
int ret;
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index dcd352ad0e7f..a94e930575f2 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -88,9 +88,9 @@ static struct hwrng timeriomem_rng_ops = {
.priv = 0,
};
-static int __init timeriomem_rng_probe(struct platform_device *pdev)
+static int __devinit timeriomem_rng_probe(struct platform_device *pdev)
{
- struct resource *res, *mem;
+ struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -98,21 +98,12 @@ static int __init timeriomem_rng_probe(struct platform_device *pdev)
if (!res)
return -ENOENT;
- mem = request_mem_region(res->start, res->end - res->start + 1,
- pdev->name);
- if (mem == NULL)
- return -EBUSY;
-
- dev_set_drvdata(&pdev->dev, mem);
-
timeriomem_rng_data = pdev->dev.platform_data;
timeriomem_rng_data->address = ioremap(res->start,
res->end - res->start + 1);
- if (!timeriomem_rng_data->address) {
- ret = -ENOMEM;
- goto err_ioremap;
- }
+ if (!timeriomem_rng_data->address)
+ return -EIO;
if (timeriomem_rng_data->period != 0
&& usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
@@ -125,7 +116,7 @@ static int __init timeriomem_rng_probe(struct platform_device *pdev)
ret = hwrng_register(&timeriomem_rng_ops);
if (ret)
- goto err_register;
+ goto failed;
dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
timeriomem_rng_data->address,
@@ -133,24 +124,19 @@ static int __init timeriomem_rng_probe(struct platform_device *pdev)
return 0;
-err_register:
+failed:
dev_err(&pdev->dev, "problem registering\n");
iounmap(timeriomem_rng_data->address);
-err_ioremap:
- release_resource(mem);
return ret;
}
static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
{
- struct resource *mem = dev_get_drvdata(&pdev->dev);
-
del_timer_sync(&timeriomem_rng_timer);
hwrng_unregister(&timeriomem_rng_ops);
iounmap(timeriomem_rng_data->address);
- release_resource(mem);
return 0;
}
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index 4e9573c1d39e..794aacb715c1 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -132,6 +132,19 @@ static int via_rng_init(struct hwrng *rng)
struct cpuinfo_x86 *c = &cpu_data(0);
u32 lo, hi, old_lo;
+ /* VIA Nano CPUs don't have the MSR_VIA_RNG anymore. The RNG
+ * is always enabled if CPUID rng_en is set. There is no
+ * RNG configuration like it used to be the case in this
+ * register */
+ if ((c->x86 == 6) && (c->x86_model >= 0x0f)) {
+ if (!cpu_has_xstore_enabled) {
+ printk(KERN_ERR PFX "can't enable hardware RNG "
+ "if XSTORE is not enabled\n");
+ return -ENODEV;
+ }
+ return 0;
+ }
+
/* Control the RNG via MSR. Tread lightly and pay very close
* close attention to values written, as the reserved fields
* are documented to be "undefined and unpredictable"; but it
@@ -205,5 +218,5 @@ static void __exit mod_exit(void)
module_init(mod_init);
module_exit(mod_exit);
-MODULE_DESCRIPTION("H/W RNG driver for VIA chipsets");
+MODULE_DESCRIPTION("H/W RNG driver for VIA CPU with PadLock");
MODULE_LICENSE("GPL");
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 b2ced39d76b2..8c7444857a4b 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1673,7 +1673,7 @@ unsigned int get_random_int(void)
int ret;
keyptr = get_keyptr();
- hash[0] += current->pid + jiffies + get_cycles() + (int)(long)&ret;
+ hash[0] += current->pid + jiffies + get_cycles();
ret = half_md4_transform(hash, keyptr->secret);
put_cpu_var(get_random_int_hash);
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 d6a807f4077d..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,
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/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/connector/Kconfig b/drivers/connector/Kconfig
index 100bfd422066..6e6730f9dfd1 100644
--- a/drivers/connector/Kconfig
+++ b/drivers/connector/Kconfig
@@ -7,7 +7,7 @@ menuconfig CONNECTOR
of the netlink socket protocol.
Connector support can also be built as a module. If so, the module
- will be called cn.ko.
+ will be called cn.
if CONNECTOR
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/Kconfig b/drivers/crypto/Kconfig
index 01afd758072f..5b27692372bf 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -12,7 +12,7 @@ if CRYPTO_HW
config CRYPTO_DEV_PADLOCK
tristate "Support for VIA PadLock ACE"
- depends on X86_32 && !UML
+ depends on X86 && !UML
select CRYPTO_ALGAPI
help
Some VIA processors come with an integrated crypto engine
@@ -34,7 +34,7 @@ config CRYPTO_DEV_PADLOCK_AES
Available in VIA C3 and newer CPUs.
If unsure say M. The compiled module will be
- called padlock-aes.ko
+ called padlock-aes.
config CRYPTO_DEV_PADLOCK_SHA
tristate "PadLock driver for SHA1 and SHA256 algorithms"
@@ -47,7 +47,7 @@ config CRYPTO_DEV_PADLOCK_SHA
Available in VIA C7 and newer processors.
If unsure say M. The compiled module will be
- called padlock-sha.ko
+ called padlock-sha.
config CRYPTO_DEV_GEODE
tristate "Support for the Geode LX AES engine"
@@ -79,7 +79,7 @@ config ZCRYPT_MONOLITHIC
bool "Monolithic zcrypt module"
depends on ZCRYPT="m"
help
- Select this option if you want to have a single module z90crypt.ko
+ Select this option if you want to have a single module z90crypt,
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 2bef086fb342..5f753fc08730 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -2564,7 +2564,7 @@ static void hifn_tasklet_callback(unsigned long data)
hifn_process_queue(dev);
}
-static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int __devinit hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int err, i;
struct hifn_device *dev;
@@ -2696,7 +2696,7 @@ err_out_disable_pci_device:
return err;
}
-static void hifn_remove(struct pci_dev *pdev)
+static void __devexit hifn_remove(struct pci_dev *pdev)
{
int i;
struct hifn_device *dev;
@@ -2744,7 +2744,7 @@ static struct pci_driver hifn_pci_driver = {
.remove = __devexit_p(hifn_remove),
};
-static int __devinit hifn_init(void)
+static int __init hifn_init(void)
{
unsigned int freq;
int err;
@@ -2789,7 +2789,7 @@ static int __devinit hifn_init(void)
return 0;
}
-static void __devexit hifn_fini(void)
+static void __exit hifn_fini(void)
{
pci_unregister_driver(&hifn_pci_driver);
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..87f92c39b5f0 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -154,7 +154,11 @@ static inline void padlock_reset_key(struct cword *cword)
int cpu = raw_smp_processor_id();
if (cword != per_cpu(last_cword, cpu))
+#ifndef CONFIG_X86_64
asm volatile ("pushfl; popfl");
+#else
+ asm volatile ("pushfq; popfq");
+#endif
}
static inline void padlock_store_cword(struct cword *cword)
@@ -208,10 +212,19 @@ static inline void padlock_xcrypt_ecb(const u8 *input, u8 *output, void *key,
asm volatile ("test $1, %%cl;"
"je 1f;"
+#ifndef CONFIG_X86_64
"lea -1(%%ecx), %%eax;"
"mov $1, %%ecx;"
+#else
+ "lea -1(%%rcx), %%rax;"
+ "mov $1, %%rcx;"
+#endif
".byte 0xf3,0x0f,0xa7,0xc8;" /* rep xcryptecb */
+#ifndef CONFIG_X86_64
"mov %%eax, %%ecx;"
+#else
+ "mov %%rax, %%rcx;"
+#endif
"1:"
".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */
: "+S"(input), "+D"(output)
@@ -489,4 +502,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/crypto/talitos.c b/drivers/crypto/talitos.c
index a3918c16b3db..c70775fd3ce2 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -44,6 +44,8 @@
#include <crypto/sha.h>
#include <crypto/aead.h>
#include <crypto/authenc.h>
+#include <crypto/skcipher.h>
+#include <crypto/scatterwalk.h>
#include "talitos.h"
@@ -339,7 +341,8 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
status = error;
dma_unmap_single(dev, request->dma_desc,
- sizeof(struct talitos_desc), DMA_BIDIRECTIONAL);
+ sizeof(struct talitos_desc),
+ DMA_BIDIRECTIONAL);
/* copy entries so we can call callback outside lock */
saved_req.desc = request->desc;
@@ -413,7 +416,8 @@ static struct talitos_desc *current_desc(struct device *dev, int ch)
/*
* user diagnostics; report root cause of error based on execution unit status
*/
-static void report_eu_error(struct device *dev, int ch, struct talitos_desc *desc)
+static void report_eu_error(struct device *dev, int ch,
+ struct talitos_desc *desc)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int i;
@@ -684,8 +688,8 @@ struct talitos_ctx {
unsigned int authsize;
};
-static int aead_authenc_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
+static int aead_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
{
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
@@ -694,8 +698,8 @@ static int aead_authenc_setauthsize(struct crypto_aead *authenc,
return 0;
}
-static int aead_authenc_setkey(struct crypto_aead *authenc,
- const u8 *key, unsigned int keylen)
+static int aead_setkey(struct crypto_aead *authenc,
+ const u8 *key, unsigned int keylen)
{
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
struct rtattr *rta = (void *)key;
@@ -740,7 +744,7 @@ badkey:
}
/*
- * ipsec_esp_edesc - s/w-extended ipsec_esp descriptor
+ * talitos_edesc - s/w-extended descriptor
* @src_nents: number of segments in input scatterlist
* @dst_nents: number of segments in output scatterlist
* @dma_len: length of dma mapped link_tbl space
@@ -752,17 +756,67 @@ badkey:
* is greater than 1, an integrity check value is concatenated to the end
* of link_tbl data
*/
-struct ipsec_esp_edesc {
+struct talitos_edesc {
int src_nents;
int dst_nents;
+ int src_is_chained;
+ int dst_is_chained;
int dma_len;
dma_addr_t dma_link_tbl;
struct talitos_desc desc;
struct talitos_ptr link_tbl[0];
};
+static int talitos_map_sg(struct device *dev, struct scatterlist *sg,
+ unsigned int nents, enum dma_data_direction dir,
+ int chained)
+{
+ if (unlikely(chained))
+ while (sg) {
+ dma_map_sg(dev, sg, 1, dir);
+ sg = scatterwalk_sg_next(sg);
+ }
+ else
+ dma_map_sg(dev, sg, nents, dir);
+ return nents;
+}
+
+static void talitos_unmap_sg_chain(struct device *dev, struct scatterlist *sg,
+ enum dma_data_direction dir)
+{
+ while (sg) {
+ dma_unmap_sg(dev, sg, 1, dir);
+ sg = scatterwalk_sg_next(sg);
+ }
+}
+
+static void talitos_sg_unmap(struct device *dev,
+ struct talitos_edesc *edesc,
+ struct scatterlist *src,
+ struct scatterlist *dst)
+{
+ unsigned int src_nents = edesc->src_nents ? : 1;
+ unsigned int dst_nents = edesc->dst_nents ? : 1;
+
+ if (src != dst) {
+ if (edesc->src_is_chained)
+ talitos_unmap_sg_chain(dev, src, DMA_TO_DEVICE);
+ else
+ dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE);
+
+ if (edesc->dst_is_chained)
+ talitos_unmap_sg_chain(dev, dst, DMA_FROM_DEVICE);
+ else
+ dma_unmap_sg(dev, dst, dst_nents, DMA_FROM_DEVICE);
+ } else
+ if (edesc->src_is_chained)
+ talitos_unmap_sg_chain(dev, src, DMA_BIDIRECTIONAL);
+ else
+ dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL);
+}
+
static void ipsec_esp_unmap(struct device *dev,
- struct ipsec_esp_edesc *edesc,
+ struct talitos_edesc *edesc,
struct aead_request *areq)
{
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[6], DMA_FROM_DEVICE);
@@ -772,15 +826,7 @@ static void ipsec_esp_unmap(struct device *dev,
dma_unmap_sg(dev, areq->assoc, 1, DMA_TO_DEVICE);
- if (areq->src != areq->dst) {
- dma_unmap_sg(dev, areq->src, edesc->src_nents ? : 1,
- DMA_TO_DEVICE);
- dma_unmap_sg(dev, areq->dst, edesc->dst_nents ? : 1,
- DMA_FROM_DEVICE);
- } else {
- dma_unmap_sg(dev, areq->src, edesc->src_nents ? : 1,
- DMA_BIDIRECTIONAL);
- }
+ talitos_sg_unmap(dev, edesc, areq->src, areq->dst);
if (edesc->dma_len)
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
@@ -795,13 +841,14 @@ static void ipsec_esp_encrypt_done(struct device *dev,
int err)
{
struct aead_request *areq = context;
- struct ipsec_esp_edesc *edesc =
- container_of(desc, struct ipsec_esp_edesc, desc);
struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+ struct talitos_edesc *edesc;
struct scatterlist *sg;
void *icvdata;
+ edesc = container_of(desc, struct talitos_edesc, desc);
+
ipsec_esp_unmap(dev, edesc, areq);
/* copy the generated ICV to dst */
@@ -819,17 +866,18 @@ static void ipsec_esp_encrypt_done(struct device *dev,
}
static void ipsec_esp_decrypt_swauth_done(struct device *dev,
- struct talitos_desc *desc, void *context,
- int err)
+ struct talitos_desc *desc,
+ void *context, int err)
{
struct aead_request *req = context;
- struct ipsec_esp_edesc *edesc =
- container_of(desc, struct ipsec_esp_edesc, desc);
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+ struct talitos_edesc *edesc;
struct scatterlist *sg;
void *icvdata;
+ edesc = container_of(desc, struct talitos_edesc, desc);
+
ipsec_esp_unmap(dev, edesc, req);
if (!err) {
@@ -851,20 +899,20 @@ static void ipsec_esp_decrypt_swauth_done(struct device *dev,
}
static void ipsec_esp_decrypt_hwauth_done(struct device *dev,
- struct talitos_desc *desc, void *context,
- int err)
+ struct talitos_desc *desc,
+ void *context, int err)
{
struct aead_request *req = context;
- struct ipsec_esp_edesc *edesc =
- container_of(desc, struct ipsec_esp_edesc, desc);
+ struct talitos_edesc *edesc;
+
+ edesc = container_of(desc, struct talitos_edesc, desc);
ipsec_esp_unmap(dev, edesc, req);
/* check ICV auth status */
- if (!err)
- if ((desc->hdr_lo & DESC_HDR_LO_ICCR1_MASK) !=
- DESC_HDR_LO_ICCR1_PASS)
- err = -EBADMSG;
+ if (!err && ((desc->hdr_lo & DESC_HDR_LO_ICCR1_MASK) !=
+ DESC_HDR_LO_ICCR1_PASS))
+ err = -EBADMSG;
kfree(edesc);
@@ -886,7 +934,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
link_tbl_ptr->j_extent = 0;
link_tbl_ptr++;
cryptlen -= sg_dma_len(sg);
- sg = sg_next(sg);
+ sg = scatterwalk_sg_next(sg);
}
/* adjust (decrease) last one (or two) entry's len to cryptlen */
@@ -910,7 +958,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
/*
* fill in and submit ipsec_esp descriptor
*/
-static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
+static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
u8 *giv, u64 seq,
void (*callback) (struct device *dev,
struct talitos_desc *desc,
@@ -952,32 +1000,31 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
desc->ptr[4].len = cpu_to_be16(cryptlen);
desc->ptr[4].j_extent = authsize;
- if (areq->src == areq->dst)
- sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ? : 1,
- DMA_BIDIRECTIONAL);
- else
- sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ? : 1,
- DMA_TO_DEVICE);
+ sg_count = talitos_map_sg(dev, areq->src, edesc->src_nents ? : 1,
+ (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
+ : DMA_TO_DEVICE,
+ edesc->src_is_chained);
if (sg_count == 1) {
desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
} else {
sg_link_tbl_len = cryptlen;
- if ((edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV) &&
- (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
+ if (edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV)
sg_link_tbl_len = cryptlen + authsize;
- }
+
sg_count = sg_to_link_tbl(areq->src, sg_count, sg_link_tbl_len,
&edesc->link_tbl[0]);
if (sg_count > 1) {
desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl);
- dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
- edesc->dma_len, DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+ edesc->dma_len,
+ DMA_BIDIRECTIONAL);
} else {
/* Only one segment now, so no link tbl needed */
- desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
+ desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->
+ src));
}
}
@@ -985,10 +1032,11 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
desc->ptr[5].len = cpu_to_be16(cryptlen);
desc->ptr[5].j_extent = authsize;
- if (areq->src != areq->dst) {
- sg_count = dma_map_sg(dev, areq->dst, edesc->dst_nents ? : 1,
- DMA_FROM_DEVICE);
- }
+ if (areq->src != areq->dst)
+ sg_count = talitos_map_sg(dev, areq->dst,
+ edesc->dst_nents ? : 1,
+ DMA_FROM_DEVICE,
+ edesc->dst_is_chained);
if (sg_count == 1) {
desc->ptr[5].ptr = cpu_to_be32(sg_dma_address(areq->dst));
@@ -1033,49 +1081,55 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
return ret;
}
-
/*
* derive number of elements in scatterlist
*/
-static int sg_count(struct scatterlist *sg_list, int nbytes)
+static int sg_count(struct scatterlist *sg_list, int nbytes, int *chained)
{
struct scatterlist *sg = sg_list;
int sg_nents = 0;
- while (nbytes) {
+ *chained = 0;
+ while (nbytes > 0) {
sg_nents++;
nbytes -= sg->length;
- sg = sg_next(sg);
+ if (!sg_is_last(sg) && (sg + 1)->length == 0)
+ *chained = 1;
+ sg = scatterwalk_sg_next(sg);
}
return sg_nents;
}
/*
- * allocate and map the ipsec_esp extended descriptor
+ * allocate and map the extended descriptor
*/
-static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
- int icv_stashing)
+static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ unsigned int cryptlen,
+ unsigned int authsize,
+ int icv_stashing,
+ u32 cryptoflags)
{
- struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
- struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
- struct ipsec_esp_edesc *edesc;
+ struct talitos_edesc *edesc;
int src_nents, dst_nents, alloc_len, dma_len;
- gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
+ int src_chained, dst_chained = 0;
+ gfp_t flags = cryptoflags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
GFP_ATOMIC;
- if (areq->cryptlen + ctx->authsize > TALITOS_MAX_DATA_LEN) {
- dev_err(ctx->dev, "cryptlen exceeds h/w max limit\n");
+ if (cryptlen + authsize > TALITOS_MAX_DATA_LEN) {
+ dev_err(dev, "length exceeds h/w max limit\n");
return ERR_PTR(-EINVAL);
}
- src_nents = sg_count(areq->src, areq->cryptlen + ctx->authsize);
+ src_nents = sg_count(src, cryptlen + authsize, &src_chained);
src_nents = (src_nents == 1) ? 0 : src_nents;
- if (areq->dst == areq->src) {
+ if (dst == src) {
dst_nents = src_nents;
} else {
- dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize);
+ dst_nents = sg_count(dst, cryptlen + authsize, &dst_chained);
dst_nents = (dst_nents == 1) ? 0 : dst_nents;
}
@@ -1084,39 +1138,52 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
* allowing for two separate entries for ICV and generated ICV (+ 2),
* and the ICV data itself
*/
- alloc_len = sizeof(struct ipsec_esp_edesc);
+ alloc_len = sizeof(struct talitos_edesc);
if (src_nents || dst_nents) {
dma_len = (src_nents + dst_nents + 2) *
- sizeof(struct talitos_ptr) + ctx->authsize;
+ sizeof(struct talitos_ptr) + authsize;
alloc_len += dma_len;
} else {
dma_len = 0;
- alloc_len += icv_stashing ? ctx->authsize : 0;
+ alloc_len += icv_stashing ? authsize : 0;
}
edesc = kmalloc(alloc_len, GFP_DMA | flags);
if (!edesc) {
- dev_err(ctx->dev, "could not allocate edescriptor\n");
+ dev_err(dev, "could not allocate edescriptor\n");
return ERR_PTR(-ENOMEM);
}
edesc->src_nents = src_nents;
edesc->dst_nents = dst_nents;
+ edesc->src_is_chained = src_chained;
+ edesc->dst_is_chained = dst_chained;
edesc->dma_len = dma_len;
- edesc->dma_link_tbl = dma_map_single(ctx->dev, &edesc->link_tbl[0],
+ edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0],
edesc->dma_len, DMA_BIDIRECTIONAL);
return edesc;
}
-static int aead_authenc_encrypt(struct aead_request *req)
+static struct talitos_edesc *aead_edesc_alloc(struct aead_request *areq,
+ int icv_stashing)
+{
+ struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+
+ return talitos_edesc_alloc(ctx->dev, areq->src, areq->dst,
+ areq->cryptlen, ctx->authsize, icv_stashing,
+ areq->base.flags);
+}
+
+static int aead_encrypt(struct aead_request *req)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
- struct ipsec_esp_edesc *edesc;
+ struct talitos_edesc *edesc;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(req, 0);
+ edesc = aead_edesc_alloc(req, 0);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
@@ -1126,70 +1193,67 @@ static int aead_authenc_encrypt(struct aead_request *req)
return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_encrypt_done);
}
-
-
-static int aead_authenc_decrypt(struct aead_request *req)
+static int aead_decrypt(struct aead_request *req)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
unsigned int authsize = ctx->authsize;
struct talitos_private *priv = dev_get_drvdata(ctx->dev);
- struct ipsec_esp_edesc *edesc;
+ struct talitos_edesc *edesc;
struct scatterlist *sg;
void *icvdata;
req->cryptlen -= authsize;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(req, 1);
+ edesc = aead_edesc_alloc(req, 1);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
if ((priv->features & TALITOS_FTR_HW_AUTH_CHECK) &&
- (((!edesc->src_nents && !edesc->dst_nents) ||
- priv->features & TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT))) {
+ ((!edesc->src_nents && !edesc->dst_nents) ||
+ priv->features & TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT)) {
/* decrypt and check the ICV */
- edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND |
+ edesc->desc.hdr = ctx->desc_hdr_template |
+ DESC_HDR_DIR_INBOUND |
DESC_HDR_MODE1_MDEU_CICV;
/* reset integrity check result bits */
edesc->desc.hdr_lo = 0;
- return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_hwauth_done);
+ return ipsec_esp(edesc, req, NULL, 0,
+ ipsec_esp_decrypt_hwauth_done);
- } else {
-
- /* Have to check the ICV with software */
+ }
- edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+ /* Have to check the ICV with software */
+ edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
- /* stash incoming ICV for later cmp with ICV generated by the h/w */
- if (edesc->dma_len)
- icvdata = &edesc->link_tbl[edesc->src_nents +
- edesc->dst_nents + 2];
- else
- icvdata = &edesc->link_tbl[0];
+ /* stash incoming ICV for later cmp with ICV generated by the h/w */
+ if (edesc->dma_len)
+ icvdata = &edesc->link_tbl[edesc->src_nents +
+ edesc->dst_nents + 2];
+ else
+ icvdata = &edesc->link_tbl[0];
- sg = sg_last(req->src, edesc->src_nents ? : 1);
+ sg = sg_last(req->src, edesc->src_nents ? : 1);
- memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
- ctx->authsize);
+ memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
+ ctx->authsize);
- return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_swauth_done);
- }
+ return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_swauth_done);
}
-static int aead_authenc_givencrypt(
- struct aead_givcrypt_request *req)
+static int aead_givencrypt(struct aead_givcrypt_request *req)
{
struct aead_request *areq = &req->areq;
struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
- struct ipsec_esp_edesc *edesc;
+ struct talitos_edesc *edesc;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(areq, 0);
+ edesc = aead_edesc_alloc(areq, 0);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
@@ -1204,31 +1268,228 @@ static int aead_authenc_givencrypt(
ipsec_esp_encrypt_done);
}
+static int ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct ablkcipher_alg *alg = crypto_ablkcipher_alg(cipher);
+
+ if (keylen > TALITOS_MAX_KEY_SIZE)
+ goto badkey;
+
+ if (keylen < alg->min_keysize || keylen > alg->max_keysize)
+ goto badkey;
+
+ memcpy(&ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ return 0;
+
+badkey:
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+}
+
+static void common_nonsnoop_unmap(struct device *dev,
+ struct talitos_edesc *edesc,
+ struct ablkcipher_request *areq)
+{
+ unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
+ unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2], DMA_TO_DEVICE);
+ unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1], DMA_TO_DEVICE);
+
+ talitos_sg_unmap(dev, edesc, areq->src, areq->dst);
+
+ if (edesc->dma_len)
+ dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
+ DMA_BIDIRECTIONAL);
+}
+
+static void ablkcipher_done(struct device *dev,
+ struct talitos_desc *desc, void *context,
+ int err)
+{
+ struct ablkcipher_request *areq = context;
+ struct talitos_edesc *edesc;
+
+ edesc = container_of(desc, struct talitos_edesc, desc);
+
+ common_nonsnoop_unmap(dev, edesc, areq);
+
+ kfree(edesc);
+
+ areq->base.complete(&areq->base, err);
+}
+
+static int common_nonsnoop(struct talitos_edesc *edesc,
+ struct ablkcipher_request *areq,
+ u8 *giv,
+ void (*callback) (struct device *dev,
+ struct talitos_desc *desc,
+ void *context, int error))
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct device *dev = ctx->dev;
+ struct talitos_desc *desc = &edesc->desc;
+ unsigned int cryptlen = areq->nbytes;
+ unsigned int ivsize;
+ int sg_count, ret;
+
+ /* first DWORD empty */
+ desc->ptr[0].len = 0;
+ desc->ptr[0].ptr = 0;
+ desc->ptr[0].j_extent = 0;
+
+ /* cipher iv */
+ ivsize = crypto_ablkcipher_ivsize(cipher);
+ map_single_talitos_ptr(dev, &desc->ptr[1], ivsize, giv ?: areq->info, 0,
+ DMA_TO_DEVICE);
+
+ /* cipher key */
+ map_single_talitos_ptr(dev, &desc->ptr[2], ctx->keylen,
+ (char *)&ctx->key, 0, DMA_TO_DEVICE);
+
+ /*
+ * cipher in
+ */
+ desc->ptr[3].len = cpu_to_be16(cryptlen);
+ desc->ptr[3].j_extent = 0;
+
+ sg_count = talitos_map_sg(dev, areq->src, edesc->src_nents ? : 1,
+ (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
+ : DMA_TO_DEVICE,
+ edesc->src_is_chained);
+
+ if (sg_count == 1) {
+ desc->ptr[3].ptr = cpu_to_be32(sg_dma_address(areq->src));
+ } else {
+ sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
+ &edesc->link_tbl[0]);
+ if (sg_count > 1) {
+ desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP;
+ desc->ptr[3].ptr = cpu_to_be32(edesc->dma_link_tbl);
+ dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+ edesc->dma_len,
+ DMA_BIDIRECTIONAL);
+ } else {
+ /* Only one segment now, so no link tbl needed */
+ desc->ptr[3].ptr = cpu_to_be32(sg_dma_address(areq->
+ src));
+ }
+ }
+
+ /* cipher out */
+ desc->ptr[4].len = cpu_to_be16(cryptlen);
+ desc->ptr[4].j_extent = 0;
+
+ if (areq->src != areq->dst)
+ sg_count = talitos_map_sg(dev, areq->dst,
+ edesc->dst_nents ? : 1,
+ DMA_FROM_DEVICE,
+ edesc->dst_is_chained);
+
+ if (sg_count == 1) {
+ desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->dst));
+ } else {
+ struct talitos_ptr *link_tbl_ptr =
+ &edesc->link_tbl[edesc->src_nents + 1];
+
+ desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
+ desc->ptr[4].ptr = cpu_to_be32((struct talitos_ptr *)
+ edesc->dma_link_tbl +
+ edesc->src_nents + 1);
+ sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
+ link_tbl_ptr);
+ dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
+ edesc->dma_len, DMA_BIDIRECTIONAL);
+ }
+
+ /* iv out */
+ map_single_talitos_ptr(dev, &desc->ptr[5], ivsize, ctx->iv, 0,
+ DMA_FROM_DEVICE);
+
+ /* last DWORD empty */
+ desc->ptr[6].len = 0;
+ desc->ptr[6].ptr = 0;
+ desc->ptr[6].j_extent = 0;
+
+ ret = talitos_submit(dev, desc, callback, areq);
+ if (ret != -EINPROGRESS) {
+ common_nonsnoop_unmap(dev, edesc, areq);
+ kfree(edesc);
+ }
+ return ret;
+}
+
+static struct talitos_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request *
+ areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ return talitos_edesc_alloc(ctx->dev, areq->src, areq->dst, areq->nbytes,
+ 0, 0, areq->base.flags);
+}
+
+static int ablkcipher_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct talitos_edesc *edesc;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(areq);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* set encrypt */
+ edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_MODE0_ENCRYPT;
+
+ return common_nonsnoop(edesc, areq, NULL, ablkcipher_done);
+}
+
+static int ablkcipher_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct talitos_edesc *edesc;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(areq);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+
+ return common_nonsnoop(edesc, areq, NULL, ablkcipher_done);
+}
+
struct talitos_alg_template {
- char name[CRYPTO_MAX_ALG_NAME];
- char driver_name[CRYPTO_MAX_ALG_NAME];
- unsigned int blocksize;
- struct aead_alg aead;
- struct device *dev;
+ struct crypto_alg alg;
__be32 desc_hdr_template;
};
static struct talitos_alg_template driver_algs[] = {
- /* single-pass ipsec_esp descriptor */
+ /* AEAD algorithms. These use a single-pass ipsec_esp descriptor */
{
- .name = "authenc(hmac(sha1),cbc(aes))",
- .driver_name = "authenc-hmac-sha1-cbc-aes-talitos",
- .blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(sha1),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-sha1-cbc-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_AESU |
DESC_HDR_MODE0_AESU_CBC |
@@ -1238,19 +1499,23 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_SHA1_HMAC,
},
{
- .name = "authenc(hmac(sha1),cbc(des3_ede))",
- .driver_name = "authenc-hmac-sha1-cbc-3des-talitos",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(sha1),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha1-cbc-3des-talitos",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_DEU |
DESC_HDR_MODE0_DEU_CBC |
@@ -1261,19 +1526,23 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_SHA1_HMAC,
},
{
- .name = "authenc(hmac(sha256),cbc(aes))",
- .driver_name = "authenc-hmac-sha256-cbc-aes-talitos",
- .blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(sha256),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-sha256-cbc-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_AESU |
DESC_HDR_MODE0_AESU_CBC |
@@ -1283,19 +1552,23 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_SHA256_HMAC,
},
{
- .name = "authenc(hmac(sha256),cbc(des3_ede))",
- .driver_name = "authenc-hmac-sha256-cbc-3des-talitos",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(sha256),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-sha256-cbc-3des-talitos",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_DEU |
DESC_HDR_MODE0_DEU_CBC |
@@ -1306,19 +1579,23 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_SHA256_HMAC,
},
{
- .name = "authenc(hmac(md5),cbc(aes))",
- .driver_name = "authenc-hmac-md5-cbc-aes-talitos",
- .blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = MD5_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(md5),cbc(aes))",
+ .cra_driver_name = "authenc-hmac-md5-cbc-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_AESU |
DESC_HDR_MODE0_AESU_CBC |
@@ -1328,19 +1605,23 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_MD5_HMAC,
},
{
- .name = "authenc(hmac(md5),cbc(des3_ede))",
- .driver_name = "authenc-hmac-md5-cbc-3des-talitos",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
- .geniv = "<built-in>",
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = MD5_DIGEST_SIZE,
- },
+ .alg = {
+ .cra_name = "authenc(hmac(md5),cbc(des3_ede))",
+ .cra_driver_name = "authenc-hmac-md5-cbc-3des-talitos",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_aead_type,
+ .cra_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ }
+ },
.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
DESC_HDR_SEL0_DEU |
DESC_HDR_MODE0_DEU_CBC |
@@ -1349,6 +1630,52 @@ static struct talitos_alg_template driver_algs[] = {
DESC_HDR_MODE1_MDEU_INIT |
DESC_HDR_MODE1_MDEU_PAD |
DESC_HDR_MODE1_MDEU_MD5_HMAC,
+ },
+ /* ABLKCIPHER algorithms. */
+ {
+ .alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_AESU |
+ DESC_HDR_MODE0_AESU_CBC,
+ },
+ {
+ .alg = {
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "cbc-3des-talitos",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_DEU |
+ DESC_HDR_MODE0_DEU_CBC |
+ DESC_HDR_MODE0_DEU_3DES,
}
};
@@ -1362,12 +1689,14 @@ struct talitos_crypto_alg {
static int talitos_cra_init(struct crypto_tfm *tfm)
{
struct crypto_alg *alg = tfm->__crt_alg;
- struct talitos_crypto_alg *talitos_alg =
- container_of(alg, struct talitos_crypto_alg, crypto_alg);
+ struct talitos_crypto_alg *talitos_alg;
struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
+ talitos_alg = container_of(alg, struct talitos_crypto_alg, crypto_alg);
+
/* update context with ptr to dev */
ctx->dev = talitos_alg->dev;
+
/* copy descriptor header template value */
ctx->desc_hdr_template = talitos_alg->desc_hdr_template;
@@ -1453,19 +1782,13 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
return ERR_PTR(-ENOMEM);
alg = &t_alg->crypto_alg;
+ *alg = template->alg;
- snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", template->name);
- snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->driver_name);
alg->cra_module = THIS_MODULE;
alg->cra_init = talitos_cra_init;
alg->cra_priority = TALITOS_CRA_PRIORITY;
- alg->cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
- alg->cra_blocksize = template->blocksize;
alg->cra_alignmask = 0;
- alg->cra_type = &crypto_aead_type;
alg->cra_ctxsize = sizeof(struct talitos_ctx);
- alg->cra_u.aead = template->aead;
t_alg->desc_hdr_template = template->desc_hdr_template;
t_alg->dev = dev;
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 1955ee8d6d20..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
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/e752x_edac.c b/drivers/edac/e752x_edac.c
index facfdb1fa71c..d205d493a68a 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -1084,7 +1084,7 @@ static void e752x_init_sysbus_parity_mask(struct e752x_pvt *pvt)
struct pci_dev *dev = pvt->dev_d0f1;
int enable = 1;
- /* Allow module paramter override, else see if CPU supports parity */
+ /* Allow module parameter override, else see if CPU supports parity */
if (sysbus_parity != -1) {
enable = sysbus_parity;
} else if (cpu_id[0] &&
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/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 4cd35d8fd799..f5d46e7199d4 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -67,12 +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
select FRAMEBUFFER_CONSOLE if !EMBEDDED
- tristate "i915 driver"
+ # 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
@@ -84,12 +90,6 @@ config DRM_I915
config DRM_I915_KMS
bool "Enable modesetting on intel by default"
depends on DRM_I915
- # i915 KMS 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 want kernel modesetting enabled by default,
and you have a new enough userspace to support this. Running old
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 45890447feec..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);
}
@@ -935,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 f01def16a669..019b7c578236 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -481,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 b9631e3a1ea6..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;
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 8f9372921f82..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,
};
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 53d544552625..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);
@@ -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,7 +1009,7 @@ 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);
@@ -1024,7 +1027,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
ret = i915_gem_init_ringbuffer(dev);
if (ret)
- goto kfree_devname;
+ goto out;
/* Allow hardware batchbuffers unless told otherwise.
*/
@@ -1056,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;
}
@@ -1161,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);
@@ -1206,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;
@@ -1240,7 +1246,8 @@ int i915_driver_unload(struct drm_device *dev)
if (dev_priv->regs != NULL)
iounmap(dev_priv->regs);
- intel_opregion_free(dev, 0);
+ if (!IS_IGDNG(dev))
+ intel_opregion_free(dev, 0);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev);
@@ -1264,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);
@@ -1273,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;
}
@@ -1311,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);
}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 9b149fe824c3..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;
@@ -284,6 +299,13 @@ typedef struct drm_i915_private {
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;
@@ -381,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 */
@@ -490,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;
};
@@ -641,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);
@@ -784,7 +811,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
(dev)->pci_device == 0x2E02 || \
(dev)->pci_device == 0x2E12 || \
(dev)->pci_device == 0x2E22 || \
- (dev)->pci_device == 0x2E32)
+ (dev)->pci_device == 0x2E32 || \
+ (dev)->pci_device == 0x0042 || \
+ (dev)->pci_device == 0x0046)
#define IS_I965GM(dev) ((dev)->pci_device == 0x2A02 || \
(dev)->pci_device == 0x2A12)
@@ -806,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 b189b49c7602..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;
@@ -1697,7 +1714,10 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
BUG_ON(seqno == 0);
if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
- ier = I915_READ(IER);
+ 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");
@@ -1749,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:
*
@@ -1972,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;
@@ -1986,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;
}
@@ -2001,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;
}
@@ -2024,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--;
@@ -2131,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;
@@ -2209,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++) {
@@ -2242,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;
@@ -2254,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.
@@ -2361,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);
@@ -2406,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;
}
@@ -2424,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);
}
@@ -2439,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);
}
@@ -3022,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++) {
@@ -3076,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.
*/
@@ -3084,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;
}
@@ -3111,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;
@@ -3125,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;
}
@@ -3165,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;
@@ -3198,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",
@@ -3302,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;
@@ -3321,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__);
@@ -3353,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++) {
@@ -3371,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,
@@ -3402,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];
@@ -3462,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);
@@ -3512,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);
}
@@ -3540,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);
@@ -3645,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
@@ -3792,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);
@@ -4344,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 52a059354e83..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;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 98bb4c878c4e..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);
}
@@ -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_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 15da44cf21b1..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
@@ -630,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)
@@ -664,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)
@@ -950,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
@@ -1327,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 */
@@ -1410,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
@@ -1420,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
@@ -1499,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 ce8a21344a71..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);
@@ -480,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 19148c3df637..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.
@@ -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 3387cf32f385..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;
}
@@ -785,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);
+ }
+ /* 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);
+ }
-/**
- * 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 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;
@@ -805,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.
@@ -890,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;
@@ -947,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;
}
@@ -1030,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,
@@ -1063,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);
@@ -1101,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;
}
@@ -1114,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;
}
@@ -1137,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;
@@ -1150,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;
@@ -1175,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) {
@@ -1207,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)
@@ -1231,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);
@@ -1245,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.
@@ -1266,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);
@@ -1276,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 {
@@ -1303,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);
@@ -1315,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 */
@@ -1336,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) |
@@ -1357,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;
@@ -1366,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);
@@ -1409,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);
@@ -1450,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);
@@ -1576,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);
@@ -1611,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);
@@ -1748,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);
}
@@ -1874,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;
@@ -1898,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_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 e4652dcdd9bb..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,9 +865,15 @@ 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 = {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index d0983bb93a18..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,6 +170,9 @@ 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);
switch (hdmi_priv->sdvox_reg) {
@@ -219,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,
@@ -268,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_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 439a86514993..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;
+
+ if (IS_IGDNG(dev))
+ reg = BLC_PWM_PCH_CTL2;
+ else
+ reg = BLC_PWM_CTL;
- return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ 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,
@@ -386,12 +445,13 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
{
- DRM_DEBUG("Skipping LVDS initialization for %s\n", id->ident);
+ 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 __initdata intel_no_lvds[] = {
+static const struct dmi_system_id intel_no_lvds[] = {
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Apple Mac Mini (Core series)",
@@ -424,8 +484,21 @@ static const struct dmi_system_id __initdata intel_no_lvds[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
},
},
-
- /* FIXME: add a check for the Aopen Mini PC */
+ {
+ .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 */
};
@@ -446,12 +519,18 @@ void intel_lvds_init(struct drm_device *dev)
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_crtc *crtc;
u32 lvds;
- int pipe;
+ int pipe, gpio = GPIOC;
/* 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);
if (!intel_output) {
return;
@@ -486,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");
@@ -511,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 |=
@@ -528,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);
@@ -546,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_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/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 8071d965f142..127d0456f628 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -146,6 +146,7 @@ enum radeon_family {
CHIP_RV770,
CHIP_RV730,
CHIP_RV710,
+ CHIP_RV740,
CHIP_LAST,
};
@@ -1964,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/Kconfig b/drivers/hid/Kconfig
index 7e67dcb3d4f6..7831a0318d3c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -116,9 +116,16 @@ config HID_CYPRESS
---help---
Support for cypress mouse and barcode readers.
-config DRAGONRISE_FF
- tristate "DragonRise Inc. force feedback support"
+config HID_DRAGONRISE
+ tristate "DragonRise Inc. support" if EMBEDDED
depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Say Y here if you have DragonRise Inc.game controllers.
+
+config DRAGONRISE_FF
+ bool "DragonRise Inc. force feedback support"
+ depends on HID_DRAGONRISE
select INPUT_FF_MEMLESS
---help---
Say Y here if you want to enable force feedback support for DragonRise Inc.
@@ -160,7 +167,7 @@ config HID_LOGITECH
Support for Logitech devices that are not fully compliant with HID standard.
config LOGITECH_FF
- bool "Logitech force feedback"
+ bool "Logitech force feedback support"
depends on HID_LOGITECH
select INPUT_FF_MEMLESS
help
@@ -176,7 +183,7 @@ config LOGITECH_FF
force feedback.
config LOGIRUMBLEPAD2_FF
- bool "Logitech Rumblepad 2 force feedback"
+ bool "Logitech Rumblepad 2 force feedback support"
depends on HID_LOGITECH
select INPUT_FF_MEMLESS
help
@@ -211,11 +218,19 @@ config HID_PANTHERLORD
---help---
Support for PantherLord/GreenAsia based device support.
+config HID_PANTHERLORD
+ tristate "Pantherlord support" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Say Y here if you have a PantherLord/GreenAsia based game controller
+ or adapter.
+
config PANTHERLORD_FF
bool "Pantherlord force feedback support"
depends on HID_PANTHERLORD
select INPUT_FF_MEMLESS
- help
+ ---help---
Say Y here if you have a PantherLord/GreenAsia based game controller
or adapter and want to enable force feedback support for it.
@@ -247,15 +262,38 @@ config HID_SUNPLUS
---help---
Support for Sunplus wireless desktop.
-config GREENASIA_FF
- tristate "GreenAsia (Product ID 0x12) force feedback support"
+config HID_GREENASIA
+ tristate "GreenAsia (Product ID 0x12) support" if EMBEDDED
depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Say Y here if you have a GreenAsia (Product ID 0x12) based game
+ controller or adapter.
+
+config GREENASIA_FF
+ bool "GreenAsia (Product ID 0x12) force feedback support"
+ depends on HID_GREENASIA
select INPUT_FF_MEMLESS
---help---
Say Y here if you have a GreenAsia (Product ID 0x12) based game controller
(like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter
and want to enable force feedback support for it.
+config HID_SMARTJOYPLUS
+ tristate "SmartJoy PLUS PS2/USB adapter support" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Support for SmartJoy PLUS PS2/USB adapter.
+
+config SMARTJOYPLUS_FF
+ bool "SmartJoy PLUS PS2/USB adapter force feedback support"
+ depends on HID_SMARTJOYPLUS
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you have a SmartJoy PLUS PS2/USB adapter and want to
+ enable force feedback support for it.
+
config HID_TOPSEED
tristate "TopSeed Cyberlink remote control support" if EMBEDDED
depends on USB_HID
@@ -263,21 +301,45 @@ config HID_TOPSEED
---help---
Say Y if you have a TopSeed Cyberlink remote control.
-config THRUSTMASTER_FF
- tristate "ThrustMaster devices support"
+config HID_THRUSTMASTER
+ tristate "ThrustMaster devices support" if EMBEDDED
depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
+ a THRUSTMASTER Ferrari GT Rumble Wheel.
+
+config THRUSTMASTER_FF
+ bool "ThrustMaster devices force feedback support"
+ depends on HID_THRUSTMASTER
select INPUT_FF_MEMLESS
- help
+ ---help---
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
- a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
+ a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel and
+ want to enable force feedback support for it.
-config ZEROPLUS_FF
- tristate "Zeroplus based game controller support"
+config HID_WACOM
+ tristate "Wacom Bluetooth devices support" if EMBEDDED
+ depends on BT_HIDP
+ default !EMBEDDED
+ ---help---
+ Support for Wacom Graphire Bluetooth tablet.
+
+config HID_ZEROPLUS
+ tristate "Zeroplus based game controller support" if EMBEDDED
depends on USB_HID
- select INPUT_FF_MEMLESS
- help
+ default !EMBEDDED
+ ---help---
Say Y here if you have a Zeroplus based game controller.
+config ZEROPLUS_FF
+ bool "Zeroplus based game controller force feedback support"
+ depends on HID_ZEROPLUS
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you have a Zeroplus based game controller and want
+ to have force feedback support for it.
+
endmenu
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 1f7cb0fd4505..db35151673b1 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -22,7 +22,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
-obj-$(CONFIG_DRAGONRISE_FF) += hid-drff.o
+obj-$(CONFIG_HID_DRAGONRISE) += hid-drff.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
@@ -34,12 +34,14 @@ obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
+obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
-obj-$(CONFIG_GREENASIA_FF) += hid-gaff.o
-obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o
+obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
+obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
-obj-$(CONFIG_ZEROPLUS_FF) += hid-zpff.o
+obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
+obj-$(CONFIG_HID_WACOM) += hid-wacom.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index acbce5745b0c..303ccce05bb3 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -436,10 +436,6 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
- /* Apple wireless Mighty Mouse */
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c),
- .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
-
{ }
};
MODULE_DEVICE_TABLE(hid, apple_devices);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8551693d645f..f2c21d5d24e8 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1312,6 +1312,8 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 47ac1a7d66e1..04359ed64b87 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -137,6 +137,14 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0x44, "BarrelSwitch"},
{0, 0x45, "Eraser"},
{0, 0x46, "TabletPick"},
+ {0, 0x47, "Confidence"},
+ {0, 0x48, "Width"},
+ {0, 0x49, "Height"},
+ {0, 0x51, "ContactID"},
+ {0, 0x52, "InputMode"},
+ {0, 0x53, "DeviceIndex"},
+ {0, 0x54, "ContactCount"},
+ {0, 0x55, "ContactMaximumNumber"},
{ 15, 0, "PhysicalInterfaceDevice" },
{0, 0x00, "Undefined"},
{0, 0x01, "Physical_Interface_Device"},
@@ -514,9 +522,11 @@ static const char *events[EV_MAX + 1] = {
[EV_FF_STATUS] = "ForceFeedbackStatus",
};
-static const char *syncs[2] = {
+static const char *syncs[3] = {
[SYN_REPORT] = "Report", [SYN_CONFIG] = "Config",
+ [SYN_MT_REPORT] = "MT Report",
};
+
static const char *keys[KEY_MAX + 1] = {
[KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc",
[KEY_1] = "1", [KEY_2] = "2",
@@ -734,8 +744,17 @@ static const char *absolutes[ABS_MAX + 1] = {
[ABS_HAT2Y] = "Hat2Y", [ABS_HAT3X] = "Hat3X",
[ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure",
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
- [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "Tool Width",
+ [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth",
[ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc",
+ [ABS_MT_TOUCH_MAJOR] = "MTMajor",
+ [ABS_MT_TOUCH_MINOR] = "MTMinor",
+ [ABS_MT_WIDTH_MAJOR] = "MTMajorW",
+ [ABS_MT_WIDTH_MINOR] = "MTMinorW",
+ [ABS_MT_ORIENTATION] = "MTOrientation",
+ [ABS_MT_POSITION_X] = "MTPositionX",
+ [ABS_MT_POSITION_Y] = "MTPositionY",
+ [ABS_MT_TOOL_TYPE] = "MTToolType",
+ [ABS_MT_BLOB_ID] = "MTBlobID",
};
static const char *misc[MSC_MAX + 1] = {
diff --git a/drivers/hid/hid-drff.c b/drivers/hid/hid-drff.c
index 34f3eb65100b..a239d20ad7a5 100644
--- a/drivers/hid/hid-drff.c
+++ b/drivers/hid/hid-drff.c
@@ -32,6 +32,8 @@
#include <linux/hid.h>
#include "hid-ids.h"
+
+#ifdef CONFIG_DRAGONRISE_FF
#include "usbhid/usbhid.h"
struct drff_device {
@@ -135,6 +137,12 @@ static int drff_init(struct hid_device *hid)
return 0;
}
+#else
+static inline int drff_init(struct hid_device *hid)
+{
+ return 0;
+}
+#endif
static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c
index 510ad3ab8d33..8a11ccddaf2e 100644
--- a/drivers/hid/hid-gaff.c
+++ b/drivers/hid/hid-gaff.c
@@ -31,6 +31,8 @@
#include <linux/usb.h>
#include <linux/hid.h>
#include "hid-ids.h"
+
+#ifdef CONFIG_GREENASIA_FF
#include "usbhid/usbhid.h"
struct gaff_device {
@@ -130,6 +132,12 @@ static int gaff_init(struct hid_device *hid)
return 0;
}
+#else
+static inline int gaff_init(struct hid_device *hdev)
+{
+ return 0;
+}
+#endif
static int ga_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 4d5ee2bbc62b..630101037921 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -414,8 +414,10 @@
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
#define USB_VENDOR_ID_WISEGROUP 0x0925
+#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index 51aff08e10ce..56099709581c 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -50,6 +50,12 @@ static const signed short ff_joystick[] = {
-1
};
+static const signed short ff_joystick_ac[] = {
+ FF_CONSTANT,
+ FF_AUTOCENTER,
+ -1
+};
+
static const signed short ff_wheel[] = {
FF_CONSTANT,
FF_AUTOCENTER,
@@ -60,8 +66,8 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc211, ff_rumble },
{ 0x046d, 0xc219, ff_rumble },
{ 0x046d, 0xc283, ff_joystick },
- { 0x046d, 0xc286, ff_joystick },
- { 0x046d, 0xc294, ff_joystick },
+ { 0x046d, 0xc286, ff_joystick_ac },
+ { 0x046d, 0xc294, ff_wheel },
{ 0x046d, 0xc295, ff_joystick },
{ 0x046d, 0xca03, ff_wheel },
};
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index c5b252be9c21..75ed9d2c1a36 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -1,13 +1,8 @@
/*
- * HID driver for some ntrig "special" devices
+ * HID driver for N-Trig touchscreens
*
- * Copyright (c) 1999 Andreas Gal
- * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
- * Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2008 Rafi Rubin
+ * Copyright (c) 2009 Stephane Chatty
*
*/
@@ -29,15 +24,79 @@
#define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
EV_KEY, (c))
+struct ntrig_data {
+ __s32 x, y, id, w, h;
+ char reading_a_point, found_contact_id;
+};
+
+/*
+ * this driver is aimed at two firmware versions in circulation:
+ * - dual pen/finger single touch
+ * - finger multitouch, pen not working
+ */
+
static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- if ((usage->hid & HID_USAGE_PAGE) == HID_UP_DIGITIZER &&
- (usage->hid & 0xff) == 0x47) {
- nt_map_key_clear(BTN_TOOL_DOUBLETAP);
- return 1;
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ /* we do not want to map these for now */
+ case HID_DG_INVERT: /* value is always 0 */
+ case HID_DG_ERASER: /* value is always 0 */
+ case HID_DG_CONTACTID: /* value is useless */
+ case HID_DG_BARRELSWITCH: /* doubtful */
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ return -1;
+
+ /* original mapping by Rafi Rubin */
+ case HID_DG_CONFIDENCE:
+ nt_map_key_clear(BTN_TOOL_DOUBLETAP);
+ return 1;
+
+ /* width/height mapped on TouchMajor/TouchMinor/Orientation */
+ case HID_DG_WIDTH:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MAJOR);
+ return 1;
+ case HID_DG_HEIGHT:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MINOR);
+ input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
+ 0, 1, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case 0xff000000:
+ /* we do not want to map these: no input-oriented meaning */
+ return -1;
}
+
return 0;
}
@@ -51,6 +110,138 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
+
+/*
+ * this function is called upon all reports
+ * so that we can filter contact point information,
+ * decide whether we are in multi or single touch mode
+ * and call input_mt_sync after each point if necessary
+ */
+static int ntrig_event (struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct input_dev *input = field->hidinput->input;
+ struct ntrig_data *nd = hid_get_drvdata(hid);
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ switch (usage->hid) {
+ case HID_GD_X:
+ nd->x = value;
+ nd->reading_a_point = 1;
+ break;
+ case HID_GD_Y:
+ nd->y = value;
+ break;
+ case HID_DG_CONTACTID:
+ nd->id = value;
+ /* we receive this only when in multitouch mode */
+ nd->found_contact_id = 1;
+ break;
+ case HID_DG_WIDTH:
+ nd->w = value;
+ break;
+ case HID_DG_HEIGHT:
+ nd->h = value;
+ /*
+ * when in single touch mode, this is the last
+ * report received in a finger event. We want
+ * to emit a normal (X, Y) position
+ */
+ if (! nd->found_contact_id) {
+ input_event(input, EV_ABS, ABS_X, nd->x);
+ input_event(input, EV_ABS, ABS_Y, nd->y);
+ }
+ break;
+ case HID_DG_TIPPRESSURE:
+ /*
+ * when in single touch mode, this is the last
+ * report received in a pen event. We want
+ * to emit a normal (X, Y) position
+ */
+ if (! nd->found_contact_id) {
+ input_event(input, EV_ABS, ABS_X, nd->x);
+ input_event(input, EV_ABS, ABS_Y, nd->y);
+ input_event(input, EV_ABS, ABS_PRESSURE, value);
+ }
+ break;
+ case 0xff000002:
+ /*
+ * we receive this when the device is in multitouch
+ * mode. The first of the three values tagged with
+ * this usage tells if the contact point is real
+ * or a placeholder
+ */
+ if (!nd->reading_a_point || value != 1)
+ break;
+ /* emit a normal (X, Y) for the first point only */
+ if (nd->id == 0) {
+ input_event(input, EV_ABS, ABS_X, nd->x);
+ input_event(input, EV_ABS, ABS_Y, nd->y);
+ }
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);
+ if (nd->w > nd->h) {
+ input_event(input, EV_ABS,
+ ABS_MT_ORIENTATION, 1);
+ input_event(input, EV_ABS,
+ ABS_MT_TOUCH_MAJOR, nd->w);
+ input_event(input, EV_ABS,
+ ABS_MT_TOUCH_MINOR, nd->h);
+ } else {
+ input_event(input, EV_ABS,
+ ABS_MT_ORIENTATION, 0);
+ input_event(input, EV_ABS,
+ ABS_MT_TOUCH_MAJOR, nd->h);
+ input_event(input, EV_ABS,
+ ABS_MT_TOUCH_MINOR, nd->w);
+ }
+ input_mt_sync(field->hidinput->input);
+ nd->reading_a_point = 0;
+ nd->found_contact_id = 0;
+ break;
+
+ default:
+ /* fallback to the generic hidinput handling */
+ return 0;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct ntrig_data *nd;
+
+ nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL);
+ if (!nd) {
+ dev_err(&hdev->dev, "cannot allocate N-Trig data\n");
+ return -ENOMEM;
+ }
+ nd->reading_a_point = 0;
+ nd->found_contact_id = 0;
+ hid_set_drvdata(hdev, nd);
+
+ ret = hid_parse(hdev);
+ if (!ret)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ if (ret)
+ kfree (nd);
+ return ret;
+}
+
+static void ntrig_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+}
+
static const struct hid_device_id ntrig_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN),
.driver_data = NTRIG_DUPLICATE_USAGES },
@@ -58,11 +249,20 @@ static const struct hid_device_id ntrig_devices[] = {
};
MODULE_DEVICE_TABLE(hid, ntrig_devices);
+static const struct hid_usage_id ntrig_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
static struct hid_driver ntrig_driver = {
.name = "ntrig",
.id_table = ntrig_devices,
+ .probe = ntrig_probe,
+ .remove = ntrig_remove,
.input_mapping = ntrig_input_mapping,
.input_mapped = ntrig_input_mapped,
+ .usage_table = ntrig_grabbed_usages,
+ .event = ntrig_event,
};
static int ntrig_init(void)
diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
new file mode 100644
index 000000000000..eab169e5c371
--- /dev/null
+++ b/drivers/hid/hid-sjoy.c
@@ -0,0 +1,180 @@
+/*
+ * Force feedback support for SmartJoy PLUS PS2->USB adapter
+ *
+ * Copyright (c) 2009 Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
+ *
+ * Based of hid-pl.c and hid-gaff.c
+ * Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
+ * Copyright (c) 2008 Lukasz Lubojanski <lukasz@lubojanski.info>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include "hid-ids.h"
+
+#ifdef CONFIG_SMARTJOYPLUS_FF
+#include "usbhid/usbhid.h"
+
+struct sjoyff_device {
+ struct hid_report *report;
+};
+
+static int hid_sjoyff_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct sjoyff_device *sjoyff = data;
+ u32 left, right;
+
+ left = effect->u.rumble.strong_magnitude;
+ right = effect->u.rumble.weak_magnitude;
+ dev_dbg(&dev->dev, "called with 0x%08x 0x%08x\n", left, right);
+
+ left = left * 0xff / 0xffff;
+ right = (right != 0); /* on/off only */
+
+ sjoyff->report->field[0]->value[1] = right;
+ sjoyff->report->field[0]->value[2] = left;
+ dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right);
+ usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
+
+ return 0;
+}
+
+static int sjoyff_init(struct hid_device *hid)
+{
+ struct sjoyff_device *sjoyff;
+ struct hid_report *report;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+ struct hid_input, list);
+ struct list_head *report_list =
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct list_head *report_ptr = report_list;
+ struct input_dev *dev;
+ int error;
+
+ if (list_empty(report_list)) {
+ dev_err(&hid->dev, "no output reports found\n");
+ return -ENODEV;
+ }
+
+ report_ptr = report_ptr->next;
+
+ if (report_ptr == report_list) {
+ dev_err(&hid->dev, "required output report is "
+ "missing\n");
+ return -ENODEV;
+ }
+
+ report = list_entry(report_ptr, struct hid_report, list);
+ if (report->maxfield < 1) {
+ dev_err(&hid->dev, "no fields in the report\n");
+ return -ENODEV;
+ }
+
+ if (report->field[0]->report_count < 3) {
+ dev_err(&hid->dev, "not enough values in the field\n");
+ return -ENODEV;
+ }
+
+ sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL);
+ if (!sjoyff)
+ return -ENOMEM;
+
+ dev = hidinput->input;
+
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
+ if (error) {
+ kfree(sjoyff);
+ return error;
+ }
+
+ sjoyff->report = report;
+ sjoyff->report->field[0]->value[0] = 0x01;
+ sjoyff->report->field[0]->value[1] = 0x00;
+ sjoyff->report->field[0]->value[2] = 0x00;
+ usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
+
+ dev_info(&hid->dev,
+ "Force feedback for SmartJoy PLUS PS2/USB adapter\n");
+
+ return 0;
+}
+#else
+static inline int sjoyff_init(struct hid_device *hid)
+{
+ return 0;
+}
+#endif
+
+static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if (ret) {
+ dev_err(&hdev->dev, "hw start failed\n");
+ goto err;
+ }
+
+ sjoyff_init(hdev);
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct hid_device_id sjoy_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, sjoy_devices);
+
+static struct hid_driver sjoy_driver = {
+ .name = "smartjoyplus",
+ .id_table = sjoy_devices,
+ .probe = sjoy_probe,
+};
+
+static int sjoy_init(void)
+{
+ return hid_register_driver(&sjoy_driver);
+}
+
+static void sjoy_exit(void)
+{
+ hid_unregister_driver(&sjoy_driver);
+}
+
+module_init(sjoy_init);
+module_exit(sjoy_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jussi Kivilinna");
+
diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c
index 7c1f7b50330c..fcd6ccd02fee 100644
--- a/drivers/hid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -33,11 +33,6 @@
#include "hid-ids.h"
-#include "usbhid/usbhid.h"
-
-/* Usages for thrustmaster devices I know about */
-#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb)
-
static const signed short ff_rumble[] = {
FF_RUMBLE,
-1
@@ -48,6 +43,12 @@ static const signed short ff_joystick[] = {
-1
};
+#ifdef CONFIG_THRUSTMASTER_FF
+#include "usbhid/usbhid.h"
+
+/* Usages for thrustmaster devices I know about */
+#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb)
+
struct tmff_device {
struct hid_report *report;
struct hid_field *ff_field;
@@ -209,6 +210,12 @@ fail:
kfree(tmff);
return error;
}
+#else
+static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits)
+{
+ return 0;
+}
+#endif
static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 000000000000..1f9237f511e3
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,259 @@
+/*
+ * Bluetooth Wacom Tablet support
+ *
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2007 Paul Walmsley
+ * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ * Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+ __u16 tool;
+ unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ struct wacom_data *wdata = hid_get_drvdata(hdev);
+ struct hid_input *hidinput;
+ struct input_dev *input;
+ unsigned char *data = (unsigned char *) raw_data;
+ int tool, x, y, rw;
+
+ if (!(hdev->claimed & HID_CLAIMED_INPUT))
+ return 0;
+
+ tool = 0;
+ hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+ input = hidinput->input;
+
+ /* Check if this is a tablet report */
+ if (data[0] != 0x03)
+ return 0;
+
+ /* Get X & Y positions */
+ x = le16_to_cpu(*(__le16 *) &data[2]);
+ y = le16_to_cpu(*(__le16 *) &data[4]);
+
+ /* Get current tool identifier */
+ if (data[1] & 0x90) { /* If pen is in the in/active area */
+ switch ((data[1] >> 5) & 3) {
+ case 0: /* Pen */
+ tool = BTN_TOOL_PEN;
+ break;
+
+ case 1: /* Rubber */
+ tool = BTN_TOOL_RUBBER;
+ break;
+
+ case 2: /* Mouse with wheel */
+ case 3: /* Mouse without wheel */
+ tool = BTN_TOOL_MOUSE;
+ break;
+ }
+
+ /* Reset tool if out of active tablet area */
+ if (!(data[1] & 0x10))
+ tool = 0;
+ }
+
+ /* If tool changed, notify input subsystem */
+ if (wdata->tool != tool) {
+ if (wdata->tool) {
+ /* Completely reset old tool state */
+ if (wdata->tool == BTN_TOOL_MOUSE) {
+ input_report_key(input, BTN_LEFT, 0);
+ input_report_key(input, BTN_RIGHT, 0);
+ input_report_key(input, BTN_MIDDLE, 0);
+ input_report_abs(input, ABS_DISTANCE,
+ input->absmax[ABS_DISTANCE]);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_key(input, BTN_STYLUS, 0);
+ input_report_key(input, BTN_STYLUS2, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ }
+ input_report_key(input, wdata->tool, 0);
+ input_sync(input);
+ }
+ wdata->tool = tool;
+ if (tool)
+ input_report_key(input, tool, 1);
+ }
+
+ if (tool) {
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+
+ switch ((data[1] >> 5) & 3) {
+ case 2: /* Mouse with wheel */
+ input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
+ rw = (data[6] & 0x01) ? -1 :
+ (data[6] & 0x02) ? 1 : 0;
+ input_report_rel(input, REL_WHEEL, rw);
+ /* fall through */
+
+ case 3: /* Mouse without wheel */
+ input_report_key(input, BTN_LEFT, data[1] & 0x01);
+ input_report_key(input, BTN_RIGHT, data[1] & 0x02);
+ /* Compute distance between mouse and tablet */
+ rw = 44 - (data[6] >> 2);
+ if (rw < 0)
+ rw = 0;
+ else if (rw > 31)
+ rw = 31;
+ input_report_abs(input, ABS_DISTANCE, rw);
+ break;
+
+ default:
+ input_report_abs(input, ABS_PRESSURE,
+ data[6] | (((__u16) (data[1] & 0x08)) << 5));
+ input_report_key(input, BTN_TOUCH, data[1] & 0x01);
+ input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+ input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
+ break;
+ }
+
+ input_sync(input);
+ }
+
+ /* Report the state of the two buttons at the top of the tablet
+ * as two extra fingerpad keys (buttons 4 & 5). */
+ rw = data[7] & 0x03;
+ if (rw != wdata->butstate) {
+ wdata->butstate = rw;
+ input_report_key(input, BTN_0, rw & 0x02);
+ input_report_key(input, BTN_1, rw & 0x01);
+ input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+ input_sync(input);
+ }
+
+ return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct hid_input *hidinput;
+ struct input_dev *input;
+ struct wacom_data *wdata;
+ int ret;
+
+ wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+ if (wdata == NULL) {
+ dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+ return -ENOMEM;
+ }
+
+ hid_set_drvdata(hdev, wdata);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err_free;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ dev_err(&hdev->dev, "hw start failed\n");
+ goto err_free;
+ }
+
+ hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+ input = hidinput->input;
+
+ /* Basics */
+ input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+ input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+ BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+ input->relbit[0] |= BIT(REL_WHEEL);
+ set_bit(BTN_TOOL_PEN, input->keybit);
+ set_bit(BTN_TOUCH, input->keybit);
+ set_bit(BTN_STYLUS, input->keybit);
+ set_bit(BTN_STYLUS2, input->keybit);
+ set_bit(BTN_LEFT, input->keybit);
+ set_bit(BTN_RIGHT, input->keybit);
+ set_bit(BTN_MIDDLE, input->keybit);
+
+ /* Pad */
+ input->evbit[0] |= BIT(EV_MSC);
+ input->mscbit[0] |= BIT(MSC_SERIAL);
+
+ /* Distance, rubber and mouse */
+ input->absbit[0] |= BIT(ABS_DISTANCE);
+ set_bit(BTN_TOOL_RUBBER, input->keybit);
+ set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+ input->absmax[ABS_PRESSURE] = 511;
+ input->absmax[ABS_DISTANCE] = 32;
+
+ input->absmax[ABS_X] = 16704;
+ input->absmax[ABS_Y] = 12064;
+ input->absfuzz[ABS_X] = 4;
+ input->absfuzz[ABS_Y] = 4;
+
+ return 0;
+err_free:
+ kfree(wdata);
+ return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+ { }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+ .name = "wacom",
+ .id_table = wacom_devices,
+ .probe = wacom_probe,
+ .remove = wacom_remove,
+ .raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+ int ret;
+
+ ret = hid_register_driver(&wacom_driver);
+ if (ret)
+ printk(KERN_ERR "can't register wacom driver\n");
+ printk(KERN_ERR "wacom driver registered\n");
+ return ret;
+}
+
+static void wacom_exit(void)
+{
+ hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index 85a198a18537..57f710757bf4 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -27,6 +27,7 @@
#include "hid-ids.h"
+#ifdef CONFIG_ZEROPLUS_FF
#include "usbhid/usbhid.h"
struct zpff_device {
@@ -108,6 +109,12 @@ static int zpff_init(struct hid_device *hid)
return 0;
}
+#else
+static inline int zpff_init(struct hid_device *hid)
+{
+ return 0;
+}
+#endif
static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 00ccf4b1985d..0c6639ea03dd 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -349,10 +349,7 @@ int hidraw_connect(struct hid_device *hid)
int minor, result;
struct hidraw *dev;
- /* TODO currently we accept any HID device. This should later
- * probably be fixed to accept only those devices which provide
- * non-input applications
- */
+ /* we accept any HID device, no matter the applications */
dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
if (!dev)
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ac8049b5f1e9..76c4bbe9dccb 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1234,12 +1234,11 @@ static int hid_post_reset(struct usb_interface *intf)
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
int status;
-
+
spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
- /* FIXME: Any more reinitialization needed? */
status = hid_start_in(hid);
if (status < 0)
hid_io_error(hid);
@@ -1251,14 +1250,14 @@ static int hid_post_reset(struct usb_interface *intf)
int usbhid_get_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
-
+
return usb_autopm_get_interface(usbhid->intf);
}
void usbhid_put_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
-
+
usb_autopm_put_interface(usbhid->intf);
}
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/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f1c6ca7e2852..c8460fa9cfac 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -298,7 +298,7 @@ config I2C_BLACKFIN_TWI
config I2C_BLACKFIN_TWI_CLK_KHZ
int "Blackfin TWI I2C clock (kHz)"
depends on I2C_BLACKFIN_TWI
- range 10 400
+ range 21 400
default 50
help
The unit of the TWI clock is kHz.
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index fc548b3d002e..26d8987e69bf 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -104,9 +104,14 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP);
else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg+1 < iface->msg_num)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | RSTART);
+ iface->cur_msg + 1 < iface->msg_num) {
+ if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | RSTART | MDIR);
+ else
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+ }
SSYNC();
/* Clear status */
write_INT_STAT(iface, XMTSERV);
@@ -134,9 +139,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
read_MASTER_CTL(iface) | STOP);
SSYNC();
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg+1 < iface->msg_num) {
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | RSTART);
+ iface->cur_msg + 1 < iface->msg_num) {
+ if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | RSTART | MDIR);
+ else
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
SSYNC();
}
/* Clear interrupt source */
@@ -196,8 +205,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
/* remove restart bit and enable master receive */
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART);
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | MEN | MDIR);
SSYNC();
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) {
@@ -222,18 +229,19 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
}
if (iface->pmsg[iface->cur_msg].len <= 255)
- write_MASTER_CTL(iface,
- iface->pmsg[iface->cur_msg].len << 6);
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) &
+ (~(0xff << 6))) |
+ (iface->pmsg[iface->cur_msg].len << 6));
else {
- write_MASTER_CTL(iface, 0xff << 6);
+ write_MASTER_CTL(iface,
+ (read_MASTER_CTL(iface) |
+ (0xff << 6)));
iface->manual_stop = 1;
}
/* remove restart bit and enable master receive */
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART);
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) |
- MEN | ((iface->read_write == I2C_SMBUS_READ) ?
- MDIR : 0));
SSYNC();
} else {
iface->result = 1;
@@ -441,6 +449,16 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
}
iface->transPtr = data->block;
break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ iface->readNum = data->block[0];
+ iface->cur_mode = TWI_I2C_MODE_COMBINED;
+ } else {
+ iface->writeNum = data->block[0];
+ iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
+ }
+ iface->transPtr = (u8 *)&data->block[1];
+ break;
default:
return -1;
}
@@ -564,7 +582,7 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap)
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
- I2C_FUNC_I2C;
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
}
static struct i2c_algorithm bfin_twi_algorithm = {
@@ -614,6 +632,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
struct i2c_adapter *p_adap;
struct resource *res;
int rc;
+ unsigned int clkhilow;
iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
if (!iface) {
@@ -675,10 +694,14 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
/* Set TWI internal clock as 10MHz */
write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
+ /*
+ * We will not end up with a CLKDIV=0 because no one will specify
+ * 20kHz SCL or less in Kconfig now. (5 * 1024 / 20 = 0x100)
+ */
+ clkhilow = 5 * 1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ;
+
/* Set Twi interface clock as specified */
- write_CLKDIV(iface, ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
- << 8) | ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
- & 0xFF));
+ write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
/* Enable TWI */
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index e5193bf75483..3542c6ba98f1 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -216,6 +216,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
struct ocores_i2c_platform_data *pdata;
struct resource *res, *res2;
int ret;
+ int i;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -271,6 +272,10 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
goto add_adapter_failed;
}
+ /* add in known devices to the bus */
+ for (i = 0; i < pdata->num_devices; i++)
+ i2c_new_device(&i2c->adap, pdata->devices + i);
+
return 0;
add_adapter_failed:
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index ece0125a1ee5..c73475dd0fba 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -333,8 +333,18 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
- /* HSI2C controller internal clk rate should be 19.2 Mhz */
- internal_clk = 19200;
+ /*
+ * HSI2C controller internal clk rate should be 19.2 Mhz for
+ * HS and for all modes on 2430. On 34xx we can use lower rate
+ * to get longer filter period for better noise suppression.
+ * The filter is iclk (fclk for HS) period.
+ */
+ if (dev->speed > 400 || cpu_is_omap_2430())
+ internal_clk = 19200;
+ else if (dev->speed > 100)
+ internal_clk = 9600;
+ else
+ internal_clk = 4000;
fclk_rate = clk_get_rate(dev->fclk) / 1000;
/* Compute prescaler divisor */
@@ -343,17 +353,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
/* If configured for High Speed */
if (dev->speed > 400) {
+ unsigned long scl;
+
/* For first phase of HS mode */
- fsscll = internal_clk / (400 * 2) - 6;
- fssclh = internal_clk / (400 * 2) - 6;
+ scl = internal_clk / 400;
+ fsscll = scl - (scl / 3) - 7;
+ fssclh = (scl / 3) - 5;
/* For second phase of HS mode */
- hsscll = fclk_rate / (dev->speed * 2) - 6;
- hssclh = fclk_rate / (dev->speed * 2) - 6;
+ scl = fclk_rate / dev->speed;
+ hsscll = scl - (scl / 3) - 7;
+ hssclh = (scl / 3) - 5;
+ } else if (dev->speed > 100) {
+ unsigned long scl;
+
+ /* Fast mode */
+ scl = internal_clk / dev->speed;
+ fsscll = scl - (scl / 3) - 7;
+ fssclh = (scl / 3) - 5;
} else {
- /* To handle F/S modes */
- fsscll = internal_clk / (dev->speed * 2) - 6;
- fssclh = internal_clk / (dev->speed * 2) - 6;
+ /* Standard mode */
+ fsscll = internal_clk / (dev->speed * 2) - 7;
+ fssclh = internal_clk / (dev->speed * 2) - 5;
}
scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll;
sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh;
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index acc7143d9655..035a6c7e59df 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -34,10 +34,24 @@
#include <linux/err.h>
#include <linux/clk.h>
-#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <mach/i2c.h>
+#include <plat/i2c.h>
+
+/*
+ * I2C register offsets will be shifted 0 or 1 bit left, depending on
+ * different SoCs
+ */
+#define REG_SHIFT_0 (0 << 0)
+#define REG_SHIFT_1 (1 << 0)
+#define REG_SHIFT(d) ((d) & 0x1)
+
+static const struct platform_device_id i2c_pxa_id_table[] = {
+ { "pxa2xx-i2c", REG_SHIFT_1 },
+ { "pxa3xx-pwri2c", REG_SHIFT_0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
/*
* I2C registers and bit definitions
@@ -985,6 +999,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
struct pxa_i2c *i2c;
struct resource *res;
struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
+ struct platform_device_id *id = platform_get_device_id(dev);
int ret;
int irq;
@@ -1028,7 +1043,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
ret = -EIO;
goto eremap;
}
- i2c->reg_shift = (cpu_is_pxa3xx() && (dev->id == 1)) ? 0 : 1;
+ i2c->reg_shift = REG_SHIFT(id->driver_data);
i2c->iobase = res->start;
i2c->iosize = res_len(res);
@@ -1150,6 +1165,7 @@ static struct platform_driver i2c_pxa_driver = {
.name = "pxa2xx-i2c",
.owner = THIS_MODULE,
},
+ .id_table = i2c_pxa_id_table,
};
static int __init i2c_adap_pxa_init(void)
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 1691ef0f1ee1..079a312d36fd 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -51,6 +51,11 @@ enum s3c24xx_i2c_state {
STATE_STOP
};
+enum s3c24xx_i2c_type {
+ TYPE_S3C2410,
+ TYPE_S3C2440,
+};
+
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
@@ -88,8 +93,10 @@ struct s3c24xx_i2c {
static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
{
struct platform_device *pdev = to_platform_device(i2c->dev);
+ enum s3c24xx_i2c_type type;
- return !strcmp(pdev->name, "s3c2440-i2c");
+ type = platform_get_device_id(pdev)->driver_data;
+ return type == TYPE_S3C2440;
}
/* s3c24xx_i2c_master_complete
@@ -969,52 +976,41 @@ static int s3c24xx_i2c_resume(struct platform_device *dev)
/* device driver for platform bus bits */
-static struct platform_driver s3c2410_i2c_driver = {
- .probe = s3c24xx_i2c_probe,
- .remove = s3c24xx_i2c_remove,
- .suspend_late = s3c24xx_i2c_suspend_late,
- .resume = s3c24xx_i2c_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2410-i2c",
- },
+static struct platform_device_id s3c24xx_driver_ids[] = {
+ {
+ .name = "s3c2410-i2c",
+ .driver_data = TYPE_S3C2410,
+ }, {
+ .name = "s3c2440-i2c",
+ .driver_data = TYPE_S3C2440,
+ }, { },
};
+MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
-static struct platform_driver s3c2440_i2c_driver = {
+static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.suspend_late = s3c24xx_i2c_suspend_late,
.resume = s3c24xx_i2c_resume,
+ .id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
- .name = "s3c2440-i2c",
+ .name = "s3c-i2c",
},
};
static int __init i2c_adap_s3c_init(void)
{
- int ret;
-
- ret = platform_driver_register(&s3c2410_i2c_driver);
- if (ret == 0) {
- ret = platform_driver_register(&s3c2440_i2c_driver);
- if (ret)
- platform_driver_unregister(&s3c2410_i2c_driver);
- }
-
- return ret;
+ return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);
static void __exit i2c_adap_s3c_exit(void)
{
- platform_driver_unregister(&s3c2410_i2c_driver);
- platform_driver_unregister(&s3c2440_i2c_driver);
+ platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);
MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c2410-i2c");
-MODULE_ALIAS("platform:s3c2440-i2c");
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/ide/Kconfig b/drivers/ide/Kconfig
index cf06494bb744..9a5d0aaac9d0 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -46,7 +46,7 @@ menuconfig IDE
SMART parameters from disk drives.
To compile this driver as a module, choose M here: the
- module will be called ide-core.ko.
+ module will be called ide-core.
For further information, please read <file:Documentation/ide/ide.txt>.
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 36da913cc553..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], &hw[1], 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 7201b176d75b..702ef64a0f12 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -80,34 +80,6 @@ void ide_init_pc(struct ide_atapi_pc *pc)
EXPORT_SYMBOL_GPL(ide_init_pc);
/*
- * Generate a new packet command request in front of the request queue, before
- * the current request, so that it will be processed immediately, on the next
- * pass through the driver.
- */
-static void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk,
- struct ide_atapi_pc *pc, struct request *rq)
-{
- blk_rq_init(NULL, rq);
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd_flags |= REQ_PREEMPT;
- rq->buffer = (char *)pc;
- rq->rq_disk = disk;
-
- if (pc->req_xfer) {
- rq->data = pc->buf;
- rq->data_len = pc->req_xfer;
- }
-
- memcpy(rq->cmd, pc->c, 12);
- if (drive->media == ide_tape)
- rq->cmd[13] = REQ_IDETAPE_PC1;
-
- drive->hwif->rq = NULL;
-
- elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
-}
-
-/*
* Add a special packet command request to the tail of the request queue,
* and wait for it to be serviced.
*/
@@ -119,19 +91,21 @@ int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk,
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->buffer = (char *)pc;
+ rq->special = (char *)pc;
if (pc->req_xfer) {
- rq->data = pc->buf;
- rq->data_len = pc->req_xfer;
+ error = blk_rq_map_kern(drive->queue, rq, pc->buf, pc->req_xfer,
+ GFP_NOIO);
+ if (error)
+ goto put_req;
}
memcpy(rq->cmd, pc->c, 12);
if (drive->media == ide_tape)
rq->cmd[13] = REQ_IDETAPE_PC1;
error = blk_execute_rq(drive->queue, disk, rq, 0);
+put_req:
blk_put_request(rq);
-
return error;
}
EXPORT_SYMBOL_GPL(ide_queue_pc_tail);
@@ -191,20 +165,113 @@ void ide_create_request_sense_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc)
}
EXPORT_SYMBOL_GPL(ide_create_request_sense_cmd);
+void ide_prep_sense(ide_drive_t *drive, struct request *rq)
+{
+ struct request_sense *sense = &drive->sense_data;
+ struct request *sense_rq = &drive->sense_rq;
+ unsigned int cmd_len, sense_len;
+ int err;
+
+ debug_log("%s: enter\n", __func__);
+
+ switch (drive->media) {
+ case ide_floppy:
+ cmd_len = 255;
+ sense_len = 18;
+ break;
+ case ide_tape:
+ cmd_len = 20;
+ sense_len = 20;
+ break;
+ default:
+ cmd_len = 18;
+ sense_len = 18;
+ }
+
+ BUG_ON(sense_len > sizeof(*sense));
+
+ if (blk_sense_request(rq) || drive->sense_rq_armed)
+ return;
+
+ memset(sense, 0, sizeof(*sense));
+
+ blk_rq_init(rq->q, sense_rq);
+
+ err = blk_rq_map_kern(drive->queue, sense_rq, sense, sense_len,
+ GFP_NOIO);
+ if (unlikely(err)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING "%s: failed to map sense buffer\n",
+ drive->name);
+ return;
+ }
+
+ sense_rq->rq_disk = rq->rq_disk;
+ sense_rq->cmd[0] = GPCMD_REQUEST_SENSE;
+ sense_rq->cmd[4] = cmd_len;
+ sense_rq->cmd_type = REQ_TYPE_SENSE;
+ sense_rq->cmd_flags |= REQ_PREEMPT;
+
+ if (drive->media == ide_tape)
+ sense_rq->cmd[13] = REQ_IDETAPE_PC1;
+
+ drive->sense_rq_armed = true;
+}
+EXPORT_SYMBOL_GPL(ide_prep_sense);
+
+int ide_queue_sense_rq(ide_drive_t *drive, void *special)
+{
+ /* deferred failure from ide_prep_sense() */
+ if (!drive->sense_rq_armed) {
+ printk(KERN_WARNING "%s: failed queue sense request\n",
+ drive->name);
+ return -ENOMEM;
+ }
+
+ drive->sense_rq.special = special;
+ drive->sense_rq_armed = false;
+
+ drive->hwif->rq = NULL;
+
+ elv_add_request(drive->queue, &drive->sense_rq,
+ ELEVATOR_INSERT_FRONT, 0);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_queue_sense_rq);
+
/*
* Called when an error was detected during the last packet command.
- * We queue a request sense packet command in the head of the request list.
+ * We queue a request sense packet command at the head of the request
+ * queue.
*/
-void ide_retry_pc(ide_drive_t *drive, struct gendisk *disk)
+void ide_retry_pc(ide_drive_t *drive)
{
- struct request *rq = &drive->request_sense_rq;
+ struct request *failed_rq = drive->hwif->rq;
+ struct request *sense_rq = &drive->sense_rq;
struct ide_atapi_pc *pc = &drive->request_sense_pc;
(void)ide_read_error(drive);
- ide_create_request_sense_cmd(drive, pc);
+
+ /* init pc from sense_rq */
+ ide_init_pc(pc);
+ memcpy(pc->c, sense_rq->cmd, 12);
+ pc->buf = bio_data(sense_rq->bio); /* pointer to mapped address */
+ pc->req_xfer = blk_rq_bytes(sense_rq);
+
if (drive->media == ide_tape)
- set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
- ide_queue_pc_head(drive, disk, pc, rq);
+ drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
+
+ /*
+ * Push back the failed request and put request sense on top
+ * of it. The failed command will be retried after sense data
+ * is acquired.
+ */
+ blk_requeue_request(failed_rq->q, failed_rq);
+ drive->hwif->rq = NULL;
+ if (ide_queue_sense_rq(drive, pc)) {
+ blk_start_request(failed_rq);
+ ide_complete_rq(drive, -EIO, blk_rq_bytes(failed_rq));
+ }
}
EXPORT_SYMBOL_GPL(ide_retry_pc);
@@ -246,7 +313,7 @@ int ide_cd_get_xferlen(struct request *rq)
return 32768;
else if (blk_sense_request(rq) || blk_pc_request(rq) ||
rq->cmd_type == REQ_TYPE_ATA_PC)
- return rq->data_len;
+ return blk_rq_bytes(rq);
else
return 0;
}
@@ -276,7 +343,6 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
struct ide_cmd *cmd = &hwif->cmd;
struct request *rq = hwif->rq;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- xfer_func_t *xferfunc;
unsigned int timeout, done;
u16 bcount;
u8 stat, ireason, dsc = 0;
@@ -303,18 +369,14 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
drive->name, rq_data_dir(pc->rq)
? "write" : "read");
pc->flags |= PC_FLAG_DMA_ERROR;
- } else {
+ } else
pc->xferred = pc->req_xfer;
- if (drive->pc_update_buffers)
- drive->pc_update_buffers(drive, pc);
- }
debug_log("%s: DMA finished\n", drive->name);
}
/* No more interrupts */
if ((stat & ATA_DRQ) == 0) {
int uptodate, error;
- unsigned int done;
debug_log("Packet command completed, %d bytes transferred\n",
pc->xferred);
@@ -343,7 +405,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
debug_log("[cmd %x]: check condition\n", rq->cmd[0]);
/* Retry operation */
- ide_retry_pc(drive, rq->rq_disk);
+ ide_retry_pc(drive);
/* queued, but not started */
return ide_stopped;
@@ -353,6 +415,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);
@@ -361,7 +429,6 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
if (blk_special_request(rq)) {
rq->errors = 0;
- done = blk_rq_bytes(rq);
error = 0;
} else {
@@ -370,15 +437,10 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
rq->errors = -EIO;
}
- if (drive->media == ide_tape)
- done = ide_rq_bytes(rq); /* FIXME */
- else
- done = blk_rq_bytes(rq);
-
error = uptodate ? 0 : -EIO;
}
- ide_complete_rq(drive, error, done);
+ ide_complete_rq(drive, error, blk_rq_bytes(rq));
return ide_stopped;
}
@@ -407,21 +469,11 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
return ide_do_reset(drive);
}
- xferfunc = write ? tp_ops->output_data : tp_ops->input_data;
-
- if (drive->media == ide_floppy && pc->buf == NULL) {
- done = min_t(unsigned int, bcount, cmd->nleft);
- ide_pio_bytes(drive, cmd, write, done);
- } else if (drive->media == ide_tape && pc->bh) {
- done = drive->pc_io_buffers(drive, pc, bcount, write);
- } else {
- done = min_t(unsigned int, bcount, pc->req_xfer - pc->xferred);
- xferfunc(drive, NULL, pc->cur_pos, done);
- }
+ done = min_t(unsigned int, bcount, cmd->nleft);
+ ide_pio_bytes(drive, cmd, write, done);
- /* Update the current position */
+ /* Update transferred byte count */
pc->xferred += done;
- pc->cur_pos += done;
bcount -= done;
@@ -525,7 +577,7 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive)
/*
* If necessary schedule the packet transfer to occur 'timeout'
- * miliseconds later in ide_delayed_transfer_pc() after the
+ * milliseconds later in ide_delayed_transfer_pc() after the
* device says it's ready for a packet.
*/
if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) {
@@ -599,7 +651,6 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd)
/* We haven't transferred any data yet */
pc->xferred = 0;
- pc->cur_pos = pc->buf;
valid_tf = IDE_VALID_DEVICE;
bcount = ((drive->media == ide_tape) ?
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 925eb9e245d1..424140c6c400 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;
@@ -206,54 +206,25 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive,
ide_cd_log_error(drive->name, failed_command, sense);
}
-static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
- struct request *failed_command)
-{
- struct cdrom_info *info = drive->driver_data;
- struct request *rq = &drive->request_sense_rq;
-
- ide_debug_log(IDE_DBG_SENSE, "enter");
-
- if (sense == NULL)
- sense = &info->sense_data;
-
- /* stuff the sense request in front of our current request */
- blk_rq_init(NULL, rq);
- rq->cmd_type = REQ_TYPE_ATA_PC;
- rq->rq_disk = info->disk;
-
- rq->data = sense;
- rq->cmd[0] = GPCMD_REQUEST_SENSE;
- rq->cmd[4] = 18;
- rq->data_len = 18;
-
- rq->cmd_type = REQ_TYPE_SENSE;
- rq->cmd_flags |= REQ_PREEMPT;
-
- /* NOTE! Save the failed command in "rq->buffer" */
- rq->buffer = (void *) failed_command;
-
- if (failed_command)
- ide_debug_log(IDE_DBG_SENSE, "failed_cmd: 0x%x",
- failed_command->cmd[0]);
-
- drive->hwif->rq = NULL;
-
- elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
-}
-
static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq)
{
/*
- * For REQ_TYPE_SENSE, "rq->buffer" points to the original
- * failed request
+ * For REQ_TYPE_SENSE, "rq->special" points to the original
+ * failed request. Also, the sense data should be read
+ * directly from rq which might be different from the original
+ * sense buffer if it got copied during mapping.
*/
- struct request *failed = (struct request *)rq->buffer;
- struct cdrom_info *info = drive->driver_data;
- void *sense = &info->sense_data;
+ struct request *failed = (struct request *)rq->special;
+ void *sense = bio_data(rq->bio);
if (failed) {
if (failed->sense) {
+ /*
+ * Sense is always read into drive->sense_data.
+ * Copy back if the failed request has its
+ * sense pointer set.
+ */
+ memcpy(failed->sense, sense, 18);
sense = failed->sense;
failed->sense_len = rq->sense_len;
}
@@ -428,22 +399,13 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
/* if we got a CHECK_CONDITION status, queue a request sense command */
if (stat & ATA_ERR)
- cdrom_queue_request_sense(drive, NULL, NULL);
+ return ide_queue_sense_rq(drive, NULL) ? 2 : 1;
return 1;
end_request:
if (stat & ATA_ERR) {
- struct request_queue *q = drive->queue;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- blkdev_dequeue_request(rq);
- spin_unlock_irqrestore(q->queue_lock, flags);
-
hwif->rq = NULL;
-
- cdrom_queue_request_sense(drive, rq->sense, rq);
- return 1;
+ return ide_queue_sense_rq(drive, rq) ? 2 : 1;
} else
return 2;
}
@@ -503,14 +465,8 @@ static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct ide_cmd *cmd)
* and some drives don't send them. Sigh.
*/
if (rq->cmd[0] == GPCMD_REQUEST_SENSE &&
- cmd->nleft > 0 && cmd->nleft <= 5) {
- unsigned int ofs = cmd->nbytes - cmd->nleft;
-
- while (cmd->nleft > 0) {
- *((u8 *)rq->data + ofs++) = 0;
- cmd->nleft--;
- }
- }
+ cmd->nleft > 0 && cmd->nleft <= 5)
+ cmd->nleft = 0;
}
int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd,
@@ -543,14 +499,18 @@ int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd,
rq->cmd_flags |= cmd_flags;
rq->timeout = timeout;
if (buffer) {
- rq->data = buffer;
- rq->data_len = *bufflen;
+ error = blk_rq_map_kern(drive->queue, rq, buffer,
+ *bufflen, GFP_NOIO);
+ if (error) {
+ blk_put_request(rq);
+ return error;
+ }
}
error = blk_execute_rq(drive->queue, info->disk, rq, 0);
if (buffer)
- *bufflen = rq->data_len;
+ *bufflen = rq->resid_len;
flags = rq->cmd_flags;
blk_put_request(rq);
@@ -608,7 +568,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
struct request *rq = hwif->rq;
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, thislen, uptodate = 0;
- int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0, nsectors;
+ int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0;
int sense = blk_sense_request(rq);
unsigned int timeout;
u16 len;
@@ -738,13 +698,8 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
out_end:
if (blk_pc_request(rq) && rc == 0) {
- unsigned int dlen = rq->data_len;
-
- rq->data_len = 0;
-
- if (blk_end_request(rq, 0, dlen))
- BUG();
-
+ rq->resid_len = 0;
+ blk_end_request_all(rq, 0);
hwif->rq = NULL;
} else {
if (sense && uptodate)
@@ -762,21 +717,13 @@ out_end:
ide_cd_error_cmd(drive, cmd);
/* make sure it's fully ended */
- if (blk_pc_request(rq))
- nsectors = (rq->data_len + 511) >> 9;
- else
- nsectors = rq->hard_nr_sectors;
-
- if (nsectors == 0)
- nsectors = 1;
-
if (blk_fs_request(rq) == 0) {
- rq->data_len -= (cmd->nbytes - cmd->nleft);
+ rq->resid_len -= cmd->nbytes - cmd->nleft;
if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE))
- rq->data_len += cmd->last_xfer_len;
+ rq->resid_len += cmd->last_xfer_len;
}
- ide_complete_rq(drive, uptodate ? 0 : -EIO, nsectors << 9);
+ ide_complete_rq(drive, uptodate ? 0 : -EIO, blk_rq_bytes(rq));
if (sense && rc == 2)
ide_error(drive, "request sense failure", stat);
@@ -790,7 +737,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",
@@ -809,8 +756,8 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq)
}
/* fs requests *must* be hardware frame aligned */
- if ((rq->nr_sectors & (sectors_per_frame - 1)) ||
- (rq->sector & (sectors_per_frame - 1)))
+ if ((blk_rq_sectors(rq) & (sectors_per_frame - 1)) ||
+ (blk_rq_pos(rq) & (sectors_per_frame - 1)))
return ide_stopped;
/* use DMA, if possible */
@@ -838,15 +785,10 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
drive->dma = 0;
/* sg request */
- if (rq->bio || ((rq->cmd_type == REQ_TYPE_ATA_PC) && rq->data_len)) {
+ if (rq->bio) {
struct request_queue *q = drive->queue;
+ char *buf = bio_data(rq->bio);
unsigned int alignment;
- char *buf;
-
- if (rq->bio)
- buf = bio_data(rq->bio);
- else
- buf = rq->data;
drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
@@ -858,7 +800,7 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
*/
alignment = queue_dma_alignment(q) | q->dma_pad_mask;
if ((unsigned long)buf & alignment
- || rq->data_len & q->dma_pad_mask
+ || blk_rq_bytes(rq) & q->dma_pad_mask
|| object_is_on_stack(buf))
drive->dma = 0;
}
@@ -896,6 +838,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
goto out_end;
}
+ /* prepare sense request for this command */
+ ide_prep_sense(drive, rq);
+
memset(&cmd, 0, sizeof(cmd));
if (rq_data_dir(rq))
@@ -903,15 +848,14 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
cmd.rq = rq;
- if (blk_fs_request(rq) || rq->data_len) {
- ide_init_sg_cmd(&cmd, blk_fs_request(rq) ? (rq->nr_sectors << 9)
- : rq->data_len);
+ if (blk_fs_request(rq) || blk_rq_bytes(rq)) {
+ ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
ide_map_sg(drive, &cmd);
}
return ide_issue_pc(drive, &cmd);
out_end:
- nsectors = rq->hard_nr_sectors;
+ nsectors = blk_rq_sectors(rq);
if (nsectors == 0)
nsectors = 1;
@@ -1077,8 +1021,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,
@@ -1394,9 +1338,9 @@ 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);
- long block = (long)rq->hard_sector / (hard_sect >> 9);
- unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9);
+ 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);
memset(rq->cmd, 0, BLK_MAX_CDB);
@@ -1599,7 +1543,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-cd.h b/drivers/ide/ide-cd.h
index 1d97101099ce..93a3cf1b0f3f 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -87,10 +87,6 @@ struct cdrom_info {
struct atapi_toc *toc;
- /* The result of the last successful request sense command
- on this device. */
- struct request_sense sense_data;
-
u8 max_speed; /* Max speed of the drive. */
u8 current_speed; /* Current speed of the drive. */
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 a9fbe2c31210..6a1de2169709 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -82,7 +82,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
sector_t block)
{
ide_hwif_t *hwif = drive->hwif;
- u16 nsectors = (u16)rq->nr_sectors;
+ u16 nsectors = (u16)blk_rq_sectors(rq);
u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48);
u8 dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
struct ide_cmd cmd;
@@ -90,7 +90,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
ide_startstop_t rc;
if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && lba48 && dma) {
- if (block + rq->nr_sectors > 1ULL << 28)
+ if (block + blk_rq_sectors(rq) > 1ULL << 28)
dma = 0;
else
lba48 = 0;
@@ -195,9 +195,9 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
ledtrig_ide_activity();
- pr_debug("%s: %sing: block=%llu, sectors=%lu, buffer=0x%08lx\n",
+ pr_debug("%s: %sing: block=%llu, sectors=%u, buffer=0x%08lx\n",
drive->name, rq_data_dir(rq) == READ ? "read" : "writ",
- (unsigned long long)block, rq->nr_sectors,
+ (unsigned long long)block, blk_rq_sectors(rq),
(unsigned long)rq->buffer);
if (hwif->rw_disk)
@@ -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;
@@ -411,7 +461,6 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
cmd->protocol = ATA_PROT_NODATA;
rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
- rq->cmd_flags |= REQ_SOFTBARRIER;
rq->special = cmd;
}
@@ -429,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);
@@ -640,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);
@@ -741,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 a0b8cab1d9a6..219e6fb78dc6 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -103,7 +103,7 @@ ide_startstop_t ide_dma_intr(ide_drive_t *drive)
ide_finish_cmd(drive, cmd, stat);
else
ide_complete_rq(drive, 0,
- cmd->rq->nr_sectors << 9);
+ blk_rq_sectors(cmd->rq) << 9);
return ide_stopped;
}
printk(KERN_ERR "%s: %s: bad DMA status (0x%02x)\n",
@@ -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)
{
@@ -510,23 +509,11 @@ ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
/*
* un-busy drive etc and make sure request is sane
*/
-
rq = hwif->rq;
- if (!rq)
- goto out;
-
- hwif->rq = NULL;
-
- rq->errors = 0;
-
- if (!rq->bio)
- goto out;
-
- rq->sector = rq->bio->bi_sector;
- rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
- rq->hard_cur_sectors = rq->current_nr_sectors;
- rq->buffer = bio_data(rq->bio);
-out:
+ if (rq) {
+ hwif->rq = NULL;
+ rq->errors = 0;
+ }
return ret;
}
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-floppy.c b/drivers/ide/ide-floppy.c
index 2b4868d95f8b..650981758f15 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -134,13 +134,17 @@ static ide_startstop_t ide_floppy_issue_pc(ide_drive_t *drive,
drive->pc = pc;
if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES) {
+ unsigned int done = blk_rq_bytes(drive->hwif->rq);
+
if (!(pc->flags & PC_FLAG_SUPPRESS_ERROR))
ide_floppy_report_error(floppy, pc);
+
/* Giving up */
pc->error = IDE_DRV_ERROR_GENERAL;
drive->failed_pc = NULL;
drive->pc_callback(drive, 0);
+ ide_complete_rq(drive, -EIO, done);
return ide_stopped;
}
@@ -190,7 +194,7 @@ static void idefloppy_create_rw_cmd(ide_drive_t *drive,
{
struct ide_disk_obj *floppy = drive->driver_data;
int block = sector / floppy->bs_factor;
- int blocks = rq->nr_sectors / floppy->bs_factor;
+ int blocks = blk_rq_sectors(rq) / floppy->bs_factor;
int cmd = rq_data_dir(rq);
ide_debug_log(IDE_DBG_FUNC, "block: %d, blocks: %d", block, blocks);
@@ -216,16 +220,14 @@ static void idefloppy_blockpc_cmd(struct ide_disk_obj *floppy,
ide_init_pc(pc);
memcpy(pc->c, rq->cmd, sizeof(pc->c));
pc->rq = rq;
- if (rq->data_len && rq_data_dir(rq) == WRITE)
- pc->flags |= PC_FLAG_WRITING;
- pc->buf = rq->data;
- if (rq->bio)
+ if (blk_rq_bytes(rq)) {
pc->flags |= PC_FLAG_DMA_OK;
- /*
- * possibly problematic, doesn't look like ide-floppy correctly
- * handled scattered requests if dma fails...
- */
- pc->req_xfer = pc->buf_size = rq->data_len;
+ if (rq_data_dir(rq) == WRITE)
+ pc->flags |= PC_FLAG_WRITING;
+ }
+ /* pio will be performed by ide_pio_bytes() which handles sg fine */
+ pc->buf = NULL;
+ pc->req_xfer = pc->buf_size = blk_rq_bytes(rq);
}
static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
@@ -257,16 +259,16 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
goto out_end;
}
if (blk_fs_request(rq)) {
- if (((long)rq->sector % floppy->bs_factor) ||
- (rq->nr_sectors % floppy->bs_factor)) {
+ if (((long)blk_rq_pos(rq) % floppy->bs_factor) ||
+ (blk_rq_sectors(rq) % floppy->bs_factor)) {
printk(KERN_ERR PFX "%s: unsupported r/w rq size\n",
drive->name);
goto out_end;
}
pc = &floppy->queued_pc;
idefloppy_create_rw_cmd(drive, pc, rq, (unsigned long)block);
- } else if (blk_special_request(rq)) {
- pc = (struct ide_atapi_pc *) rq->buffer;
+ } else if (blk_special_request(rq) || blk_sense_request(rq)) {
+ pc = (struct ide_atapi_pc *)rq->special;
} else if (blk_pc_request(rq)) {
pc = &floppy->queued_pc;
idefloppy_blockpc_cmd(floppy, pc, rq);
@@ -275,6 +277,8 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
goto out_end;
}
+ ide_prep_sense(drive, rq);
+
memset(&cmd, 0, sizeof(cmd));
if (rq_data_dir(rq))
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 35dc38d3b2c5..272cc38f6dbe 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -116,9 +116,9 @@ void ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err)
unsigned int ide_rq_bytes(struct request *rq)
{
if (blk_pc_request(rq))
- return rq->data_len;
+ return blk_rq_bytes(rq);
else
- return rq->hard_cur_sectors << 9;
+ return blk_rq_cur_sectors(rq) << 9;
}
EXPORT_SYMBOL_GPL(ide_rq_bytes);
@@ -133,7 +133,7 @@ int ide_complete_rq(ide_drive_t *drive, int error, unsigned int nr_bytes)
* and complete the whole request right now
*/
if (blk_noretry_request(rq) && error <= 0)
- nr_bytes = rq->hard_nr_sectors << 9;
+ nr_bytes = blk_rq_sectors(rq) << 9;
rc = ide_end_rq(drive, rq, error, nr_bytes);
if (rc == 0)
@@ -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,45 +230,13 @@ 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;
struct scatterlist *sg = hwif->sg_table;
struct request *rq = cmd->rq;
- if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
- sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE);
- cmd->sg_nents = 1;
- } else if (!rq->bio) {
- sg_init_one(sg, rq->data, rq->data_len);
- cmd->sg_nents = 1;
- } else
- cmd->sg_nents = blk_rq_map_sg(drive->queue, rq, sg);
+ cmd->sg_nents = blk_rq_map_sg(drive->queue, rq, sg);
}
EXPORT_SYMBOL_GPL(ide_map_sg);
@@ -286,7 +267,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
if (cmd) {
if (cmd->protocol == ATA_PROT_PIO) {
- ide_init_sg_cmd(cmd, rq->nr_sectors << 9);
+ ide_init_sg_cmd(cmd, blk_rq_sectors(rq) << 9);
ide_map_sg(drive, cmd);
}
@@ -358,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;
/*
@@ -371,7 +353,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
return execute_drive_cmd(drive, rq);
else if (blk_pm_request(rq)) {
- struct request_pm_state *pm = rq->data;
+ struct request_pm_state *pm = rq->special;
#ifdef DEBUG_PM
printk("%s: start_power_step(step: %d)\n",
drive->name, pm->pm_step);
@@ -394,7 +376,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
drv = *(struct ide_driver **)rq->rq_disk->private_data;
- return drv->do_request(drive, rq, rq->sector);
+ return drv->do_request(drive, rq, blk_rq_pos(rq));
}
return do_special(drive);
kill_rq:
@@ -484,6 +466,9 @@ void do_ide_request(struct request_queue *q)
spin_unlock_irq(q->queue_lock);
+ /* HLD do_request() callback might sleep, make sure it's okay */
+ might_sleep();
+
if (ide_lock_host(host, hwif))
goto plug_device_2;
@@ -491,10 +476,10 @@ void do_ide_request(struct request_queue *q)
if (!ide_lock_port(hwif)) {
ide_hwif_t *prev_port;
+
+ WARN_ON_ONCE(hwif->rq);
repeat:
prev_port = hwif->host->cur_port;
- hwif->rq = NULL;
-
if (drive->dev_flags & IDE_DFLAG_SLEEPING &&
time_after(drive->sleep, jiffies)) {
ide_unlock_port(hwif);
@@ -503,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);
@@ -523,7 +512,9 @@ repeat:
* we know that the queue isn't empty, but this can happen
* if the q->prep_rq_fn() decides to kill a request
*/
- rq = elv_next_request(drive->queue);
+ if (!rq)
+ rq = blk_fetch_request(drive->queue);
+
spin_unlock_irq(q->queue_lock);
spin_lock_irq(&hwif->lock);
@@ -535,7 +526,7 @@ repeat:
/*
* Sanity: don't accept a request that isn't a PM request
* if we are currently power managed. This is very important as
- * blk_stop_queue() doesn't prevent the elv_next_request()
+ * blk_stop_queue() doesn't prevent the blk_fetch_request()
* above to return us whatever is in the queue. Since we call
* ide_do_request() ourselves, we end up taking requests while
* the queue is blocked...
@@ -559,8 +550,11 @@ repeat:
startstop = start_request(drive, rq);
spin_lock_irq(&hwif->lock);
- if (startstop == ide_stopped)
+ if (startstop == ide_stopped) {
+ rq = hwif->rq;
+ hwif->rq = NULL;
goto repeat;
+ }
} else
goto plug_device;
out:
@@ -576,18 +570,24 @@ plug_device:
plug_device_2:
spin_lock_irq(q->queue_lock);
+ if (rq)
+ blk_requeue_request(q, rq);
if (!elv_queue_empty(q))
blk_plug_device(q);
}
-static void ide_plug_device(ide_drive_t *drive)
+static void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq)
{
struct request_queue *q = drive->queue;
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
+
+ if (rq)
+ blk_requeue_request(q, rq);
if (!elv_queue_empty(q))
blk_plug_device(q);
+
spin_unlock_irqrestore(q->queue_lock, flags);
}
@@ -636,6 +636,7 @@ void ide_timer_expiry (unsigned long data)
unsigned long flags;
int wait = -1;
int plug_device = 0;
+ struct request *uninitialized_var(rq_in_flight);
spin_lock_irqsave(&hwif->lock, flags);
@@ -696,7 +697,9 @@ 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);
plug_device = 1;
}
@@ -705,7 +708,7 @@ void ide_timer_expiry (unsigned long data)
if (plug_device) {
ide_unlock_host(hwif->host);
- ide_plug_device(drive);
+ ide_requeue_and_plug(drive, rq_in_flight);
}
}
@@ -791,6 +794,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
ide_startstop_t startstop;
irqreturn_t irq_ret = IRQ_NONE;
int plug_device = 0;
+ struct request *uninitialized_var(rq_in_flight);
if (host->host_flags & IDE_HFLAG_SERIALIZE) {
if (hwif != host->cur_port)
@@ -868,8 +872,10 @@ 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;
ide_unlock_port(hwif);
plug_device = 1;
}
@@ -879,7 +885,7 @@ out:
out_early:
if (plug_device) {
ide_unlock_host(hwif->host);
- ide_plug_device(drive);
+ ide_requeue_and_plug(drive, rq_in_flight);
}
return irq_ret;
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
index c1c25ebbaa1f..5991b23793f2 100644
--- a/drivers/ide/ide-ioctls.c
+++ b/drivers/ide/ide-ioctls.c
@@ -231,7 +231,6 @@ static int generic_drive_reset(ide_drive_t *drive)
rq->cmd_type = REQ_TYPE_SPECIAL;
rq->cmd_len = 1;
rq->cmd[0] = REQ_DRIVE_RESET;
- rq->cmd_flags |= REQ_SOFTBARRIER;
if (blk_execute_rq(drive->queue, NULL, rq, 1))
ret = rq->errors;
blk_put_request(rq);
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 56ff8c46c7d1..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)
@@ -114,14 +96,14 @@ static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
if (rq)
printk(KERN_CONT ", sector=%llu",
- (unsigned long long)rq->sector);
+ (unsigned long long)blk_rq_pos(rq));
}
printk(KERN_CONT "\n");
}
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-park.c b/drivers/ide/ide-park.c
index 310d03f2b5b7..a914023d6d03 100644
--- a/drivers/ide/ide-park.c
+++ b/drivers/ide/ide-park.c
@@ -24,11 +24,8 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
start_queue = 1;
spin_unlock_irq(&hwif->lock);
- if (start_queue) {
- spin_lock_irq(q->queue_lock);
- blk_start_queueing(q);
- spin_unlock_irq(q->queue_lock);
- }
+ if (start_queue)
+ blk_run_queue(q);
return;
}
spin_unlock_irq(&hwif->lock);
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-pm.c b/drivers/ide/ide-pm.c
index 0d8a151c0a01..ba1488bd8430 100644
--- a/drivers/ide/ide-pm.c
+++ b/drivers/ide/ide-pm.c
@@ -7,7 +7,6 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
struct request_pm_state rqpm;
- struct ide_cmd cmd;
int ret;
/* call ACPI _GTM only once */
@@ -15,11 +14,9 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
ide_acpi_get_timing(hwif);
memset(&rqpm, 0, sizeof(rqpm));
- memset(&cmd, 0, sizeof(cmd));
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_PM_SUSPEND;
- rq->special = &cmd;
- rq->data = &rqpm;
+ rq->special = &rqpm;
rqpm.pm_step = IDE_PM_START_SUSPEND;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
@@ -41,7 +38,6 @@ int generic_ide_resume(struct device *dev)
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
struct request_pm_state rqpm;
- struct ide_cmd cmd;
int err;
/* call ACPI _PS0 / _STM only once */
@@ -53,12 +49,10 @@ int generic_ide_resume(struct device *dev)
ide_acpi_exec_tfs(drive);
memset(&rqpm, 0, sizeof(rqpm));
- memset(&cmd, 0, sizeof(cmd));
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_PM_RESUME;
rq->cmd_flags |= REQ_PREEMPT;
- rq->special = &cmd;
- rq->data = &rqpm;
+ rq->special = &rqpm;
rqpm.pm_step = IDE_PM_START_RESUME;
rqpm.pm_state = PM_EVENT_ON;
@@ -77,7 +71,7 @@ int generic_ide_resume(struct device *dev)
void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->data;
+ struct request_pm_state *pm = rq->special;
#ifdef DEBUG_PM
printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
@@ -107,10 +101,8 @@ void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->data;
- struct ide_cmd *cmd = rq->special;
-
- memset(cmd, 0, sizeof(*cmd));
+ struct request_pm_state *pm = rq->special;
+ struct ide_cmd cmd = { };
switch (pm->pm_step) {
case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
@@ -123,12 +115,12 @@ ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
return ide_stopped;
}
if (ata_id_flush_ext_enabled(drive->id))
- cmd->tf.command = ATA_CMD_FLUSH_EXT;
+ cmd.tf.command = ATA_CMD_FLUSH_EXT;
else
- cmd->tf.command = ATA_CMD_FLUSH;
+ cmd.tf.command = ATA_CMD_FLUSH;
goto out_do_tf;
case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
- cmd->tf.command = ATA_CMD_STANDBYNOW1;
+ cmd.tf.command = ATA_CMD_STANDBYNOW1;
goto out_do_tf;
case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
ide_set_max_pio(drive);
@@ -141,7 +133,7 @@ ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
ide_complete_power_step(drive, rq);
return ide_stopped;
case IDE_PM_IDLE: /* Resume step 2 (idle) */
- cmd->tf.command = ATA_CMD_IDLEIMMEDIATE;
+ cmd.tf.command = ATA_CMD_IDLEIMMEDIATE;
goto out_do_tf;
case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
/*
@@ -163,11 +155,11 @@ ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
return ide_stopped;
out_do_tf:
- cmd->valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
- cmd->valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE;
- cmd->protocol = ATA_PROT_NODATA;
+ cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
+ cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE;
+ cmd.protocol = ATA_PROT_NODATA;
- return do_rw_taskfile(drive, cmd);
+ return do_rw_taskfile(drive, &cmd);
}
/**
@@ -181,7 +173,7 @@ out_do_tf:
void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
{
struct request_queue *q = drive->queue;
- struct request_pm_state *pm = rq->data;
+ struct request_pm_state *pm = rq->special;
unsigned long flags;
ide_complete_power_step(drive, rq);
@@ -207,7 +199,7 @@ void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->data;
+ struct request_pm_state *pm = rq->special;
if (blk_pm_suspend_request(rq) &&
pm->pm_step == IDE_PM_START_SUSPEND)
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 3a53e0834cf7..4b447a8a49d4 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -131,13 +131,6 @@ enum {
IDETAPE_DIR_WRITE = (1 << 2),
};
-struct idetape_bh {
- u32 b_size;
- atomic_t b_count;
- struct idetape_bh *b_reqnext;
- char *b_data;
-};
-
/* Tape door status */
#define DOOR_UNLOCKED 0
#define DOOR_LOCKED 1
@@ -219,18 +212,12 @@ typedef struct ide_tape_obj {
/* Data buffer size chosen based on the tape's recommendation */
int buffer_size;
- /* merge buffer */
- struct idetape_bh *merge_bh;
- /* size of the merge buffer */
- int merge_bh_size;
- /* pointer to current buffer head within the merge buffer */
- struct idetape_bh *bh;
- char *b_data;
- int b_count;
-
- int pages_per_buffer;
- /* Wasted space in each stage */
- int excess_bh_size;
+ /* Staging buffer of buffer_size bytes */
+ void *buf;
+ /* The read/write cursor */
+ void *cur;
+ /* The number of valid bytes in buf */
+ size_t valid;
/* Measures average tape speed */
unsigned long avg_time;
@@ -253,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;
}
@@ -280,102 +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;
-}
-
-static int idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount)
-{
- struct idetape_bh *bh = pc->bh;
- int count;
-
- while (bcount) {
- if (bh == NULL)
- break;
- count = min(
- (unsigned int)(bh->b_size - atomic_read(&bh->b_count)),
- bcount);
- drive->hwif->tp_ops->input_data(drive, NULL, bh->b_data +
- atomic_read(&bh->b_count), count);
- bcount -= count;
- atomic_add(count, &bh->b_count);
- if (atomic_read(&bh->b_count) == bh->b_size) {
- bh = bh->b_reqnext;
- if (bh)
- atomic_set(&bh->b_count, 0);
- }
- }
-
- pc->bh = bh;
-
- return bcount;
-}
-
-static int idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount)
-{
- struct idetape_bh *bh = pc->bh;
- int count;
-
- while (bcount) {
- if (bh == NULL)
- break;
- count = min((unsigned int)pc->b_count, (unsigned int)bcount);
- drive->hwif->tp_ops->output_data(drive, NULL, pc->b_data, count);
- bcount -= count;
- pc->b_data += count;
- pc->b_count -= count;
- if (!pc->b_count) {
- bh = bh->b_reqnext;
- pc->bh = bh;
- if (bh) {
- pc->b_data = bh->b_data;
- pc->b_count = atomic_read(&bh->b_count);
- }
- }
- }
-
- return bcount;
-}
-
-static void idetape_update_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc)
-{
- struct idetape_bh *bh = pc->bh;
- int count;
- unsigned int bcount = pc->xferred;
-
- if (pc->flags & PC_FLAG_WRITING)
- return;
- while (bcount) {
- if (bh == NULL) {
- printk(KERN_ERR "ide-tape: bh == NULL in %s\n",
- __func__);
- return;
- }
- count = min((unsigned int)bh->b_size, (unsigned int)bcount);
- atomic_set(&bh->b_count, count);
- if (atomic_read(&bh->b_count) == bh->b_size)
- bh = bh->b_reqnext;
- bcount -= count;
- }
- pc->bh = bh;
-}
-
-/*
* called on each failed packet command retry to analyze the request sense. We
* currently do not utilize this information.
*/
@@ -392,12 +292,10 @@ static void idetape_analyze_error(ide_drive_t *drive, u8 *sense)
pc->c[0], tape->sense_key, tape->asc, tape->ascq);
/* Correct pc->xferred by asking the tape. */
- if (pc->flags & PC_FLAG_DMA_ERROR) {
+ if (pc->flags & PC_FLAG_DMA_ERROR)
pc->xferred = pc->req_xfer -
tape->blk_size *
get_unaligned_be32(&sense[3]);
- idetape_update_buffers(drive, pc);
- }
/*
* If error was the result of a zero-length read or write command,
@@ -436,29 +334,6 @@ static void idetape_analyze_error(ide_drive_t *drive, u8 *sense)
}
}
-/* Free data buffers completely. */
-static void ide_tape_kfree_buffer(idetape_tape_t *tape)
-{
- struct idetape_bh *prev_bh, *bh = tape->merge_bh;
-
- while (bh) {
- u32 size = bh->b_size;
-
- while (size) {
- unsigned int order = fls(size >> PAGE_SHIFT)-1;
-
- if (bh->b_data)
- free_pages((unsigned long)bh->b_data, order);
-
- size &= (order-1);
- bh->b_data += (1 << order) * PAGE_SIZE;
- }
- prev_bh = bh;
- bh = bh->b_reqnext;
- kfree(prev_bh);
- }
-}
-
static void ide_tape_handle_dsc(ide_drive_t *);
static int ide_tape_callback(ide_drive_t *drive, int dsc)
@@ -496,7 +371,7 @@ static int ide_tape_callback(ide_drive_t *drive, int dsc)
}
tape->first_frame += blocks;
- rq->current_nr_sectors -= blocks;
+ rq->resid_len -= blocks * tape->blk_size;
if (pc->error) {
uptodate = 0;
@@ -513,7 +388,8 @@ static int ide_tape_callback(ide_drive_t *drive, int dsc)
if (readpos[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);
uptodate = 0;
err = IDE_DRV_ERROR_GENERAL;
} else {
@@ -522,7 +398,8 @@ static int ide_tape_callback(ide_drive_t *drive, int dsc)
tape->partition = readpos[1];
tape->first_frame = be32_to_cpup((__be32 *)&readpos[4]);
- set_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
+ set_bit(ilog2(IDE_AFLAG_ADDRESS_VALID),
+ &drive->atapi_flags);
}
}
@@ -558,19 +435,6 @@ static void ide_tape_handle_dsc(ide_drive_t *drive)
idetape_postpone_request(drive);
}
-static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount, int write)
-{
- unsigned int bleft;
-
- if (write)
- bleft = idetape_output_buffers(drive, pc, bcount);
- else
- bleft = idetape_input_buffers(drive, pc, bcount);
-
- return bcount - bleft;
-}
-
/*
* Packet Command Interface
*
@@ -622,6 +486,8 @@ static ide_startstop_t ide_tape_issue_pc(ide_drive_t *drive,
if (pc->retries > IDETAPE_MAX_PC_RETRIES ||
(pc->flags & PC_FLAG_ABORT)) {
+ unsigned int done = blk_rq_bytes(drive->hwif->rq);
+
/*
* We will "abort" retrying a packet command in case legitimate
* error code was received (crossing a filemark, or end of the
@@ -641,8 +507,10 @@ static ide_startstop_t ide_tape_issue_pc(ide_drive_t *drive,
/* Giving up */
pc->error = IDE_DRV_ERROR_GENERAL;
}
+
drive->failed_pc = NULL;
drive->pc_callback(drive, 0);
+ ide_complete_rq(drive, -EIO, done);
return ide_stopped;
}
debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]);
@@ -695,7 +563,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive)
printk(KERN_ERR "ide-tape: %s: I/O error, ",
tape->name);
/* Retry operation */
- ide_retry_pc(drive, tape->disk);
+ ide_retry_pc(drive);
return ide_stopped;
}
pc->error = 0;
@@ -711,27 +579,22 @@ static void ide_tape_create_rw_cmd(idetape_tape_t *tape,
struct ide_atapi_pc *pc, struct request *rq,
u8 opcode)
{
- struct idetape_bh *bh = (struct idetape_bh *)rq->special;
- unsigned int length = rq->current_nr_sectors;
+ unsigned int length = blk_rq_sectors(rq);
ide_init_pc(pc);
put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]);
pc->c[1] = 1;
- pc->bh = bh;
pc->buf = NULL;
pc->buf_size = length * tape->blk_size;
pc->req_xfer = pc->buf_size;
if (pc->req_xfer == tape->buffer_size)
pc->flags |= PC_FLAG_DMA_OK;
- if (opcode == READ_6) {
+ if (opcode == READ_6)
pc->c[0] = READ_6;
- atomic_set(&bh->b_count, 0);
- } else if (opcode == WRITE_6) {
+ else if (opcode == WRITE_6) {
pc->c[0] = WRITE_6;
pc->flags |= PC_FLAG_WRITING;
- pc->b_data = bh->b_data;
- pc->b_count = atomic_read(&bh->b_count);
}
memcpy(rq->cmd, pc->c, 12);
@@ -747,12 +610,10 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
struct ide_cmd cmd;
u8 stat;
- debug_log(DBG_SENSE, "sector: %llu, nr_sectors: %lu,"
- " current_nr_sectors: %u\n",
- (unsigned long long)rq->sector, rq->nr_sectors,
- rq->current_nr_sectors);
+ debug_log(DBG_SENSE, "sector: %llu, nr_sectors: %u\n"
+ (unsigned long long)blk_rq_pos(rq), blk_rq_sectors(rq));
- if (!blk_special_request(rq)) {
+ if (!(blk_special_request(rq) || blk_sense_request(rq))) {
/* We do not support buffer cache originated requests. */
printk(KERN_NOTICE "ide-tape: %s: Unsupported request in "
"request queue (%d)\n", drive->name, rq->cmd_type);
@@ -788,15 +649,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;
@@ -816,7 +677,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);
@@ -828,7 +691,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
goto out;
}
if (rq->cmd[13] & REQ_IDETAPE_PC1) {
- pc = (struct ide_atapi_pc *) rq->buffer;
+ pc = (struct ide_atapi_pc *)rq->special;
rq->cmd[13] &= ~(REQ_IDETAPE_PC1);
rq->cmd[13] |= REQ_IDETAPE_PC2;
goto out;
@@ -840,6 +703,9 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
BUG();
out:
+ /* prepare sense request for this command */
+ ide_prep_sense(drive, rq);
+
memset(&cmd, 0, sizeof(cmd));
if (rq_data_dir(rq))
@@ -847,167 +713,10 @@ out:
cmd.rq = rq;
- return ide_tape_issue_pc(drive, &cmd, pc);
-}
-
-/*
- * The function below uses __get_free_pages to allocate a data buffer of size
- * tape->buffer_size (or a bit more). We attempt to combine sequential pages as
- * much as possible.
- *
- * It returns a pointer to the newly allocated buffer, or NULL in case of
- * failure.
- */
-static struct idetape_bh *ide_tape_kmalloc_buffer(idetape_tape_t *tape,
- int full, int clear)
-{
- struct idetape_bh *prev_bh, *bh, *merge_bh;
- int pages = tape->pages_per_buffer;
- unsigned int order, b_allocd;
- char *b_data = NULL;
-
- merge_bh = kmalloc(sizeof(struct idetape_bh), GFP_KERNEL);
- bh = merge_bh;
- if (bh == NULL)
- goto abort;
-
- order = fls(pages) - 1;
- bh->b_data = (char *) __get_free_pages(GFP_KERNEL, order);
- if (!bh->b_data)
- goto abort;
- b_allocd = (1 << order) * PAGE_SIZE;
- pages &= (order-1);
-
- if (clear)
- memset(bh->b_data, 0, b_allocd);
- bh->b_reqnext = NULL;
- bh->b_size = b_allocd;
- atomic_set(&bh->b_count, full ? bh->b_size : 0);
-
- while (pages) {
- order = fls(pages) - 1;
- b_data = (char *) __get_free_pages(GFP_KERNEL, order);
- if (!b_data)
- goto abort;
- b_allocd = (1 << order) * PAGE_SIZE;
-
- if (clear)
- memset(b_data, 0, b_allocd);
-
- /* newly allocated page frames below buffer header or ...*/
- if (bh->b_data == b_data + b_allocd) {
- bh->b_size += b_allocd;
- bh->b_data -= b_allocd;
- if (full)
- atomic_add(b_allocd, &bh->b_count);
- continue;
- }
- /* they are above the header */
- if (b_data == bh->b_data + bh->b_size) {
- bh->b_size += b_allocd;
- if (full)
- atomic_add(b_allocd, &bh->b_count);
- continue;
- }
- prev_bh = bh;
- bh = kmalloc(sizeof(struct idetape_bh), GFP_KERNEL);
- if (!bh) {
- free_pages((unsigned long) b_data, order);
- goto abort;
- }
- bh->b_reqnext = NULL;
- bh->b_data = b_data;
- bh->b_size = b_allocd;
- atomic_set(&bh->b_count, full ? bh->b_size : 0);
- prev_bh->b_reqnext = bh;
-
- pages &= (order-1);
- }
-
- bh->b_size -= tape->excess_bh_size;
- if (full)
- atomic_sub(tape->excess_bh_size, &bh->b_count);
- return merge_bh;
-abort:
- ide_tape_kfree_buffer(tape);
- return NULL;
-}
-
-static int idetape_copy_stage_from_user(idetape_tape_t *tape,
- const char __user *buf, int n)
-{
- struct idetape_bh *bh = tape->bh;
- int count;
- int ret = 0;
-
- while (n) {
- if (bh == NULL) {
- printk(KERN_ERR "ide-tape: bh == NULL in %s\n",
- __func__);
- return 1;
- }
- count = min((unsigned int)
- (bh->b_size - atomic_read(&bh->b_count)),
- (unsigned int)n);
- if (copy_from_user(bh->b_data + atomic_read(&bh->b_count), buf,
- count))
- ret = 1;
- n -= count;
- atomic_add(count, &bh->b_count);
- buf += count;
- if (atomic_read(&bh->b_count) == bh->b_size) {
- bh = bh->b_reqnext;
- if (bh)
- atomic_set(&bh->b_count, 0);
- }
- }
- tape->bh = bh;
- return ret;
-}
+ ide_init_sg_cmd(&cmd, pc->req_xfer);
+ ide_map_sg(drive, &cmd);
-static int idetape_copy_stage_to_user(idetape_tape_t *tape, char __user *buf,
- int n)
-{
- struct idetape_bh *bh = tape->bh;
- int count;
- int ret = 0;
-
- while (n) {
- if (bh == NULL) {
- printk(KERN_ERR "ide-tape: bh == NULL in %s\n",
- __func__);
- return 1;
- }
- count = min(tape->b_count, n);
- if (copy_to_user(buf, tape->b_data, count))
- ret = 1;
- n -= count;
- tape->b_data += count;
- tape->b_count -= count;
- buf += count;
- if (!tape->b_count) {
- bh = bh->b_reqnext;
- tape->bh = bh;
- if (bh) {
- tape->b_data = bh->b_data;
- tape->b_count = atomic_read(&bh->b_count);
- }
- }
- }
- return ret;
-}
-
-static void idetape_init_merge_buffer(idetape_tape_t *tape)
-{
- struct idetape_bh *bh = tape->merge_bh;
- tape->bh = tape->merge_bh;
-
- if (tape->chrdev_dir == IDETAPE_DIR_WRITE)
- atomic_set(&bh->b_count, 0);
- else {
- tape->b_data = bh->b_data;
- tape->b_count = atomic_read(&bh->b_count);
- }
+ return ide_tape_issue_pc(drive, &cmd, pc);
}
/*
@@ -1030,7 +739,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)
@@ -1106,11 +815,11 @@ 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);
- tape->merge_bh_size = 0;
- if (tape->merge_bh != NULL) {
- ide_tape_kfree_buffer(tape);
- tape->merge_bh = NULL;
+ clear_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags);
+ tape->valid = 0;
+ if (tape->buf != NULL) {
+ kfree(tape->buf);
+ tape->buf = NULL;
}
tape->chrdev_dir = IDETAPE_DIR_NONE;
@@ -1164,36 +873,44 @@ static void ide_tape_discard_merge_buffer(ide_drive_t *drive,
* Generate a read/write request for the block device interface and wait for it
* to be serviced.
*/
-static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int blocks,
- struct idetape_bh *bh)
+static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int size)
{
idetape_tape_t *tape = drive->driver_data;
struct request *rq;
- int ret, errors;
+ int ret;
debug_log(DBG_SENSE, "%s: cmd=%d\n", __func__, cmd);
+ BUG_ON(cmd != REQ_IDETAPE_READ && cmd != REQ_IDETAPE_WRITE);
+ BUG_ON(size < 0 || size % tape->blk_size);
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_SPECIAL;
rq->cmd[13] = cmd;
rq->rq_disk = tape->disk;
- rq->special = (void *)bh;
- rq->sector = tape->first_frame;
- rq->nr_sectors = blocks;
- rq->current_nr_sectors = blocks;
- blk_execute_rq(drive->queue, tape->disk, rq, 0);
+ rq->__sector = tape->first_frame;
- errors = rq->errors;
- ret = tape->blk_size * (blocks - rq->current_nr_sectors);
- blk_put_request(rq);
+ if (size) {
+ ret = blk_rq_map_kern(drive->queue, rq, tape->buf, size,
+ __GFP_WAIT);
+ if (ret)
+ goto out_put;
+ }
- if ((cmd & (REQ_IDETAPE_READ | REQ_IDETAPE_WRITE)) == 0)
- return 0;
+ blk_execute_rq(drive->queue, tape->disk, rq, 0);
- if (tape->merge_bh)
- idetape_init_merge_buffer(tape);
- if (errors == IDE_DRV_ERROR_GENERAL)
- return -EIO;
+ /* calculate the number of transferred bytes and update buffer state */
+ size -= rq->resid_len;
+ tape->cur = tape->buf;
+ if (cmd == REQ_IDETAPE_READ)
+ tape->valid = size;
+ else
+ tape->valid = 0;
+
+ ret = size;
+ if (rq->errors == IDE_DRV_ERROR_GENERAL)
+ ret = -EIO;
+out_put:
+ blk_put_request(rq);
return ret;
}
@@ -1230,153 +947,87 @@ static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd)
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
}
-/* Queue up a character device originated write request. */
-static int idetape_add_chrdev_write_request(ide_drive_t *drive, int blocks)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- debug_log(DBG_CHRDEV, "Enter %s\n", __func__);
-
- return idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE,
- blocks, tape->merge_bh);
-}
-
static void ide_tape_flush_merge_buffer(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
- int blocks, min;
- struct idetape_bh *bh;
if (tape->chrdev_dir != IDETAPE_DIR_WRITE) {
printk(KERN_ERR "ide-tape: bug: Trying to empty merge buffer"
" but we are not writing.\n");
return;
}
- if (tape->merge_bh_size > tape->buffer_size) {
- printk(KERN_ERR "ide-tape: bug: merge_buffer too big\n");
- tape->merge_bh_size = tape->buffer_size;
- }
- if (tape->merge_bh_size) {
- blocks = tape->merge_bh_size / tape->blk_size;
- if (tape->merge_bh_size % tape->blk_size) {
- unsigned int i;
-
- blocks++;
- i = tape->blk_size - tape->merge_bh_size %
- tape->blk_size;
- bh = tape->bh->b_reqnext;
- while (bh) {
- atomic_set(&bh->b_count, 0);
- bh = bh->b_reqnext;
- }
- bh = tape->bh;
- while (i) {
- if (bh == NULL) {
- printk(KERN_INFO "ide-tape: bug,"
- " bh NULL\n");
- break;
- }
- min = min(i, (unsigned int)(bh->b_size -
- atomic_read(&bh->b_count)));
- memset(bh->b_data + atomic_read(&bh->b_count),
- 0, min);
- atomic_add(min, &bh->b_count);
- i -= min;
- bh = bh->b_reqnext;
- }
- }
- (void) idetape_add_chrdev_write_request(drive, blocks);
- tape->merge_bh_size = 0;
- }
- if (tape->merge_bh != NULL) {
- ide_tape_kfree_buffer(tape);
- tape->merge_bh = NULL;
+ if (tape->buf) {
+ size_t aligned = roundup(tape->valid, tape->blk_size);
+
+ memset(tape->cur, 0, aligned - tape->valid);
+ idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, aligned);
+ kfree(tape->buf);
+ tape->buf = NULL;
}
tape->chrdev_dir = IDETAPE_DIR_NONE;
}
-static int idetape_init_read(ide_drive_t *drive)
+static int idetape_init_rw(ide_drive_t *drive, int dir)
{
idetape_tape_t *tape = drive->driver_data;
- int bytes_read;
-
- /* Initialize read operation */
- if (tape->chrdev_dir != IDETAPE_DIR_READ) {
- if (tape->chrdev_dir == IDETAPE_DIR_WRITE) {
- ide_tape_flush_merge_buffer(drive);
- idetape_flush_tape_buffers(drive);
- }
- if (tape->merge_bh || tape->merge_bh_size) {
- printk(KERN_ERR "ide-tape: merge_bh_size should be"
- " 0 now\n");
- tape->merge_bh_size = 0;
- }
- tape->merge_bh = ide_tape_kmalloc_buffer(tape, 0, 0);
- if (!tape->merge_bh)
- return -ENOMEM;
- tape->chrdev_dir = IDETAPE_DIR_READ;
+ int rc;
- /*
- * Issue a read 0 command to ensure that DSC handshake is
- * switched from completion mode to buffer available mode.
- * No point in issuing this if DSC overlap isn't supported, some
- * drives (Seagate STT3401A) will return an error.
- */
- if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) {
- bytes_read = idetape_queue_rw_tail(drive,
- REQ_IDETAPE_READ, 0,
- tape->merge_bh);
- if (bytes_read < 0) {
- ide_tape_kfree_buffer(tape);
- tape->merge_bh = NULL;
- tape->chrdev_dir = IDETAPE_DIR_NONE;
- return bytes_read;
- }
- }
- }
+ BUG_ON(dir != IDETAPE_DIR_READ && dir != IDETAPE_DIR_WRITE);
- return 0;
-}
+ if (tape->chrdev_dir == dir)
+ return 0;
-/* called from idetape_chrdev_read() to service a chrdev read request. */
-static int idetape_add_chrdev_read_request(ide_drive_t *drive, int blocks)
-{
- idetape_tape_t *tape = drive->driver_data;
+ if (tape->chrdev_dir == IDETAPE_DIR_READ)
+ ide_tape_discard_merge_buffer(drive, 1);
+ else if (tape->chrdev_dir == IDETAPE_DIR_WRITE) {
+ ide_tape_flush_merge_buffer(drive);
+ idetape_flush_tape_buffers(drive);
+ }
- debug_log(DBG_PROCS, "Enter %s, %d blocks\n", __func__, blocks);
+ if (tape->buf || tape->valid) {
+ printk(KERN_ERR "ide-tape: valid should be 0 now\n");
+ tape->valid = 0;
+ }
- /* If we are at a filemark, return a read length of 0 */
- if (test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
- return 0;
+ tape->buf = kmalloc(tape->buffer_size, GFP_KERNEL);
+ if (!tape->buf)
+ return -ENOMEM;
+ tape->chrdev_dir = dir;
+ tape->cur = tape->buf;
- idetape_init_read(drive);
+ /*
+ * Issue a 0 rw command to ensure that DSC handshake is
+ * switched from completion mode to buffer available mode. No
+ * point in issuing this if DSC overlap isn't supported, some
+ * drives (Seagate STT3401A) will return an error.
+ */
+ if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) {
+ int cmd = dir == IDETAPE_DIR_READ ? REQ_IDETAPE_READ
+ : REQ_IDETAPE_WRITE;
+
+ rc = idetape_queue_rw_tail(drive, cmd, 0);
+ if (rc < 0) {
+ kfree(tape->buf);
+ tape->buf = NULL;
+ tape->chrdev_dir = IDETAPE_DIR_NONE;
+ return rc;
+ }
+ }
- return idetape_queue_rw_tail(drive, REQ_IDETAPE_READ, blocks,
- tape->merge_bh);
+ return 0;
}
static void idetape_pad_zeros(ide_drive_t *drive, int bcount)
{
idetape_tape_t *tape = drive->driver_data;
- struct idetape_bh *bh;
- int blocks;
+
+ memset(tape->buf, 0, tape->buffer_size);
while (bcount) {
- unsigned int count;
+ unsigned int count = min(tape->buffer_size, bcount);
- bh = tape->merge_bh;
- count = min(tape->buffer_size, bcount);
+ idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, count);
bcount -= count;
- blocks = count / tape->blk_size;
- while (count) {
- atomic_set(&bh->b_count,
- min(count, (unsigned int)bh->b_size));
- memset(bh->b_data, 0, atomic_read(&bh->b_count));
- count -= atomic_read(&bh->b_count);
- bh = bh->b_reqnext;
- }
- idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, blocks,
- tape->merge_bh);
}
}
@@ -1456,8 +1107,9 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op,
}
if (tape->chrdev_dir == IDETAPE_DIR_READ) {
- tape->merge_bh_size = 0;
- if (test_and_clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
+ tape->valid = 0;
+ if (test_and_clear_bit(ilog2(IDE_AFLAG_FILEMARK),
+ &drive->atapi_flags))
++count;
ide_tape_discard_merge_buffer(drive, 0);
}
@@ -1505,61 +1157,56 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
{
struct ide_tape_obj *tape = file->private_data;
ide_drive_t *drive = tape->drive;
- ssize_t bytes_read, temp, actually_read = 0, rc;
+ size_t done = 0;
ssize_t ret = 0;
- u16 ctl = *(u16 *)&tape->caps[12];
+ int rc;
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;
}
- rc = idetape_init_read(drive);
+
+ rc = idetape_init_rw(drive, IDETAPE_DIR_READ);
if (rc < 0)
return rc;
- if (count == 0)
- return (0);
- if (tape->merge_bh_size) {
- actually_read = min((unsigned int)(tape->merge_bh_size),
- (unsigned int)count);
- if (idetape_copy_stage_to_user(tape, buf, actually_read))
- ret = -EFAULT;
- buf += actually_read;
- tape->merge_bh_size -= actually_read;
- count -= actually_read;
- }
- while (count >= tape->buffer_size) {
- bytes_read = idetape_add_chrdev_read_request(drive, ctl);
- if (bytes_read <= 0)
- goto finish;
- if (idetape_copy_stage_to_user(tape, buf, bytes_read))
- ret = -EFAULT;
- buf += bytes_read;
- count -= bytes_read;
- actually_read += bytes_read;
- }
- if (count) {
- bytes_read = idetape_add_chrdev_read_request(drive, ctl);
- if (bytes_read <= 0)
- goto finish;
- temp = min((unsigned long)count, (unsigned long)bytes_read);
- if (idetape_copy_stage_to_user(tape, buf, temp))
+
+ while (done < count) {
+ size_t todo;
+
+ /* refill if staging buffer is empty */
+ if (!tape->valid) {
+ /* If we are at a filemark, nothing more to read */
+ if (test_bit(ilog2(IDE_AFLAG_FILEMARK),
+ &drive->atapi_flags))
+ break;
+ /* read */
+ if (idetape_queue_rw_tail(drive, REQ_IDETAPE_READ,
+ tape->buffer_size) <= 0)
+ break;
+ }
+
+ /* copy out */
+ todo = min_t(size_t, count - done, tape->valid);
+ if (copy_to_user(buf + done, tape->cur, todo))
ret = -EFAULT;
- actually_read += temp;
- tape->merge_bh_size = bytes_read-temp;
+
+ tape->cur += todo;
+ tape->valid -= todo;
+ done += todo;
}
-finish:
- if (!actually_read && 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);
return 0;
}
- return ret ? ret : actually_read;
+ return ret ? ret : done;
}
static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf,
@@ -1567,9 +1214,9 @@ static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf,
{
struct ide_tape_obj *tape = file->private_data;
ide_drive_t *drive = tape->drive;
- ssize_t actually_written = 0;
+ size_t done = 0;
ssize_t ret = 0;
- u16 ctl = *(u16 *)&tape->caps[12];
+ int rc;
/* The drive is write protected. */
if (tape->write_prot)
@@ -1578,80 +1225,31 @@ static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf,
debug_log(DBG_CHRDEV, "Enter %s, count %Zd\n", __func__, count);
/* Initialize write operation */
- if (tape->chrdev_dir != IDETAPE_DIR_WRITE) {
- if (tape->chrdev_dir == IDETAPE_DIR_READ)
- ide_tape_discard_merge_buffer(drive, 1);
- if (tape->merge_bh || tape->merge_bh_size) {
- printk(KERN_ERR "ide-tape: merge_bh_size "
- "should be 0 now\n");
- tape->merge_bh_size = 0;
- }
- tape->merge_bh = ide_tape_kmalloc_buffer(tape, 0, 0);
- if (!tape->merge_bh)
- return -ENOMEM;
- tape->chrdev_dir = IDETAPE_DIR_WRITE;
- idetape_init_merge_buffer(tape);
+ rc = idetape_init_rw(drive, IDETAPE_DIR_WRITE);
+ if (rc < 0)
+ return rc;
- /*
- * Issue a write 0 command to ensure that DSC handshake is
- * switched from completion mode to buffer available mode. No
- * point in issuing this if DSC overlap isn't supported, some
- * drives (Seagate STT3401A) will return an error.
- */
- if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) {
- ssize_t retval = idetape_queue_rw_tail(drive,
- REQ_IDETAPE_WRITE, 0,
- tape->merge_bh);
- if (retval < 0) {
- ide_tape_kfree_buffer(tape);
- tape->merge_bh = NULL;
- tape->chrdev_dir = IDETAPE_DIR_NONE;
- return retval;
- }
- }
- }
- if (count == 0)
- return (0);
- if (tape->merge_bh_size) {
- if (tape->merge_bh_size >= tape->buffer_size) {
- printk(KERN_ERR "ide-tape: bug: merge buf too big\n");
- tape->merge_bh_size = 0;
- }
- actually_written = min((unsigned int)
- (tape->buffer_size - tape->merge_bh_size),
- (unsigned int)count);
- if (idetape_copy_stage_from_user(tape, buf, actually_written))
- ret = -EFAULT;
- buf += actually_written;
- tape->merge_bh_size += actually_written;
- count -= actually_written;
-
- if (tape->merge_bh_size == tape->buffer_size) {
- ssize_t retval;
- tape->merge_bh_size = 0;
- retval = idetape_add_chrdev_write_request(drive, ctl);
- if (retval <= 0)
- return (retval);
- }
- }
- while (count >= tape->buffer_size) {
- ssize_t retval;
- if (idetape_copy_stage_from_user(tape, buf, tape->buffer_size))
- ret = -EFAULT;
- buf += tape->buffer_size;
- count -= tape->buffer_size;
- retval = idetape_add_chrdev_write_request(drive, ctl);
- actually_written += tape->buffer_size;
- if (retval <= 0)
- return (retval);
- }
- if (count) {
- actually_written += count;
- if (idetape_copy_stage_from_user(tape, buf, count))
+ while (done < count) {
+ size_t todo;
+
+ /* flush if staging buffer is full */
+ if (tape->valid == tape->buffer_size &&
+ idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE,
+ tape->buffer_size) <= 0)
+ return rc;
+
+ /* copy in */
+ todo = min_t(size_t, count - done,
+ tape->buffer_size - tape->valid);
+ if (copy_from_user(tape->cur, buf + done, todo))
ret = -EFAULT;
- tape->merge_bh_size += count;
+
+ tape->cur += todo;
+ tape->valid += todo;
+ done += todo;
}
- return ret ? ret : actually_written;
+
+ return ret ? ret : done;
}
static int idetape_write_filemark(ide_drive_t *drive)
@@ -1735,7 +1333,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);
@@ -1757,9 +1356,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);
@@ -1812,7 +1413,7 @@ static int idetape_chrdev_ioctl(struct inode *inode, struct file *file,
idetape_flush_tape_buffers(drive);
}
if (cmd == MTIOCGET || cmd == MTIOCPOS) {
- block_offset = tape->merge_bh_size /
+ block_offset = tape->valid /
(tape->blk_size * tape->user_bs_factor);
position = idetape_read_position(drive);
if (position < 0)
@@ -1885,7 +1486,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;
@@ -1904,20 +1505,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;
}
idetape_read_position(drive);
- if (!test_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags))
+ 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. */
@@ -1933,7 +1534,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;
}
@@ -1960,12 +1561,12 @@ static void idetape_write_release(ide_drive_t *drive, unsigned int minor)
idetape_tape_t *tape = drive->driver_data;
ide_tape_flush_merge_buffer(drive);
- tape->merge_bh = ide_tape_kmalloc_buffer(tape, 1, 0);
- if (tape->merge_bh != NULL) {
+ tape->buf = kmalloc(tape->buffer_size, GFP_KERNEL);
+ if (tape->buf != NULL) {
idetape_pad_zeros(drive, tape->blk_size *
(tape->user_bs_factor - 1));
- ide_tape_kfree_buffer(tape);
- tape->merge_bh = NULL;
+ kfree(tape->buf);
+ tape->buf = NULL;
}
idetape_write_filemark(drive);
idetape_flush_tape_buffers(drive);
@@ -1990,15 +1591,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;
@@ -2159,8 +1762,6 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor)
u16 *ctl = (u16 *)&tape->caps[12];
drive->pc_callback = ide_tape_callback;
- drive->pc_update_buffers = idetape_update_buffers;
- drive->pc_io_buffers = ide_tape_io_buffers;
drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
@@ -2191,11 +1792,6 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor)
tape->buffer_size = *ctl * tape->blk_size;
}
buffer_size = tape->buffer_size;
- tape->pages_per_buffer = buffer_size / PAGE_SIZE;
- if (buffer_size % PAGE_SIZE) {
- tape->pages_per_buffer++;
- tape->excess_bh_size = PAGE_SIZE - buffer_size % PAGE_SIZE;
- }
/* select the "best" DSC read/write polling freq */
speed = max(*(u16 *)&tape->caps[14], *(u16 *)&tape->caps[8]);
@@ -2238,7 +1834,7 @@ static void ide_tape_release(struct device *dev)
ide_drive_t *drive = tape->drive;
struct gendisk *g = tape->disk;
- BUG_ON(tape->merge_bh_size);
+ BUG_ON(tape->valid);
drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
drive->driver_data = NULL;
@@ -2311,7 +1907,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 4aa6223c11be..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) {
@@ -385,7 +384,7 @@ out_end:
if ((cmd->tf_flags & IDE_TFLAG_FS) == 0)
ide_finish_cmd(drive, cmd, stat);
else
- ide_complete_rq(drive, 0, cmd->rq->nr_sectors << 9);
+ ide_complete_rq(drive, 0, blk_rq_sectors(cmd->rq) << 9);
return ide_stopped;
out_err:
ide_error_cmd(drive, cmd);
@@ -424,7 +423,9 @@ int ide_raw_taskfile(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf,
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
- rq->buffer = buf;
+
+ if (cmd->tf_flags & IDE_TFLAG_WRITE)
+ rq->cmd_flags |= REQ_RW;
/*
* (ks) We transfer currently only whole sectors.
@@ -432,18 +433,20 @@ int ide_raw_taskfile(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf,
* if we would find a solution to transfer any size.
* To support special commands like READ LONG.
*/
- rq->hard_nr_sectors = rq->nr_sectors = nsect;
- rq->hard_cur_sectors = rq->current_nr_sectors = nsect;
-
- if (cmd->tf_flags & IDE_TFLAG_WRITE)
- rq->cmd_flags |= REQ_RW;
+ if (nsect) {
+ error = blk_rq_map_kern(drive->queue, rq, buf,
+ nsect * SECTOR_SIZE, __GFP_WAIT);
+ if (error)
+ goto put_req;
+ }
rq->special = cmd;
cmd->rq = rq;
error = blk_execute_rq(drive->queue, NULL, rq, 0);
- blk_put_request(rq);
+put_req:
+ blk_put_request(rq);
return error;
}
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 248a54bd2386..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)
@@ -177,7 +152,7 @@ static void pdc202xx_dma_start(ide_drive_t *drive)
u8 clock = inb(high_16 + 0x11);
outb(clock | (hwif->channel ? 0x08 : 0x02), high_16 + 0x11);
- word_count = (rq->nr_sectors << 8);
+ word_count = (blk_rq_sectors(rq) << 8);
word_count = (rq_data_dir(rq) == READ) ?
word_count | 0x05000000 :
word_count | 0x06000000;
@@ -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/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/tc86c001.c b/drivers/ide/tc86c001.c
index b4cf42dc8a6f..05a93d6baecc 100644
--- a/drivers/ide/tc86c001.c
+++ b/drivers/ide/tc86c001.c
@@ -112,7 +112,7 @@ static void tc86c001_dma_start(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
unsigned long sc_base = hwif->config_data;
unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04);
- unsigned long nsectors = hwif->rq->nr_sectors;
+ unsigned long nsectors = blk_rq_sectors(hwif->rq);
/*
* We have to manually load the sector count and size into
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 564422d23976..64b58ecc3f0e 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -307,7 +307,7 @@ static int tx4939ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
tx4939ide_writew(SECTOR_SIZE / 2, base, drive->dn ?
TX4939IDE_Xfer_Cnt_2 : TX4939IDE_Xfer_Cnt_1);
- tx4939ide_writew(cmd->rq->nr_sectors, base, TX4939IDE_Sec_Cnt);
+ tx4939ide_writew(blk_rq_sectors(cmd->rq), base, TX4939IDE_Sec_Cnt);
return 0;
}
@@ -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/amso1100/c2_cq.c b/drivers/infiniband/hw/amso1100/c2_cq.c
index bb17cce3cb59..f5c45b194f53 100644
--- a/drivers/infiniband/hw/amso1100/c2_cq.c
+++ b/drivers/infiniband/hw/amso1100/c2_cq.c
@@ -133,7 +133,7 @@ static inline int c2_poll_one(struct c2_dev *c2dev,
struct c2_qp *qp;
int is_recv = 0;
- ce = (struct c2wr_ce *) c2_mq_consume(&cq->mq);
+ ce = c2_mq_consume(&cq->mq);
if (!ce) {
return -EAGAIN;
}
@@ -146,7 +146,7 @@ static inline int c2_poll_one(struct c2_dev *c2dev,
while ((qp =
(struct c2_qp *) (unsigned long) ce->qp_user_context) == NULL) {
c2_mq_free(&cq->mq);
- ce = (struct c2wr_ce *) c2_mq_consume(&cq->mq);
+ ce = c2_mq_consume(&cq->mq);
if (!ce)
return -EAGAIN;
}
diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h
index ff9be1a13106..32e3b1461d81 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_wr.h
+++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h
@@ -176,7 +176,7 @@ struct t3_send_wr {
struct t3_sge sgl[T3_MAX_SGE]; /* 4+ */
};
-#define T3_MAX_FASTREG_DEPTH 24
+#define T3_MAX_FASTREG_DEPTH 10
#define T3_MAX_FASTREG_FRAG 10
struct t3_fastreg_wr {
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 160ef482712d..e2a63214008a 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -40,6 +40,7 @@
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
+#include <linux/inetdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -1152,12 +1153,39 @@ static int iwch_query_device(struct ib_device *ibdev,
static int iwch_query_port(struct ib_device *ibdev,
u8 port, struct ib_port_attr *props)
{
+ struct iwch_dev *dev;
+ struct net_device *netdev;
+ struct in_device *inetdev;
+
PDBG("%s ibdev %p\n", __func__, ibdev);
+ dev = to_iwch_dev(ibdev);
+ netdev = dev->rdev.port_info.lldevs[port-1];
+
memset(props, 0, sizeof(struct ib_port_attr));
props->max_mtu = IB_MTU_4096;
- props->active_mtu = IB_MTU_2048;
- props->state = IB_PORT_ACTIVE;
+ if (netdev->mtu >= 4096)
+ props->active_mtu = IB_MTU_4096;
+ else if (netdev->mtu >= 2048)
+ props->active_mtu = IB_MTU_2048;
+ else if (netdev->mtu >= 1024)
+ props->active_mtu = IB_MTU_1024;
+ else if (netdev->mtu >= 512)
+ props->active_mtu = IB_MTU_512;
+ else
+ props->active_mtu = IB_MTU_256;
+
+ if (!netif_carrier_ok(netdev))
+ props->state = IB_PORT_DOWN;
+ else {
+ inetdev = in_dev_get(netdev);
+ if (inetdev->ifa_list)
+ props->state = IB_PORT_ACTIVE;
+ else
+ props->state = IB_PORT_INIT;
+ in_dev_put(inetdev);
+ }
+
props->port_cap_flags =
IB_PORT_CM_SUP |
IB_PORT_SNMP_TUNNEL_SUP |
diff --git a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h b/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h
index 1798e6466bd0..689c35786dd2 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h
@@ -165,7 +165,6 @@ struct hcp_modify_qp_control_block {
#define MQPCB_MASK_ALT_P_KEY_IDX EHCA_BMASK_IBM( 7, 7)
#define MQPCB_MASK_RDMA_ATOMIC_CTRL EHCA_BMASK_IBM( 8, 8)
#define MQPCB_MASK_QP_STATE EHCA_BMASK_IBM( 9, 9)
-#define MQPCB_QP_STATE EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES EHCA_BMASK_IBM(11, 11)
#define MQPCB_MASK_PATH_MIGRATION_STATE EHCA_BMASK_IBM(12, 12)
#define MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP EHCA_BMASK_IBM(13, 13)
@@ -176,60 +175,33 @@ struct hcp_modify_qp_control_block {
#define MQPCB_MASK_RETRY_COUNT EHCA_BMASK_IBM(18, 18)
#define MQPCB_MASK_TIMEOUT EHCA_BMASK_IBM(19, 19)
#define MQPCB_MASK_PATH_MTU EHCA_BMASK_IBM(20, 20)
-#define MQPCB_PATH_MTU EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_MAX_STATIC_RATE EHCA_BMASK_IBM(21, 21)
-#define MQPCB_MAX_STATIC_RATE EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_DLID EHCA_BMASK_IBM(22, 22)
-#define MQPCB_DLID EHCA_BMASK_IBM(16, 31)
#define MQPCB_MASK_RNR_RETRY_COUNT EHCA_BMASK_IBM(23, 23)
-#define MQPCB_RNR_RETRY_COUNT EHCA_BMASK_IBM(29, 31)
#define MQPCB_MASK_SOURCE_PATH_BITS EHCA_BMASK_IBM(24, 24)
-#define MQPCB_SOURCE_PATH_BITS EHCA_BMASK_IBM(25, 31)
#define MQPCB_MASK_TRAFFIC_CLASS EHCA_BMASK_IBM(25, 25)
-#define MQPCB_TRAFFIC_CLASS EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_HOP_LIMIT EHCA_BMASK_IBM(26, 26)
-#define MQPCB_HOP_LIMIT EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_SOURCE_GID_IDX EHCA_BMASK_IBM(27, 27)
-#define MQPCB_SOURCE_GID_IDX EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_FLOW_LABEL EHCA_BMASK_IBM(28, 28)
-#define MQPCB_FLOW_LABEL EHCA_BMASK_IBM(12, 31)
#define MQPCB_MASK_DEST_GID EHCA_BMASK_IBM(30, 30)
#define MQPCB_MASK_SERVICE_LEVEL_AL EHCA_BMASK_IBM(31, 31)
-#define MQPCB_SERVICE_LEVEL_AL EHCA_BMASK_IBM(28, 31)
#define MQPCB_MASK_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(32, 32)
-#define MQPCB_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(31, 31)
#define MQPCB_MASK_RETRY_COUNT_AL EHCA_BMASK_IBM(33, 33)
-#define MQPCB_RETRY_COUNT_AL EHCA_BMASK_IBM(29, 31)
#define MQPCB_MASK_TIMEOUT_AL EHCA_BMASK_IBM(34, 34)
-#define MQPCB_TIMEOUT_AL EHCA_BMASK_IBM(27, 31)
#define MQPCB_MASK_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(35, 35)
-#define MQPCB_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_DLID_AL EHCA_BMASK_IBM(36, 36)
-#define MQPCB_DLID_AL EHCA_BMASK_IBM(16, 31)
#define MQPCB_MASK_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(37, 37)
-#define MQPCB_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(29, 31)
#define MQPCB_MASK_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(38, 38)
-#define MQPCB_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(25, 31)
#define MQPCB_MASK_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(39, 39)
-#define MQPCB_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_HOP_LIMIT_AL EHCA_BMASK_IBM(40, 40)
-#define MQPCB_HOP_LIMIT_AL EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(41, 41)
-#define MQPCB_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(24, 31)
#define MQPCB_MASK_FLOW_LABEL_AL EHCA_BMASK_IBM(42, 42)
-#define MQPCB_FLOW_LABEL_AL EHCA_BMASK_IBM(12, 31)
#define MQPCB_MASK_DEST_GID_AL EHCA_BMASK_IBM(44, 44)
#define MQPCB_MASK_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(45, 45)
-#define MQPCB_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(16, 31)
#define MQPCB_MASK_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(46, 46)
-#define MQPCB_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(16, 31)
#define MQPCB_MASK_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(47, 47)
-#define MQPCB_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(31, 31)
-#define MQPCB_QP_NUMBER EHCA_BMASK_IBM( 8, 31)
#define MQPCB_MASK_QP_ENABLE EHCA_BMASK_IBM(48, 48)
-#define MQPCB_QP_ENABLE EHCA_BMASK_IBM(31, 31)
#define MQPCB_MASK_CURR_SRQ_LIMIT EHCA_BMASK_IBM(49, 49)
-#define MQPCB_CURR_SRQ_LIMIT EHCA_BMASK_IBM(16, 31)
#define MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG EHCA_BMASK_IBM(50, 50)
#define MQPCB_MASK_SHARED_RQ_HNDL EHCA_BMASK_IBM(51, 51)
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 99bcbd7ffb0a..4b89b791be6a 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -479,13 +479,13 @@ void ehca_tasklet_neq(unsigned long data)
struct ehca_eqe *eqe;
u64 ret;
- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+ eqe = ehca_poll_eq(shca, &shca->neq);
while (eqe) {
if (!EHCA_BMASK_GET(NEQE_COMPLETION_EVENT, eqe->entry))
parse_ec(shca, eqe->entry);
- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+ eqe = ehca_poll_eq(shca, &shca->neq);
}
ret = hipz_h_reset_event(shca->ipz_hca_handle,
@@ -572,8 +572,7 @@ void ehca_process_eq(struct ehca_shca *shca, int is_irq)
eqe_cnt = 0;
do {
u32 token;
- eqe_cache[eqe_cnt].eqe =
- (struct ehca_eqe *)ehca_poll_eq(shca, eq);
+ eqe_cache[eqe_cnt].eqe = ehca_poll_eq(shca, eq);
if (!eqe_cache[eqe_cnt].eqe)
break;
eqe_value = eqe_cache[eqe_cnt].eqe->entry;
@@ -637,7 +636,7 @@ void ehca_process_eq(struct ehca_shca *shca, int is_irq)
goto unlock_irq_spinlock;
do {
struct ehca_eqe *eqe;
- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq);
+ eqe = ehca_poll_eq(shca, &shca->eq);
if (!eqe)
break;
process_eqe(shca, eqe);
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 368311ce332b..85905ab9391f 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -52,7 +52,7 @@
#include "ehca_tools.h"
#include "hcp_if.h"
-#define HCAD_VERSION "0026"
+#define HCAD_VERSION "0027"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index 00c108159714..0338f1fabe8a 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -461,7 +461,7 @@ static struct ehca_qp *internal_create_qp(
ib_device);
struct ib_ucontext *context = NULL;
u64 h_ret;
- int is_llqp = 0, has_srq = 0;
+ int is_llqp = 0, has_srq = 0, is_user = 0;
int qp_type, max_send_sge, max_recv_sge, ret;
/* h_call's out parameters */
@@ -609,9 +609,6 @@ static struct ehca_qp *internal_create_qp(
}
}
- if (pd->uobject && udata)
- context = pd->uobject->context;
-
my_qp = kmem_cache_zalloc(qp_cache, GFP_KERNEL);
if (!my_qp) {
ehca_err(pd->device, "pd=%p not enough memory to alloc qp", pd);
@@ -619,6 +616,11 @@ static struct ehca_qp *internal_create_qp(
return ERR_PTR(-ENOMEM);
}
+ if (pd->uobject && udata) {
+ is_user = 1;
+ context = pd->uobject->context;
+ }
+
atomic_set(&my_qp->nr_events, 0);
init_waitqueue_head(&my_qp->wait_completion);
spin_lock_init(&my_qp->spinlock_s);
@@ -707,7 +709,7 @@ static struct ehca_qp *internal_create_qp(
(parms.squeue.is_small || parms.rqueue.is_small);
}
- h_ret = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, &parms);
+ h_ret = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, &parms, is_user);
if (h_ret != H_SUCCESS) {
ehca_err(pd->device, "h_alloc_resource_qp() failed h_ret=%lli",
h_ret);
@@ -769,18 +771,20 @@ static struct ehca_qp *internal_create_qp(
goto create_qp_exit2;
}
- my_qp->sq_map.entries = my_qp->ipz_squeue.queue_length /
- my_qp->ipz_squeue.qe_size;
- my_qp->sq_map.map = vmalloc(my_qp->sq_map.entries *
- sizeof(struct ehca_qmap_entry));
- if (!my_qp->sq_map.map) {
- ehca_err(pd->device, "Couldn't allocate squeue "
- "map ret=%i", ret);
- goto create_qp_exit3;
+ if (!is_user) {
+ my_qp->sq_map.entries = my_qp->ipz_squeue.queue_length /
+ my_qp->ipz_squeue.qe_size;
+ my_qp->sq_map.map = vmalloc(my_qp->sq_map.entries *
+ sizeof(struct ehca_qmap_entry));
+ if (!my_qp->sq_map.map) {
+ ehca_err(pd->device, "Couldn't allocate squeue "
+ "map ret=%i", ret);
+ goto create_qp_exit3;
+ }
+ INIT_LIST_HEAD(&my_qp->sq_err_node);
+ /* to avoid the generation of bogus flush CQEs */
+ reset_queue_map(&my_qp->sq_map);
}
- INIT_LIST_HEAD(&my_qp->sq_err_node);
- /* to avoid the generation of bogus flush CQEs */
- reset_queue_map(&my_qp->sq_map);
}
if (HAS_RQ(my_qp)) {
@@ -792,20 +796,21 @@ static struct ehca_qp *internal_create_qp(
"and pages ret=%i", ret);
goto create_qp_exit4;
}
-
- my_qp->rq_map.entries = my_qp->ipz_rqueue.queue_length /
- my_qp->ipz_rqueue.qe_size;
- my_qp->rq_map.map = vmalloc(my_qp->rq_map.entries *
- sizeof(struct ehca_qmap_entry));
- if (!my_qp->rq_map.map) {
- ehca_err(pd->device, "Couldn't allocate squeue "
- "map ret=%i", ret);
- goto create_qp_exit5;
+ if (!is_user) {
+ my_qp->rq_map.entries = my_qp->ipz_rqueue.queue_length /
+ my_qp->ipz_rqueue.qe_size;
+ my_qp->rq_map.map = vmalloc(my_qp->rq_map.entries *
+ sizeof(struct ehca_qmap_entry));
+ if (!my_qp->rq_map.map) {
+ ehca_err(pd->device, "Couldn't allocate squeue "
+ "map ret=%i", ret);
+ goto create_qp_exit5;
+ }
+ INIT_LIST_HEAD(&my_qp->rq_err_node);
+ /* to avoid the generation of bogus flush CQEs */
+ reset_queue_map(&my_qp->rq_map);
}
- INIT_LIST_HEAD(&my_qp->rq_err_node);
- /* to avoid the generation of bogus flush CQEs */
- reset_queue_map(&my_qp->rq_map);
- } else if (init_attr->srq) {
+ } else if (init_attr->srq && !is_user) {
/* this is a base QP, use the queue map of the SRQ */
my_qp->rq_map = my_srq->rq_map;
INIT_LIST_HEAD(&my_qp->rq_err_node);
@@ -918,7 +923,7 @@ create_qp_exit7:
kfree(my_qp->mod_qp_parm);
create_qp_exit6:
- if (HAS_RQ(my_qp))
+ if (HAS_RQ(my_qp) && !is_user)
vfree(my_qp->rq_map.map);
create_qp_exit5:
@@ -926,7 +931,7 @@ create_qp_exit5:
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
create_qp_exit4:
- if (HAS_SQ(my_qp))
+ if (HAS_SQ(my_qp) && !is_user)
vfree(my_qp->sq_map.map);
create_qp_exit3:
@@ -1244,6 +1249,7 @@ static int internal_modify_qp(struct ib_qp *ibqp,
u64 update_mask;
u64 h_ret;
int bad_wqe_cnt = 0;
+ int is_user = 0;
int squeue_locked = 0;
unsigned long flags = 0;
@@ -1266,6 +1272,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
ret = ehca2ib_return_code(h_ret);
goto modify_qp_exit1;
}
+ if (ibqp->uobject)
+ is_user = 1;
qp_cur_state = ehca2ib_qp_state(mqpcb->qp_state);
@@ -1728,7 +1736,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
goto modify_qp_exit2;
}
}
- if ((qp_new_state == IB_QPS_ERR) && (qp_cur_state != IB_QPS_ERR)) {
+ if ((qp_new_state == IB_QPS_ERR) && (qp_cur_state != IB_QPS_ERR)
+ && !is_user) {
ret = check_for_left_cqes(my_qp, shca);
if (ret)
goto modify_qp_exit2;
@@ -1738,16 +1747,17 @@ static int internal_modify_qp(struct ib_qp *ibqp,
ipz_qeit_reset(&my_qp->ipz_rqueue);
ipz_qeit_reset(&my_qp->ipz_squeue);
- if (qp_cur_state == IB_QPS_ERR) {
+ if (qp_cur_state == IB_QPS_ERR && !is_user) {
del_from_err_list(my_qp->send_cq, &my_qp->sq_err_node);
if (HAS_RQ(my_qp))
del_from_err_list(my_qp->recv_cq,
&my_qp->rq_err_node);
}
- reset_queue_map(&my_qp->sq_map);
+ if (!is_user)
+ reset_queue_map(&my_qp->sq_map);
- if (HAS_RQ(my_qp))
+ if (HAS_RQ(my_qp) && !is_user)
reset_queue_map(&my_qp->rq_map);
}
@@ -1952,19 +1962,13 @@ int ehca_query_qp(struct ib_qp *qp,
qp_attr->cap.max_inline_data = my_qp->sq_max_inline_data_size;
qp_attr->dest_qp_num = qpcb->dest_qp_nr;
- qp_attr->pkey_index =
- EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->prim_p_key_idx);
-
- qp_attr->port_num =
- EHCA_BMASK_GET(MQPCB_PRIM_PHYS_PORT, qpcb->prim_phys_port);
-
+ qp_attr->pkey_index = qpcb->prim_p_key_idx;
+ qp_attr->port_num = qpcb->prim_phys_port;
qp_attr->timeout = qpcb->timeout;
qp_attr->retry_cnt = qpcb->retry_count;
qp_attr->rnr_retry = qpcb->rnr_retry_count;
- qp_attr->alt_pkey_index =
- EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->alt_p_key_idx);
-
+ qp_attr->alt_pkey_index = qpcb->alt_p_key_idx;
qp_attr->alt_port_num = qpcb->alt_phys_port;
qp_attr->alt_timeout = qpcb->timeout_al;
@@ -2051,8 +2055,7 @@ int ehca_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
update_mask |=
EHCA_BMASK_SET(MQPCB_MASK_CURR_SRQ_LIMIT, 1)
| EHCA_BMASK_SET(MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG, 1);
- mqpcb->curr_srq_limit =
- EHCA_BMASK_SET(MQPCB_CURR_SRQ_LIMIT, attr->srq_limit);
+ mqpcb->curr_srq_limit = attr->srq_limit;
mqpcb->qp_aff_asyn_ev_log_reg =
EHCA_BMASK_SET(QPX_AAELOG_RESET_SRQ_LIMIT, 1);
}
@@ -2115,8 +2118,7 @@ int ehca_query_srq(struct ib_srq *srq, struct ib_srq_attr *srq_attr)
srq_attr->max_wr = qpcb->max_nr_outst_recv_wr - 1;
srq_attr->max_sge = 3;
- srq_attr->srq_limit = EHCA_BMASK_GET(
- MQPCB_CURR_SRQ_LIMIT, qpcb->curr_srq_limit);
+ srq_attr->srq_limit = qpcb->curr_srq_limit;
if (ehca_debug_level >= 2)
ehca_dmp(qpcb, 4*70, "qp_num=%x", my_qp->real_qp_num);
@@ -2138,10 +2140,12 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
int ret;
u64 h_ret;
u8 port_num;
+ int is_user = 0;
enum ib_qp_type qp_type;
unsigned long flags;
if (uobject) {
+ is_user = 1;
if (my_qp->mm_count_galpa ||
my_qp->mm_count_rqueue || my_qp->mm_count_squeue) {
ehca_err(dev, "Resources still referenced in "
@@ -2168,10 +2172,10 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
* SRQs will never get into an error list and do not have a recv_cq,
* so we need to skip them here.
*/
- if (HAS_RQ(my_qp) && !IS_SRQ(my_qp))
+ if (HAS_RQ(my_qp) && !IS_SRQ(my_qp) && !is_user)
del_from_err_list(my_qp->recv_cq, &my_qp->rq_err_node);
- if (HAS_SQ(my_qp))
+ if (HAS_SQ(my_qp) && !is_user)
del_from_err_list(my_qp->send_cq, &my_qp->sq_err_node);
/* now wait until all pending events have completed */
@@ -2209,13 +2213,13 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
if (HAS_RQ(my_qp)) {
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
-
- vfree(my_qp->rq_map.map);
+ if (!is_user)
+ vfree(my_qp->rq_map.map);
}
if (HAS_SQ(my_qp)) {
ipz_queue_dtor(my_pd, &my_qp->ipz_squeue);
-
- vfree(my_qp->sq_map.map);
+ if (!is_user)
+ vfree(my_qp->sq_map.map);
}
kmem_cache_free(qp_cache, my_qp);
atomic_dec(&shca->num_qps);
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
index d0ab0c0d5e91..4d5dc3304d42 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.c
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -284,7 +284,7 @@ u64 hipz_h_alloc_resource_cq(const struct ipz_adapter_handle adapter_handle,
param->act_pages = (u32)outs[4];
if (ret == H_SUCCESS)
- hcp_galpas_ctor(&cq->galpas, outs[5], outs[6]);
+ hcp_galpas_ctor(&cq->galpas, 0, outs[5], outs[6]);
if (ret == H_NOT_ENOUGH_RESOURCES)
ehca_gen_err("Not enough resources. ret=%lli", ret);
@@ -293,7 +293,7 @@ u64 hipz_h_alloc_resource_cq(const struct ipz_adapter_handle adapter_handle,
}
u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle,
- struct ehca_alloc_qp_parms *parms)
+ struct ehca_alloc_qp_parms *parms, int is_user)
{
u64 ret;
u64 allocate_controls, max_r10_reg, r11, r12;
@@ -359,7 +359,7 @@ u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle,
(u32)EHCA_BMASK_GET(H_ALL_RES_QP_RQUEUE_SIZE_PAGES, outs[4]);
if (ret == H_SUCCESS)
- hcp_galpas_ctor(&parms->galpas, outs[6], outs[6]);
+ hcp_galpas_ctor(&parms->galpas, is_user, outs[6], outs[6]);
if (ret == H_NOT_ENOUGH_RESOURCES)
ehca_gen_err("Not enough resources. ret=%lli", ret);
diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h
index 2c3c6e0ea5c2..39c1c3618ec7 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.h
+++ b/drivers/infiniband/hw/ehca/hcp_if.h
@@ -78,7 +78,7 @@ u64 hipz_h_alloc_resource_cq(const struct ipz_adapter_handle adapter_handle,
* initialize resources, create empty QPPTs (2 rings).
*/
u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle,
- struct ehca_alloc_qp_parms *parms);
+ struct ehca_alloc_qp_parms *parms, int is_user);
u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle,
const u8 port_id,
diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.c b/drivers/infiniband/hw/ehca/hcp_phyp.c
index 214821095cb1..b3e0e72e8a73 100644
--- a/drivers/infiniband/hw/ehca/hcp_phyp.c
+++ b/drivers/infiniband/hw/ehca/hcp_phyp.c
@@ -54,12 +54,15 @@ int hcall_unmap_page(u64 mapaddr)
return 0;
}
-int hcp_galpas_ctor(struct h_galpas *galpas,
+int hcp_galpas_ctor(struct h_galpas *galpas, int is_user,
u64 paddr_kernel, u64 paddr_user)
{
- int ret = hcall_map_page(paddr_kernel, &galpas->kernel.fw_handle);
- if (ret)
- return ret;
+ if (!is_user) {
+ int ret = hcall_map_page(paddr_kernel, &galpas->kernel.fw_handle);
+ if (ret)
+ return ret;
+ } else
+ galpas->kernel.fw_handle = 0;
galpas->user.fw_handle = paddr_user;
diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.h b/drivers/infiniband/hw/ehca/hcp_phyp.h
index 5305c2a3ed94..204227d5303a 100644
--- a/drivers/infiniband/hw/ehca/hcp_phyp.h
+++ b/drivers/infiniband/hw/ehca/hcp_phyp.h
@@ -78,7 +78,7 @@ static inline void hipz_galpa_store(struct h_galpa galpa, u32 offset, u64 value)
*(volatile u64 __force *)addr = value;
}
-int hcp_galpas_ctor(struct h_galpas *galpas,
+int hcp_galpas_ctor(struct h_galpas *galpas, int is_user,
u64 paddr_kernel, u64 paddr_user);
int hcp_galpas_dtor(struct h_galpas *galpas);
diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
index c3a328465431..1227c593627a 100644
--- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c
+++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
@@ -220,10 +220,13 @@ int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue,
queue->small_page = NULL;
/* allocate queue page pointers */
- queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *));
+ queue->queue_pages = kmalloc(nr_of_pages * sizeof(void *), GFP_KERNEL);
if (!queue->queue_pages) {
- ehca_gen_err("Couldn't allocate queue page list");
- return 0;
+ queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *));
+ if (!queue->queue_pages) {
+ ehca_gen_err("Couldn't allocate queue page list");
+ return 0;
+ }
}
memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *));
@@ -240,7 +243,10 @@ int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue,
ipz_queue_ctor_exit0:
ehca_gen_err("Couldn't alloc pages queue=%p "
"nr_of_pages=%x", queue, nr_of_pages);
- vfree(queue->queue_pages);
+ if (is_vmalloc_addr(queue->queue_pages))
+ vfree(queue->queue_pages);
+ else
+ kfree(queue->queue_pages);
return 0;
}
@@ -262,7 +268,10 @@ int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue)
free_page((unsigned long)queue->queue_pages[i]);
}
- vfree(queue->queue_pages);
+ if (is_vmalloc_addr(queue->queue_pages))
+ vfree(queue->queue_pages);
+ else
+ kfree(queue->queue_pages);
return 1;
}
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 20724aee76f4..c4a02648c8af 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1585,12 +1585,16 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
break;
case IB_WR_LOCAL_INV:
+ ctrl->srcrb_flags |=
+ cpu_to_be32(MLX4_WQE_CTRL_STRONG_ORDER);
set_local_inv_seg(wqe, wr->ex.invalidate_rkey);
wqe += sizeof (struct mlx4_wqe_local_inval_seg);
size += sizeof (struct mlx4_wqe_local_inval_seg) / 16;
break;
case IB_WR_FAST_REG_MR:
+ ctrl->srcrb_flags |=
+ cpu_to_be32(MLX4_WQE_CTRL_STRONG_ORDER);
set_fmr_seg(wqe, wr);
wqe += sizeof (struct mlx4_wqe_fmr_seg);
size += sizeof (struct mlx4_wqe_fmr_seg) / 16;
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
index 6d55f9d748f6..8c2ed994d540 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.c
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.c
@@ -1059,7 +1059,7 @@ int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_MTT_OFFSET);
if (mthca_is_memfree(dev))
dev_lim->reserved_mtts = ALIGN((1 << (field >> 4)) * sizeof(u64),
- MTHCA_MTT_SEG_SIZE) / MTHCA_MTT_SEG_SIZE;
+ dev->limits.mtt_seg_size) / dev->limits.mtt_seg_size;
else
dev_lim->reserved_mtts = 1 << (field >> 4);
MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_MRW_SZ_OFFSET);
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h
index 252590116df5..9ef611f6dd36 100644
--- a/drivers/infiniband/hw/mthca/mthca_dev.h
+++ b/drivers/infiniband/hw/mthca/mthca_dev.h
@@ -159,6 +159,7 @@ struct mthca_limits {
int reserved_eqs;
int num_mpts;
int num_mtt_segs;
+ int mtt_seg_size;
int fmr_reserved_mtts;
int reserved_mtts;
int reserved_mrws;
diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c
index 28f0e0c40d7d..90e4e450a120 100644
--- a/drivers/infiniband/hw/mthca/mthca_eq.c
+++ b/drivers/infiniband/hw/mthca/mthca_eq.c
@@ -641,9 +641,11 @@ static void mthca_free_irqs(struct mthca_dev *dev)
if (dev->eq_table.have_irq)
free_irq(dev->pdev->irq, dev);
for (i = 0; i < MTHCA_NUM_EQ; ++i)
- if (dev->eq_table.eq[i].have_irq)
+ if (dev->eq_table.eq[i].have_irq) {
free_irq(dev->eq_table.eq[i].msi_x_vector,
dev->eq_table.eq + i);
+ dev->eq_table.eq[i].have_irq = 0;
+ }
}
static int mthca_map_reg(struct mthca_dev *dev,
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 1d83cf7caf38..13da9f1d24c0 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -125,6 +125,10 @@ module_param_named(fmr_reserved_mtts, hca_profile.fmr_reserved_mtts, int, 0444);
MODULE_PARM_DESC(fmr_reserved_mtts,
"number of memory translation table segments reserved for FMR");
+static int log_mtts_per_seg = ilog2(MTHCA_MTT_SEG_SIZE / 8);
+module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444);
+MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-5)");
+
static char mthca_version[] __devinitdata =
DRV_NAME ": Mellanox InfiniBand HCA driver v"
DRV_VERSION " (" DRV_RELDATE ")\n";
@@ -162,6 +166,7 @@ static int mthca_dev_lim(struct mthca_dev *mdev, struct mthca_dev_lim *dev_lim)
int err;
u8 status;
+ mdev->limits.mtt_seg_size = (1 << log_mtts_per_seg) * 8;
err = mthca_QUERY_DEV_LIM(mdev, dev_lim, &status);
if (err) {
mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
@@ -460,11 +465,11 @@ static int mthca_init_icm(struct mthca_dev *mdev,
}
/* CPU writes to non-reserved MTTs, while HCA might DMA to reserved mtts */
- mdev->limits.reserved_mtts = ALIGN(mdev->limits.reserved_mtts * MTHCA_MTT_SEG_SIZE,
- dma_get_cache_alignment()) / MTHCA_MTT_SEG_SIZE;
+ mdev->limits.reserved_mtts = ALIGN(mdev->limits.reserved_mtts * mdev->limits.mtt_seg_size,
+ dma_get_cache_alignment()) / mdev->limits.mtt_seg_size;
mdev->mr_table.mtt_table = mthca_alloc_icm_table(mdev, init_hca->mtt_base,
- MTHCA_MTT_SEG_SIZE,
+ mdev->limits.mtt_seg_size,
mdev->limits.num_mtt_segs,
mdev->limits.reserved_mtts,
1, 0);
@@ -1315,6 +1320,12 @@ static void __init mthca_validate_profile(void)
printk(KERN_WARNING PFX "Corrected fmr_reserved_mtts to %d.\n",
hca_profile.fmr_reserved_mtts);
}
+
+ if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 5)) {
+ printk(KERN_WARNING PFX "bad log_mtts_per_seg (%d). Using default - %d\n",
+ log_mtts_per_seg, ilog2(MTHCA_MTT_SEG_SIZE / 8));
+ log_mtts_per_seg = ilog2(MTHCA_MTT_SEG_SIZE / 8);
+ }
}
static int __init mthca_init(void)
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index 882e6b735915..d606edf10858 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -220,7 +220,7 @@ static struct mthca_mtt *__mthca_alloc_mtt(struct mthca_dev *dev, int size,
mtt->buddy = buddy;
mtt->order = 0;
- for (i = MTHCA_MTT_SEG_SIZE / 8; i < size; i <<= 1)
+ for (i = dev->limits.mtt_seg_size / 8; i < size; i <<= 1)
++mtt->order;
mtt->first_seg = mthca_alloc_mtt_range(dev, mtt->order, buddy);
@@ -267,7 +267,7 @@ static int __mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
while (list_len > 0) {
mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base +
- mtt->first_seg * MTHCA_MTT_SEG_SIZE +
+ mtt->first_seg * dev->limits.mtt_seg_size +
start_index * 8);
mtt_entry[1] = 0;
for (i = 0; i < list_len && i < MTHCA_MAILBOX_SIZE / 8 - 2; ++i)
@@ -326,7 +326,7 @@ static void mthca_tavor_write_mtt_seg(struct mthca_dev *dev,
u64 __iomem *mtts;
int i;
- mtts = dev->mr_table.tavor_fmr.mtt_base + mtt->first_seg * MTHCA_MTT_SEG_SIZE +
+ mtts = dev->mr_table.tavor_fmr.mtt_base + mtt->first_seg * dev->limits.mtt_seg_size +
start_index * sizeof (u64);
for (i = 0; i < list_len; ++i)
mthca_write64_raw(cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT),
@@ -345,10 +345,10 @@ static void mthca_arbel_write_mtt_seg(struct mthca_dev *dev,
/* For Arbel, all MTTs must fit in the same page. */
BUG_ON(s / PAGE_SIZE != (s + list_len * sizeof(u64) - 1) / PAGE_SIZE);
/* Require full segments */
- BUG_ON(s % MTHCA_MTT_SEG_SIZE);
+ BUG_ON(s % dev->limits.mtt_seg_size);
mtts = mthca_table_find(dev->mr_table.mtt_table, mtt->first_seg +
- s / MTHCA_MTT_SEG_SIZE, &dma_handle);
+ s / dev->limits.mtt_seg_size, &dma_handle);
BUG_ON(!mtts);
@@ -479,7 +479,7 @@ int mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift,
if (mr->mtt)
mpt_entry->mtt_seg =
cpu_to_be64(dev->mr_table.mtt_base +
- mr->mtt->first_seg * MTHCA_MTT_SEG_SIZE);
+ mr->mtt->first_seg * dev->limits.mtt_seg_size);
if (0) {
mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey);
@@ -626,7 +626,7 @@ int mthca_fmr_alloc(struct mthca_dev *dev, u32 pd,
goto err_out_table;
}
- mtt_seg = mr->mtt->first_seg * MTHCA_MTT_SEG_SIZE;
+ mtt_seg = mr->mtt->first_seg * dev->limits.mtt_seg_size;
if (mthca_is_memfree(dev)) {
mr->mem.arbel.mtts = mthca_table_find(dev->mr_table.mtt_table,
@@ -908,7 +908,7 @@ int mthca_init_mr_table(struct mthca_dev *dev)
dev->mr_table.mtt_base);
dev->mr_table.tavor_fmr.mtt_base =
- ioremap(addr, mtts * MTHCA_MTT_SEG_SIZE);
+ ioremap(addr, mtts * dev->limits.mtt_seg_size);
if (!dev->mr_table.tavor_fmr.mtt_base) {
mthca_warn(dev, "MTT ioremap for FMR failed.\n");
err = -ENOMEM;
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.c b/drivers/infiniband/hw/mthca/mthca_profile.c
index d168c2540611..8edb28a9a0e7 100644
--- a/drivers/infiniband/hw/mthca/mthca_profile.c
+++ b/drivers/infiniband/hw/mthca/mthca_profile.c
@@ -94,7 +94,7 @@ s64 mthca_make_profile(struct mthca_dev *dev,
profile[MTHCA_RES_RDB].size = MTHCA_RDB_ENTRY_SIZE;
profile[MTHCA_RES_MCG].size = MTHCA_MGM_ENTRY_SIZE;
profile[MTHCA_RES_MPT].size = dev_lim->mpt_entry_sz;
- profile[MTHCA_RES_MTT].size = MTHCA_MTT_SEG_SIZE;
+ profile[MTHCA_RES_MTT].size = dev->limits.mtt_seg_size;
profile[MTHCA_RES_UAR].size = dev_lim->uar_scratch_entry_sz;
profile[MTHCA_RES_UDAV].size = MTHCA_AV_SIZE;
profile[MTHCA_RES_UARC].size = request->uarc_size;
@@ -232,7 +232,7 @@ s64 mthca_make_profile(struct mthca_dev *dev,
dev->limits.num_mtt_segs = profile[i].num;
dev->mr_table.mtt_base = profile[i].start;
init_hca->mtt_base = profile[i].start;
- init_hca->mtt_seg_sz = ffs(MTHCA_MTT_SEG_SIZE) - 7;
+ init_hca->mtt_seg_sz = ffs(dev->limits.mtt_seg_size) - 7;
break;
case MTHCA_RES_UAR:
dev->limits.num_uars = profile[i].num;
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index b832a7b814a2..4a84d02ece06 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -667,7 +667,7 @@ static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_
i = 0;
while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET) & 0x00000040) == 0) && i++ < 10000)
mdelay(1);
- if (i >= 10000) {
+ if (i > 10000) {
nes_debug(NES_DBG_INIT, "Did not see full soft reset done.\n");
return 0;
}
@@ -675,7 +675,7 @@ static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_
i = 0;
while ((nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS) != 0x80) && i++ < 10000)
mdelay(1);
- if (i >= 10000) {
+ if (i > 10000) {
printk(KERN_ERR PFX "Internal CPU not ready, status = %02X\n",
nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS));
return 0;
@@ -701,7 +701,7 @@ static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_
i = 0;
while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET) & 0x00000040) == 0) && i++ < 10000)
mdelay(1);
- if (i >= 10000) {
+ if (i > 10000) {
nes_debug(NES_DBG_INIT, "Did not see port soft reset done.\n");
return 0;
}
@@ -711,7 +711,7 @@ static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_
while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0)
& 0x0000000f)) != 0x0000000f) && i++ < 5000)
mdelay(1);
- if (i >= 5000) {
+ if (i > 5000) {
nes_debug(NES_DBG_INIT, "Serdes 0 not ready, status=%x\n", u32temp);
return 0;
}
@@ -722,7 +722,7 @@ static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_
while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS1)
& 0x0000000f)) != 0x0000000f) && i++ < 5000)
mdelay(1);
- if (i >= 5000) {
+ if (i > 5000) {
nes_debug(NES_DBG_INIT, "Serdes 1 not ready, status=%x\n", u32temp);
return 0;
}
@@ -792,7 +792,7 @@ static int nes_init_serdes(struct nes_device *nesdev, u8 hw_rev, u8 port_count,
while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0)
& 0x0000000f)) != 0x0000000f) && i++ < 5000)
mdelay(1);
- if (i >= 5000) {
+ if (i > 5000) {
nes_debug(NES_DBG_PHY, "Init: serdes 0 not ready, status=%x\n", u32temp);
return 1;
}
@@ -815,7 +815,7 @@ static int nes_init_serdes(struct nes_device *nesdev, u8 hw_rev, u8 port_count,
while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS1)
& 0x0000000f)) != 0x0000000f) && (i++ < 5000))
mdelay(1);
- if (i >= 5000) {
+ if (i > 5000) {
printk("%s: Init: serdes 1 not ready, status=%x\n", __func__, u32temp);
/* return 1; */
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 75223f50de58..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;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index e54e002665b0..5d445f48789b 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -42,6 +42,7 @@ static unsigned int input_abs_bypass_init_data[] __initdata = {
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)];
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5c0a631d1455..06f46fcc0772 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -232,7 +232,7 @@ config INPUT_GPIO_ROTARY_ENCODER
depends on GPIOLIB && GENERIC_GPIO
help
Say Y here to add support for rotary encoders connected to GPIO lines.
- Check file:Documentation/incput/rotary_encoder.txt for more
+ Check file:Documentation/input/rotary-encoder.txt for more
information.
To compile this driver as a module, choose M here: the
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..89b394183a75 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;
@@ -135,7 +135,7 @@ static int amba_kmi_probe(struct amba_device *dev, void *id)
io->dev.parent = &dev->dev;
kmi->io = io;
- kmi->base = ioremap(dev->res.start, KMI_SIZE);
+ kmi->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!kmi->base) {
ret = -ENOMEM;
goto out;
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/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index f100c7f4c1db..6954f5500108 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -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/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
index 7d97d54588d9..77e9fdda0597 100644
--- a/drivers/isdn/divert/isdn_divert.c
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -183,7 +183,7 @@ int cf_command(int drvid, int mode,
(mode != 1) ? "" : " 0 ",
(mode != 1) ? "" : fwd_nr);
- retval = divert_if.ll_cmd(&cs->ics); /* excute command */
+ retval = divert_if.ll_cmd(&cs->ics); /* execute command */
if (!retval)
{ cs->prev = NULL;
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index c12cd2f94254..77ee2867c8b4 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -506,7 +506,7 @@ tone_off:
break;
}
dsp->cmx_delay = (*((int *)data)) << 3;
- /* miliseconds to samples */
+ /* milliseconds to samples */
if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1))
/* clip to half of maximum usable buffer
(half of half buffer) */
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c
index 1aa46a390a0d..173d104d9ff2 100644
--- a/drivers/leds/leds-h1940.c
+++ b/drivers/leds/leds-h1940.c
@@ -16,6 +16,8 @@
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
+#include <linux/gpio.h>
+
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <mach/h1940-latch.h>
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index aa2e7ae0cdae..aa7acf3b9224 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
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 47c68bc75a17..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
*/
@@ -1097,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);
@@ -1121,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;
}
@@ -1181,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) {
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 fccc8343a250..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);
@@ -3303,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";
@@ -3326,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);
@@ -3680,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;
}
@@ -5557,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,
@@ -6352,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 499620afb44b..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;
@@ -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/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 60955a70d880..1bb66e1ed5a7 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -216,7 +216,7 @@ config DVB_USB_TTUSB2
help
Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
firmware protocol used by this module is similar to the one used by the
- old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko.
+ old ttusb-driver - that's why the module is called dvb-usb-ttusb2.
config DVB_USB_DTT200U
tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 9d48da2fb013..57835f5715fc 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -758,10 +758,14 @@ config VIDEO_MX1
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
+config MX3_VIDEO
+ bool
+
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
select VIDEOBUF_DMA_CONTIG
+ select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 3e6ffee8dfed..ccd47f57f42c 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -181,7 +181,7 @@ static int hdpvr_submit_buffers(struct hdpvr_device *dev)
buff_list);
if (buf->status != BUFSTAT_AVAILABLE) {
v4l2_err(&dev->v4l2_dev,
- "buffer not marked as availbale\n");
+ "buffer not marked as available\n");
ret = -EFAULT;
goto err;
}
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/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index de143deb06f0..7847bbc1440d 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -672,15 +672,14 @@ try_again:
msb->req_sg);
if (!msb->seg_count) {
- chunk = __blk_end_request(msb->block_req, -ENOMEM,
- blk_rq_cur_bytes(msb->block_req));
+ chunk = __blk_end_request_cur(msb->block_req, -ENOMEM);
continue;
}
- t_sec = msb->block_req->sector << 9;
+ t_sec = blk_rq_pos(msb->block_req) << 9;
sector_div(t_sec, msb->page_size);
- count = msb->block_req->nr_sectors << 9;
+ count = blk_rq_bytes(msb->block_req);
count /= msb->page_size;
param.system = msb->system;
@@ -705,8 +704,8 @@ try_again:
return 0;
}
- dev_dbg(&card->dev, "elv_next\n");
- msb->block_req = elv_next_request(msb->queue);
+ dev_dbg(&card->dev, "blk_fetch\n");
+ msb->block_req = blk_fetch_request(msb->queue);
if (!msb->block_req) {
dev_dbg(&card->dev, "issue end\n");
return -EAGAIN;
@@ -745,7 +744,7 @@ static int mspro_block_complete_req(struct memstick_dev *card, int error)
t_len *= msb->page_size;
}
} else
- t_len = msb->block_req->nr_sectors << 9;
+ t_len = blk_rq_bytes(msb->block_req);
dev_dbg(&card->dev, "transferred %x (%d)\n", t_len, error);
@@ -825,8 +824,8 @@ static void mspro_block_submit_req(struct request_queue *q)
return;
if (msb->eject) {
- while ((req = elv_next_request(q)) != NULL)
- __blk_end_request(req, -ENODEV, blk_rq_bytes(req));
+ while ((req = blk_fetch_request(q)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
return;
}
@@ -1243,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/lsi/mpi_history.txt b/drivers/message/fusion/lsi/mpi_history.txt
index 693e4b511354..fa9249b4971a 100644
--- a/drivers/message/fusion/lsi/mpi_history.txt
+++ b/drivers/message/fusion/lsi/mpi_history.txt
@@ -130,7 +130,7 @@ mpi_ioc.h
* 08-08-01 01.02.01 Original release for v1.2 work.
* New format for FWVersion and ProductId in
* MSG_IOC_FACTS_REPLY and MPI_FW_HEADER.
- * 08-31-01 01.02.02 Addded event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and
+ * 08-31-01 01.02.02 Added event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and
* related structure and defines.
* Added event MPI_EVENT_ON_BUS_TIMER_EXPIRED.
* Added MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE.
@@ -190,7 +190,7 @@ mpi_ioc.h
* 10-11-06 01.05.12 Added MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED.
* Added MaxInitiators field to PortFacts reply.
* Added SAS Device Status Change ReasonCode for
- * asynchronous notificaiton.
+ * asynchronous notification.
* Added MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE and event
* data structure.
* Added new ImageType values for FWDownload and FWUpload
@@ -623,7 +623,7 @@ mpi_fc.h
* 11-02-00 01.01.01 Original release for post 1.0 work
* 12-04-00 01.01.02 Added messages for Common Transport Send and
* Primitive Send.
- * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix
+ * 01-09-01 01.01.03 Modified some of the new flags to have an MPI prefix
* and modified the FcPrimitiveSend flags.
* 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger
* field.
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 5d496a99e034..0df065275cd3 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,116 @@ 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 +1305,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 +1614,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 +1733,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 +1752,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 +1794,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 +1811,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 +1887,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 +1936,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 +2013,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 +2127,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 +2239,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 +2269,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 +2283,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 +2306,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 +2351,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 +2373,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 +2398,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 +2436,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 +2481,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 +2497,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 +2514,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 +2540,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 +2623,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 +2656,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 +2666,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 +2749,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 +2904,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 +2982,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 +2997,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 +3051,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 +3115,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 +3127,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 +3147,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 +3329,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 +3344,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 +3547,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 +3585,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 +3943,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 +3964,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 +4115,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 +4130,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 +4253,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 +4325,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 +4401,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 +4575,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 +4601,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 +5043,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 +5060,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 +5082,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 +5702,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 +5762,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 +6241,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 +6278,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 +6315,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 +6337,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 +6399,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 +6414,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 +6512,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 +6851,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 +6957,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 +6972,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 +6990,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 +7074,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 +7276,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 +7370,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 +7429,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 +7456,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 +7486,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 +7558,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 +7644,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 +8244,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 +8280,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 a9019f081b97..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;
@@ -1277,8 +2135,8 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
/* do we need to support multiple segments? */
if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u %u, rsp %u %u\n",
- ioc->name, __func__, req->bio->bi_vcnt, req->data_len,
- rsp->bio->bi_vcnt, rsp->data_len);
+ ioc->name, __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
+ rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
return -EINVAL;
}
@@ -1295,7 +2153,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
smpreq = (SmpPassthroughRequest_t *)mf;
memset(smpreq, 0, sizeof(*smpreq));
- smpreq->RequestDataLength = cpu_to_le16(req->data_len - 4);
+ smpreq->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4);
smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
if (rphy)
@@ -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;
- flagsLength |= (req->data_len - 4);
+ 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),
- req->data_len, PCI_DMA_BIDIRECTIONAL);
+ 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 |= rsp->data_len + 4;
+ 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),
- rsp->data_len, PCI_DMA_BIDIRECTIONAL);
+ 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,30 +2215,32 @@ 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);
- req->data_len = 0;
- rsp->data_len -= 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;
}
unmap:
if (dma_addr_out)
- pci_unmap_single(ioc->pcidev, dma_addr_out, req->data_len,
+ pci_unmap_single(ioc->pcidev, dma_addr_out, blk_rq_bytes(req),
PCI_DMA_BIDIRECTIONAL);
if (dma_addr_in)
- pci_unmap_single(ioc->pcidev, dma_addr_in, rsp->data_len,
+ pci_unmap_single(ioc->pcidev, dma_addr_in, blk_rq_bytes(rsp),
PCI_DMA_BIDIRECTIONAL);
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;
@@ -1438,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;
@@ -1600,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;
@@ -1644,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 =
@@ -1675,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;
@@ -1721,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:
@@ -1752,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;
@@ -1791,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;
@@ -2010,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;
@@ -2032,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 ==
@@ -2102,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;
@@ -2112,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++) {
@@ -2130,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)
@@ -2163,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)
{
@@ -2429,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)
{
@@ -2517,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));
@@ -2557,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:
@@ -2582,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;
@@ -3001,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
@@ -3037,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
@@ -3128,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;
@@ -3211,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) {
@@ -3251,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
@@ -3273,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) {
@@ -3291,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:
@@ -3301,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);
@@ -3315,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);
}
@@ -3344,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,
@@ -3364,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);
@@ -3392,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..8440f78f6969 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
+ * @req: Pointer to original MPT request frame
+ * @reply: Pointer to MPT reply frame (NULL if TurboReply)
+ *
+ **/
+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 a443e136dc41..335d4c78a775 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -426,15 +426,9 @@ static void i2o_block_end_request(struct request *req, int error,
struct request_queue *q = req->q;
unsigned long flags;
- if (blk_end_request(req, error, nr_bytes)) {
- int leftover = (req->hard_nr_sectors << KERNEL_SECTOR_SHIFT);
-
- if (blk_pc_request(req))
- leftover = req->data_len;
-
+ if (blk_end_request(req, error, nr_bytes))
if (error)
- blk_end_request(req, -EIO, leftover);
- }
+ blk_end_request_all(req, -EIO);
spin_lock_irqsave(q->queue_lock, flags);
@@ -761,7 +755,7 @@ static int i2o_block_transfer(struct request *req)
break;
case CACHE_SMARTFETCH:
- if (req->nr_sectors > 16)
+ if (blk_rq_sectors(req) > 16)
ctl_flags = 0x201F0008;
else
ctl_flags = 0x001F0000;
@@ -781,13 +775,13 @@ static int i2o_block_transfer(struct request *req)
ctl_flags = 0x001F0010;
break;
case CACHE_SMARTBACK:
- if (req->nr_sectors > 16)
+ if (blk_rq_sectors(req) > 16)
ctl_flags = 0x001F0004;
else
ctl_flags = 0x001F0010;
break;
case CACHE_SMARTTHROUGH:
- if (req->nr_sectors > 16)
+ if (blk_rq_sectors(req) > 16)
ctl_flags = 0x001F0004;
else
ctl_flags = 0x001F0010;
@@ -800,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;
@@ -827,22 +822,22 @@ static int i2o_block_transfer(struct request *req)
*mptr++ = cpu_to_le32(scsi_flags);
- *((u32 *) & cmd[2]) = cpu_to_be32(req->sector * hwsec);
- *((u16 *) & cmd[7]) = cpu_to_be16(req->nr_sectors * hwsec);
+ *((u32 *) & cmd[2]) = cpu_to_be32(blk_rq_pos(req) * hwsec);
+ *((u16 *) & cmd[7]) = cpu_to_be16(blk_rq_sectors(req) * hwsec);
memcpy(mptr, cmd, 10);
mptr += 4;
- *mptr++ = cpu_to_le32(req->nr_sectors << KERNEL_SECTOR_SHIFT);
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
} else
#endif
{
msg->u.head[1] = cpu_to_le32(cmd | HOST_TID << 12 | tid);
*mptr++ = cpu_to_le32(ctl_flags);
- *mptr++ = cpu_to_le32(req->nr_sectors << KERNEL_SECTOR_SHIFT);
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
*mptr++ =
- cpu_to_le32((u32) (req->sector << KERNEL_SECTOR_SHIFT));
+ cpu_to_le32((u32) (blk_rq_pos(req) << KERNEL_SECTOR_SHIFT));
*mptr++ =
- cpu_to_le32(req->sector >> (32 - KERNEL_SECTOR_SHIFT));
+ cpu_to_le32(blk_rq_pos(req) >> (32 - KERNEL_SECTOR_SHIFT));
}
if (!i2o_block_sglist_alloc(c, ireq, &mptr)) {
@@ -883,7 +878,7 @@ static void i2o_block_request_fn(struct request_queue *q)
struct request *req;
while (!blk_queue_plugged(q)) {
- req = elv_next_request(q);
+ req = blk_peek_request(q);
if (!req)
break;
@@ -896,7 +891,7 @@ static void i2o_block_request_fn(struct request_queue *q)
if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) {
if (!i2o_block_transfer(req)) {
- blkdev_dequeue_request(req);
+ blk_start_request(req);
continue;
} else
osm_info("transfer error\n");
@@ -922,8 +917,10 @@ static void i2o_block_request_fn(struct request_queue *q)
blk_stop_queue(q);
break;
}
- } else
- end_request(req, 0);
+ } else {
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
+ }
}
};
@@ -1082,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/t7l66xb.c b/drivers/mfd/t7l66xb.c
index e9f4323dd2cb..875f7a875734 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -108,6 +108,10 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc)
/*--------------------------------------------------------------------------*/
+static const struct tmio_mmc_data t7166xb_mmc_data = {
+ .hclk = 24000000,
+};
+
static const struct resource t7l66xb_mmc_resources[] = {
{
.start = 0x800,
@@ -149,6 +153,7 @@ static struct mfd_cell t7l66xb_cells[] = {
.name = "tmio-mmc",
.enable = t7l66xb_mmc_enable,
.disable = t7l66xb_mmc_disable,
+ .driver_data = &t7166xb_mmc_data,
.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
.resources = t7l66xb_mmc_resources,
},
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index 43222c12fec1..c3993ac20542 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -75,6 +75,10 @@ static int tc6387xb_mmc_disable(struct platform_device *mmc)
/*--------------------------------------------------------------------------*/
+const static struct tmio_mmc_data tc6387xb_mmc_data = {
+ .hclk = 24000000,
+};
+
static struct resource tc6387xb_mmc_resources[] = {
{
.start = 0x800,
@@ -98,6 +102,7 @@ static struct mfd_cell tc6387xb_cells[] = {
.name = "tmio-mmc",
.enable = tc6387xb_mmc_enable,
.disable = tc6387xb_mmc_disable,
+ .driver_data = &tc6387xb_mmc_data,
.num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
.resources = tc6387xb_mmc_resources,
},
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index 77a12fc8045e..9d2abb5d6e2c 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -136,6 +136,10 @@ static int tc6393xb_nand_enable(struct platform_device *nand)
return 0;
}
+const static struct tmio_mmc_data tc6393xb_mmc_data = {
+ .hclk = 24000000,
+};
+
static struct resource __devinitdata tc6393xb_nand_resources[] = {
{
.start = 0x1000,
@@ -351,6 +355,7 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = {
},
[TC6393XB_CELL_MMC] = {
.name = "tmio-mmc",
+ .driver_data = &tc6393xb_mmc_data,
.num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
.resources = tc6393xb_mmc_resources,
},
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/Kconfig b/drivers/misc/Kconfig
index 6d1ac180f6ee..68ab39d7cb35 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -235,5 +235,6 @@ config ISL29003
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
+source "drivers/misc/cb710/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7871f05dcb9b..36f733cd60e6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_C2PORT) += c2port/
obj-y += eeprom/
+obj-y += cb710/
diff --git a/drivers/misc/cb710/Kconfig b/drivers/misc/cb710/Kconfig
new file mode 100644
index 000000000000..22429b8b1068
--- /dev/null
+++ b/drivers/misc/cb710/Kconfig
@@ -0,0 +1,25 @@
+config CB710_CORE
+ tristate "ENE CB710/720 Flash memory card reader support"
+ depends on PCI
+ help
+ This option enables support for PCI ENE CB710/720 Flash memory card
+ reader found in some laptops (ie. some versions of HP Compaq nx9500).
+
+ You will also have to select some flash card format drivers (MMC/SD,
+ MemoryStick).
+
+ This driver can also be built as a module. If so, the module
+ will be called cb710.
+
+config CB710_DEBUG
+ bool "Enable driver debugging"
+ depends on CB710_CORE != n
+ default n
+ help
+ This is an option for use by developers; most people should
+ say N here. This adds a lot of debugging output to dmesg.
+
+config CB710_DEBUG_ASSUMPTIONS
+ bool
+ depends on CB710_CORE != n
+ default y
diff --git a/drivers/misc/cb710/Makefile b/drivers/misc/cb710/Makefile
new file mode 100644
index 000000000000..7b80cbf1a609
--- /dev/null
+++ b/drivers/misc/cb710/Makefile
@@ -0,0 +1,8 @@
+ifeq ($(CONFIG_CB710_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_CB710_CORE) += cb710.o
+
+cb710-y := core.o sgbuf2.o
+cb710-$(CONFIG_CB710_DEBUG) += debug.o
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
new file mode 100644
index 000000000000..b14eab0f2ba5
--- /dev/null
+++ b/drivers/misc/cb710/core.c
@@ -0,0 +1,357 @@
+/*
+ * cb710/core.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * This program is free software; you can 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/slab.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+#include <linux/cb710.h>
+
+static DEFINE_IDA(cb710_ida);
+static DEFINE_SPINLOCK(cb710_ida_lock);
+
+void cb710_pci_update_config_reg(struct pci_dev *pdev,
+ int reg, uint32_t mask, uint32_t xor)
+{
+ u32 rval;
+
+ pci_read_config_dword(pdev, reg, &rval);
+ rval = (rval & mask) ^ xor;
+ pci_write_config_dword(pdev, reg, rval);
+}
+EXPORT_SYMBOL_GPL(cb710_pci_update_config_reg);
+
+/* Some magic writes based on Windows driver init code */
+static int __devinit cb710_pci_configure(struct pci_dev *pdev)
+{
+ unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+ struct pci_dev *pdev0 = pci_get_slot(pdev->bus, devfn);
+ u32 val;
+
+ cb710_pci_update_config_reg(pdev, 0x48,
+ ~0x000000FF, 0x0000003F);
+
+ pci_read_config_dword(pdev, 0x48, &val);
+ if (val & 0x80000000)
+ return 0;
+
+ if (!pdev0)
+ return -ENODEV;
+
+ if (pdev0->vendor == PCI_VENDOR_ID_ENE
+ && pdev0->device == PCI_DEVICE_ID_ENE_720) {
+ cb710_pci_update_config_reg(pdev0, 0x8C,
+ ~0x00F00000, 0x00100000);
+ cb710_pci_update_config_reg(pdev0, 0xB0,
+ ~0x08000000, 0x08000000);
+ }
+
+ cb710_pci_update_config_reg(pdev0, 0x8C,
+ ~0x00000F00, 0x00000200);
+ cb710_pci_update_config_reg(pdev0, 0x90,
+ ~0x00060000, 0x00040000);
+
+ pci_dev_put(pdev0);
+
+ return 0;
+}
+
+static irqreturn_t cb710_irq_handler(int irq, void *data)
+{
+ struct cb710_chip *chip = data;
+ struct cb710_slot *slot = &chip->slot[0];
+ irqreturn_t handled = IRQ_NONE;
+ unsigned nr;
+
+ spin_lock(&chip->irq_lock); /* incl. smp_rmb() */
+
+ for (nr = chip->slots; nr; ++slot, --nr) {
+ cb710_irq_handler_t handler_func = slot->irq_handler;
+ if (handler_func && handler_func(slot))
+ handled = IRQ_HANDLED;
+ }
+
+ spin_unlock(&chip->irq_lock);
+
+ return handled;
+}
+
+static void cb710_release_slot(struct device *dev)
+{
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ struct cb710_slot *slot = cb710_pdev_to_slot(to_platform_device(dev));
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+
+ /* slot struct can be freed now */
+ atomic_dec(&chip->slot_refs_count);
+#endif
+}
+
+static int __devinit cb710_register_slot(struct cb710_chip *chip,
+ unsigned slot_mask, unsigned io_offset, const char *name)
+{
+ int nr = chip->slots;
+ struct cb710_slot *slot = &chip->slot[nr];
+ int err;
+
+ dev_dbg(cb710_chip_dev(chip),
+ "register: %s.%d; slot %d; mask %d; IO offset: 0x%02X\n",
+ name, chip->platform_id, nr, slot_mask, io_offset);
+
+ /* slot->irq_handler == NULL here; this needs to be
+ * seen before platform_device_register() */
+ ++chip->slots;
+ smp_wmb();
+
+ slot->iobase = chip->iobase + io_offset;
+ slot->pdev.name = name;
+ slot->pdev.id = chip->platform_id;
+ slot->pdev.dev.parent = &chip->pdev->dev;
+ slot->pdev.dev.release = cb710_release_slot;
+
+ err = platform_device_register(&slot->pdev);
+
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ atomic_inc(&chip->slot_refs_count);
+#endif
+
+ if (err) {
+ /* device_initialize() called from platform_device_register()
+ * wants this on error path */
+ platform_device_put(&slot->pdev);
+
+ /* slot->irq_handler == NULL here anyway, so no lock needed */
+ --chip->slots;
+ return err;
+ }
+
+ chip->slot_mask |= slot_mask;
+
+ return 0;
+}
+
+static void cb710_unregister_slot(struct cb710_chip *chip,
+ unsigned slot_mask)
+{
+ int nr = chip->slots - 1;
+
+ if (!(chip->slot_mask & slot_mask))
+ return;
+
+ platform_device_unregister(&chip->slot[nr].pdev);
+
+ /* complementary to spin_unlock() in cb710_set_irq_handler() */
+ smp_rmb();
+ BUG_ON(chip->slot[nr].irq_handler != NULL);
+
+ /* slot->irq_handler == NULL here, so no lock needed */
+ --chip->slots;
+ chip->slot_mask &= ~slot_mask;
+}
+
+void cb710_set_irq_handler(struct cb710_slot *slot,
+ cb710_irq_handler_t handler)
+{
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ slot->irq_handler = handler;
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+EXPORT_SYMBOL_GPL(cb710_set_irq_handler);
+
+#ifdef CONFIG_PM
+
+static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+
+ free_irq(pdev->irq, chip);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ if (state.event & PM_EVENT_SLEEP)
+ pci_set_power_state(pdev, PCI_D3cold);
+ return 0;
+}
+
+static int cb710_resume(struct pci_dev *pdev)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+ int err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ return devm_request_irq(&pdev->dev, pdev->irq,
+ cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
+}
+
+#endif /* CONFIG_PM */
+
+static int __devinit cb710_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct cb710_chip *chip;
+ unsigned long flags;
+ u32 val;
+ int err;
+ int n = 0;
+
+ err = cb710_pci_configure(pdev);
+ if (err)
+ return err;
+
+ /* this is actually magic... */
+ pci_read_config_dword(pdev, 0x48, &val);
+ if (!(val & 0x80000000)) {
+ pci_write_config_dword(pdev, 0x48, val|0x71000000);
+ pci_read_config_dword(pdev, 0x48, &val);
+ }
+
+ dev_dbg(&pdev->dev, "PCI config[0x48] = 0x%08X\n", val);
+ if (!(val & 0x70000000))
+ return -ENODEV;
+ val = (val >> 28) & 7;
+ if (val & CB710_SLOT_MMC)
+ ++n;
+ if (val & CB710_SLOT_MS)
+ ++n;
+ if (val & CB710_SLOT_SM)
+ ++n;
+
+ chip = devm_kzalloc(&pdev->dev,
+ sizeof(*chip) + n * sizeof(*chip->slot), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pcim_iomap_regions(pdev, 0x0001, KBUILD_MODNAME);
+ if (err)
+ return err;
+
+ chip->pdev = pdev;
+ chip->iobase = pcim_iomap_table(pdev)[0];
+
+ pci_set_drvdata(pdev, chip);
+
+ err = devm_request_irq(&pdev->dev, pdev->irq,
+ cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err)
+ return err;
+
+ do {
+ if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
+ return -ENOMEM;
+
+ spin_lock_irqsave(&cb710_ida_lock, flags);
+ err = ida_get_new(&cb710_ida, &chip->platform_id);
+ spin_unlock_irqrestore(&cb710_ida_lock, flags);
+
+ if (err && err != -EAGAIN)
+ return err;
+ } while (err);
+
+
+ dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
+ chip->platform_id, chip->iobase, pdev->irq);
+
+ if (val & CB710_SLOT_MMC) { /* MMC/SD slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_MMC, 0x00, "cb710-mmc");
+ if (err)
+ return err;
+ }
+
+ if (val & CB710_SLOT_MS) { /* MemoryStick slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_MS, 0x40, "cb710-ms");
+ if (err)
+ goto unreg_mmc;
+ }
+
+ if (val & CB710_SLOT_SM) { /* SmartMedia slot */
+ err = cb710_register_slot(chip,
+ CB710_SLOT_SM, 0x60, "cb710-sm");
+ if (err)
+ goto unreg_ms;
+ }
+
+ return 0;
+unreg_ms:
+ cb710_unregister_slot(chip, CB710_SLOT_MS);
+unreg_mmc:
+ cb710_unregister_slot(chip, CB710_SLOT_MMC);
+
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
+#endif
+ return err;
+}
+
+static void __devexit cb710_remove_one(struct pci_dev *pdev)
+{
+ struct cb710_chip *chip = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ cb710_unregister_slot(chip, CB710_SLOT_SM);
+ cb710_unregister_slot(chip, CB710_SLOT_MS);
+ cb710_unregister_slot(chip, CB710_SLOT_MMC);
+#ifdef CONFIG_CB710_DEBUG_ASSUMPTIONS
+ BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
+#endif
+
+ spin_lock_irqsave(&cb710_ida_lock, flags);
+ ida_remove(&cb710_ida, chip->platform_id);
+ spin_unlock_irqrestore(&cb710_ida_lock, flags);
+}
+
+static const struct pci_device_id cb710_pci_tbl[] = {
+ { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+
+static struct pci_driver cb710_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = cb710_pci_tbl,
+ .probe = cb710_probe,
+ .remove = __devexit_p(cb710_remove_one),
+#ifdef CONFIG_PM
+ .suspend = cb710_suspend,
+ .resume = cb710_resume,
+#endif
+};
+
+static int __init cb710_init_module(void)
+{
+ return pci_register_driver(&cb710_driver);
+}
+
+static void __exit cb710_cleanup_module(void)
+{
+ pci_unregister_driver(&cb710_driver);
+ ida_destroy(&cb710_ida);
+}
+
+module_init(cb710_init_module);
+module_exit(cb710_cleanup_module);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ENE CB710 memory card reader driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, cb710_pci_tbl);
diff --git a/drivers/misc/cb710/debug.c b/drivers/misc/cb710/debug.c
new file mode 100644
index 000000000000..02358d086e03
--- /dev/null
+++ b/drivers/misc/cb710/debug.c
@@ -0,0 +1,119 @@
+/*
+ * cb710/debug.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * This program is free software; you can 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/cb710.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define CB710_REG_COUNT 0x80
+
+static const u16 allow[CB710_REG_COUNT/16] = {
+ 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF,
+};
+static const char *const prefix[ARRAY_SIZE(allow)] = {
+ "MMC", "MMC", "MMC", "MMC",
+ "MS?", "MS?", "SM?", "SM?"
+};
+
+static inline int allow_reg_read(unsigned block, unsigned offset, unsigned bits)
+{
+ unsigned mask = (1 << bits/8) - 1;
+ offset *= bits/8;
+ return ((allow[block] >> offset) & mask) == mask;
+}
+
+#define CB710_READ_REGS_TEMPLATE(t) \
+static void cb710_read_regs_##t(void __iomem *iobase, \
+ u##t *reg, unsigned select) \
+{ \
+ unsigned i, j; \
+ \
+ for (i = 0; i < ARRAY_SIZE(allow); ++i, reg += 16/(t/8)) { \
+ if (!(select & (1 << i))) \
+ continue; \
+ \
+ for (j = 0; j < 0x10/(t/8); ++j) { \
+ if (!allow_reg_read(i, j, t)) \
+ continue; \
+ reg[j] = ioread##t(iobase \
+ + (i << 4) + (j * (t/8))); \
+ } \
+ } \
+}
+
+static const char cb710_regf_8[] = "%02X";
+static const char cb710_regf_16[] = "%04X";
+static const char cb710_regf_32[] = "%08X";
+static const char cb710_xes[] = "xxxxxxxx";
+
+#define CB710_DUMP_REGS_TEMPLATE(t) \
+static void cb710_dump_regs_##t(struct device *dev, \
+ const u##t *reg, unsigned select) \
+{ \
+ const char *const xp = &cb710_xes[8 - t/4]; \
+ const char *const format = cb710_regf_##t; \
+ \
+ char msg[100], *p; \
+ unsigned i, j; \
+ \
+ for (i = 0; i < ARRAY_SIZE(allow); ++i, reg += 16/(t/8)) { \
+ if (!(select & (1 << i))) \
+ continue; \
+ p = msg; \
+ for (j = 0; j < 0x10/(t/8); ++j) { \
+ *p++ = ' '; \
+ if (j == 8/(t/8)) \
+ *p++ = ' '; \
+ if (allow_reg_read(i, j, t)) \
+ p += sprintf(p, format, reg[j]); \
+ else \
+ p += sprintf(p, "%s", xp); \
+ } \
+ dev_dbg(dev, "%s 0x%02X %s\n", prefix[i], i << 4, msg); \
+ } \
+}
+
+#define CB710_READ_AND_DUMP_REGS_TEMPLATE(t) \
+static void cb710_read_and_dump_regs_##t(struct cb710_chip *chip, \
+ unsigned select) \
+{ \
+ u##t regs[CB710_REG_COUNT/sizeof(u##t)]; \
+ \
+ memset(&regs, 0, sizeof(regs)); \
+ cb710_read_regs_##t(chip->iobase, regs, select); \
+ cb710_dump_regs_##t(cb710_chip_dev(chip), regs, select); \
+}
+
+#define CB710_REG_ACCESS_TEMPLATES(t) \
+ CB710_READ_REGS_TEMPLATE(t) \
+ CB710_DUMP_REGS_TEMPLATE(t) \
+ CB710_READ_AND_DUMP_REGS_TEMPLATE(t)
+
+CB710_REG_ACCESS_TEMPLATES(8)
+CB710_REG_ACCESS_TEMPLATES(16)
+CB710_REG_ACCESS_TEMPLATES(32)
+
+void cb710_dump_regs(struct cb710_chip *chip, unsigned select)
+{
+ if (!(select & CB710_DUMP_REGS_MASK))
+ select = CB710_DUMP_REGS_ALL;
+ if (!(select & CB710_DUMP_ACCESS_MASK))
+ select |= CB710_DUMP_ACCESS_8;
+
+ if (select & CB710_DUMP_ACCESS_32)
+ cb710_read_and_dump_regs_32(chip, select);
+ if (select & CB710_DUMP_ACCESS_16)
+ cb710_read_and_dump_regs_16(chip, select);
+ if (select & CB710_DUMP_ACCESS_8)
+ cb710_read_and_dump_regs_8(chip, select);
+}
+EXPORT_SYMBOL_GPL(cb710_dump_regs);
+
diff --git a/drivers/misc/cb710/sgbuf2.c b/drivers/misc/cb710/sgbuf2.c
new file mode 100644
index 000000000000..d38a7acdb6ec
--- /dev/null
+++ b/drivers/misc/cb710/sgbuf2.c
@@ -0,0 +1,150 @@
+/*
+ * cb710/sgbuf2.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * This program is free software; you can 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/cb710.h>
+
+static bool sg_dwiter_next(struct sg_mapping_iter *miter)
+{
+ if (sg_miter_next(miter)) {
+ miter->consumed = 0;
+ return true;
+ } else
+ return false;
+}
+
+static bool sg_dwiter_is_at_end(struct sg_mapping_iter *miter)
+{
+ return miter->length == miter->consumed && !sg_dwiter_next(miter);
+}
+
+static uint32_t sg_dwiter_read_buffer(struct sg_mapping_iter *miter)
+{
+ size_t len, left = 4;
+ uint32_t data;
+ void *addr = &data;
+
+ do {
+ len = min(miter->length - miter->consumed, left);
+ memcpy(addr, miter->addr + miter->consumed, len);
+ miter->consumed += len;
+ left -= len;
+ if (!left)
+ return data;
+ addr += len;
+ } while (sg_dwiter_next(miter));
+
+ memset(addr, 0, left);
+ return data;
+}
+
+static inline bool needs_unaligned_copy(const void *ptr)
+{
+#ifdef HAVE_EFFICIENT_UNALIGNED_ACCESS
+ return false;
+#else
+ return ((ptr - NULL) & 3) != 0;
+#endif
+}
+
+static bool sg_dwiter_get_next_block(struct sg_mapping_iter *miter, uint32_t **ptr)
+{
+ size_t len;
+
+ if (sg_dwiter_is_at_end(miter))
+ return true;
+
+ len = miter->length - miter->consumed;
+
+ if (likely(len >= 4 && !needs_unaligned_copy(
+ miter->addr + miter->consumed))) {
+ *ptr = miter->addr + miter->consumed;
+ miter->consumed += 4;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * cb710_sg_dwiter_read_next_block() - get next 32-bit word from sg buffer
+ * @miter: sg mapping iterator used for reading
+ *
+ * Description:
+ * Returns 32-bit word starting at byte pointed to by @miter@
+ * handling any alignment issues. Bytes past the buffer's end
+ * are not accessed (read) but are returned as zeroes. @miter@
+ * is advanced by 4 bytes or to the end of buffer whichever is
+ * closer.
+ *
+ * Context:
+ * Same requirements as in sg_miter_next().
+ *
+ * Returns:
+ * 32-bit word just read.
+ */
+uint32_t cb710_sg_dwiter_read_next_block(struct sg_mapping_iter *miter)
+{
+ uint32_t *ptr = NULL;
+
+ if (likely(sg_dwiter_get_next_block(miter, &ptr)))
+ return ptr ? *ptr : 0;
+
+ return sg_dwiter_read_buffer(miter);
+}
+EXPORT_SYMBOL_GPL(cb710_sg_dwiter_read_next_block);
+
+static void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data)
+{
+ size_t len, left = 4;
+ void *addr = &data;
+
+ do {
+ len = min(miter->length - miter->consumed, left);
+ memcpy(miter->addr, addr, len);
+ miter->consumed += len;
+ left -= len;
+ if (!left)
+ return;
+ addr += len;
+ flush_kernel_dcache_page(miter->page);
+ } while (sg_dwiter_next(miter));
+}
+
+/**
+ * cb710_sg_dwiter_write_next_block() - write next 32-bit word to sg buffer
+ * @miter: sg mapping iterator used for writing
+ *
+ * Description:
+ * Writes 32-bit word starting at byte pointed to by @miter@
+ * handling any alignment issues. Bytes which would be written
+ * past the buffer's end are silently discarded. @miter@ is
+ * advanced by 4 bytes or to the end of buffer whichever is closer.
+ *
+ * Context:
+ * Same requirements as in sg_miter_next().
+ */
+void cb710_sg_dwiter_write_next_block(struct sg_mapping_iter *miter, uint32_t data)
+{
+ uint32_t *ptr = NULL;
+
+ if (likely(sg_dwiter_get_next_block(miter, &ptr))) {
+ if (ptr)
+ *ptr = data;
+ else
+ return;
+ } else
+ sg_dwiter_write_slow(miter, data);
+
+ if (miter->length == miter->consumed)
+ flush_kernel_dcache_page(miter->page);
+}
+EXPORT_SYMBOL_GPL(cb710_sg_dwiter_write_next_block);
+
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/mmc/card/block.c b/drivers/mmc/card/block.c
index b25e9b6516ae..adc205c49fbf 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -147,7 +147,8 @@ struct mmc_blk_request {
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
{
int err;
- __be32 blocks;
+ u32 result;
+ __be32 *blocks;
struct mmc_request mrq;
struct mmc_command cmd;
@@ -199,14 +200,21 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
mrq.cmd = &cmd;
mrq.data = &data;
- sg_init_one(&sg, &blocks, 4);
+ blocks = kmalloc(4, GFP_KERNEL);
+ if (!blocks)
+ return (u32)-1;
+
+ sg_init_one(&sg, blocks, 4);
mmc_wait_for_req(card->host, &mrq);
+ result = ntohl(*blocks);
+ kfree(blocks);
+
if (cmd.error || data.error)
- return (u32)-1;
+ result = (u32)-1;
- return ntohl(blocks);
+ return result;
}
static u32 get_card_status(struct mmc_card *card, struct request *req)
@@ -243,7 +251,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.mrq.cmd = &brq.cmd;
brq.mrq.data = &brq.data;
- brq.cmd.arg = req->sector;
+ brq.cmd.arg = blk_rq_pos(req);
if (!mmc_card_blockaddr(card))
brq.cmd.arg <<= 9;
brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -251,7 +259,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.stop.opcode = MMC_STOP_TRANSMISSION;
brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- brq.data.blocks = req->nr_sectors;
+ brq.data.blocks = blk_rq_sectors(req);
/*
* The block layer doesn't support all sector count
@@ -301,7 +309,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
* Adjust the sg list so it is the same size as the
* request.
*/
- if (brq.data.blocks != req->nr_sectors) {
+ if (brq.data.blocks != blk_rq_sectors(req)) {
int i, data_size = brq.data.blocks << 9;
struct scatterlist *sg;
@@ -352,8 +360,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
printk(KERN_ERR "%s: error %d transferring data,"
" sector %u, nr %u, card status %#x\n",
req->rq_disk->disk_name, brq.data.error,
- (unsigned)req->sector,
- (unsigned)req->nr_sectors, status);
+ (unsigned)blk_rq_pos(req),
+ (unsigned)blk_rq_sectors(req), status);
}
if (brq.stop.error) {
@@ -521,7 +529,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/card/queue.c b/drivers/mmc/card/queue.c
index 7a72e75d5c67..49e582356c65 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -55,7 +55,7 @@ static int mmc_queue_thread(void *d)
spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
if (!blk_queue_plugged(q))
- req = elv_next_request(q);
+ req = blk_fetch_request(q);
mq->req = req;
spin_unlock_irq(q->queue_lock);
@@ -88,16 +88,11 @@ static void mmc_request(struct request_queue *q)
{
struct mmc_queue *mq = q->queuedata;
struct request *req;
- int ret;
if (!mq) {
printk(KERN_ERR "MMC: killing requests for dead queue\n");
- while ((req = elv_next_request(q)) != NULL) {
- do {
- ret = __blk_end_request(req, -EIO,
- blk_rq_cur_bytes(req));
- } while (ret);
- }
+ while ((req = blk_fetch_request(q)) != NULL)
+ __blk_end_request_all(req, -EIO);
return;
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 264911732756..d84c880fac84 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -708,7 +708,13 @@ static void mmc_power_up(struct mmc_host *host)
*/
mmc_delay(10);
- host->ios.clock = host->f_min;
+ if (host->f_min > 400000) {
+ pr_warning("%s: Minimum clock frequency too high for "
+ "identification mode\n", mmc_hostname(host));
+ host->ios.clock = host->f_min;
+ } else
+ host->ios.clock = 400000;
+
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
@@ -855,61 +861,72 @@ void mmc_rescan(struct work_struct *work)
mmc_bus_get(host);
- if (host->bus_ops == NULL) {
- /*
- * Only we can add a new handler, so it's safe to
- * release the lock here.
- */
+ /* if there is a card registered, check whether it is still present */
+ if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+
+ mmc_bus_put(host);
+
+
+ mmc_bus_get(host);
+
+ /* if there still is a card present, stop here */
+ if (host->bus_ops != NULL) {
mmc_bus_put(host);
+ goto out;
+ }
- if (host->ops->get_cd && host->ops->get_cd(host) == 0)
- goto out;
+ /* detect a newly inserted card */
- mmc_claim_host(host);
+ /*
+ * Only we can add a new handler, so it's safe to
+ * release the lock here.
+ */
+ mmc_bus_put(host);
- mmc_power_up(host);
- mmc_go_idle(host);
+ if (host->ops->get_cd && host->ops->get_cd(host) == 0)
+ goto out;
- mmc_send_if_cond(host, host->ocr_avail);
+ mmc_claim_host(host);
- /*
- * First we search for SDIO...
- */
- err = mmc_send_io_op_cond(host, 0, &ocr);
- if (!err) {
- if (mmc_attach_sdio(host, ocr))
- mmc_power_off(host);
- goto out;
- }
+ mmc_power_up(host);
+ mmc_go_idle(host);
- /*
- * ...then normal SD...
- */
- err = mmc_send_app_op_cond(host, 0, &ocr);
- if (!err) {
- if (mmc_attach_sd(host, ocr))
- mmc_power_off(host);
- goto out;
- }
+ mmc_send_if_cond(host, host->ocr_avail);
- /*
- * ...and finally MMC.
- */
- err = mmc_send_op_cond(host, 0, &ocr);
- if (!err) {
- if (mmc_attach_mmc(host, ocr))
- mmc_power_off(host);
- goto out;
- }
+ /*
+ * First we search for SDIO...
+ */
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_sdio(host, ocr))
+ mmc_power_off(host);
+ goto out;
+ }
- mmc_release_host(host);
- mmc_power_off(host);
- } else {
- if (host->bus_ops->detect && !host->bus_dead)
- host->bus_ops->detect(host);
+ /*
+ * ...then normal SD...
+ */
+ err = mmc_send_app_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+ goto out;
+ }
- mmc_bus_put(host);
+ /*
+ * ...and finally MMC.
+ */
+ err = mmc_send_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_mmc(host, ocr))
+ mmc_power_off(host);
+ goto out;
}
+
+ mmc_release_host(host);
+ mmc_power_off(host);
+
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index b4cf691f3f64..40111a6d8d5b 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -83,6 +83,17 @@ config MMC_SDHCI_OF
If unsure, say N.
+config MMC_SDHCI_PLTFM
+ tristate "SDHCI support on the platform specific bus"
+ depends on MMC_SDHCI
+ help
+ This selects the platform specific bus support for Secure Digital Host
+ Controller Interface.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
@@ -155,7 +166,7 @@ config MMC_ATMELMCI_DMA
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
- depends on ARCH_IMX
+ depends on ARCH_MX1
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
@@ -237,7 +248,20 @@ config MMC_SDRICOH_CS
config MMC_TMIO
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
- depends on MFD_TMIO
+ depends on MFD_TMIO || MFD_ASIC3
help
This provides support for the SD/MMC cell found in TC6393XB,
- T7L66XB and also ipaq ASIC3
+ T7L66XB and also HTC ASIC3
+
+config MMC_CB710
+ tristate "ENE CB710 MMC/SD Interface support"
+ depends on PCI
+ select CB710_CORE
+ help
+ This option enables support for MMC/SD part of ENE CB710/720 Flash
+ memory card reader found in some laptops (ie. some versions of
+ HP Compaq nx9500).
+
+ This driver can also be built as a module. If so, the module
+ will be called cb710-mmc.
+
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 970a997620e1..79da397c5fea 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
+obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
@@ -29,4 +30,8 @@ endif
obj-$(CONFIG_MMC_S3C) += s3cmci.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
+obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
+ifeq ($(CONFIG_CB710_DEBUG),y)
+ CFLAGS-cb710-mmc += -DDEBUG
+endif
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
index b58364ed6bba..fc8a0fe7c5c5 100644
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -7,6 +7,12 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+/*
+ * Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors
+ * Registers and bitfields marked with [2] are only available in MCI2
+ */
+
#ifndef __DRIVERS_MMC_ATMEL_MCI_H__
#define __DRIVERS_MMC_ATMEL_MCI_H__
@@ -14,11 +20,17 @@
#define MCI_CR 0x0000 /* Control */
# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
+# define MCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
+# define MCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */
#define MCI_MR 0x0004 /* Mode */
# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
+# define MCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
+# define MCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
+# define MCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
+# define MCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
#define MCI_DTOR 0x0008 /* Data Timeout */
# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
@@ -28,6 +40,7 @@
# define MCI_SDCSEL_MASK ( 3 << 0)
# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
+# define MCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
# define MCI_SDCBUS_MASK ( 3 << 6)
#define MCI_ARGR 0x0010 /* Command Argument */
#define MCI_CMDR 0x0014 /* Command */
@@ -56,6 +69,9 @@
#define MCI_BLKR 0x0018 /* Block */
# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */
# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
+#define MCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
+# define MCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
+# define MCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
#define MCI_RSPR 0x0020 /* Response 0 */
#define MCI_RSPR1 0x0024 /* Response 1 */
#define MCI_RSPR2 0x0028 /* Response 2 */
@@ -83,7 +99,24 @@
# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */
# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
+#define MCI_DMA 0x0050 /* DMA Configuration[2] */
+# define MCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
+# define MCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
+# define MCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
+#define MCI_CFG 0x0054 /* Configuration[2] */
+# define MCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
+# define MCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
+# define MCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
+# define MCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
+#define MCI_WPMR 0x00e4 /* Write Protection Mode[2] */
+# define MCI_WP_EN ( 1 << 0) /* WP Enable */
+# define MCI_WP_KEY (0x4d4349 << 8) /* WP Key */
+#define MCI_WPSR 0x00e8 /* Write Protection Status[2] */
+# define MCI_GET_WP_VS(x) ((x) & 0x0f)
+# define MCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
+#define MCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
+/* This is not including the FIFO Aperture on MCI2 */
#define MCI_REGS_SIZE 0x100
/* Register access macros */
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index cf6a100bb38f..7b603e4b41db 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -177,6 +177,7 @@ struct atmel_mci {
* available.
* @wp_pin: GPIO pin used for card write protect sending, or negative
* if not available.
+ * @detect_is_active_high: The state of the detect pin when it is active.
* @detect_timer: Timer used for debouncing @detect_pin interrupts.
*/
struct atmel_mci_slot {
@@ -196,6 +197,7 @@ struct atmel_mci_slot {
int detect_pin;
int wp_pin;
+ bool detect_is_active_high;
struct timer_list detect_timer;
};
@@ -924,7 +926,8 @@ static int atmci_get_cd(struct mmc_host *mmc)
struct atmel_mci_slot *slot = mmc_priv(mmc);
if (gpio_is_valid(slot->detect_pin)) {
- present = !gpio_get_value(slot->detect_pin);
+ present = !(gpio_get_value(slot->detect_pin) ^
+ slot->detect_is_active_high);
dev_dbg(&mmc->class_dev, "card is %spresent\n",
present ? "" : "not ");
}
@@ -1028,7 +1031,8 @@ static void atmci_detect_change(unsigned long data)
return;
enable_irq(gpio_to_irq(slot->detect_pin));
- present = !gpio_get_value(slot->detect_pin);
+ present = !(gpio_get_value(slot->detect_pin) ^
+ slot->detect_is_active_high);
present_old = test_bit(ATMCI_CARD_PRESENT, &slot->flags);
dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n",
@@ -1456,6 +1460,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
slot->host = host;
slot->detect_pin = slot_data->detect_pin;
slot->wp_pin = slot_data->wp_pin;
+ slot->detect_is_active_high = slot_data->detect_is_active_high;
slot->sdc_reg = sdc_reg;
mmc->ops = &atmci_ops;
@@ -1477,7 +1482,8 @@ static int __init atmci_init_slot(struct atmel_mci *host,
if (gpio_request(slot->detect_pin, "mmc_detect")) {
dev_dbg(&mmc->class_dev, "no detect pin available\n");
slot->detect_pin = -EBUSY;
- } else if (gpio_get_value(slot->detect_pin)) {
+ } else if (gpio_get_value(slot->detect_pin) ^
+ slot->detect_is_active_high) {
clear_bit(ATMCI_CARD_PRESENT, &slot->flags);
}
}
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
new file mode 100644
index 000000000000..11efefb1af51
--- /dev/null
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -0,0 +1,804 @@
+/*
+ * cb710/mmc.c
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * This program is free software; you can 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/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "cb710-mmc.h"
+
+static const u8 cb710_clock_divider_log2[8] = {
+/* 1, 2, 4, 8, 16, 32, 128, 512 */
+ 0, 1, 2, 3, 4, 5, 7, 9
+};
+#define CB710_MAX_DIVIDER_IDX \
+ (ARRAY_SIZE(cb710_clock_divider_log2) - 1)
+
+static const u8 cb710_src_freq_mhz[16] = {
+ 33, 10, 20, 25, 30, 35, 40, 45,
+ 50, 55, 60, 65, 70, 75, 80, 85
+};
+
+static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz)
+{
+ struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
+ struct pci_dev *pdev = cb710_slot_to_chip(slot)->pdev;
+ u32 src_freq_idx;
+ u32 divider_idx;
+ int src_hz;
+
+ /* this is magic, unverifiable for me, unless I get
+ * MMC card with cables connected to bus signals */
+ pci_read_config_dword(pdev, 0x48, &src_freq_idx);
+ src_freq_idx = (src_freq_idx >> 16) & 0xF;
+ src_hz = cb710_src_freq_mhz[src_freq_idx] * 1000000;
+
+ for (divider_idx = 0; divider_idx < CB710_MAX_DIVIDER_IDX; ++divider_idx) {
+ if (hz >= src_hz >> cb710_clock_divider_log2[divider_idx])
+ break;
+ }
+
+ if (src_freq_idx)
+ divider_idx |= 0x8;
+
+ cb710_pci_update_config_reg(pdev, 0x40, ~0xF0000000, divider_idx << 28);
+
+ dev_dbg(cb710_slot_dev(slot),
+ "clock set to %d Hz, wanted %d Hz; flag = %d\n",
+ src_hz >> cb710_clock_divider_log2[divider_idx & 7],
+ hz, (divider_idx & 8) != 0);
+}
+
+static void __cb710_mmc_enable_irq(struct cb710_slot *slot,
+ unsigned short enable, unsigned short mask)
+{
+ /* clear global IE
+ * - it gets set later if any interrupt sources are enabled */
+ mask |= CB710_MMC_IE_IRQ_ENABLE;
+
+ /* look like interrupt is fired whenever
+ * WORD[0x0C] & WORD[0x10] != 0;
+ * -> bit 15 port 0x0C seems to be global interrupt enable
+ */
+
+ enable = (cb710_read_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT)
+ & ~mask) | enable;
+
+ if (enable)
+ enable |= CB710_MMC_IE_IRQ_ENABLE;
+
+ cb710_write_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT, enable);
+}
+
+static void cb710_mmc_enable_irq(struct cb710_slot *slot,
+ unsigned short enable, unsigned short mask)
+{
+ struct cb710_mmc_reader *reader = mmc_priv(cb710_slot_to_mmc(slot));
+ unsigned long flags;
+
+ spin_lock_irqsave(&reader->irq_lock, flags);
+ /* this is the only thing irq_lock protects */
+ __cb710_mmc_enable_irq(slot, enable, mask);
+ spin_unlock_irqrestore(&reader->irq_lock, flags);
+}
+
+static void cb710_mmc_reset_events(struct cb710_slot *slot)
+{
+ cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, 0xFF);
+ cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, 0xFF);
+ cb710_write_port_8(slot, CB710_MMC_STATUS2_PORT, 0xFF);
+}
+
+static int cb710_mmc_is_card_inserted(struct cb710_slot *slot)
+{
+ return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT)
+ & CB710_MMC_S3_CARD_DETECTED;
+}
+
+static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable)
+{
+ dev_dbg(cb710_slot_dev(slot), "configuring %d-data-line%s mode\n",
+ enable ? 4 : 1, enable ? "s" : "");
+ if (enable)
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT,
+ CB710_MMC_C1_4BIT_DATA_BUS, 0);
+ else
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT,
+ 0, CB710_MMC_C1_4BIT_DATA_BUS);
+}
+
+static int cb710_check_event(struct cb710_slot *slot, u8 what)
+{
+ u16 status;
+
+ status = cb710_read_port_16(slot, CB710_MMC_STATUS_PORT);
+
+ if (status & CB710_MMC_S0_FIFO_UNDERFLOW) {
+ /* it is just a guess, so log it */
+ dev_dbg(cb710_slot_dev(slot),
+ "CHECK : ignoring bit 6 in status %04X\n", status);
+ cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT,
+ CB710_MMC_S0_FIFO_UNDERFLOW);
+ status &= ~CB710_MMC_S0_FIFO_UNDERFLOW;
+ }
+
+ if (status & CB710_MMC_STATUS_ERROR_EVENTS) {
+ dev_dbg(cb710_slot_dev(slot),
+ "CHECK : returning EIO on status %04X\n", status);
+ cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, status & 0xFF);
+ cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT,
+ CB710_MMC_S1_RESET);
+ return -EIO;
+ }
+
+ /* 'what' is a bit in MMC_STATUS1 */
+ if ((status >> 8) & what) {
+ cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, what);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int cb710_wait_for_event(struct cb710_slot *slot, u8 what)
+{
+ int err = 0;
+ unsigned limit = 2000000; /* FIXME: real timeout */
+
+#ifdef CONFIG_CB710_DEBUG
+ u32 e, x;
+ e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT);
+#endif
+
+ while (!(err = cb710_check_event(slot, what))) {
+ if (!--limit) {
+ cb710_dump_regs(cb710_slot_to_chip(slot),
+ CB710_DUMP_REGS_MMC);
+ err = -ETIMEDOUT;
+ break;
+ }
+ udelay(1);
+ }
+
+#ifdef CONFIG_CB710_DEBUG
+ x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT);
+
+ limit = 2000000 - limit;
+ if (limit > 100)
+ dev_dbg(cb710_slot_dev(slot),
+ "WAIT10: waited %d loops, what %d, entry val %08X, exit val %08X\n",
+ limit, what, e, x);
+#endif
+ return err < 0 ? err : 0;
+}
+
+
+static int cb710_wait_while_busy(struct cb710_slot *slot, uint8_t mask)
+{
+ unsigned limit = 500000; /* FIXME: real timeout */
+ int err = 0;
+
+#ifdef CONFIG_CB710_DEBUG
+ u32 e, x;
+ e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT);
+#endif
+
+ while (cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & mask) {
+ if (!--limit) {
+ cb710_dump_regs(cb710_slot_to_chip(slot),
+ CB710_DUMP_REGS_MMC);
+ err = -ETIMEDOUT;
+ break;
+ }
+ udelay(1);
+ }
+
+#ifdef CONFIG_CB710_DEBUG
+ x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT);
+
+ limit = 500000 - limit;
+ if (limit > 100)
+ dev_dbg(cb710_slot_dev(slot),
+ "WAIT12: waited %d loops, mask %02X, entry val %08X, exit val %08X\n",
+ limit, mask, e, x);
+#endif
+ return 0;
+}
+
+static void cb710_mmc_set_transfer_size(struct cb710_slot *slot,
+ size_t count, size_t blocksize)
+{
+ cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ cb710_write_port_32(slot, CB710_MMC_TRANSFER_SIZE_PORT,
+ ((count - 1) << 16)|(blocksize - 1));
+
+ dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n",
+ count, count == 1 ? "" : "s", blocksize);
+}
+
+static void cb710_mmc_fifo_hack(struct cb710_slot *slot)
+{
+ /* without this, received data is prepended with 8-bytes of zeroes */
+ u32 r1, r2;
+ int ok = 0;
+
+ r1 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT);
+ r2 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT);
+ if (cb710_read_port_8(slot, CB710_MMC_STATUS0_PORT)
+ & CB710_MMC_S0_FIFO_UNDERFLOW) {
+ cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT,
+ CB710_MMC_S0_FIFO_UNDERFLOW);
+ ok = 1;
+ }
+
+ dev_dbg(cb710_slot_dev(slot),
+ "FIFO-read-hack: expected STATUS0 bit was %s\n",
+ ok ? "set." : "NOT SET!");
+ dev_dbg(cb710_slot_dev(slot),
+ "FIFO-read-hack: dwords ignored: %08X %08X - %s\n",
+ r1, r2, (r1|r2) ? "BAD (NOT ZERO)!" : "ok");
+}
+
+static int cb710_mmc_receive_pio(struct cb710_slot *slot,
+ struct sg_mapping_iter *miter, size_t dw_count)
+{
+ if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & CB710_MMC_S2_FIFO_READY)) {
+ int err = cb710_wait_for_event(slot,
+ CB710_MMC_S1_PIO_TRANSFER_DONE);
+ if (err)
+ return err;
+ }
+
+ cb710_sg_dwiter_write_from_io(miter,
+ slot->iobase + CB710_MMC_DATA_PORT, dw_count);
+
+ return 0;
+}
+
+static bool cb710_is_transfer_size_supported(struct mmc_data *data)
+{
+ return !(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8));
+}
+
+static int cb710_mmc_receive(struct cb710_slot *slot, struct mmc_data *data)
+{
+ struct sg_mapping_iter miter;
+ size_t len, blocks = data->blocks;
+ int err = 0;
+
+ /* TODO: I don't know how/if the hardware handles non-16B-boundary blocks
+ * except single 8B block */
+ if (unlikely(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8)))
+ return -EINVAL;
+
+ sg_miter_start(&miter, data->sg, data->sg_len, 0);
+
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT,
+ 15, CB710_MMC_C2_READ_PIO_SIZE_MASK);
+
+ cb710_mmc_fifo_hack(slot);
+
+ while (blocks-- > 0) {
+ len = data->blksz;
+
+ while (len >= 16) {
+ err = cb710_mmc_receive_pio(slot, &miter, 4);
+ if (err)
+ goto out;
+ len -= 16;
+ }
+
+ if (!len)
+ continue;
+
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT,
+ len - 1, CB710_MMC_C2_READ_PIO_SIZE_MASK);
+
+ len = (len >= 8) ? 4 : 2;
+ err = cb710_mmc_receive_pio(slot, &miter, len);
+ if (err)
+ goto out;
+ }
+out:
+ cb710_sg_miter_stop_writing(&miter);
+ return err;
+}
+
+static int cb710_mmc_send(struct cb710_slot *slot, struct mmc_data *data)
+{
+ struct sg_mapping_iter miter;
+ size_t len, blocks = data->blocks;
+ int err = 0;
+
+ /* TODO: I don't know how/if the hardware handles multiple
+ * non-16B-boundary blocks */
+ if (unlikely(data->blocks > 1 && data->blksz & 15))
+ return -EINVAL;
+
+ sg_miter_start(&miter, data->sg, data->sg_len, 0);
+
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT,
+ 0, CB710_MMC_C2_READ_PIO_SIZE_MASK);
+
+ while (blocks-- > 0) {
+ len = (data->blksz + 15) >> 4;
+ do {
+ if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT)
+ & CB710_MMC_S2_FIFO_EMPTY)) {
+ err = cb710_wait_for_event(slot,
+ CB710_MMC_S1_PIO_TRANSFER_DONE);
+ if (err)
+ goto out;
+ }
+ cb710_sg_dwiter_read_to_io(&miter,
+ slot->iobase + CB710_MMC_DATA_PORT, 4);
+ } while (--len);
+ }
+out:
+ sg_miter_stop(&miter);
+ return err;
+}
+
+static u16 cb710_encode_cmd_flags(struct cb710_mmc_reader *reader,
+ struct mmc_command *cmd)
+{
+ unsigned int flags = cmd->flags;
+ u16 cb_flags = 0;
+
+ /* Windows driver returned 0 for commands for which no response
+ * is expected. It happened that there were only two such commands
+ * used: MMC_GO_IDLE_STATE and MMC_GO_INACTIVE_STATE so it might
+ * as well be a bug in that driver.
+ *
+ * Original driver set bit 14 for MMC/SD application
+ * commands. There's no difference 'on the wire' and
+ * it apparently works without it anyway.
+ */
+
+ switch (flags & MMC_CMD_MASK) {
+ case MMC_CMD_AC: cb_flags = CB710_MMC_CMD_AC; break;
+ case MMC_CMD_ADTC: cb_flags = CB710_MMC_CMD_ADTC; break;
+ case MMC_CMD_BC: cb_flags = CB710_MMC_CMD_BC; break;
+ case MMC_CMD_BCR: cb_flags = CB710_MMC_CMD_BCR; break;
+ }
+
+ if (flags & MMC_RSP_BUSY)
+ cb_flags |= CB710_MMC_RSP_BUSY;
+
+ cb_flags |= cmd->opcode << CB710_MMC_CMD_CODE_SHIFT;
+
+ if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
+ cb_flags |= CB710_MMC_DATA_READ;
+
+ if (flags & MMC_RSP_PRESENT) {
+ /* Windows driver set 01 at bits 4,3 except for
+ * MMC_SET_BLOCKLEN where it set 10. Maybe the
+ * hardware can do something special about this
+ * command? The original driver looks buggy/incomplete
+ * anyway so we ignore this for now.
+ *
+ * I assume that 00 here means no response is expected.
+ */
+ cb_flags |= CB710_MMC_RSP_PRESENT;
+
+ if (flags & MMC_RSP_136)
+ cb_flags |= CB710_MMC_RSP_136;
+ if (!(flags & MMC_RSP_CRC))
+ cb_flags |= CB710_MMC_RSP_NO_CRC;
+ }
+
+ return cb_flags;
+}
+
+static void cb710_receive_response(struct cb710_slot *slot,
+ struct mmc_command *cmd)
+{
+ unsigned rsp_opcode, wanted_opcode;
+
+ /* Looks like final byte with CRC is always stripped (same as SDHCI) */
+ if (cmd->flags & MMC_RSP_136) {
+ u32 resp[4];
+
+ resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE3_PORT);
+ resp[1] = cb710_read_port_32(slot, CB710_MMC_RESPONSE2_PORT);
+ resp[2] = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT);
+ resp[3] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT);
+ rsp_opcode = resp[0] >> 24;
+
+ cmd->resp[0] = (resp[0] << 8)|(resp[1] >> 24);
+ cmd->resp[1] = (resp[1] << 8)|(resp[2] >> 24);
+ cmd->resp[2] = (resp[2] << 8)|(resp[3] >> 24);
+ cmd->resp[3] = (resp[3] << 8);
+ } else {
+ rsp_opcode = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT) & 0x3F;
+ cmd->resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT);
+ }
+
+ wanted_opcode = (cmd->flags & MMC_RSP_OPCODE) ? cmd->opcode : 0x3F;
+ if (rsp_opcode != wanted_opcode)
+ cmd->error = -EILSEQ;
+}
+
+static int cb710_mmc_transfer_data(struct cb710_slot *slot,
+ struct mmc_data *data)
+{
+ int error, to;
+
+ if (data->flags & MMC_DATA_READ)
+ error = cb710_mmc_receive(slot, data);
+ else
+ error = cb710_mmc_send(slot, data);
+
+ to = cb710_wait_for_event(slot, CB710_MMC_S1_DATA_TRANSFER_DONE);
+ if (!error)
+ error = to;
+
+ if (!error)
+ data->bytes_xfered = data->blksz * data->blocks;
+ return error;
+}
+
+static int cb710_mmc_command(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+ struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ struct mmc_data *data = cmd->data;
+
+ u16 cb_cmd = cb710_encode_cmd_flags(reader, cmd);
+ dev_dbg(cb710_slot_dev(slot), "cmd request: 0x%04X\n", cb_cmd);
+
+ if (data) {
+ if (!cb710_is_transfer_size_supported(data)) {
+ data->error = -EINVAL;
+ return -1;
+ }
+ cb710_mmc_set_transfer_size(slot, data->blocks, data->blksz);
+ }
+
+ cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20|CB710_MMC_S2_BUSY_10);
+ cb710_write_port_16(slot, CB710_MMC_CMD_TYPE_PORT, cb_cmd);
+ cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ cb710_write_port_32(slot, CB710_MMC_CMD_PARAM_PORT, cmd->arg);
+ cb710_mmc_reset_events(slot);
+ cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x01, 0);
+
+ cmd->error = cb710_wait_for_event(slot, CB710_MMC_S1_COMMAND_SENT);
+ if (cmd->error)
+ return -1;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ cb710_receive_response(slot, cmd);
+ if (cmd->error)
+ return -1;
+ }
+
+ if (data)
+ data->error = cb710_mmc_transfer_data(slot, data);
+ return 0;
+}
+
+static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+
+ WARN_ON(reader->mrq != NULL);
+
+ reader->mrq = mrq;
+ cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0);
+
+ if (cb710_mmc_is_card_inserted(slot)) {
+ if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
+ cb710_mmc_command(mmc, mrq->stop);
+ mdelay(1);
+ } else {
+ mrq->cmd->error = -ENOMEDIUM;
+ }
+
+ tasklet_schedule(&reader->finish_req_tasklet);
+}
+
+static int cb710_mmc_powerup(struct cb710_slot *slot)
+{
+#ifdef CONFIG_CB710_DEBUG
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+#endif
+ int err;
+
+ /* a lot of magic; see comment in cb710_mmc_set_clock() */
+ dev_dbg(cb710_slot_dev(slot), "bus powerup\n");
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ if (unlikely(err))
+ return err;
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x80, 0);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x80, 0);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ mdelay(1);
+ dev_dbg(cb710_slot_dev(slot), "after delay 1\n");
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ if (unlikely(err))
+ return err;
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x09, 0);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ mdelay(1);
+ dev_dbg(cb710_slot_dev(slot), "after delay 2\n");
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ if (unlikely(err))
+ return err;
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x08);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ mdelay(2);
+ dev_dbg(cb710_slot_dev(slot), "after delay 3\n");
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x70, 0);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 0x80, 0);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x03, 0);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
+ if (unlikely(err))
+ return err;
+ /* This port behaves weird: quick byte reads of 0x08,0x09 return
+ * 0xFF,0x00 after writing 0xFFFF to 0x08; it works correctly when
+ * read/written from userspace... What am I missing here?
+ * (it doesn't depend on write-to-read delay) */
+ cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0xFFFF);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+ dev_dbg(cb710_slot_dev(slot), "bus powerup finished\n");
+
+ return cb710_check_event(slot, 0);
+}
+
+static void cb710_mmc_powerdown(struct cb710_slot *slot)
+{
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x81);
+ cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0, 0x80);
+}
+
+static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ int err;
+
+ cb710_mmc_set_clock(mmc, ios->clock);
+
+ if (!cb710_mmc_is_card_inserted(slot)) {
+ dev_dbg(cb710_slot_dev(slot),
+ "no card inserted - ignoring bus powerup request\n");
+ ios->power_mode = MMC_POWER_OFF;
+ }
+
+ if (ios->power_mode != reader->last_power_mode)
+ switch (ios->power_mode) {
+ case MMC_POWER_ON:
+ err = cb710_mmc_powerup(slot);
+ if (err) {
+ dev_warn(cb710_slot_dev(slot),
+ "powerup failed (%d)- retrying\n", err);
+ cb710_mmc_powerdown(slot);
+ udelay(1);
+ err = cb710_mmc_powerup(slot);
+ if (err)
+ dev_warn(cb710_slot_dev(slot),
+ "powerup retry failed (%d) - expect errors\n",
+ err);
+ }
+ reader->last_power_mode = MMC_POWER_ON;
+ break;
+ case MMC_POWER_OFF:
+ cb710_mmc_powerdown(slot);
+ reader->last_power_mode = MMC_POWER_OFF;
+ break;
+ case MMC_POWER_UP:
+ default:
+ /* ignore */;
+ }
+
+ cb710_mmc_enable_4bit_data(slot, ios->bus_width != MMC_BUS_WIDTH_1);
+
+ cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0);
+}
+
+static int cb710_mmc_get_ro(struct mmc_host *mmc)
+{
+ struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
+
+ return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT)
+ & CB710_MMC_S3_WRITE_PROTECTED;
+}
+
+static int cb710_mmc_irq_handler(struct cb710_slot *slot)
+{
+ struct mmc_host *mmc = cb710_slot_to_mmc(slot);
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ u32 status, config1, config2, irqen;
+
+ status = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT);
+ irqen = cb710_read_port_32(slot, CB710_MMC_IRQ_ENABLE_PORT);
+ config2 = cb710_read_port_32(slot, CB710_MMC_CONFIGB_PORT);
+ config1 = cb710_read_port_32(slot, CB710_MMC_CONFIG_PORT);
+
+ dev_dbg(cb710_slot_dev(slot), "interrupt; status: %08X, "
+ "ie: %08X, c2: %08X, c1: %08X\n",
+ status, irqen, config2, config1);
+
+ if (status & (CB710_MMC_S1_CARD_CHANGED << 8)) {
+ /* ack the event */
+ cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT,
+ CB710_MMC_S1_CARD_CHANGED);
+ if ((irqen & CB710_MMC_IE_CISTATUS_MASK)
+ == CB710_MMC_IE_CISTATUS_MASK)
+ mmc_detect_change(mmc, HZ/5);
+ } else {
+ dev_dbg(cb710_slot_dev(slot), "unknown interrupt (test)\n");
+ spin_lock(&reader->irq_lock);
+ __cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_TEST_MASK);
+ spin_unlock(&reader->irq_lock);
+ }
+
+ return 1;
+}
+
+static void cb710_mmc_finish_request_tasklet(unsigned long data)
+{
+ struct mmc_host *mmc = (void *)data;
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ struct mmc_request *mrq = reader->mrq;
+
+ reader->mrq = NULL;
+ mmc_request_done(mmc, mrq);
+}
+
+static const struct mmc_host_ops cb710_mmc_host = {
+ .request = cb710_mmc_request,
+ .set_ios = cb710_mmc_set_ios,
+ .get_ro = cb710_mmc_get_ro
+};
+
+#ifdef CONFIG_PM
+
+static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
+ struct mmc_host *mmc = cb710_slot_to_mmc(slot);
+ int err;
+
+ err = mmc_suspend_host(mmc, state);
+ if (err)
+ return err;
+
+ cb710_mmc_enable_irq(slot, 0, ~0);
+ return 0;
+}
+
+static int cb710_mmc_resume(struct platform_device *pdev)
+{
+ struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
+ struct mmc_host *mmc = cb710_slot_to_mmc(slot);
+
+ cb710_mmc_enable_irq(slot, 0, ~0);
+
+ return mmc_resume_host(mmc);
+}
+
+#endif /* CONFIG_PM */
+
+static int __devinit cb710_mmc_init(struct platform_device *pdev)
+{
+ struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
+ struct cb710_chip *chip = cb710_slot_to_chip(slot);
+ struct mmc_host *mmc;
+ struct cb710_mmc_reader *reader;
+ int err;
+ u32 val;
+
+ mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot));
+ if (!mmc)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, mmc);
+
+ /* harmless (maybe) magic */
+ pci_read_config_dword(chip->pdev, 0x48, &val);
+ val = cb710_src_freq_mhz[(val >> 16) & 0xF];
+ dev_dbg(cb710_slot_dev(slot), "source frequency: %dMHz\n", val);
+ val *= 1000000;
+
+ mmc->ops = &cb710_mmc_host;
+ mmc->f_max = val;
+ mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
+ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ reader = mmc_priv(mmc);
+
+ tasklet_init(&reader->finish_req_tasklet,
+ cb710_mmc_finish_request_tasklet, (unsigned long)mmc);
+ spin_lock_init(&reader->irq_lock);
+ cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
+
+ cb710_mmc_enable_irq(slot, 0, ~0);
+ cb710_set_irq_handler(slot, cb710_mmc_irq_handler);
+
+ err = mmc_add_host(mmc);
+ if (unlikely(err))
+ goto err_free_mmc;
+
+ dev_dbg(cb710_slot_dev(slot), "mmc_hostname is %s\n",
+ mmc_hostname(mmc));
+
+ cb710_mmc_enable_irq(slot, CB710_MMC_IE_CARD_INSERTION_STATUS, 0);
+
+ return 0;
+
+err_free_mmc:
+ dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err);
+
+ mmc_free_host(mmc);
+ return err;
+}
+
+static int __devexit cb710_mmc_exit(struct platform_device *pdev)
+{
+ struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
+ struct mmc_host *mmc = cb710_slot_to_mmc(slot);
+ struct cb710_mmc_reader *reader = mmc_priv(mmc);
+
+ cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_CARD_INSERTION_STATUS);
+
+ mmc_remove_host(mmc);
+
+ /* IRQs should be disabled now, but let's stay on the safe side */
+ cb710_mmc_enable_irq(slot, 0, ~0);
+ cb710_set_irq_handler(slot, NULL);
+
+ /* clear config ports - just in case */
+ cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0);
+ cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
+
+ tasklet_kill(&reader->finish_req_tasklet);
+
+ mmc_free_host(mmc);
+ return 0;
+}
+
+static struct platform_driver cb710_mmc_driver = {
+ .driver.name = "cb710-mmc",
+ .probe = cb710_mmc_init,
+ .remove = __devexit_p(cb710_mmc_exit),
+#ifdef CONFIG_PM
+ .suspend = cb710_mmc_suspend,
+ .resume = cb710_mmc_resume,
+#endif
+};
+
+static int __init cb710_mmc_init_module(void)
+{
+ return platform_driver_register(&cb710_mmc_driver);
+}
+
+static void __exit cb710_mmc_cleanup_module(void)
+{
+ platform_driver_unregister(&cb710_mmc_driver);
+}
+
+module_init(cb710_mmc_init_module);
+module_exit(cb710_mmc_cleanup_module);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cb710-mmc");
diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
new file mode 100644
index 000000000000..e845c776bdd7
--- /dev/null
+++ b/drivers/mmc/host/cb710-mmc.h
@@ -0,0 +1,104 @@
+/*
+ * cb710/cb710-mmc.h
+ *
+ * Copyright by Michał Mirosław, 2008-2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef LINUX_CB710_MMC_H
+#define LINUX_CB710_MMC_H
+
+#include <linux/cb710.h>
+
+/* per-MMC-reader structure */
+struct cb710_mmc_reader {
+ struct tasklet_struct finish_req_tasklet;
+ struct mmc_request *mrq;
+ spinlock_t irq_lock;
+ unsigned char last_power_mode;
+};
+
+/* some device struct walking */
+
+static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot)
+{
+ return dev_get_drvdata(&slot->pdev.dev);
+}
+
+static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc)
+{
+ struct platform_device *pdev = container_of(mmc_dev(mmc),
+ struct platform_device, dev);
+ return cb710_pdev_to_slot(pdev);
+}
+
+/* registers (this might be all wrong ;) */
+
+#define CB710_MMC_DATA_PORT 0x00
+
+#define CB710_MMC_CONFIG_PORT 0x04
+#define CB710_MMC_CONFIG0_PORT 0x04
+#define CB710_MMC_CONFIG1_PORT 0x05
+#define CB710_MMC_C1_4BIT_DATA_BUS 0x40
+#define CB710_MMC_CONFIG2_PORT 0x06
+#define CB710_MMC_C2_READ_PIO_SIZE_MASK 0x0F /* N-1 */
+#define CB710_MMC_CONFIG3_PORT 0x07
+
+#define CB710_MMC_CONFIGB_PORT 0x08
+
+#define CB710_MMC_IRQ_ENABLE_PORT 0x0C
+#define CB710_MMC_IE_TEST_MASK 0x00BF
+#define CB710_MMC_IE_CARD_INSERTION_STATUS 0x1000
+#define CB710_MMC_IE_IRQ_ENABLE 0x8000
+#define CB710_MMC_IE_CISTATUS_MASK \
+ (CB710_MMC_IE_CARD_INSERTION_STATUS|CB710_MMC_IE_IRQ_ENABLE)
+
+#define CB710_MMC_STATUS_PORT 0x10
+#define CB710_MMC_STATUS_ERROR_EVENTS 0x60FF
+#define CB710_MMC_STATUS0_PORT 0x10
+#define CB710_MMC_S0_FIFO_UNDERFLOW 0x40
+#define CB710_MMC_STATUS1_PORT 0x11
+#define CB710_MMC_S1_COMMAND_SENT 0x01
+#define CB710_MMC_S1_DATA_TRANSFER_DONE 0x02
+#define CB710_MMC_S1_PIO_TRANSFER_DONE 0x04
+#define CB710_MMC_S1_CARD_CHANGED 0x10
+#define CB710_MMC_S1_RESET 0x20
+#define CB710_MMC_STATUS2_PORT 0x12
+#define CB710_MMC_S2_FIFO_READY 0x01
+#define CB710_MMC_S2_FIFO_EMPTY 0x02
+#define CB710_MMC_S2_BUSY_10 0x10
+#define CB710_MMC_S2_BUSY_20 0x20
+#define CB710_MMC_STATUS3_PORT 0x13
+#define CB710_MMC_S3_CARD_DETECTED 0x02
+#define CB710_MMC_S3_WRITE_PROTECTED 0x04
+
+#define CB710_MMC_CMD_TYPE_PORT 0x14
+#define CB710_MMC_RSP_TYPE_MASK 0x0007
+#define CB710_MMC_RSP_R1 (0)
+#define CB710_MMC_RSP_136 (5)
+#define CB710_MMC_RSP_NO_CRC (2)
+#define CB710_MMC_RSP_PRESENT_MASK 0x0018
+#define CB710_MMC_RSP_NONE (0 << 3)
+#define CB710_MMC_RSP_PRESENT (1 << 3)
+#define CB710_MMC_RSP_PRESENT_X (2 << 3)
+#define CB710_MMC_CMD_TYPE_MASK 0x0060
+#define CB710_MMC_CMD_BC (0 << 5)
+#define CB710_MMC_CMD_BCR (1 << 5)
+#define CB710_MMC_CMD_AC (2 << 5)
+#define CB710_MMC_CMD_ADTC (3 << 5)
+#define CB710_MMC_DATA_READ 0x0080
+#define CB710_MMC_CMD_CODE_MASK 0x3F00
+#define CB710_MMC_CMD_CODE_SHIFT 8
+#define CB710_MMC_IS_APP_CMD 0x4000
+#define CB710_MMC_RSP_BUSY 0x8000
+
+#define CB710_MMC_CMD_PARAM_PORT 0x18
+#define CB710_MMC_TRANSFER_SIZE_PORT 0x1C
+#define CB710_MMC_RESPONSE0_PORT 0x20
+#define CB710_MMC_RESPONSE1_PORT 0x24
+#define CB710_MMC_RESPONSE2_PORT 0x28
+#define CB710_MMC_RESPONSE3_PORT 0x2C
+
+#endif /* LINUX_CB710_MMC_H */
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index f48349d18c92..240608cc7ae9 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -97,6 +97,14 @@
*/
#define r1b_timeout (HZ * 3)
+/* One of the critical speed parameters is the amount of data which may
+ * be transfered in one command. If this value is too low, the SD card
+ * controller has to do multiple partial block writes (argggh!). With
+ * today (2008) SD cards there is little speed gain if we transfer more
+ * than 64 KBytes at a time. So use this value until there is any indication
+ * that we should do more here.
+ */
+#define MMC_SPI_BLOCKSATONCE 128
/****************************************************************************/
@@ -327,15 +335,16 @@ checkstatus:
/* Status byte: the entire seven-bit R1 response. */
if (cmd->resp[0] != 0) {
- if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
- | R1_SPI_ILLEGAL_COMMAND)
+ if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS)
& cmd->resp[0])
- value = -EINVAL;
+ value = -EFAULT; /* Bad address */
+ else if (R1_SPI_ILLEGAL_COMMAND & cmd->resp[0])
+ value = -ENOSYS; /* Function not implemented */
else if (R1_SPI_COM_CRC & cmd->resp[0])
- value = -EILSEQ;
+ value = -EILSEQ; /* Illegal byte sequence */
else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
& cmd->resp[0])
- value = -EIO;
+ value = -EIO; /* I/O error */
/* else R1_SPI_IDLE, "it's resetting" */
}
@@ -1366,6 +1375,10 @@ static int mmc_spi_probe(struct spi_device *spi)
mmc->ops = &mmc_spi_ops;
mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
+ mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE;
+ mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE;
+ mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE;
+ mmc->max_blk_count = MMC_SPI_BLOCKSATONCE;
mmc->caps = MMC_CAP_SPI;
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 36875dcfa492..e1aa8471ab1c 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;
@@ -546,7 +546,7 @@ 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->base = ioremap(dev->res.start, SZ_4K);
+ host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
goto clk_disable;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index c643d0fe118f..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);
@@ -882,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..bc14bb1b0579 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,7 @@ 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;
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 bfa25c01c872..e7a331de5733 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -822,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;
}
@@ -1593,7 +1593,6 @@ static int mmc_omap_resume(struct platform_device *pdev)
#endif
static struct platform_driver mmc_omap_driver = {
- .probe = mmc_omap_probe,
.remove = mmc_omap_remove,
.suspend = mmc_omap_suspend,
.resume = mmc_omap_resume,
@@ -1605,7 +1604,7 @@ static struct platform_driver mmc_omap_driver = {
static int __init mmc_omap_init(void)
{
- return platform_driver_register(&mmc_omap_driver);
+ return platform_driver_probe(&mmc_omap_driver, mmc_omap_probe);
}
static void __exit mmc_omap_exit(void)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e62a22a7f00c..1cf9cfb3b64f 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);
}
@@ -1073,7 +1073,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
- mmc->ocr_avail = mmc_slot(host).ocr_mask;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
if (pdata->slots[host->slot_id].wires >= 8)
@@ -1110,13 +1109,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err_irq;
}
+ /* initialize power supplies, gpios, etc */
if (pdata->init != NULL) {
if (pdata->init(&pdev->dev) != 0) {
- dev_dbg(mmc_dev(host->mmc),
- "Unable to configure MMC IRQs\n");
+ dev_dbg(mmc_dev(host->mmc), "late init error\n");
goto err_irq_cd_init;
}
}
+ mmc->ocr_avail = mmc_slot(host).ocr_mask;
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq)) {
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 430095725f9f..d7d7109ef47e 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -27,6 +27,7 @@
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/io.h>
+#include <linux/regulator/consumer.h>
#include <asm/sizes.h>
@@ -67,8 +68,42 @@ struct pxamci_host {
unsigned int dma_dir;
unsigned int dma_drcmrrx;
unsigned int dma_drcmrtx;
+
+ struct regulator *vcc;
};
+static inline void pxamci_init_ocr(struct pxamci_host *host)
+{
+#ifdef CONFIG_REGULATOR
+ host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
+
+ if (IS_ERR(host->vcc))
+ host->vcc = NULL;
+ else {
+ host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
+ if (host->pdata && host->pdata->ocr_mask)
+ dev_warn(mmc_dev(host->mmc),
+ "ocr_mask/setpower will not be used\n");
+ }
+#endif
+ if (host->vcc == NULL) {
+ /* fall-back to platform data */
+ host->mmc->ocr_avail = host->pdata ?
+ host->pdata->ocr_mask :
+ MMC_VDD_32_33 | MMC_VDD_33_34;
+ }
+}
+
+static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
+{
+#ifdef CONFIG_REGULATOR
+ if (host->vcc)
+ mmc_regulator_set_ocr(host->vcc, vdd);
+#endif
+ if (!host->vcc && host->pdata && host->pdata->setpower)
+ host->pdata->setpower(mmc_dev(host->mmc), vdd);
+}
+
static void pxamci_stop_clock(struct pxamci_host *host)
{
if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
@@ -438,8 +473,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->power_mode != ios->power_mode) {
host->power_mode = ios->power_mode;
- if (host->pdata && host->pdata->setpower)
- host->pdata->setpower(mmc_dev(mmc), ios->vdd);
+ pxamci_set_power(host, ios->vdd);
if (ios->power_mode == MMC_POWER_ON)
host->cmdat |= CMDAT_INIT;
@@ -562,9 +596,8 @@ static int pxamci_probe(struct platform_device *pdev)
mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000
: host->clkrate;
- mmc->ocr_avail = host->pdata ?
- host->pdata->ocr_mask :
- MMC_VDD_32_33|MMC_VDD_33_34;
+ pxamci_init_ocr(host);
+
mmc->caps = 0;
host->cmdat = 0;
if (!cpu_is_pxa25x()) {
@@ -661,6 +694,9 @@ static int pxamci_remove(struct platform_device *pdev)
if (mmc) {
struct pxamci_host *host = mmc_priv(mmc);
+ if (host->vcc)
+ regulator_put(host->vcc);
+
if (host->pdata && host->pdata->exit)
host->pdata->exit(&pdev->dev, mmc);
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 2db166b7096f..4eb4f37544ab 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -17,6 +17,7 @@
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
#include <linux/cpufreq.h>
+#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/io.h>
@@ -789,7 +790,7 @@ static void s3cmci_dma_setup(struct s3cmci_host *host,
last_source = source;
- s3c2410_dma_devconfig(host->dma, source, 3,
+ s3c2410_dma_devconfig(host->dma, source,
host->mem->start + host->sdidata);
if (!setup_ok) {
@@ -1121,7 +1122,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
default:
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
- s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPIO_OUTPUT);
if (host->is2440)
mci_con |= S3C2440_SDICON_SDRESET;
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-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
new file mode 100644
index 000000000000..297f40ae6ad5
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -0,0 +1,168 @@
+/*
+ * sdhci-pltfm.c Support for SDHCI platform devices
+ * 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:
+ * SDHCI platform devices
+ *
+ * Inspired by sdhci-pci.c, by Pierre Ossman
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/platform_device.h>
+
+#include <linux/mmc/host.h>
+
+#include <linux/io.h>
+
+#include "sdhci.h"
+
+/*****************************************************************************\
+ * *
+ * SDHCI core callbacks *
+ * *
+\*****************************************************************************/
+
+static struct sdhci_ops sdhci_pltfm_ops = {
+};
+
+/*****************************************************************************\
+ * *
+ * Device probing/removal *
+ * *
+\*****************************************************************************/
+
+static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct resource *iomem;
+ int ret;
+
+ BUG_ON(pdev == NULL);
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (resource_size(iomem) != 0x100)
+ dev_err(&pdev->dev, "Invalid iomem size. You may "
+ "experience problems.\n");
+
+ if (pdev->dev.parent)
+ host = sdhci_alloc_host(pdev->dev.parent, 0);
+ else
+ host = sdhci_alloc_host(&pdev->dev, 0);
+
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto err;
+ }
+
+ host->hw_name = "platform";
+ host->ops = &sdhci_pltfm_ops;
+ host->irq = platform_get_irq(pdev, 0);
+
+ if (!request_mem_region(iomem->start, resource_size(iomem),
+ mmc_hostname(host->mmc))) {
+ dev_err(&pdev->dev, "cannot request region\n");
+ ret = -EBUSY;
+ goto err_request;
+ }
+
+ host->ioaddr = ioremap(iomem->start, resource_size(iomem));
+ if (!host->ioaddr) {
+ dev_err(&pdev->dev, "failed to remap registers\n");
+ ret = -ENOMEM;
+ goto err_remap;
+ }
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_add_host;
+
+ platform_set_drvdata(pdev, host);
+
+ return 0;
+
+err_add_host:
+ iounmap(host->ioaddr);
+err_remap:
+ release_mem_region(iomem->start, resource_size(iomem));
+err_request:
+ sdhci_free_host(host);
+err:
+ printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret);
+ return ret;
+}
+
+static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+
+ sdhci_remove_host(host, dead);
+ iounmap(host->ioaddr);
+ release_mem_region(iomem->start, resource_size(iomem));
+ sdhci_free_host(host);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_pltfm_driver = {
+ .driver = {
+ .name = "sdhci",
+ .owner = THIS_MODULE,
+ },
+ .probe = sdhci_pltfm_probe,
+ .remove = __devexit_p(sdhci_pltfm_remove),
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+ return platform_driver_register(&sdhci_pltfm_driver);
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+ platform_driver_unregister(&sdhci_pltfm_driver);
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sdhci");
+
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9234be2226e7..35789c6edc19 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -78,6 +78,11 @@ static void sdhci_dumpregs(struct sdhci_host *host)
sdhci_readl(host, SDHCI_CAPABILITIES),
sdhci_readl(host, SDHCI_MAX_CURRENT));
+ if (host->flags & SDHCI_USE_ADMA)
+ printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+
printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
}
@@ -1005,12 +1010,34 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
{
u8 pwr;
- if (host->power == power)
+ if (power == (unsigned short)-1)
+ pwr = 0;
+ else {
+ switch (1 << power) {
+ case MMC_VDD_165_195:
+ pwr = SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr = SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr = SDHCI_POWER_330;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (host->pwr == pwr)
return;
- if (power == (unsigned short)-1) {
+ host->pwr = pwr;
+
+ if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
- goto out;
+ return;
}
/*
@@ -1020,35 +1047,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
- pwr = SDHCI_POWER_ON;
-
- switch (1 << power) {
- case MMC_VDD_165_195:
- pwr |= SDHCI_POWER_180;
- break;
- case MMC_VDD_29_30:
- case MMC_VDD_30_31:
- pwr |= SDHCI_POWER_300;
- break;
- case MMC_VDD_32_33:
- case MMC_VDD_33_34:
- pwr |= SDHCI_POWER_330;
- break;
- default:
- BUG();
- }
-
/*
* At least the Marvell CaFe chip gets confused if we set the voltage
* and set turn on power at the same time, so set the voltage first.
*/
if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
- sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ pwr |= SDHCI_POWER_ON;
-out:
- host->power = power;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
}
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 65c6f996bbd3..2de08349c3ca 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -255,7 +255,7 @@ struct sdhci_host {
unsigned int timeout_clk; /* Timeout freq (KHz) */
unsigned int clock; /* Current clock (MHz) */
- unsigned short power; /* Current voltage */
+ u8 pwr; /* Current voltage */
struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 63fbd5b7d312..91991b460c45 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -10,7 +10,7 @@
*
* Driver for the MMC / SD / SDIO cell found in:
*
- * TC6393XB TC6391XB TC6387XB T7L66XB
+ * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3
*
* This driver draws mainly on scattered spec sheets, Reverse engineering
* of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit
@@ -35,69 +35,47 @@
#include "tmio_mmc.h"
-/*
- * Fixme - documentation conflicts on what the clock values are for the
- * various dividers.
- * One document I have says that its a divisor of a 24MHz clock, another 33.
- * This probably depends on HCLK for a given platform, so we may need to
- * require HCLK be passed to us from the MFD core.
- *
- */
-
static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
{
- void __iomem *cnf = host->cnf;
- void __iomem *ctl = host->ctl;
u32 clk = 0, clock;
if (new_clock) {
- for (clock = 46875, clk = 0x100; new_clock >= (clock<<1); ) {
+ for (clock = host->mmc->f_min, clk = 0x80000080;
+ new_clock >= (clock<<1); clk >>= 1)
clock <<= 1;
- clk >>= 1;
- }
- if (clk & 0x1)
- clk = 0x20000;
-
- clk >>= 2;
- tmio_iowrite8((clk & 0x8000) ? 0 : 1, cnf + CNF_SD_CLK_MODE);
clk |= 0x100;
}
- tmio_iowrite16(clk, ctl + CTL_SD_CARD_CLK_CTL);
+ sd_config_write8(host, CNF_SD_CLK_MODE, clk >> 22);
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
}
static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
{
- void __iomem *ctl = host->ctl;
-
- tmio_iowrite16(0x0000, ctl + CTL_CLK_AND_WAIT_CTL);
+ sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
msleep(10);
- tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) & ~0x0100,
- ctl + CTL_SD_CARD_CLK_CTL);
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
msleep(10);
}
static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
{
- void __iomem *ctl = host->ctl;
-
- tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) | 0x0100,
- ctl + CTL_SD_CARD_CLK_CTL);
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
msleep(10);
- tmio_iowrite16(0x0100, ctl + CTL_CLK_AND_WAIT_CTL);
+ sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
msleep(10);
}
static void reset(struct tmio_mmc_host *host)
{
- void __iomem *ctl = host->ctl;
-
/* FIXME - should we set stop clock reg here */
- tmio_iowrite16(0x0000, ctl + CTL_RESET_SD);
- tmio_iowrite16(0x0000, ctl + CTL_RESET_SDIO);
+ sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
+ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
msleep(10);
- tmio_iowrite16(0x0001, ctl + CTL_RESET_SD);
- tmio_iowrite16(0x0001, ctl + CTL_RESET_SDIO);
+ sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
msleep(10);
}
@@ -129,13 +107,12 @@ tmio_mmc_finish_request(struct tmio_mmc_host *host)
static int
tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
{
- void __iomem *ctl = host->ctl;
struct mmc_data *data = host->data;
int c = cmd->opcode;
/* Command 12 is handled by hardware */
if (cmd->opcode == 12 && !cmd->arg) {
- tmio_iowrite16(0x001, ctl + CTL_STOP_INTERNAL_ACTION);
+ sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001);
return 0;
}
@@ -160,18 +137,18 @@ tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
if (data) {
c |= DATA_PRESENT;
if (data->blocks > 1) {
- tmio_iowrite16(0x100, ctl + CTL_STOP_INTERNAL_ACTION);
+ sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100);
c |= TRANSFER_MULTI;
}
if (data->flags & MMC_DATA_READ)
c |= TRANSFER_READ;
}
- enable_mmc_irqs(ctl, TMIO_MASK_CMD);
+ enable_mmc_irqs(host, TMIO_MASK_CMD);
/* Fire off the command */
- tmio_iowrite32(cmd->arg, ctl + CTL_ARG_REG);
- tmio_iowrite16(c, ctl + CTL_SD_CMD);
+ sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg);
+ sd_ctrl_write16(host, CTL_SD_CMD, c);
return 0;
}
@@ -183,7 +160,6 @@ tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
*/
static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
{
- void __iomem *ctl = host->ctl;
struct mmc_data *data = host->data;
unsigned short *buf;
unsigned int count;
@@ -206,9 +182,9 @@ static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
/* Transfer the data */
if (data->flags & MMC_DATA_READ)
- tmio_ioread16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1);
+ sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
else
- tmio_iowrite16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1);
+ sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
host->sg_off += count;
@@ -222,7 +198,6 @@ static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host)
{
- void __iomem *ctl = host->ctl;
struct mmc_data *data = host->data;
struct mmc_command *stop;
@@ -251,13 +226,13 @@ static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host)
*/
if (data->flags & MMC_DATA_READ)
- disable_mmc_irqs(ctl, TMIO_MASK_READOP);
+ disable_mmc_irqs(host, TMIO_MASK_READOP);
else
- disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
+ disable_mmc_irqs(host, TMIO_MASK_WRITEOP);
if (stop) {
if (stop->opcode == 12 && !stop->arg)
- tmio_iowrite16(0x000, ctl + CTL_STOP_INTERNAL_ACTION);
+ sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000);
else
BUG();
}
@@ -268,9 +243,8 @@ static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host)
static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
unsigned int stat)
{
- void __iomem *ctl = host->ctl, *addr;
struct mmc_command *cmd = host->cmd;
- int i;
+ int i, addr;
if (!host->cmd) {
pr_debug("Spurious CMD irq\n");
@@ -284,8 +258,8 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
* modify the order of the response for short response command types.
*/
- for (i = 3, addr = ctl + CTL_RESPONSE ; i >= 0 ; i--, addr += 4)
- cmd->resp[i] = tmio_ioread32(addr);
+ for (i = 3, addr = CTL_RESPONSE ; i >= 0 ; i--, addr += 4)
+ cmd->resp[i] = sd_ctrl_read32(host, addr);
if (cmd->flags & MMC_RSP_136) {
cmd->resp[0] = (cmd->resp[0] << 8) | (cmd->resp[1] >> 24);
@@ -307,9 +281,9 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
*/
if (host->data && !cmd->error) {
if (host->data->flags & MMC_DATA_READ)
- enable_mmc_irqs(ctl, TMIO_MASK_READOP);
+ enable_mmc_irqs(host, TMIO_MASK_READOP);
else
- enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
+ enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
} else {
tmio_mmc_finish_request(host);
}
@@ -321,20 +295,19 @@ static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
static irqreturn_t tmio_mmc_irq(int irq, void *devid)
{
struct tmio_mmc_host *host = devid;
- void __iomem *ctl = host->ctl;
unsigned int ireg, irq_mask, status;
pr_debug("MMC IRQ begin\n");
- status = tmio_ioread32(ctl + CTL_STATUS);
- irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK);
+ status = sd_ctrl_read32(host, CTL_STATUS);
+ irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
ireg = status & TMIO_MASK_IRQ & ~irq_mask;
pr_debug_status(status);
pr_debug_status(ireg);
if (!ireg) {
- disable_mmc_irqs(ctl, status & ~irq_mask);
+ disable_mmc_irqs(host, status & ~irq_mask);
pr_debug("tmio_mmc: Spurious irq, disabling! "
"0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
@@ -346,7 +319,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
while (ireg) {
/* Card insert / remove attempts */
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
- ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT |
+ ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT |
TMIO_STAT_CARD_REMOVE);
mmc_detect_change(host->mmc, 0);
}
@@ -358,25 +331,25 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
/* Command completion */
if (ireg & TMIO_MASK_CMD) {
- ack_mmc_irqs(ctl, TMIO_MASK_CMD);
+ ack_mmc_irqs(host, TMIO_MASK_CMD);
tmio_mmc_cmd_irq(host, status);
}
/* Data transfer */
if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
- ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
+ ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
tmio_mmc_pio_irq(host);
}
/* Data transfer completion */
if (ireg & TMIO_STAT_DATAEND) {
- ack_mmc_irqs(ctl, TMIO_STAT_DATAEND);
+ ack_mmc_irqs(host, TMIO_STAT_DATAEND);
tmio_mmc_data_irq(host);
}
/* Check status - keep going until we've handled it all */
- status = tmio_ioread32(ctl + CTL_STATUS);
- irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK);
+ status = sd_ctrl_read32(host, CTL_STATUS);
+ irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
ireg = status & TMIO_MASK_IRQ & ~irq_mask;
pr_debug("Status at end of loop: %08x\n", status);
@@ -391,8 +364,6 @@ out:
static int tmio_mmc_start_data(struct tmio_mmc_host *host,
struct mmc_data *data)
{
- void __iomem *ctl = host->ctl;
-
pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n",
data->blksz, data->blocks);
@@ -407,8 +378,8 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
host->data = data;
/* Set transfer length / blocksize */
- tmio_iowrite16(data->blksz, ctl + CTL_SD_XFER_LEN);
- tmio_iowrite16(data->blocks, ctl + CTL_XFER_BLK_COUNT);
+ sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
+ sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks);
return 0;
}
@@ -449,8 +420,6 @@ fail:
static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
- void __iomem *cnf = host->cnf;
- void __iomem *ctl = host->ctl;
if (ios->clock)
tmio_mmc_set_clock(host, ios->clock);
@@ -458,12 +427,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Power sequence - OFF -> ON -> UP */
switch (ios->power_mode) {
case MMC_POWER_OFF: /* power down SD bus */
- tmio_iowrite8(0x00, cnf + CNF_PWR_CTL_2);
+ sd_config_write8(host, CNF_PWR_CTL_2, 0x00);
tmio_mmc_clk_stop(host);
break;
case MMC_POWER_ON: /* power up SD bus */
- tmio_iowrite8(0x02, cnf + CNF_PWR_CTL_2);
+ sd_config_write8(host, CNF_PWR_CTL_2, 0x02);
break;
case MMC_POWER_UP: /* start bus clock */
tmio_mmc_clk_start(host);
@@ -472,10 +441,10 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
- tmio_iowrite16(0x80e0, ctl + CTL_SD_MEM_CARD_OPT);
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
break;
case MMC_BUS_WIDTH_4:
- tmio_iowrite16(0x00e0, ctl + CTL_SD_MEM_CARD_OPT);
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
break;
}
@@ -486,9 +455,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int tmio_mmc_get_ro(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
- void __iomem *ctl = host->ctl;
- return (tmio_ioread16(ctl + CTL_STATUS) & TMIO_STAT_WRPROTECT) ? 0 : 1;
+ return (sd_ctrl_read16(host, CTL_STATUS) & TMIO_STAT_WRPROTECT) ? 0 : 1;
}
static struct mmc_host_ops tmio_mmc_ops = {
@@ -518,13 +486,8 @@ static int tmio_mmc_resume(struct platform_device *dev)
struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
struct mmc_host *mmc = platform_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- void __iomem *cnf = host->cnf;
int ret = 0;
- /* Enable the MMC/SD Control registers */
- tmio_iowrite16(SDCREN, cnf + CNF_CMD);
- tmio_iowrite32(dev->resource[0].start & 0xfffe, cnf + CNF_CTL_BASE);
-
/* Tell the MFD core we are ready to be enabled */
if (cell->enable) {
ret = cell->enable(dev);
@@ -532,6 +495,11 @@ static int tmio_mmc_resume(struct platform_device *dev)
goto out;
}
+ /* Enable the MMC/SD Control registers */
+ sd_config_write16(host, CNF_CMD, SDCREN);
+ sd_config_write32(host, CNF_CTL_BASE,
+ (dev->resource[0].start >> host->bus_shift) & 0xfffe);
+
mmc_resume_host(mmc);
out:
@@ -545,20 +513,25 @@ out:
static int __devinit tmio_mmc_probe(struct platform_device *dev)
{
struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+ struct tmio_mmc_data *pdata;
struct resource *res_ctl, *res_cnf;
struct tmio_mmc_host *host;
struct mmc_host *mmc;
- int ret = -ENOMEM;
+ int ret = -EINVAL;
if (dev->num_resources != 3)
goto out;
res_ctl = platform_get_resource(dev, IORESOURCE_MEM, 0);
res_cnf = platform_get_resource(dev, IORESOURCE_MEM, 1);
- if (!res_ctl || !res_cnf) {
- ret = -EINVAL;
+ if (!res_ctl || !res_cnf)
goto out;
- }
+
+ pdata = cell->driver_data;
+ if (!pdata || !pdata->hclk)
+ goto out;
+
+ ret = -ENOMEM;
mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev);
if (!mmc)
@@ -568,6 +541,9 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
host->mmc = mmc;
platform_set_drvdata(dev, mmc);
+ /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
+ host->bus_shift = resource_size(res_ctl) >> 10;
+
host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
if (!host->ctl)
goto host_free;
@@ -578,15 +554,10 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
mmc->ops = &tmio_mmc_ops;
mmc->caps = MMC_CAP_4_BIT_DATA;
- mmc->f_min = 46875; /* 24000000 / 512 */
- mmc->f_max = 24000000;
+ mmc->f_max = pdata->hclk;
+ mmc->f_min = mmc->f_max / 512;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- /* Enable the MMC/SD Control registers */
- tmio_iowrite16(SDCREN, host->cnf + CNF_CMD);
- tmio_iowrite32(dev->resource[0].start & 0xfffe,
- host->cnf + CNF_CTL_BASE);
-
/* Tell the MFD core we are ready to be enabled */
if (cell->enable) {
ret = cell->enable(dev);
@@ -594,14 +565,19 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
goto unmap_cnf;
}
+ /* Enable the MMC/SD Control registers */
+ sd_config_write16(host, CNF_CMD, SDCREN);
+ sd_config_write32(host, CNF_CTL_BASE,
+ (dev->resource[0].start >> host->bus_shift) & 0xfffe);
+
/* Disable SD power during suspend */
- tmio_iowrite8(0x01, host->cnf + CNF_PWR_CTL_3);
+ sd_config_write8(host, CNF_PWR_CTL_3, 0x01);
/* The below is required but why? FIXME */
- tmio_iowrite8(0x1f, host->cnf + CNF_STOP_CLK_CTL);
+ sd_config_write8(host, CNF_STOP_CLK_CTL, 0x1f);
/* Power down SD bus*/
- tmio_iowrite8(0x0, host->cnf + CNF_PWR_CTL_2);
+ sd_config_write8(host, CNF_PWR_CTL_2, 0x00);
tmio_mmc_clk_stop(host);
reset(host);
@@ -612,22 +588,20 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
else
goto unmap_cnf;
- disable_mmc_irqs(host->ctl, TMIO_MASK_ALL);
+ disable_mmc_irqs(host, TMIO_MASK_ALL);
- ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc",
- host);
+ ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED |
+ IRQF_TRIGGER_FALLING, "tmio-mmc", host);
if (ret)
goto unmap_cnf;
- set_irq_type(host->irq, IRQ_TYPE_EDGE_FALLING);
-
mmc_add_host(mmc);
printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
(unsigned long)host->ctl, host->irq);
/* Unmask the IRQs we want to know about */
- enable_mmc_irqs(host->ctl, TMIO_MASK_IRQ);
+ enable_mmc_irqs(host, TMIO_MASK_IRQ);
return 0;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 9c831ab2ece6..9fa998594974 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -83,34 +83,36 @@
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
-#define enable_mmc_irqs(ctl, i) \
+
+#define enable_mmc_irqs(host, i) \
do { \
u32 mask;\
- mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \
+ mask = sd_ctrl_read32((host), CTL_IRQ_MASK); \
mask &= ~((i) & TMIO_MASK_IRQ); \
- tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \
+ sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \
} while (0)
-#define disable_mmc_irqs(ctl, i) \
+#define disable_mmc_irqs(host, i) \
do { \
u32 mask;\
- mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \
+ mask = sd_ctrl_read32((host), CTL_IRQ_MASK); \
mask |= ((i) & TMIO_MASK_IRQ); \
- tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \
+ sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \
} while (0)
-#define ack_mmc_irqs(ctl, i) \
+#define ack_mmc_irqs(host, i) \
do { \
u32 mask;\
- mask = tmio_ioread32((ctl) + CTL_STATUS); \
+ mask = sd_ctrl_read32((host), CTL_STATUS); \
mask &= ~((i) & TMIO_MASK_IRQ); \
- tmio_iowrite32(mask, (ctl) + CTL_STATUS); \
+ sd_ctrl_write32((host), CTL_STATUS, mask); \
} while (0)
struct tmio_mmc_host {
void __iomem *cnf;
void __iomem *ctl;
+ unsigned long bus_shift;
struct mmc_command *cmd;
struct mmc_request *mrq;
struct mmc_data *data;
@@ -123,6 +125,63 @@ struct tmio_mmc_host {
unsigned int sg_off;
};
+#include <linux/io.h>
+
+static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
+{
+ return readw(host->ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
+ u16 *buf, int count)
+{
+ readsw(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
+{
+ return readw(host->ctl + (addr << host->bus_shift)) |
+ readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
+}
+
+static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr,
+ u16 val)
+{
+ writew(val, host->ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
+ u16 *buf, int count)
+{
+ writesw(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr,
+ u32 val)
+{
+ writew(val, host->ctl + (addr << host->bus_shift));
+ writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
+}
+
+static inline void sd_config_write8(struct tmio_mmc_host *host, int addr,
+ u8 val)
+{
+ writeb(val, host->cnf + (addr << host->bus_shift));
+}
+
+static inline void sd_config_write16(struct tmio_mmc_host *host, int addr,
+ u16 val)
+{
+ writew(val, host->cnf + (addr << host->bus_shift));
+}
+
+static inline void sd_config_write32(struct tmio_mmc_host *host, int addr,
+ u32 val)
+{
+ writew(val, host->cnf + (addr << host->bus_shift));
+ writew(val >> 16, host->cnf + ((addr + 2) << host->bus_shift));
+}
+
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 7d04fb9ddcaa..b8e35a0b4d72 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -154,7 +154,8 @@ config MTD_AFS_PARTS
You will still need the parsing functions to be called by the driver
for your particular device. It won't happen automatically. The
- 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
+ 'armflash' map driver (CONFIG_MTD_ARM_INTEGRATOR) does this, for
+ example.
config MTD_OF_PARTS
tristate "Flash partition map based on OF description"
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 6fde0a2e3567..325fab92a62c 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -49,7 +49,7 @@ config MTD_MS02NV
If you want to compile this driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/kbuild/modules.txt>.
- The module will be called ms02-nv.ko.
+ The module will be called ms02-nv.
config MTD_DATAFLASH
tristate "Support for AT45xxx DataFlash"
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 a49a9c8f2cb1..aaac3b6800b7 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -47,40 +47,41 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
unsigned long block, nsect;
char *buf;
- block = req->sector << 9 >> tr->blkshift;
- nsect = req->current_nr_sectors << 9 >> tr->blkshift;
+ block = blk_rq_pos(req) << 9 >> tr->blkshift;
+ nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
buf = req->buffer;
if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
req->cmd[0] == REQ_LB_OP_DISCARD)
- return !tr->discard(dev, block, nsect);
+ return tr->discard(dev, block, nsect);
if (!blk_fs_request(req))
- return 0;
+ return -EIO;
- if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk))
- return 0;
+ if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))
+ return -EIO;
switch(rq_data_dir(req)) {
case READ:
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf))
- return 0;
- return 1;
+ return -EIO;
+ return 0;
case WRITE:
if (!tr->writesect)
- return 0;
+ return -EIO;
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->writesect(dev, block, buf))
- return 0;
- return 1;
+ return -EIO;
+ return 0;
default:
printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
- return 0;
+ return -EIO;
}
}
@@ -88,19 +89,18 @@ static int mtd_blktrans_thread(void *arg)
{
struct mtd_blktrans_ops *tr = arg;
struct request_queue *rq = tr->blkcore_priv->rq;
+ struct request *req = NULL;
/* we might get involved when memory gets low, so use PF_MEMALLOC */
current->flags |= PF_MEMALLOC;
spin_lock_irq(rq->queue_lock);
+
while (!kthread_should_stop()) {
- struct request *req;
struct mtd_blktrans_dev *dev;
- int res = 0;
-
- req = elv_next_request(rq);
+ int res;
- if (!req) {
+ if (!req && !(req = blk_fetch_request(rq))) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irq(rq->queue_lock);
schedule();
@@ -119,8 +119,13 @@ static int mtd_blktrans_thread(void *arg)
spin_lock_irq(rq->queue_lock);
- end_request(req, res);
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
}
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
spin_unlock_irq(rq->queue_lock);
return 0;
@@ -373,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/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 890936d0275e..f3276897859e 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -260,7 +260,7 @@ config MTD_NAND_BASLER_EXCITE
help
This enables the driver for the NAND flash device found on the
Basler eXcite Smart Camera. If built as a module, the driver
- will be named "excite_nandflash.ko".
+ will be named excite_nandflash.
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
@@ -282,7 +282,7 @@ config MTD_NAND_CS553X
controller is enabled for NAND, and currently requires that
the controller be in MMIO mode.
- If you say "m", the module will be called "cs553x_nand.ko".
+ If you say "m", the module will be called cs553x_nand.
config MTD_NAND_ATMEL
tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32"
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/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index f2e9de1414df..6391e3dc8002 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -39,7 +39,6 @@
#include <mach/gpmc.h>
#include <mach/onenand.h>
#include <mach/gpio.h>
-#include <mach/pm.h>
#include <mach/dma.h>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3f739cfd92fa..01f282cd0989 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1886,7 +1886,7 @@ config FEC_MPC52xx
---help---
This option enables support for the MPC5200's on-chip
Fast Ethernet Controller
- If compiled as module, it will be called 'fec_mpc52xx.ko'.
+ If compiled as module, it will be called fec_mpc52xx.
config FEC_MPC52xx_MDIO
bool "MPC52xx FEC MDIO bus driver"
@@ -1898,7 +1898,7 @@ config FEC_MPC52xx_MDIO
(Motorola? industry standard).
If your board uses an external PHY connected to FEC, enable this.
If not sure, enable.
- If compiled as module, it will be called 'fec_mpc52xx_phy.ko'.
+ If compiled as module, it will be called fec_mpc52xx_phy.
config NE_H8300
tristate "NE2000 compatible support for H8/300"
@@ -2270,6 +2270,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 1c378dd5933e..d366fb2b40e9 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -75,6 +75,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
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 78cc71469136..b642647170be 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -1220,7 +1220,7 @@ static int __init ltpc_setup(char *str)
if (ints[0] > 2) {
dma = ints[3];
}
- /* ignore any other paramters */
+ /* ignore any other parameters */
}
return 1;
}
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 1fcf8388b1c8..6f42ad728915 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -456,7 +456,8 @@ static inline void queue_put_desc(unsigned int queue, u32 phys,
debug_desc(phys, desc);
BUG_ON(phys & 0x1F);
qmgr_put_entry(queue, phys);
- BUG_ON(qmgr_stat_overflow(queue));
+ /* Don't check for queue overflow here, we've allocated sufficient
+ length and queues >= 32 don't support this check anyway. */
}
@@ -512,8 +513,8 @@ static int eth_poll(struct napi_struct *napi, int budget)
#endif
napi_complete(napi);
qmgr_enable_irq(rxq);
- if (!qmgr_stat_empty(rxq) &&
- napi_reschedule(napi)) {
+ if (!qmgr_stat_below_low_watermark(rxq) &&
+ napi_reschedule(napi)) { /* not empty again */
#if DEBUG_RX
printk(KERN_DEBUG "%s: eth_poll"
" napi_reschedule successed\n",
@@ -630,9 +631,9 @@ static void eth_txdone_irq(void *unused)
port->tx_buff_tab[n_desc] = NULL;
}
- start = qmgr_stat_empty(port->plat->txreadyq);
+ start = qmgr_stat_below_low_watermark(port->plat->txreadyq);
queue_put_desc(port->plat->txreadyq, phys, desc);
- if (start) {
+ if (start) { /* TX-ready queue was empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_txdone_irq xmit ready\n",
port->netdev->name);
@@ -708,13 +709,14 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
queue_put_desc(TX_QUEUE(port->id), tx_desc_phys(port, n), desc);
dev->trans_start = jiffies;
- if (qmgr_stat_empty(txreadyq)) {
+ if (qmgr_stat_below_low_watermark(txreadyq)) { /* empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_xmit queue full\n", dev->name);
#endif
netif_stop_queue(dev);
/* we could miss TX ready interrupt */
- if (!qmgr_stat_empty(txreadyq)) {
+ /* really empty in fact */
+ if (!qmgr_stat_below_low_watermark(txreadyq)) {
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_xmit ready again\n",
dev->name);
@@ -814,29 +816,29 @@ static int request_queues(struct port *port)
int err;
err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0,
- "%s:RX-free", port->netdev->name);
+ "%s:RX-free", port->netdev->name);
if (err)
return err;
err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0,
- "%s:RX", port->netdev->name);
+ "%s:RX", port->netdev->name);
if (err)
goto rel_rxfree;
err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0,
- "%s:TX", port->netdev->name);
+ "%s:TX", port->netdev->name);
if (err)
goto rel_rx;
err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0,
- "%s:TX-ready", port->netdev->name);
+ "%s:TX-ready", port->netdev->name);
if (err)
goto rel_tx;
/* TX-done queue handles skbs sent out by the NPEs */
if (!ports_open) {
err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0,
- "%s:TX-done", DRV_NAME);
+ "%s:TX-done", DRV_NAME);
if (err)
goto rel_txready;
}
diff --git a/drivers/net/b44.h b/drivers/net/b44.h
index 0443f6801f60..e1905a49279f 100644
--- a/drivers/net/b44.h
+++ b/drivers/net/b44.h
@@ -97,7 +97,7 @@
#define B44_DMARX_STAT 0x021CUL /* DMA RX Current Active Desc. + Status */
#define DMARX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */
#define DMARX_STAT_SMASK 0x0000f000 /* State Mask */
-#define DMARX_STAT_SDISABLED 0x00000000 /* State Disbaled */
+#define DMARX_STAT_SDISABLED 0x00000000 /* State Disabled */
#define DMARX_STAT_SACTIVE 0x00001000 /* State Active */
#define DMARX_STAT_SIDLE 0x00002000 /* State Idle Wait */
#define DMARX_STAT_SSTOPPED 0x00003000 /* State Stopped */
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index f99e17e0a319..7e3738112c4e 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -50,6 +50,10 @@
#include <linux/log2.h>
#include <linux/list.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"
@@ -316,6 +320,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)
{
@@ -489,6 +645,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);
@@ -505,6 +662,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);
}
}
}
@@ -3165,6 +3323,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;
@@ -3194,6 +3357,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;
@@ -3268,6 +3448,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.
@@ -4631,8 +4815,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);
@@ -7471,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 026ed1c84698..f1edfaa9e56a 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
@@ -6680,6 +6684,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;
};
@@ -6729,6 +6738,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;
@@ -6887,6 +6901,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/cnic.c b/drivers/net/cnic.c
new file mode 100644
index 000000000000..44f77eb1180f
--- /dev/null
+++ b/drivers/net/cnic.c
@@ -0,0 +1,2717 @@
+/* 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>
+#include <linux/module.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)
+{
+#if defined(CONFIG_INET)
+ 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;
+#else
+ return -ENETUNREACH;
+#endif
+}
+
+static int cnic_get_v6_route(struct sockaddr_in6 *dst_addr,
+ struct dst_entry **dst)
+{
+#if defined(CONFIG_IPV6) || (defined(CONFIG_IPV6_MODULE) && defined(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) && defined(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/e100.c b/drivers/net/e100.c
index e52a2018e91e..f7929e89eb03 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -2921,7 +2921,7 @@ static int e100_resume(struct pci_dev *pdev)
/* ack any pending wake events, disable PME */
pci_enable_wake(pdev, 0, 0);
- /* disbale reverse auto-negotiation */
+ /* disable reverse auto-negotiation */
if (nic->phy == phy_82552_v) {
u16 smartspeed = mdio_read(netdev, nic->mii.phy_id,
E100_82552_SMARTSPEED);
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index d6e491bc58c9..981936c1fb46 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -62,7 +62,7 @@ struct e1000_info;
e_printk(KERN_NOTICE, adapter, format, ## arg)
-/* Interrupt modes, as used by the IntMode paramter */
+/* Interrupt modes, as used by the IntMode parameter */
#define E1000E_INT_MODE_LEGACY 0
#define E1000E_INT_MODE_MSI 1
#define E1000E_INT_MODE_MSIX 2
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 16a41389575a..78952f8324e2 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -268,7 +268,7 @@ struct ehea_qp_init_attr {
};
/*
- * Event Queue attributes, passed as paramter
+ * Event Queue attributes, passed as parameter
*/
struct ehea_eq_attr {
u32 type;
diff --git a/drivers/net/igbvf/igbvf.h b/drivers/net/igbvf/igbvf.h
index 2ad6cd756539..8e9b67ebbf8b 100644
--- a/drivers/net/igbvf/igbvf.h
+++ b/drivers/net/igbvf/igbvf.h
@@ -45,7 +45,7 @@ struct igbvf_adapter;
/* Interrupt defines */
#define IGBVF_START_ITR 648 /* ~6000 ints/sec */
-/* Interrupt modes, as used by the IntMode paramter */
+/* Interrupt modes, as used by the IntMode parameter */
#define IGBVF_INT_MODE_LEGACY 0
#define IGBVF_INT_MODE_MSI 1
#define IGBVF_INT_MODE_MSIX 2
diff --git a/drivers/net/ipg.h b/drivers/net/ipg.h
index dd9318f19497..dfc2541bb556 100644
--- a/drivers/net/ipg.h
+++ b/drivers/net/ipg.h
@@ -514,7 +514,7 @@ enum ipg_regs {
#define IPG_DMALIST_ALIGN_PAD 0x07
#define IPG_MULTICAST_HASHTABLE_SIZE 0x40
-/* Number of miliseconds to wait after issuing a software reset.
+/* Number of milliseconds to wait after issuing a software reset.
* 0x05 <= IPG_AC_RESETWAIT to account for proper 10Mbps operation.
*/
#define IPG_AC_RESETWAIT 0x05
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c
index 0a7e78ade63f..e02bafdd3682 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/mlx4/en_netdev.c
@@ -367,7 +367,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
int i;
/* If we haven't received a specific coalescing setting
- * (module param), we set the moderation paramters as follows:
+ * (module param), we set the moderation parameters as follows:
* - moder_cnt is set to the number of mtu sized packets to
* satisfy our coelsing target.
* - moder_time is set to a fixed value.
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c
index dee188761a3c..b9ceddde46c0 100644
--- a/drivers/net/mlx4/eq.c
+++ b/drivers/net/mlx4/eq.c
@@ -497,8 +497,10 @@ static void mlx4_free_irqs(struct mlx4_dev *dev)
if (eq_table->have_irq)
free_irq(dev->pdev->irq, dev);
for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i)
- if (eq_table->eq[i].have_irq)
+ if (eq_table->eq[i].have_irq) {
free_irq(eq_table->eq[i].irq, eq_table->eq + i);
+ eq_table->eq[i].have_irq = 0;
+ }
kfree(eq_table->irq_names);
}
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index 30bea9689694..018348c01193 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -100,6 +100,10 @@ module_param_named(use_prio, use_prio, bool, 0444);
MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports "
"(0/1, default 0)");
+static int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG);
+module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444);
+MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-5)");
+
int mlx4_check_port_params(struct mlx4_dev *dev,
enum mlx4_port_type *port_type)
{
@@ -203,12 +207,13 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.max_cqes = dev_cap->max_cq_sz - 1;
dev->caps.reserved_cqs = dev_cap->reserved_cqs;
dev->caps.reserved_eqs = dev_cap->reserved_eqs;
+ dev->caps.mtts_per_seg = 1 << log_mtts_per_seg;
dev->caps.reserved_mtts = DIV_ROUND_UP(dev_cap->reserved_mtts,
- MLX4_MTT_ENTRY_PER_SEG);
+ dev->caps.mtts_per_seg);
dev->caps.reserved_mrws = dev_cap->reserved_mrws;
dev->caps.reserved_uars = dev_cap->reserved_uars;
dev->caps.reserved_pds = dev_cap->reserved_pds;
- dev->caps.mtt_entry_sz = MLX4_MTT_ENTRY_PER_SEG * dev_cap->mtt_entry_sz;
+ dev->caps.mtt_entry_sz = dev->caps.mtts_per_seg * dev_cap->mtt_entry_sz;
dev->caps.max_msg_sz = dev_cap->max_msg_sz;
dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1);
dev->caps.flags = dev_cap->flags;
@@ -1304,6 +1309,11 @@ static int __init mlx4_verify_params(void)
return -1;
}
+ if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 5)) {
+ printk(KERN_WARNING "mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
+ return -1;
+ }
+
return 0;
}
diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c
index 0a467785f065..5887e4764d22 100644
--- a/drivers/net/mlx4/mr.c
+++ b/drivers/net/mlx4/mr.c
@@ -209,7 +209,7 @@ int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift,
} else
mtt->page_shift = page_shift;
- for (mtt->order = 0, i = MLX4_MTT_ENTRY_PER_SEG; i < npages; i <<= 1)
+ for (mtt->order = 0, i = dev->caps.mtts_per_seg; i < npages; i <<= 1)
++mtt->order;
mtt->first_seg = mlx4_alloc_mtt_range(dev, mtt->order);
@@ -350,7 +350,7 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr)
mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG |
MLX4_MPT_PD_FLAG_RAE);
mpt_entry->mtt_sz = cpu_to_be32((1 << mr->mtt.order) *
- MLX4_MTT_ENTRY_PER_SEG);
+ dev->caps.mtts_per_seg);
} else {
mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS);
}
@@ -391,7 +391,7 @@ static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt,
(start_index + npages - 1) / (PAGE_SIZE / sizeof (u64)))
return -EINVAL;
- if (start_index & (MLX4_MTT_ENTRY_PER_SEG - 1))
+ if (start_index & (dev->caps.mtts_per_seg - 1))
return -EINVAL;
mtts = mlx4_table_find(&priv->mr_table.mtt_table, mtt->first_seg +
diff --git a/drivers/net/mlx4/profile.c b/drivers/net/mlx4/profile.c
index cebdf3243ca1..bd22df95adf9 100644
--- a/drivers/net/mlx4/profile.c
+++ b/drivers/net/mlx4/profile.c
@@ -98,7 +98,7 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
profile[MLX4_RES_EQ].size = dev_cap->eqc_entry_sz;
profile[MLX4_RES_DMPT].size = dev_cap->dmpt_entry_sz;
profile[MLX4_RES_CMPT].size = dev_cap->cmpt_entry_sz;
- profile[MLX4_RES_MTT].size = MLX4_MTT_ENTRY_PER_SEG * dev_cap->mtt_entry_sz;
+ profile[MLX4_RES_MTT].size = dev->caps.mtts_per_seg * dev_cap->mtt_entry_sz;
profile[MLX4_RES_MCG].size = MLX4_MGM_ENTRY_SIZE;
profile[MLX4_RES_QP].num = request->num_qp;
diff --git a/drivers/net/niu.h b/drivers/net/niu.h
index 8754e44cadae..3bd0b5933d59 100644
--- a/drivers/net/niu.h
+++ b/drivers/net/niu.h
@@ -3242,8 +3242,8 @@ struct niu {
struct niu_parent *parent;
u32 flags;
-#define NIU_FLAGS_HOTPLUG_PHY_PRESENT 0x02000000 /* Removebale PHY detected*/
-#define NIU_FLAGS_HOTPLUG_PHY 0x01000000 /* Removebale PHY */
+#define NIU_FLAGS_HOTPLUG_PHY_PRESENT 0x02000000 /* Removeable PHY detected*/
+#define NIU_FLAGS_HOTPLUG_PHY 0x01000000 /* Removeable PHY */
#define NIU_FLAGS_VPD_VALID 0x00800000 /* VPD has valid version */
#define NIU_FLAGS_MSIX 0x00400000 /* MSI-X in use */
#define NIU_FLAGS_MCAST 0x00200000 /* multicast filter enabled */
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index b9a5f59d6c9b..90d1f76c0e8b 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -3224,7 +3224,7 @@ static int ql_adapter_reset(struct ql_adapter *qdev)
if (value & RST_FO_FR) {
QPRINTK(qdev, IFDOWN, ERR,
- "ETIMEOUT!!! errored out of resetting the chip!\n");
+ "ETIMEDOUT!!! errored out of resetting the chip!\n");
status = -ETIMEDOUT;
}
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c
index a67c14a7befd..71afbf8b9c50 100644
--- a/drivers/net/qlge/qlge_mpi.c
+++ b/drivers/net/qlge/qlge_mpi.c
@@ -141,7 +141,7 @@ end:
/* We are being asked by firmware to accept
* a change to the port. This is only
* a change to max frame sizes (Tx/Rx), pause
- * paramters, or loopback mode. We wake up a worker
+ * parameters, or loopback mode. We wake up a worker
* to handler processing this since a mailbox command
* will need to be sent to ACK the request.
*/
@@ -371,7 +371,7 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp)
/* We are being asked by firmware to accept
* a change to the port. This is only
* a change to max frame sizes (Tx/Rx), pause
- * paramters, or loopback mode.
+ * parameters, or loopback mode.
*/
case AEN_IDC_REQ:
status = ql_idc_req_aen(qdev);
@@ -380,7 +380,7 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp)
/* Process and inbound IDC event.
* This will happen when we're trying to
* change tx/rx max frame size, change pause
- * paramters or loopback mode.
+ * parameters or loopback mode.
*/
case AEN_IDC_CMPLT:
case AEN_IDC_EXT:
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 007c881896d2..35196faa084e 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 */
@@ -2366,10 +2365,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)
@@ -2416,7 +2415,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) ||
@@ -2677,7 +2676,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;
@@ -2855,7 +2854,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;
diff --git a/drivers/net/skfp/h/smt.h b/drivers/net/skfp/h/smt.h
index 1ff589988d10..2976757a36fb 100644
--- a/drivers/net/skfp/h/smt.h
+++ b/drivers/net/skfp/h/smt.h
@@ -413,7 +413,7 @@ struct smt_p_reason {
#define SMT_RDF_SUCCESS 0x00000003 /* success (PMF) */
#define SMT_RDF_BADSET 0x00000004 /* bad set count (PMF) */
#define SMT_RDF_ILLEGAL 0x00000005 /* read only (PMF) */
-#define SMT_RDF_NOPARAM 0x6 /* paramter not supported (PMF) */
+#define SMT_RDF_NOPARAM 0x6 /* parameter not supported (PMF) */
#define SMT_RDF_RANGE 0x8 /* out of range */
#define SMT_RDF_AUTHOR 0x9 /* not autohorized */
#define SMT_RDF_LENGTH 0x0a /* length error */
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 329f890e2903..f1f773b17fe1 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -45,7 +45,8 @@
defined(CONFIG_MACH_ZYLONITE) ||\
defined(CONFIG_MACH_LITTLETON) ||\
defined(CONFIG_MACH_ZYLONITE2) ||\
- defined(CONFIG_ARCH_VIPER)
+ defined(CONFIG_ARCH_VIPER) ||\
+ defined(CONFIG_MACH_STARGATE2)
#include <asm/mach-types.h>
@@ -73,7 +74,7 @@
/* We actually can't write halfwords properly if not word aligned */
static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
{
- if (machine_is_mainstone() && reg & 2) {
+ if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) {
unsigned int v = val << 16;
v |= readl(ioaddr + (reg & ~2)) & 0xffff;
writel(v, ioaddr + (reg & ~2));
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index 13dbc59bfe42..b40b6de2d086 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -79,7 +79,7 @@ MODULE_AUTHOR("Mike Phillips <mikep@linuxtr.net>") ;
MODULE_DESCRIPTION("3Com 3C359 Velocity XL Token Ring Adapter Driver \n") ;
MODULE_FIRMWARE(FW_NAME);
-/* Module paramters */
+/* Module parameters */
/* Ring Speed 0,4,16
* 0 = Autosense
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index b358bbbce33a..b3715efdce56 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -169,7 +169,7 @@ static char *open_min_error[] = {
"Monitor Contention failer for RPL", "FDX Protocol Error"
};
-/* Module paramters */
+/* Module parameters */
/* Ring Speed 0,4,16
* 0 = Autosense
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index c36974925c15..451b54136ede 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -132,7 +132,7 @@ static char *open_min_error[] = {"No error", "Function Failure", "Signal Lost",
"Reserved", "Reserved", "No Monitor Detected for RPL",
"Monitor Contention failer for RPL", "FDX Protocol Error"};
-/* Module paramters */
+/* Module parameters */
MODULE_AUTHOR("Mike Phillips <mikep@linuxtr.net>") ;
MODULE_DESCRIPTION("Olympic PCI/Cardbus Chipset Driver") ;
diff --git a/drivers/net/ucc_geth_ethtool.c b/drivers/net/ucc_geth_ethtool.c
index 6fcb500257bc..61fe80dda3e3 100644
--- a/drivers/net/ucc_geth_ethtool.c
+++ b/drivers/net/ucc_geth_ethtool.c
@@ -7,7 +7,7 @@
*
* Limitation:
* Can only get/set setttings of the first queue.
- * Need to re-open the interface manually after changing some paramters.
+ * Need to re-open the interface manually after changing some parameters.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index c94de6243140..22c0585a0319 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -429,7 +429,7 @@ static void rx_complete (struct urb *urb)
/* stalls need manual reset. this is rare ... except that
* when going through USB 2.0 TTs, unplug appears this way.
- * we avoid the highspeed version of the ETIMEOUT/EILSEQ
+ * we avoid the highspeed version of the ETIMEDOUT/EILSEQ
* storm, recovering as needed.
*/
case -EPIPE:
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f0bb1a4c8323..52198f6797a4 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -843,6 +843,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));
@@ -903,25 +907,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;
@@ -939,7 +937,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. */
@@ -960,13 +958,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;
@@ -992,12 +985,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/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 08b1a284b690..bb719b6114cb 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -579,7 +579,8 @@ static inline void queue_put_desc(unsigned int queue, u32 phys,
debug_desc(phys, desc);
BUG_ON(phys & 0x1F);
qmgr_put_entry(queue, phys);
- BUG_ON(qmgr_stat_overflow(queue));
+ /* Don't check for queue overflow here, we've allocated sufficient
+ length and queues >= 32 don't support this check anyway. */
}
@@ -789,10 +790,10 @@ static void hss_hdlc_txdone_irq(void *pdev)
free_buffer_irq(port->tx_buff_tab[n_desc]);
port->tx_buff_tab[n_desc] = NULL;
- start = qmgr_stat_empty(port->plat->txreadyq);
+ start = qmgr_stat_below_low_watermark(port->plat->txreadyq);
queue_put_desc(port->plat->txreadyq,
tx_desc_phys(port, n_desc), desc);
- if (start) {
+ if (start) { /* TX-ready queue was empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_txdone_irq xmit"
" ready\n", dev->name);
@@ -867,13 +868,13 @@ static int hss_hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
queue_put_desc(queue_ids[port->id].tx, tx_desc_phys(port, n), desc);
dev->trans_start = jiffies;
- if (qmgr_stat_empty(txreadyq)) {
+ if (qmgr_stat_below_low_watermark(txreadyq)) { /* empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_xmit queue full\n", dev->name);
#endif
netif_stop_queue(dev);
/* we could miss TX ready interrupt */
- if (!qmgr_stat_empty(txreadyq)) {
+ if (!qmgr_stat_below_low_watermark(txreadyq)) {
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_xmit ready again\n",
dev->name);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index fb7541c28e58..5bc00db21b24 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -310,7 +310,7 @@ config PRISM54
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/kbuild/modules.txt>.
- The module will be called prism54.ko.
+ The module will be called prism54.
config USB_ZD1201
tristate "USB ZD1201 based Wireless device support"
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
index 932d207bce23..c15db2293515 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -29,7 +29,7 @@ config HOSTAP
PLX/PCI/CS version of the driver to actually use the driver.
The driver can be compiled as a module and it will be called
- "hostap.ko".
+ hostap.
config HOSTAP_FIRMWARE
bool "Support downloading firmware images with Host AP driver"
@@ -68,7 +68,7 @@ config HOSTAP_PLX
driver.
The driver can be compiled as a module and will be named
- "hostap_plx.ko".
+ hostap_plx.
config HOSTAP_PCI
tristate "Host AP driver for Prism2.5 PCI adaptors"
@@ -81,7 +81,7 @@ config HOSTAP_PCI
driver.
The driver can be compiled as a module and will be named
- "hostap_pci.ko".
+ hostap_pci.
config HOSTAP_CS
tristate "Host AP driver for Prism2/2.5/3 PC Cards"
@@ -94,4 +94,4 @@ config HOSTAP_CS
driver.
The driver can be compiled as a module and will be named
- "hostap_cs.ko".
+ hostap_cs.
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 029ccb6bdbaa..e092af09d6bf 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -70,7 +70,7 @@ config IWLAGN
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/kbuild/modules.txt>. The
- module will be called iwlagn.ko.
+ module will be called iwlagn.
config IWL4965
@@ -108,7 +108,7 @@ config IWL3945
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/kbuild/modules.txt>. The
- module will be called iwl3945.ko.
+ module will be called iwl3945.
config IWL3945_SPECTRUM_MEASUREMENT
bool "Enable Spectrum Measurement in iwl3945 driver"
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 7441d5585110..3bec3dbd3450 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -647,7 +647,7 @@ static int rndis_set_config_parameter(struct usbnet *dev, char *param,
ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
infobuf, info_len);
if (ret != 0)
- devdbg(dev, "setting rndis config paramater failed, %d.", ret);
+ devdbg(dev, "setting rndis config parameter failed, %d.", ret);
kfree(infobuf);
return ret;
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index 18ee7d6c4028..8aab3e6754bd 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -9,11 +9,11 @@ menuconfig RT2X00
When building one of the individual drivers, the rt2x00 library
will also be created. That library (when the driver is built as
- a module) will be called "rt2x00lib.ko".
+ a module) will be called rt2x00lib.
Additionally PCI and USB libraries will also be build depending
on the types of drivers being selected, these libraries will be
- called "rt2x00pci.ko" and "rt2x00usb.ko".
+ called rt2x00pci and rt2x00usb.
if RT2X00
@@ -26,7 +26,7 @@ config RT2400PCI
This adds support for rt2400 wireless chipset family.
Supported chips: RT2460.
- When compiled as a module, this driver will be called "rt2400pci.ko".
+ When compiled as a module, this driver will be called rt2400pci.
config RT2500PCI
tristate "Ralink rt2500 (PCI/PCMCIA) support"
@@ -37,7 +37,7 @@ config RT2500PCI
This adds support for rt2500 wireless chipset family.
Supported chips: RT2560.
- When compiled as a module, this driver will be called "rt2500pci.ko".
+ When compiled as a module, this driver will be called rt2500pci.
config RT61PCI
tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support"
@@ -51,7 +51,7 @@ config RT61PCI
This adds support for rt2501 wireless chipset family.
Supported chips: RT2561, RT2561S & RT2661.
- When compiled as a module, this driver will be called "rt61pci.ko".
+ When compiled as a module, this driver will be called rt61pci.
config RT2500USB
tristate "Ralink rt2500 (USB) support"
@@ -62,7 +62,7 @@ config RT2500USB
This adds support for rt2500 wireless chipset family.
Supported chips: RT2571 & RT2572.
- When compiled as a module, this driver will be called "rt2500usb.ko".
+ When compiled as a module, this driver will be called rt2500usb.
config RT73USB
tristate "Ralink rt2501/rt73 (USB) support"
@@ -75,7 +75,7 @@ config RT73USB
This adds support for rt2501 wireless chipset family.
Supported chips: RT2571W, RT2573 & RT2671.
- When compiled as a module, this driver will be called "rt73usb.ko".
+ When compiled as a module, this driver will be called rt73usb.
config RT2800USB
tristate "Ralink rt2800 (USB) support"
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 39e00b3d7811..0bf2715fa93a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -261,7 +261,7 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna);
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* Initialize work structure and all link tuning related
- * paramters. This will not start the link tuning process itself.
+ * parameters. This will not start the link tuning process itself.
*/
void rt2x00link_register(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index 1a90d69f18a9..6af706408ac0 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -138,7 +138,7 @@ psa_read(struct net_device * dev,
/*------------------------------------------------------------------*/
/*
- * Write the Paramter Storage Area to the WaveLAN card's memory
+ * Write the Parameter Storage Area to the WaveLAN card's memory
*/
static void
psa_write(struct net_device * dev,
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 6fe043bd3770..d2fa27c5c1b2 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,22 +1,22 @@
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/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/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 3eee70928d45..2d6da78fddb6 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -679,7 +679,7 @@ alloc_err:
return rc;
}
-static int sn_pci_hotplug_init(void)
+static int __init sn_pci_hotplug_init(void)
{
struct pci_bus *pci_bus = NULL;
int rc;
@@ -716,7 +716,7 @@ static int sn_pci_hotplug_init(void)
return registered == 1 ? 0 : -ENODEV;
}
-static void sn_pci_hotplug_exit(void)
+static void __exit sn_pci_hotplug_exit(void)
{
struct hotplug_slot *bss_hotplug_slot;
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
index 6808d8333ecc..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);
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index a563fbe559d0..cd389162735f 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -1972,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
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/pci.c b/drivers/pci/pci.c
index 34bf0fdf5047..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;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index e3c3e081b834..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 */
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 276473543982..fbf965b31c14 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -217,7 +217,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER || ARCH_PXA_ESERIES)
+ || ARCH_VIPER || ARCH_PXA_ESERIES || MACH_STARGATE2)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index bbac46327227..047394d98ac2 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -73,5 +73,6 @@ pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
+pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/pxa2xx_stargate2.c b/drivers/pcmcia/pxa2xx_stargate2.c
new file mode 100644
index 000000000000..490749ea677f
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_stargate2.c
@@ -0,0 +1,174 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_stargate2.c
+ *
+ * Stargate 2 PCMCIA specific routines.
+ *
+ * Created: December 6, 2005
+ * Author: Ed C. Epp
+ * Copyright: Intel Corp 2005
+ * Jonathan Cameron <jic23@cam.ac.uk> 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include "soc_common.h"
+
+#define SG2_S0_BUFF_CTL 120
+#define SG2_S0_POWER_CTL 108
+#define SG2_S0_GPIO_RESET 82
+#define SG2_S0_GPIO_DETECT 53
+#define SG2_S0_GPIO_READY 81
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, IRQ_GPIO(SG2_S0_GPIO_DETECT), "PCMCIA0 CD" },
+};
+
+static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ skt->irq = IRQ_GPIO(SG2_S0_GPIO_READY);
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ state->detect = !gpio_get_value(SG2_S0_GPIO_DETECT);
+ state->ready = !!gpio_get_value(SG2_S0_GPIO_READY);
+ state->bvd1 = 0; /* not available - battery detect on card */
+ state->bvd2 = 0; /* not available */
+ state->vs_3v = 1; /* not available - voltage detect for card */
+ state->vs_Xv = 0; /* not available */
+ state->wrprot = 0; /* not available - write protect */
+}
+
+static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ /* Enable card power */
+ switch (state->Vcc) {
+ case 0:
+ /* sets power ctl register high */
+ gpio_set_value(SG2_S0_POWER_CTL, 1);
+ break;
+ case 33:
+ case 50:
+ /* sets power control register low (clear) */
+ gpio_set_value(SG2_S0_POWER_CTL, 0);
+ msleep(100);
+ break;
+ default:
+ pr_err("%s(): bad Vcc %u\n",
+ __func__, state->Vcc);
+ return -1;
+ }
+
+ /* reset */
+ gpio_set_value(SG2_S0_GPIO_RESET, !!(state->flags & SS_RESET));
+
+ return 0;
+}
+
+static void sg2_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static struct pcmcia_low_level sg2_pcmcia_ops __initdata = {
+ .owner = THIS_MODULE,
+ .hw_init = sg2_pcmcia_hw_init,
+ .hw_shutdown = sg2_pcmcia_hw_shutdown,
+ .socket_state = sg2_pcmcia_socket_state,
+ .configure_socket = sg2_pcmcia_configure_socket,
+ .socket_init = sg2_pcmcia_socket_init,
+ .socket_suspend = sg2_pcmcia_socket_suspend,
+ .nr = 1,
+};
+
+static struct platform_device *sg2_pcmcia_device;
+
+static int __init sg2_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_stargate2())
+ return -ENODEV;
+
+ sg2_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!sg2_pcmcia_device)
+ return -ENOMEM;
+
+ ret = gpio_request(SG2_S0_BUFF_CTL, "SG2 CF buff ctl");
+ if (ret)
+ goto error_put_platform_device;
+ ret = gpio_request(SG2_S0_POWER_CTL, "SG2 CF power ctl");
+ if (ret)
+ goto error_free_gpio_buff_ctl;
+ ret = gpio_request(SG2_S0_GPIO_RESET, "SG2 CF reset");
+ if (ret)
+ goto error_free_gpio_power_ctl;
+ /* Set gpio directions */
+ gpio_direction_output(SG2_S0_BUFF_CTL, 0);
+ gpio_direction_output(SG2_S0_POWER_CTL, 1);
+ gpio_direction_output(SG2_S0_GPIO_RESET, 1);
+
+ ret = platform_device_add_data(sg2_pcmcia_device,
+ &sg2_pcmcia_ops,
+ sizeof(sg2_pcmcia_ops));
+ if (ret)
+ goto error_free_gpio_reset;
+
+ ret = platform_device_add(sg2_pcmcia_device);
+ if (ret)
+ goto error_free_gpio_reset;
+
+ return 0;
+error_free_gpio_reset:
+ gpio_free(SG2_S0_GPIO_RESET);
+error_free_gpio_power_ctl:
+ gpio_free(SG2_S0_POWER_CTL);
+error_free_gpio_buff_ctl:
+ gpio_free(SG2_S0_BUFF_CTL);
+error_put_platform_device:
+ platform_device_put(sg2_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit sg2_pcmcia_exit(void)
+{
+ platform_device_unregister(sg2_pcmcia_device);
+ gpio_free(SG2_S0_BUFF_CTL);
+ gpio_free(SG2_S0_POWER_CTL);
+ gpio_free(SG2_S0_GPIO_RESET);
+}
+
+fs_initcall(sg2_pcmcia_init);
+module_exit(sg2_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
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/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/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-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index f7a3283dd029..551332e4ed02 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -12,32 +12,56 @@
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
-#include <mach/hardware.h>
+#include <linux/io.h>
+
+#define EP93XX_RTC_DATA 0x000
+#define EP93XX_RTC_MATCH 0x004
+#define EP93XX_RTC_STATUS 0x008
+#define EP93XX_RTC_STATUS_INTR (1<<0)
+#define EP93XX_RTC_LOAD 0x00C
+#define EP93XX_RTC_CONTROL 0x010
+#define EP93XX_RTC_CONTROL_MIE (1<<0)
+#define EP93XX_RTC_SWCOMP 0x108
+#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000
+#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16
+#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff
+#define EP93XX_RTC_SWCOMP_INT_SHIFT 0
+
+#define DRV_VERSION "0.3"
-#define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x))
-#define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000)
-#define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C)
-#define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108)
-
-#define DRV_VERSION "0.2"
+/*
+ * struct device dev.platform_data is used to store our private data
+ * because struct rtc_device does not have a variable to hold it.
+ */
+struct ep93xx_rtc {
+ void __iomem *mmio_base;
+};
-static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload,
+static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
unsigned short *delete)
{
- unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+ unsigned long comp;
+
+ comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
if (preload)
- *preload = comp & 0xffff;
+ *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK)
+ >> EP93XX_RTC_SWCOMP_INT_SHIFT;
if (delete)
- *delete = (comp >> 16) & 0x1f;
+ *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK)
+ >> EP93XX_RTC_SWCOMP_DEL_SHIFT;
return 0;
}
static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- unsigned long time = __raw_readl(EP93XX_RTC_DATA);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+ unsigned long time;
+
+ time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
rtc_time_to_tm(time, tm);
return 0;
@@ -45,7 +69,9 @@ static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
{
- __raw_writel(secs + 1, EP93XX_RTC_LOAD);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+
+ __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
return 0;
}
@@ -53,7 +79,7 @@ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned short preload, delete;
- ep93xx_get_swcomp(dev, &preload, &delete);
+ ep93xx_rtc_get_swcomp(dev, &preload, &delete);
seq_printf(seq, "preload\t\t: %d\n", preload);
seq_printf(seq, "delete\t\t: %d\n", delete);
@@ -67,54 +93,104 @@ static const struct rtc_class_ops ep93xx_rtc_ops = {
.proc = ep93xx_rtc_proc,
};
-static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev,
+static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short preload;
- ep93xx_get_swcomp(dev, &preload, NULL);
+ ep93xx_rtc_get_swcomp(dev, &preload, NULL);
return sprintf(buf, "%d\n", preload);
}
-static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL);
+static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL);
-static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev,
+static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short delete;
- ep93xx_get_swcomp(dev, NULL, &delete);
+ ep93xx_rtc_get_swcomp(dev, NULL, &delete);
return sprintf(buf, "%d\n", delete);
}
-static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL);
+static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
-static int __devinit ep93xx_rtc_probe(struct platform_device *dev)
+static int __init ep93xx_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc = rtc_device_register("ep93xx",
- &dev->dev, &ep93xx_rtc_ops, THIS_MODULE);
+ struct ep93xx_rtc *ep93xx_rtc;
+ struct resource *res;
+ struct rtc_device *rtc;
+ int err;
+
+ ep93xx_rtc = kzalloc(sizeof(struct ep93xx_rtc), GFP_KERNEL);
+ if (ep93xx_rtc == NULL)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENXIO;
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL)
+ return -EBUSY;
+
+ ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res));
+ if (ep93xx_rtc->mmio_base == NULL) {
+ err = -ENXIO;
+ goto fail;
+ }
+ pdev->dev.platform_data = ep93xx_rtc;
+
+ rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &ep93xx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
- return PTR_ERR(rtc);
+ err = PTR_ERR(rtc);
+ goto fail;
}
- platform_set_drvdata(dev, rtc);
+ platform_set_drvdata(pdev, rtc);
- device_create_file(&dev->dev, &dev_attr_comp_preload);
- device_create_file(&dev->dev, &dev_attr_comp_delete);
+ err = device_create_file(&pdev->dev, &dev_attr_comp_preload);
+ if (err)
+ goto fail;
+ err = device_create_file(&pdev->dev, &dev_attr_comp_delete);
+ if (err) {
+ device_remove_file(&pdev->dev, &dev_attr_comp_preload);
+ goto fail;
+ }
return 0;
+
+fail:
+ if (ep93xx_rtc->mmio_base) {
+ iounmap(ep93xx_rtc->mmio_base);
+ pdev->dev.platform_data = NULL;
+ }
+ release_mem_region(res->start, resource_size(res));
+ return err;
}
-static int __devexit ep93xx_rtc_remove(struct platform_device *dev)
+static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct ep93xx_rtc *ep93xx_rtc = pdev->dev.platform_data;
+ struct resource *res;
+
+ /* cleanup sysfs */
+ device_remove_file(&pdev->dev, &dev_attr_comp_delete);
+ device_remove_file(&pdev->dev, &dev_attr_comp_preload);
+
+ rtc_device_unregister(rtc);
+
+ iounmap(ep93xx_rtc->mmio_base);
+ pdev->dev.platform_data = NULL;
- if (rtc)
- rtc_device_unregister(rtc);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
- platform_set_drvdata(dev, NULL);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -122,23 +198,22 @@ static int __devexit ep93xx_rtc_remove(struct platform_device *dev)
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:ep93xx-rtc");
-static struct platform_driver ep93xx_rtc_platform_driver = {
+static struct platform_driver ep93xx_rtc_driver = {
.driver = {
.name = "ep93xx-rtc",
.owner = THIS_MODULE,
},
- .probe = ep93xx_rtc_probe,
- .remove = __devexit_p(ep93xx_rtc_remove),
+ .remove = __exit_p(ep93xx_rtc_remove),
};
static int __init ep93xx_rtc_init(void)
{
- return platform_driver_register(&ep93xx_rtc_platform_driver);
+ return platform_driver_probe(&ep93xx_rtc_driver, ep93xx_rtc_probe);
}
static void __exit ep93xx_rtc_exit(void)
{
- platform_driver_unregister(&ep93xx_rtc_platform_driver);
+ platform_driver_unregister(&ep93xx_rtc_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index 826153552157..457231bb1029 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;
@@ -117,7 +117,7 @@ static int pl030_probe(struct amba_device *dev, void *id)
goto err_rtc;
}
- rtc->base = ioremap(dev->res.start, SZ_4K);
+ rtc->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!rtc->base) {
ret = -ENOMEM;
goto err_map;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 333eec689d2f..f41873f98f66 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;
@@ -142,8 +142,7 @@ static int pl031_probe(struct amba_device *adev, void *id)
goto out;
}
- ldata->base = ioremap(adev->res.start,
- adev->res.end - adev->res.start + 1);
+ ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
if (!ldata->base) {
ret = -ENOMEM;
goto out_no_remap;
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index d1815272c435..442bb98a2821 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -603,7 +603,7 @@ static void dasd_profile_end(struct dasd_block *block,
if (dasd_profile_level != DASD_PROFILE_ON)
return;
- sectors = req->nr_sectors;
+ sectors = blk_rq_sectors(req);
if (!cqr->buildclk || !cqr->startclk ||
!cqr->stopclk || !cqr->endclk ||
!sectors)
@@ -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;
}
@@ -1614,15 +1632,6 @@ void dasd_block_clear_timer(struct dasd_block *block)
}
/*
- * posts the buffer_cache about a finalized request
- */
-static inline void dasd_end_request(struct request *req, int error)
-{
- if (__blk_end_request(req, error, blk_rq_bytes(req)))
- BUG();
-}
-
-/*
* Process finished error recovery ccw.
*/
static inline void __dasd_block_process_erp(struct dasd_block *block,
@@ -1665,18 +1674,14 @@ static void __dasd_process_request_queue(struct dasd_block *block)
if (basedev->state < DASD_STATE_READY)
return;
/* Now we try to fetch requests from the request queue */
- while (!blk_queue_plugged(queue) &&
- elv_next_request(queue)) {
-
- req = elv_next_request(queue);
-
+ while (!blk_queue_plugged(queue) && (req = blk_peek_request(queue))) {
if (basedev->features & DASD_FEATURE_READONLY &&
rq_data_dir(req) == WRITE) {
DBF_DEV_EVENT(DBF_ERR, basedev,
"Rejecting write request %p",
req);
- blkdev_dequeue_request(req);
- dasd_end_request(req, -EIO);
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
continue;
}
cqr = basedev->discipline->build_cp(basedev, block, req);
@@ -1704,8 +1709,8 @@ static void __dasd_process_request_queue(struct dasd_block *block)
"CCW creation failed (rc=%ld) "
"on request %p",
PTR_ERR(cqr), req);
- blkdev_dequeue_request(req);
- dasd_end_request(req, -EIO);
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
continue;
}
/*
@@ -1714,7 +1719,7 @@ static void __dasd_process_request_queue(struct dasd_block *block)
*/
cqr->callback_data = (void *) req;
cqr->status = DASD_CQR_FILLED;
- blkdev_dequeue_request(req);
+ blk_start_request(req);
list_add_tail(&cqr->blocklist, &block->ccw_queue);
dasd_profile_start(block, cqr, req);
}
@@ -1731,7 +1736,7 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
status = cqr->block->base->discipline->free_cp(cqr, req);
if (status <= 0)
error = status ? status : -EIO;
- dasd_end_request(req, error);
+ __blk_end_request_all(req, error);
}
/*
@@ -2003,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);
@@ -2038,10 +2043,8 @@ static void dasd_flush_request_queue(struct dasd_block *block)
return;
spin_lock_irq(&block->request_queue_lock);
- while ((req = elv_next_request(block->request_queue))) {
- blkdev_dequeue_request(req);
- dasd_end_request(req, -EIO);
- }
+ while ((req = blk_fetch_request(block->request_queue)))
+ __blk_end_request_all(req, -EIO);
spin_unlock_irq(&block->request_queue_lock);
}
@@ -2442,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 b9a7f7733446..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;
}
@@ -505,8 +506,9 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
return ERR_PTR(-EINVAL);
blksize = block->bp_block;
/* Calculate record id of first and last block. */
- first_rec = req->sector >> block->s2b_shift;
- last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
+ first_rec = blk_rq_pos(req) >> block->s2b_shift;
+ last_rec =
+ (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
/* Check struct bio and count the number of blocks for the request. */
count = 0;
rq_for_each_segment(bv, req, iter) {
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index cb52da033f06..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;
@@ -2354,18 +2354,22 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
blksize = block->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
/* Calculate record id of first and last block. */
- first_rec = first_trk = req->sector >> block->s2b_shift;
+ first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift;
first_offs = sector_div(first_trk, blk_per_trk);
last_rec = last_trk =
- (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
+ (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
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;
@@ -2420,7 +2424,7 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
private = (struct dasd_eckd_private *) cqr->block->base->private;
blksize = cqr->block->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
- recid = req->sector >> cqr->block->s2b_shift;
+ recid = blk_rq_pos(req) >> cqr->block->s2b_shift;
ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */
ccw++;
@@ -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 a3eb6fd14673..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",
@@ -270,8 +270,9 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
return ERR_PTR(-EINVAL);
blksize = block->bp_block;
/* Calculate record id of first and last block. */
- first_rec = req->sector >> block->s2b_shift;
- last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
+ first_rec = blk_rq_pos(req) >> block->s2b_shift;
+ last_rec =
+ (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
/* Check struct bio and count the number of blocks for the request. */
count = 0;
cidaw = 0;
@@ -309,7 +310,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
ccw = cqr->cpaddr;
/* First ccw is define extent. */
define_extent(ccw++, cqr->data, rq_data_dir(req),
- block->bp_block, req->sector, req->nr_sectors);
+ block->bp_block, blk_rq_pos(req), blk_rq_sectors(req));
/* Build locate_record + read/write ccws. */
idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data));
LO_data = (struct LO_fba_data *) (idaws + cidaw);
@@ -603,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_34xx.c b/drivers/s390/char/tape_34xx.c
index 5f8e8ef43dd3..2d00a383a475 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -1134,7 +1134,7 @@ tape_34xx_bread(struct tape_device *device, struct request *req)
/* Setup ccws. */
request->op = TO_BLOCK;
start_block = (struct tape_34xx_block_id *) request->cpdata;
- start_block->block = req->sector >> TAPEBLOCK_HSEC_S2B;
+ start_block->block = blk_rq_pos(req) >> TAPEBLOCK_HSEC_S2B;
DBF_EVENT(6, "start_block = %i\n", start_block->block);
ccw = request->cpaddr;
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 823b05bd0dd7..c453b2f3e9f4 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -633,7 +633,7 @@ tape_3590_bread(struct tape_device *device, struct request *req)
struct req_iterator iter;
DBF_EVENT(6, "xBREDid:");
- start_block = req->sector >> TAPEBLOCK_HSEC_S2B;
+ start_block = blk_rq_pos(req) >> TAPEBLOCK_HSEC_S2B;
DBF_EVENT(6, "start_block = %i\n", start_block);
rq_for_each_segment(bv, req, iter)
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index f32e89e7c4f2..47ff695255ea 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -74,13 +74,6 @@ tapeblock_trigger_requeue(struct tape_device *device)
* Post finished request.
*/
static void
-tapeblock_end_request(struct request *req, int error)
-{
- if (blk_end_request(req, error, blk_rq_bytes(req)))
- BUG();
-}
-
-static void
__tapeblock_end_request(struct tape_request *ccw_req, void *data)
{
struct tape_device *device;
@@ -90,17 +83,17 @@ __tapeblock_end_request(struct tape_request *ccw_req, void *data)
device = ccw_req->device;
req = (struct request *) data;
- tapeblock_end_request(req, (ccw_req->rc == 0) ? 0 : -EIO);
+ blk_end_request_all(req, (ccw_req->rc == 0) ? 0 : -EIO);
if (ccw_req->rc == 0)
/* Update position. */
device->blk_data.block_position =
- (req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B;
+ (blk_rq_pos(req) + blk_rq_sectors(req)) >> TAPEBLOCK_HSEC_S2B;
else
/* We lost the position information due to an error. */
device->blk_data.block_position = -1;
device->discipline->free_bread(ccw_req);
if (!list_empty(&device->req_queue) ||
- elv_next_request(device->blk_data.request_queue))
+ blk_peek_request(device->blk_data.request_queue))
tapeblock_trigger_requeue(device);
}
@@ -118,7 +111,7 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
ccw_req = device->discipline->bread(device, req);
if (IS_ERR(ccw_req)) {
DBF_EVENT(1, "TBLOCK: bread failed\n");
- tapeblock_end_request(req, -EIO);
+ blk_end_request_all(req, -EIO);
return PTR_ERR(ccw_req);
}
ccw_req->callback = __tapeblock_end_request;
@@ -131,7 +124,7 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
* Start/enqueueing failed. No retries in
* this case.
*/
- tapeblock_end_request(req, -EIO);
+ blk_end_request_all(req, -EIO);
device->discipline->free_bread(ccw_req);
}
@@ -169,19 +162,16 @@ tapeblock_requeue(struct work_struct *work) {
spin_lock_irq(&device->blk_data.request_queue_lock);
while (
!blk_queue_plugged(queue) &&
- elv_next_request(queue) &&
+ (req = blk_fetch_request(queue)) &&
nr_queued < TAPEBLOCK_MIN_REQUEUE
) {
- req = elv_next_request(queue);
if (rq_data_dir(req) == WRITE) {
DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
- blkdev_dequeue_request(req);
spin_unlock_irq(&device->blk_data.request_queue_lock);
- tapeblock_end_request(req, -EIO);
+ blk_end_request_all(req, -EIO);
spin_lock_irq(&device->blk_data.request_queue_lock);
continue;
}
- blkdev_dequeue_request(req);
nr_queued++;
spin_unlock_irq(&device->blk_data.request_queue_lock);
rc = tapeblock_start_request(device, req);
@@ -232,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/net/Kconfig b/drivers/s390/net/Kconfig
index a7745c82b4ae..cb909a5b5047 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -8,7 +8,7 @@ config LCS
Select this option if you want to use LCS networking on IBM System z.
This device driver supports Token Ring (IEEE 802.5),
FDDI (IEEE 802.7) and Ethernet.
- To compile as a module, choose M. The module name is lcs.ko.
+ To compile as a module, choose M. The module name is lcs.
If you do not know what it is, it's safe to choose Y.
config CTCM
@@ -21,7 +21,7 @@ config CTCM
It also supports virtual CTCs when running under VM.
This driver also supports channel-to-channel MPC SNA devices.
MPC is an SNA protocol device used by Communication Server for Linux.
- To compile as a module, choose M. The module name is ctcm.ko.
+ To compile as a module, choose M. The module name is ctcm.
To compile into the kernel, choose Y.
If you do not need any channel-to-channel connection, choose N.
@@ -34,7 +34,7 @@ config NETIUCV
link between VM guests. Using ifconfig a point-to-point connection
can be established to the Linux on IBM System z
running on the other VM guest. To compile as a module, choose M.
- The module name is netiucv.ko. If unsure, choose Y.
+ The module name is netiucv. If unsure, choose Y.
config SMSGIUCV
tristate "IUCV special message support (VM only)"
@@ -50,7 +50,7 @@ config CLAW
This driver supports channel attached CLAW devices.
CLAW is Common Link Access for Workstation. Common devices
that use CLAW are RS/6000s, Cisco Routers (CIP) and 3172 devices.
- To compile as a module, choose M. The module name is claw.ko.
+ To compile as a module, choose M. The module name is claw.
To compile into the kernel, choose Y.
config QETH
@@ -65,14 +65,14 @@ config QETH
<http://www.ibm.com/developerworks/linux/linux390>
To compile this driver as a module, choose M.
- The module name is qeth.ko.
+ The module name is qeth.
config QETH_L2
tristate "qeth layer 2 device support"
depends on QETH
help
Select this option to be able to run qeth devices in layer 2 mode.
- To compile as a module, choose M. The module name is qeth_l2.ko.
+ To compile as a module, choose M. The module name is qeth_l2.
If unsure, choose y.
config QETH_L3
@@ -80,7 +80,7 @@ config QETH_L3
depends on QETH
help
Select this option to be able to run qeth devices in layer 3 mode.
- To compile as a module choose M. The module name is qeth_l3.ko.
+ To compile as a module choose M. The module name is qeth_l3.
If unsure, choose Y.
config QETH_IPV6
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 733fe3bf6285..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
@@ -176,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;
}
@@ -199,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_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 4c362a9069f0..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)
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index fdc9b4352a64..e50ea465bc2b 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -880,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;
@@ -894,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;
}
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 2e31b536548c..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 */
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 19ae0842047c..35493a82d2a8 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -116,7 +116,7 @@ static void zfcp_wka_port_put(struct zfcp_wka_port *wka_port)
{
if (atomic_dec_return(&wka_port->refcount) != 0)
return;
- /* wait 10 miliseconds, other reqs might pop in */
+ /* wait 10 milliseconds, other reqs might pop in */
schedule_delayed_work(&wka_port->work, HZ / 100);
}
@@ -150,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);
}
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 74dee32afba8..e6dae3744e79 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -526,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 "
@@ -897,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;
@@ -993,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:
@@ -1399,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:
@@ -1461,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);
}
/**
@@ -1473,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);
@@ -1493,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);
@@ -1590,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;
@@ -1876,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 &
@@ -2314,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;
@@ -2356,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:
@@ -2369,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;
}
@@ -2394,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",
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index e8fbeaeb5fbf..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;
@@ -647,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/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c
index a85ad05e8548..6d4651684688 100644
--- a/drivers/sbus/char/jsflash.c
+++ b/drivers/sbus/char/jsflash.c
@@ -186,31 +186,31 @@ static void jsfd_do_request(struct request_queue *q)
{
struct request *req;
- while ((req = elv_next_request(q)) != NULL) {
+ req = blk_fetch_request(q);
+ while (req) {
struct jsfd_part *jdp = req->rq_disk->private_data;
- unsigned long offset = req->sector << 9;
- size_t len = req->current_nr_sectors << 9;
+ unsigned long offset = blk_rq_pos(req) << 9;
+ size_t len = blk_rq_cur_bytes(req);
+ int err = -EIO;
- if ((offset + len) > jdp->dsize) {
- end_request(req, 0);
- continue;
- }
+ if ((offset + len) > jdp->dsize)
+ goto end;
if (rq_data_dir(req) != READ) {
printk(KERN_ERR "jsfd: write\n");
- end_request(req, 0);
- continue;
+ goto end;
}
if ((jdp->dbase & 0xff000000) != 0x20000000) {
printk(KERN_ERR "jsfd: bad base %x\n", (int)jdp->dbase);
- end_request(req, 0);
- continue;
+ goto end;
}
jsfd_read(req->buffer, jdp->dbase + offset, len);
-
- end_request(req, 1);
+ err = 0;
+ end:
+ if (!__blk_end_request_cur(req, err))
+ req = blk_fetch_request(q);
}
}
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 59b0958d2d11..e3133b58e594 100644
--- a/drivers/scsi/cxgb3i/cxgb3i.h
+++ b/drivers/scsi/cxgb3i/cxgb3i.h
@@ -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_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index 9212400b9b13..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");
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c
index e11c9c180f39..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;
}
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/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/dpt/osd_util.h b/drivers/scsi/dpt/osd_util.h
index 4b56c0436ba2..b2613c2eaac7 100644
--- a/drivers/scsi/dpt/osd_util.h
+++ b/drivers/scsi/dpt/osd_util.h
@@ -342,7 +342,7 @@ uLONG osdGetThreadID(void);
/* wakes up the specifed thread */
void osdWakeThread(uLONG);
-/* osd sleep for x miliseconds */
+/* osd sleep for x milliseconds */
void osdSleep(uLONG);
#define DPT_THREAD_PRIORITY_LOWEST 0x00
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index be5099dd94b5..c7076ce25e21 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1825,7 +1825,7 @@ static int eata2x_queuecommand(struct scsi_cmnd *SCpnt,
if (linked_comm && SCpnt->device->queue_depth > 2
&& TLDEV(SCpnt->device->type)) {
ha->cp_stat[i] = READY;
- flush_dev(SCpnt->device, SCpnt->request->sector, ha, 0);
+ flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 0);
return 0;
}
@@ -2144,13 +2144,13 @@ static int reorder(struct hostdata *ha, unsigned long cursec,
if (!cpp->din)
input_only = 0;
- if (SCpnt->request->sector < minsec)
- minsec = SCpnt->request->sector;
- if (SCpnt->request->sector > maxsec)
- maxsec = SCpnt->request->sector;
+ if (blk_rq_pos(SCpnt->request) < minsec)
+ minsec = blk_rq_pos(SCpnt->request);
+ if (blk_rq_pos(SCpnt->request) > maxsec)
+ maxsec = blk_rq_pos(SCpnt->request);
- sl[n] = SCpnt->request->sector;
- ioseek += SCpnt->request->nr_sectors;
+ sl[n] = blk_rq_pos(SCpnt->request);
+ ioseek += blk_rq_sectors(SCpnt->request);
if (!n)
continue;
@@ -2190,7 +2190,7 @@ static int reorder(struct hostdata *ha, unsigned long cursec,
k = il[n];
cpp = &ha->cp[k];
SCpnt = cpp->SCpnt;
- ll[n] = SCpnt->request->nr_sectors;
+ ll[n] = blk_rq_sectors(SCpnt->request);
pl[n] = SCpnt->serial_number;
if (!n)
@@ -2236,12 +2236,12 @@ static int reorder(struct hostdata *ha, unsigned long cursec,
cpp = &ha->cp[k];
SCpnt = cpp->SCpnt;
scmd_printk(KERN_INFO, SCpnt,
- "%s pid %ld mb %d fc %d nr %d sec %ld ns %ld"
+ "%s pid %ld mb %d fc %d nr %d sec %ld ns %u"
" cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
(ihdlr ? "ihdlr" : "qcomm"),
SCpnt->serial_number, k, flushcount,
- n_ready, SCpnt->request->sector,
- SCpnt->request->nr_sectors, cursec, YESNO(s),
+ n_ready, blk_rq_pos(SCpnt->request),
+ blk_rq_sectors(SCpnt->request), cursec, YESNO(s),
YESNO(r), YESNO(rev), YESNO(input_only),
YESNO(overlap), cpp->din);
}
@@ -2408,7 +2408,7 @@ static irqreturn_t ihdlr(struct Scsi_Host *shost)
if (linked_comm && SCpnt->device->queue_depth > 2
&& TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, SCpnt->request->sector, ha, 1);
+ flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 1);
tstatus = status_byte(spp->target_status);
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index f791348871fc..c15878e88157 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -54,7 +54,6 @@ 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 Prototypes */
@@ -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);
@@ -198,6 +197,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 |
@@ -243,6 +243,18 @@ void fcoe_netdev_cleanup(struct fcoe_softc *fc)
}
/**
+ * 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
@@ -313,6 +325,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);
/* look for SAN MAC address, if multiple SAN MACs exist, only
* use the first one for SPMA */
@@ -474,6 +487,9 @@ static int fcoe_if_destroy(struct net_device *netdev)
/* 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);
@@ -1021,7 +1037,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;
@@ -1054,8 +1070,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;
@@ -1140,18 +1155,9 @@ 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;
}
@@ -1301,32 +1307,6 @@ 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() - attempt to clear the transmit backlog
* @lp: the fc_lport
*
@@ -1338,16 +1318,17 @@ void fcoe_watchdog(ulong vp)
* The wait_queue is used when the skb transmit fails. skb will go
* 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;
@@ -1373,23 +1354,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)
{
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)
{
@@ -1848,10 +1832,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;
@@ -1877,9 +1857,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 b9aa280100b0..2f5bc7fd3fa9 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -215,7 +215,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);
@@ -369,7 +369,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);
@@ -432,7 +432,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
else if (fip->spma)
memcpy(mac->fd_mac, fip->ctl_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;
@@ -455,14 +455,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) {
@@ -474,6 +470,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 dd689ded8609..0f8bc772b112 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -7003,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);
}
@@ -7688,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);
@@ -7864,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/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 521f996f9b13..ad8b747837b0 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1896,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_rport.c b/drivers/scsi/libfc/fc_rport.c
index 747d73c5c8af..7bfbff7e0efb 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -478,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++;
@@ -1330,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)
@@ -1339,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 e72b4ad47d35..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",
@@ -1608,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);
}
}
@@ -1655,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);
@@ -1663,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)
@@ -1702,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;
@@ -1738,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;
@@ -1788,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);
/*
@@ -1810,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;
}
@@ -1829,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;
}
@@ -1860,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);
@@ -1967,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);
@@ -2274,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);
}
@@ -2360,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);
@@ -2531,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;
+
+ 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);
- conn->task = NULL;
+ }
}
static void iscsi_start_session_recovery(struct iscsi_session *session,
@@ -2559,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) {
@@ -2578,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
@@ -2605,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);
}
@@ -2651,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)
@@ -2723,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;
@@ -2762,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;
}
@@ -2851,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;
@@ -2920,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;
@@ -2959,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 b579ca9f4836..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");
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 3da02e436788..54fa1e42dc4d 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1927,21 +1927,21 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
/* do we need to support multiple segments? */
if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
printk("%s: multiple segments req %u %u, rsp %u %u\n",
- __func__, req->bio->bi_vcnt, req->data_len,
- rsp->bio->bi_vcnt, rsp->data_len);
+ __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
+ rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
return -EINVAL;
}
- ret = smp_execute_task(dev, bio_data(req->bio), req->data_len,
- bio_data(rsp->bio), rsp->data_len);
+ ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req),
+ bio_data(rsp->bio), blk_rq_bytes(rsp));
if (ret > 0) {
/* positive number is the untransferred residual */
- rsp->data_len = ret;
- req->data_len = 0;
+ rsp->resid_len = ret;
+ req->resid_len = 0;
ret = 0;
} else if (ret == 0) {
- rsp->data_len = 0;
- req->data_len = 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 d110a366c48a..1bc3b7567994 100644
--- a/drivers/scsi/libsas/sas_host_smp.c
+++ b/drivers/scsi/libsas/sas_host_smp.c
@@ -134,24 +134,24 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
{
u8 *req_data = NULL, *resp_data = NULL, *buf;
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
- int error = -EINVAL, resp_data_len = rsp->data_len;
+ int error = -EINVAL;
/* eight is the minimum size for request and response frames */
- if (req->data_len < 8 || rsp->data_len < 8)
+ if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8)
goto out;
- if (bio_offset(req->bio) + req->data_len > PAGE_SIZE ||
- bio_offset(rsp->bio) + rsp->data_len > PAGE_SIZE) {
+ if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE ||
+ bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) {
shost_printk(KERN_ERR, shost,
"SMP request/response frame crosses page boundary");
goto out;
}
- req_data = kzalloc(req->data_len, GFP_KERNEL);
+ req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL);
/* make sure frame can always be built ... we copy
* back only the requested length */
- resp_data = kzalloc(max(rsp->data_len, 128U), GFP_KERNEL);
+ resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL);
if (!req_data || !resp_data) {
error = -ENOMEM;
@@ -160,7 +160,7 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
local_irq_disable();
buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio);
- memcpy(req_data, buf, req->data_len);
+ memcpy(req_data, buf, blk_rq_bytes(req));
kunmap_atomic(buf - bio_offset(req->bio), KM_USER0);
local_irq_enable();
@@ -178,15 +178,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
switch (req_data[1]) {
case SMP_REPORT_GENERAL:
- req->data_len -= 8;
- resp_data_len -= 32;
+ req->resid_len -= 8;
+ rsp->resid_len -= 32;
resp_data[2] = SMP_RESP_FUNC_ACC;
resp_data[9] = sas_ha->num_phys;
break;
case SMP_REPORT_MANUF_INFO:
- req->data_len -= 8;
- resp_data_len -= 64;
+ req->resid_len -= 8;
+ rsp->resid_len -= 64;
resp_data[2] = SMP_RESP_FUNC_ACC;
memcpy(resp_data + 12, shost->hostt->name,
SAS_EXPANDER_VENDOR_ID_LEN);
@@ -199,13 +199,13 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_DISCOVER:
- req->data_len -= 16;
- if ((int)req->data_len < 0) {
- req->data_len = 0;
+ req->resid_len -= 16;
+ if ((int)req->resid_len < 0) {
+ req->resid_len = 0;
error = -EINVAL;
goto out;
}
- resp_data_len -= 56;
+ rsp->resid_len -= 56;
sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
break;
@@ -215,13 +215,13 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_REPORT_PHY_SATA:
- req->data_len -= 16;
- if ((int)req->data_len < 0) {
- req->data_len = 0;
+ req->resid_len -= 16;
+ if ((int)req->resid_len < 0) {
+ req->resid_len = 0;
error = -EINVAL;
goto out;
}
- resp_data_len -= 60;
+ rsp->resid_len -= 60;
sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
break;
@@ -238,13 +238,13 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_PHY_CONTROL:
- req->data_len -= 44;
- if ((int)req->data_len < 0) {
- req->data_len = 0;
+ req->resid_len -= 44;
+ if ((int)req->resid_len < 0) {
+ req->resid_len = 0;
error = -EINVAL;
goto out;
}
- resp_data_len -= 8;
+ rsp->resid_len -= 8;
sas_phy_control(sas_ha, req_data[9], req_data[10],
req_data[32] >> 4, req_data[33] >> 4,
resp_data);
@@ -261,11 +261,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
local_irq_disable();
buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio);
- memcpy(buf, resp_data, rsp->data_len);
+ memcpy(buf, resp_data, blk_rq_bytes(rsp));
flush_kernel_dcache_page(bio_page(rsp->bio));
kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0);
local_irq_enable();
- rsp->data_len = resp_data_len;
out:
kfree(req_data);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 1105f9a111ba..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,8 +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 0x4 /* Deferred error attention in progress */
+#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;
@@ -502,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;
@@ -511,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 */
@@ -526,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 */
@@ -593,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;
@@ -609,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 */
@@ -667,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 *
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index c14f0cbdb125..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"
@@ -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);
@@ -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,18 +833,37 @@ 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;
@@ -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");
/*
@@ -2266,6 +2277,36 @@ lpfc_param_init(topology, 0, 0, 6)
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
@@ -2341,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 */
@@ -2356,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 */
@@ -2373,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;
@@ -2844,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)
@@ -2876,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
@@ -2942,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,
@@ -2969,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,
@@ -2991,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,
@@ -3003,6 +3081,7 @@ 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,
};
@@ -3199,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;
@@ -3241,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;
@@ -3265,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:
@@ -3283,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;
@@ -3319,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;
@@ -3335,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;
@@ -3356,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,
@@ -3368,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);
}
@@ -3391,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;
@@ -3585,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;
@@ -3652,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)
@@ -3663,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);
@@ -3695,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);
@@ -3769,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 */
@@ -3777,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);
@@ -3795,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);
@@ -3962,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,
@@ -4105,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);
@@ -4113,26 +4213,10 @@ 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;
}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index f88ce3f26190..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);
@@ -186,6 +212,7 @@ 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);
@@ -198,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 *);
@@ -237,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 *,
@@ -254,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 *);
@@ -314,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 52be5644e07a..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"
@@ -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);
@@ -489,12 +493,15 @@ 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;
}
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index ffd108972072..1142070e9484 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -135,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 b8b34cf5c3d2..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"
@@ -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 */
@@ -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)
@@ -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,6 +388,75 @@ fail:
}
/**
+ * 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.
@@ -497,17 +568,24 @@ 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
* @vport: pointer to a host virtual N_Port data structure.
@@ -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;
@@ -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)) {
@@ -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);
@@ -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);
@@ -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;
}
@@ -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);
@@ -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);
@@ -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.
@@ -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.
@@ -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,6 +3041,7 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
*/
lpfc_nlp_not_used(ndlp);
}
+
return;
}
@@ -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;
@@ -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);
@@ -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;
@@ -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;
@@ -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;
@@ -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:
@@ -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);
@@ -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,7 +4576,7 @@ 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;
}
@@ -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;
}
@@ -4883,7 +4951,10 @@ 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;
@@ -5566,11 +5637,10 @@ 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++;
}
@@ -5646,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.
@@ -5781,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;
@@ -5818,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 +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;
@@ -6139,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;
@@ -6169,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);
@@ -6224,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:
@@ -6248,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;
@@ -6394,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;
@@ -6418,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;
@@ -6524,3 +6594,38 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
}
+
+/**
+ * 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 e764ce0bf704..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"
@@ -273,6 +275,8 @@ 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);
}
/**
@@ -295,10 +299,11 @@ 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;
}
@@ -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);
@@ -2055,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)
@@ -2102,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];
@@ -2150,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);
@@ -2162,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;
@@ -2252,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;
@@ -2261,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) {
@@ -2309,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);
@@ -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;
}
@@ -2919,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 */
@@ -2939,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);
@@ -2959,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;
@@ -3036,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;
@@ -3044,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);
@@ -3297,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 86d1bdcbf2d8..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,9 +53,23 @@ 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;
@@ -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,
@@ -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;
@@ -260,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;
}
@@ -268,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)
@@ -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);
@@ -364,6 +385,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
/* 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 */
@@ -460,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)
@@ -571,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);
}
@@ -588,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
@@ -598,8 +625,8 @@ 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;
@@ -642,6 +669,77 @@ 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_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
@@ -809,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);
@@ -834,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);
@@ -849,6 +949,25 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
}
/**
+ * 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.
*
@@ -864,6 +983,16 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
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",
@@ -871,7 +1000,7 @@ lpfc_handle_deferred_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);
@@ -909,13 +1038,30 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
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 - The HBA hardware error handler
+ * 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
@@ -924,8 +1070,8 @@ lpfc_handle_deferred_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;
@@ -934,24 +1080,23 @@ 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);
@@ -965,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);
/*
@@ -1037,6 +1182,65 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
}
/**
+ * 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.
*
@@ -1137,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;
@@ -1292,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;
@@ -1437,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;
@@ -1444,13 +1657,24 @@ 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");
+ }
}
/**
@@ -1533,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++;
@@ -1761,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");
@@ -1782,7 +2006,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
/* Wait for any activity on ndlps to settle */
msleep(10);
}
- return;
}
/**
@@ -1803,22 +2026,36 @@ 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;
}
@@ -1878,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);
@@ -1947,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,
@@ -1975,7 +2220,7 @@ 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);
}
/**
@@ -1996,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,
@@ -2013,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;
@@ -2106,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
@@ -2123,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);
@@ -2314,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 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 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 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
@@ -2333,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;
@@ -2349,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 "
@@ -2371,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,
@@ -2401,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;
}
@@ -2428,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;
@@ -2444,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;
@@ -2474,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);
@@ -2485,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);
@@ -2503,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;
@@ -2587,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;
@@ -2597,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);
@@ -2636,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;
+ struct lpfc_vport *vport = phba->pport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- phba = kzalloc(sizeof (struct lpfc_hba), GFP_KERNEL);
- if (!phba)
- goto out_release_regions;
+ spin_lock_irq(shost->host_lock);
+ vport->load_flag |= FC_UNLOADING;
+ spin_unlock_irq(shost->host_lock);
- atomic_set(&phba->fast_event_count, 0);
- spin_lock_init(&phba->hbalock);
+ lpfc_stop_hba_timers(phba);
- /* Initialize ndlp management spinlock */
- spin_lock_init(&phba->ndlp_lock);
+ phba->pport->work_port_events = 0;
- phba->pcidev = pdev;
+ lpfc_sli_hba_down(phba);
- /* Assign an unused board number */
- if ((phba->brd_no = lpfc_get_instance()) < 0)
- goto out_free_phba;
+ lpfc_sli_brdrestart(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_disable_intr(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;
+ return;
+}
- 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;
+/**
+ * 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);
- pci_set_master(pdev);
- pci_save_state(pdev);
- pci_try_set_mwi(pdev);
+ spin_lock_irq(shost->host_lock);
+ vport->load_flag |= FC_UNLOADING;
+ spin_unlock_irq(shost->host_lock);
- 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;
+ phba->pport->work_port_events = 0;
- /*
- * 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);
+ lpfc_sli4_hba_down(phba);
- phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2);
- bar2map_len = pci_resource_len(phba->pcidev, 2);
+ lpfc_sli4_disable_intr(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;
- }
-
- /* 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");
@@ -2902,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) {
-
- if (lpfc_prot_mask && lpfc_prot_guard) {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "1478 Registering BlockGuard with the "
- "SCSI layer\n");
+ /* Perform post initialization setup */
+ lpfc_post_init_setup(phba);
- 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;
@@ -3098,7 +6872,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
/* 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);
@@ -3120,7 +6894,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
/* 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);
@@ -3128,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);
@@ -3138,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);
@@ -3151,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;
@@ -3194,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);
@@ -3204,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;
@@ -3250,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");
@@ -3269,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;
@@ -3312,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;
@@ -3354,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 "
@@ -3378,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[] = {
@@ -3469,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 }
};
@@ -3486,7 +7960,7 @@ 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,
};
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index 1aa85709b012..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 /* BlockGuard 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 134fc7fc2127..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,6 +41,44 @@
#include "lpfc_compat.h"
/**
+ * 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.
@@ -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 */
@@ -90,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;
@@ -125,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;
@@ -151,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;
@@ -177,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;
@@ -211,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);
@@ -248,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;
@@ -275,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
@@ -321,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 */
@@ -405,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;
@@ -492,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;
@@ -515,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;
@@ -544,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;
@@ -573,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;
@@ -598,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;
@@ -607,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.
@@ -631,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 */
@@ -697,15 +745,16 @@ 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;
}
@@ -725,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;
@@ -760,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;
@@ -852,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 */
@@ -945,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));
@@ -1020,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;
@@ -1075,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;
@@ -1099,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 */
@@ -1245,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;
@@ -1305,29 +1355,98 @@ lpfc_mbox_get(struct lpfc_hba * phba)
}
/**
+ * __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 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)
+{
+ 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 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 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)
+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_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.
@@ -1350,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 35a976733398..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"
@@ -45,7 +47,7 @@
* @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,27 +258,14 @@ 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;
}
/**
@@ -305,7 +362,7 @@ lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
* 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.
@@ -323,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);
@@ -334,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
*
@@ -348,12 +405,73 @@ 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_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 08cdc77af41c..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"
@@ -361,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);
@@ -495,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);
@@ -567,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;
}
@@ -857,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:
@@ -1068,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;
@@ -1093,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);
@@ -1100,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;
}
@@ -1190,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;
@@ -1200,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) {
@@ -1251,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) {
@@ -1283,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)) {
@@ -1878,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 167b66dd34c7..7991ba1980ae 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)
@@ -325,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 =
@@ -379,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 <=
@@ -427,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));
@@ -438,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;
@@ -463,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;
+ }
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
+ iflag);
+}
+
+/**
+ * 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;
+
+ if (index == phba->sli4_hba.scsi_xri_cnt - 1)
+ /* End of XRI array for SCSI buffer, complete */
+ bcnt = rcnt;
+
+ /* 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);
+ }
}
- psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+ return rc;
+}
- 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_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;
- /* 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);
+ /*
+ * 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;
+ }
- /*
- * 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);
+ /* Initialize virtual ptrs to dma_buf region. */
+ memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
- /*
- * 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;
+ /* 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.
@@ -591,7 +890,7 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
}
/**
- * lpfc_release_scsi_buf - Return a scsi buffer back to hba's lpfc_scsi_buf_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.
*
@@ -599,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;
@@ -610,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;
@@ -827,8 +1174,8 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
* @reftag: out: ref tag (reference tag)
*
* Description:
- * Extract DIF paramters from the command if possible. Otherwise,
- * use default paratmers.
+ * Extract DIF parameters from the command if possible. Otherwise,
+ * use default parameters.
*
**/
static inline void
@@ -1312,10 +1659,10 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
uint32_t bgstat = bgf->bgstat;
uint64_t failing_sector = 0;
- printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%lx "
+ printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%x "
"bgstat=0x%x bghm=0x%x\n",
cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
- cmd->request->nr_sectors, bgstat, bghm);
+ blk_rq_sectors(cmd->request), bgstat, bghm);
spin_lock(&_dump_buf_lock);
if (!_dump_buf_done) {
@@ -1412,6 +1759,133 @@ out:
}
/**
+ * 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.
@@ -1504,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
@@ -1529,6 +2003,36 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
}
/**
+ * 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.
@@ -1676,7 +2180,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
* 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
@@ -1957,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;
@@ -2013,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 {
@@ -2051,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)
@@ -2114,6 +2661,107 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
}
/**
+ * 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.
@@ -2178,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;
@@ -2305,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;
@@ -2378,15 +3024,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
if (cmnd->cmnd[0] == READ_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9035 BLKGRD: READ @ sector %llu, "
- "count %lu\n",
- (unsigned long long)scsi_get_lba(cmnd),
- cmnd->request->nr_sectors);
+ "count %u\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ blk_rq_sectors(cmnd->request));
else if (cmnd->cmnd[0] == WRITE_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9036 BLKGRD: WRITE @ sector %llu, "
- "count %lu cmd=%p\n",
+ "count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
- cmnd->request->nr_sectors,
+ blk_rq_sectors(cmnd->request),
cmnd);
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
@@ -2406,15 +3052,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
if (cmnd->cmnd[0] == READ_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9040 dbg: READ @ sector %llu, "
- "count %lu\n",
+ "count %u\n",
(unsigned long long)scsi_get_lba(cmnd),
- cmnd->request->nr_sectors);
+ blk_rq_sectors(cmnd->request));
else if (cmnd->cmnd[0] == WRITE_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9041 dbg: WRITE @ sector %llu, "
- "count %lu cmd=%p\n",
+ "count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
- cmnd->request->nr_sectors, cmnd);
+ blk_rq_sectors(cmnd->request), cmnd);
else
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9042 dbg: parser not implemented\n");
@@ -2427,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);
@@ -2490,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;
@@ -2531,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;
@@ -2542,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;
@@ -2668,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;
@@ -2825,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;
@@ -2863,20 +3510,13 @@ 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;
}
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 eb5c75c45ba4..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,6 +53,350 @@ 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_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.
@@ -121,6 +451,76 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba)
}
/**
+ * __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.
*
@@ -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,10 +613,27 @@ __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
+ * @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.
@@ -281,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:
@@ -348,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);
@@ -779,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) {
@@ -814,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;
@@ -838,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. */
@@ -914,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
@@ -965,6 +1493,25 @@ lpfc_sli_hbqbuf_init_hbqs(struct lpfc_hba *phba, uint32_t qno)
}
/**
+ * 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.
@@ -985,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);
@@ -1013,9 +1563,8 @@ 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);
- }
}
}
@@ -1086,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:
@@ -1106,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;
@@ -1140,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);
@@ -1150,24 +1708,30 @@ 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);
}
/**
@@ -1204,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) {
@@ -1233,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);
@@ -1250,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],
@@ -1317,6 +1882,45 @@ 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
@@ -1339,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;
@@ -1482,35 +2086,12 @@ 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;
}
@@ -1552,6 +2133,37 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
}
/**
+ * 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.
@@ -1954,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);
}
@@ -2068,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);
@@ -2128,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();
@@ -2173,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
@@ -2338,7 +2998,37 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
}
spin_unlock_irqrestore(&phba->hbalock, iflag);
- return rc;
+ return;
+}
+
+/**
+ * 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);
+ }
}
/**
@@ -2420,7 +3110,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
}
/**
- * 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.
*
@@ -2432,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;
@@ -2477,6 +3167,56 @@ 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)
/**
@@ -2532,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
@@ -2613,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);
@@ -2636,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;
@@ -2647,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
@@ -2683,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);
@@ -2710,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
@@ -2722,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;
@@ -2762,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));
@@ -2777,6 +3579,55 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
}
/**
+ * 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.
*
@@ -2940,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;
@@ -2984,6 +3835,26 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba)
}
/**
+ * 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
@@ -3047,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;
@@ -3089,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,
@@ -3184,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)
@@ -3200,6 +4082,488 @@ 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_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
@@ -3244,7 +4608,7 @@ 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;
@@ -3281,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];
@@ -3289,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.
@@ -3332,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;
@@ -3349,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);
@@ -3365,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;
}
@@ -3385,21 +4742,29 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
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;
}
@@ -3413,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;
}
@@ -3462,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 */
@@ -3506,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 {
@@ -3529,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;
}
}
@@ -3552,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);
@@ -3591,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);
@@ -3604,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;
}
}
@@ -3616,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 {
@@ -3643,13 +5023,420 @@ 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_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.
@@ -3701,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) &&
@@ -3833,6 +5619,498 @@ __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
@@ -3848,14 +6126,14 @@ __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;
@@ -4148,6 +6426,52 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba)
}
/**
+ * 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.
*
@@ -4240,9 +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;
- 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);
@@ -4287,28 +6613,42 @@ 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;
}
@@ -4639,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;
@@ -4655,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);
@@ -4838,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;
@@ -4850,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++;
@@ -4931,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)
@@ -4962,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,
@@ -5077,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;
- if (ha_copy & HA_MBATT)
- if (lpfc_sli_handle_mb_event(phba) == 0)
- i = 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);
+
+ /* 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;
+}
+
+/**
+ * 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;
- return (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) ? 1 : 0;
+ /* 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
* @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
@@ -5134,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.
*/
@@ -5161,56 +7607,84 @@ 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);
-
- /* 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);
- }
-
- /* Set the driver HA work bitmap */
- phba->work_ha |= HA_ERATT;
- /* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ /* If PCI channel is offline, don't process it */
+ if (unlikely(pci_channel_offline(phba->pcidev))) {
spin_unlock_irq(&phba->hbalock);
- return 1;
+ 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;
@@ -5240,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);
@@ -5271,7 +7740,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
* interrupt.
*/
if (unlikely(phba->hba_flag & DEFER_ERATT)) {
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return IRQ_NONE;
}
@@ -5364,7 +7833,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
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;
@@ -5434,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(
@@ -5471,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;
@@ -5513,13 +7983,8 @@ 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);
@@ -5530,7 +7995,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
* any interrupt.
*/
if (unlikely(phba->hba_flag & DEFER_ERATT)) {
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return IRQ_NONE;
}
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
@@ -5566,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;
@@ -5600,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);
@@ -5650,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;
@@ -5670,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 e599519e3078..6b8a148f0a55 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.1"
+#define LPFC_DRIVER_VERSION "8.3.2"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 917ad56b0aff..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);
/*
@@ -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,7 +764,7 @@ 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);
}
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index 795201fa0b48..512c2cc1a33f 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -469,7 +469,7 @@ typedef struct {
u8 type; /* Type of the device */
u8 cur_status; /* current status of the device */
u8 tag_depth; /* Level of tagging */
- u8 sync_neg; /* sync negotiation - ENABLE or DISBALE */
+ u8 sync_neg; /* sync negotiation - ENABLE or DISABLE */
u32 size; /* configurable size in terms of 512 byte
blocks */
}__attribute__ ((packed)) phys_drv;
diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h
index 170399ef06f4..b25b74764ec3 100644
--- a/drivers/scsi/megaraid/mbox_defs.h
+++ b/drivers/scsi/megaraid/mbox_defs.h
@@ -686,7 +686,7 @@ typedef struct {
* @type : Type of the device
* @cur_status : current status of the device
* @tag_depth : Level of tagging
- * @sync_neg : sync negotiation - ENABLE or DISBALE
+ * @sync_neg : sync negotiation - ENABLE or DISABLE
* @size : configurable size in terms of 512 byte
*/
typedef struct {
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index babd4cc0cb25..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,10 +69,10 @@
#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 "01.100.02.00"
-#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 02
+#define MPT2SAS_BUILD_VERSION 03
#define MPT2SAS_RELEASE_VERSION 00
/*
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index ba6ab170bdf0..14e473d1fa7b 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -473,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
@@ -482,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;
@@ -494,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);
@@ -517,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;
@@ -535,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;
}
@@ -739,8 +747,10 @@ _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;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index e3a7967259e7..2a01a5f2a84d 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -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);
/**
@@ -884,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
@@ -1047,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;
@@ -1079,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);
@@ -1101,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);
@@ -1163,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);
@@ -1212,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;
@@ -1273,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;
@@ -1295,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;
@@ -1401,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);
@@ -1489,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;
}
@@ -1532,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);
@@ -1543,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)
@@ -1555,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;
@@ -1636,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
@@ -1648,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;
@@ -1823,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;
@@ -1889,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;
@@ -1912,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);
@@ -1962,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;
@@ -2390,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
*
@@ -2401,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;
@@ -2470,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)
@@ -2604,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;
@@ -2783,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
@@ -2794,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;
@@ -2939,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:
@@ -5130,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,
@@ -5228,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);
@@ -5442,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;
@@ -5503,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);
@@ -5536,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);
@@ -5564,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);
@@ -5599,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;
@@ -5630,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);
@@ -5659,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);
@@ -5682,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 e03dc0b1e1a0..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);
@@ -1041,7 +1041,7 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, "
"rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt,
- req->data_len, rsp->bio->bi_vcnt, rsp->data_len);
+ blk_rq_bytes(req), rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
return -EINVAL;
}
@@ -1104,7 +1104,7 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
*((u64 *)&mpi_request->SASAddress) = (rphy) ?
cpu_to_le64(rphy->identify.sas_address) :
cpu_to_le64(ioc->sas_hba.sas_address);
- mpi_request->RequestDataLength = cpu_to_le16(req->data_len - 4);
+ mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4);
psge = &mpi_request->SGL;
/* WRITE sgel first */
@@ -1112,13 +1112,13 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
- req->data_len, PCI_DMA_BIDIRECTIONAL);
+ blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_out) {
mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
goto unmap;
}
- ioc->base_add_sg_single(psge, sgl_flags | (req->data_len - 4),
+ ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(req) - 4),
dma_addr_out);
/* incr sgel */
@@ -1129,14 +1129,14 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
MPI2_SGE_FLAGS_END_OF_LIST);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
- rsp->data_len, PCI_DMA_BIDIRECTIONAL);
+ dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
+ blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_in) {
mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
goto unmap;
}
- ioc->base_add_sg_single(psge, sgl_flags | (rsp->data_len + 4),
+ ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(rsp) + 4),
dma_addr_in);
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - "
@@ -1170,9 +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);
- req->data_len = 0;
- rsp->data_len -= 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__));
@@ -1188,10 +1187,10 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
unmap:
if (dma_addr_out)
- pci_unmap_single(ioc->pdev, dma_addr_out, req->data_len,
+ pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req),
PCI_DMA_BIDIRECTIONAL);
if (dma_addr_in)
- pci_unmap_single(ioc->pdev, dma_addr_in, rsp->data_len,
+ pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp),
PCI_DMA_BIDIRECTIONAL);
out:
@@ -1201,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 1ce6b24abab2..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:
@@ -669,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;
@@ -778,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 *,
@@ -813,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)
{
@@ -889,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)
@@ -924,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;
@@ -1233,7 +1245,7 @@ static inline void osd_sec_parms_set_in_offset(bool is_v1,
}
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;
@@ -1248,8 +1260,7 @@ 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(
@@ -1293,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)
@@ -1301,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;
@@ -1318,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;
@@ -1341,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)
@@ -1364,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), or->out.req->data_len);
- }
- 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), or->in.req->data_len);
- }
-
or->out.pad_buff = sg_out_pad_buffer;
or->in.pad_buff = sg_in_pad_buffer;
@@ -1410,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 22b59e13ba83..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>
@@ -175,10 +176,9 @@ static const struct file_operations osd_fops = {
struct osd_dev *osduld_path_lookup(const char *name)
{
- struct path path;
- 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 (!name || !*name) {
@@ -186,52 +186,46 @@ struct osd_dev *osduld_path_lookup(const char *name)
return ERR_PTR(-EINVAL);
}
- error = kern_path(name, LOOKUP_FOLLOW, &path);
- if (error) {
- OSD_ERR("path_lookup of %s failed=>%d\n", name, error);
- return ERR_PTR(error);
- }
+ od = kzalloc(sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return ERR_PTR(-ENOMEM);
- inode = 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", name);
- OSD_ERR(" Example: bash: echo < %s\n", name);
- 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", name);
- 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(&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 bb218c8b6e98..30f3275e119e 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -240,11 +240,11 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
* is invalid. Prevent the garbage from being misinterpreted
* and prevent security leaks by zeroing out the excess data.
*/
- if (unlikely(req->data_len > 0 && req->data_len <= bufflen))
- memset(buffer + (bufflen - req->data_len), 0, req->data_len);
+ if (unlikely(req->resid_len > 0 && req->resid_len <= bufflen))
+ memset(buffer + (bufflen - req->resid_len), 0, req->resid_len);
if (resid)
- *resid = req->data_len;
+ *resid = req->resid_len;
ret = req->errors;
out:
blk_put_request(req);
@@ -546,14 +546,9 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int error,
* to queue the remainder of them.
*/
if (blk_end_request(req, error, bytes)) {
- int leftover = (req->hard_nr_sectors << 9);
-
- if (blk_pc_request(req))
- leftover = req->data_len;
-
/* kill remainder if no retrys */
if (error && scsi_noretry_cmd(cmd))
- blk_end_request(req, error, leftover);
+ blk_end_request_all(req, error);
else {
if (requeue) {
/*
@@ -673,34 +668,6 @@ void scsi_release_buffers(struct scsi_cmnd *cmd)
EXPORT_SYMBOL(scsi_release_buffers);
/*
- * Bidi commands Must be complete as a whole, both sides at once.
- * If part of the bytes were written and lld returned
- * scsi_in()->resid and/or scsi_out()->resid this information will be left
- * in req->data_len and req->next_rq->data_len. The upper-layer driver can
- * decide what to do with this information.
- */
-static void scsi_end_bidi_request(struct scsi_cmnd *cmd)
-{
- struct request *req = cmd->request;
- unsigned int dlen = req->data_len;
- unsigned int next_dlen = req->next_rq->data_len;
-
- req->data_len = scsi_out(cmd)->resid;
- req->next_rq->data_len = scsi_in(cmd)->resid;
-
- /* The req and req->next_rq have not been completed */
- BUG_ON(blk_end_bidi_request(req, 0, dlen, next_dlen));
-
- scsi_release_buffers(cmd);
-
- /*
- * This will goose the queue request function at the end, so we don't
- * need to worry about launching another command.
- */
- scsi_next_command(cmd);
-}
-
-/*
* Function: scsi_io_completion()
*
* Purpose: Completion processing for block device I/O requests.
@@ -739,7 +706,6 @@ static void scsi_end_bidi_request(struct scsi_cmnd *cmd)
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;
@@ -773,12 +739,22 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
if (!sense_deferred)
error = -EIO;
}
+
+ req->resid_len = scsi_get_resid(cmd);
+
if (scsi_bidi_cmnd(cmd)) {
- /* will also release_buffers */
- scsi_end_bidi_request(cmd);
+ /*
+ * Bidi commands Must be complete as a whole,
+ * both sides at once.
+ */
+ req->next_rq->resid_len = scsi_in(cmd)->resid;
+
+ blk_end_request_all(req, 0);
+
+ scsi_release_buffers(cmd);
+ scsi_next_command(cmd);
return;
}
- req->data_len = scsi_get_resid(cmd);
}
BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet */
@@ -787,9 +763,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* Next deal with any sectors which we were able to correctly
* handle.
*/
- SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, "
+ SCSI_LOG_HLCOMPLETE(1, printk("%u sectors total, "
"%d bytes done.\n",
- req->nr_sectors, good_bytes));
+ blk_rq_sectors(req), good_bytes));
/*
* Recovered errors need reporting, but they're always treated
@@ -812,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;
@@ -922,7 +897,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
}
- blk_end_request(req, -EIO, blk_rq_bytes(req));
+ blk_end_request_all(req, -EIO);
scsi_next_command(cmd);
break;
case ACTION_REPREP:
@@ -965,10 +940,7 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
count = blk_rq_map_sg(req->q, req, sdb->table.sgl);
BUG_ON(count > sdb->table.nents);
sdb->table.nents = count;
- if (blk_pc_request(req))
- sdb->length = req->data_len;
- else
- sdb->length = req->nr_sectors << 9;
+ sdb->length = blk_rq_bytes(req);
return BLKPREP_OK;
}
@@ -1087,22 +1059,21 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
if (unlikely(ret))
return ret;
} else {
- BUG_ON(req->data_len);
- BUG_ON(req->data);
+ BUG_ON(blk_rq_bytes(req));
memset(&cmd->sdb, 0, sizeof(cmd->sdb));
req->buffer = NULL;
}
cmd->cmd_len = req->cmd_len;
- if (!req->data_len)
+ if (!blk_rq_bytes(req))
cmd->sc_data_direction = DMA_NONE;
else if (rq_data_dir(req) == WRITE)
cmd->sc_data_direction = DMA_TO_DEVICE;
else
cmd->sc_data_direction = DMA_FROM_DEVICE;
- cmd->transfersize = req->data_len;
+ cmd->transfersize = blk_rq_bytes(req);
cmd->allowed = req->retries;
return BLKPREP_OK;
}
@@ -1212,7 +1183,7 @@ int scsi_prep_return(struct request_queue *q, struct request *req, int ret)
break;
case BLKPREP_DEFER:
/*
- * If we defer, the elv_next_request() returns NULL, but the
+ * If we defer, the blk_peek_request() returns NULL, but the
* queue must be restarted, so we plug here if no returning
* command will automatically do that.
*/
@@ -1388,7 +1359,7 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
struct scsi_target *starget = scsi_target(sdev);
struct Scsi_Host *shost = sdev->host;
- blkdev_dequeue_request(req);
+ blk_start_request(req);
if (unlikely(cmd == NULL)) {
printk(KERN_CRIT "impossible request in %s.\n",
@@ -1480,7 +1451,7 @@ static void scsi_request_fn(struct request_queue *q)
if (!sdev) {
printk("scsi: killing requests for dead queue\n");
- while ((req = elv_next_request(q)) != NULL)
+ while ((req = blk_peek_request(q)) != NULL)
scsi_kill_request(req, q);
return;
}
@@ -1501,7 +1472,7 @@ static void scsi_request_fn(struct request_queue *q)
* that the request is fully prepared even if we cannot
* accept it.
*/
- req = elv_next_request(q);
+ req = blk_peek_request(q);
if (!req || !scsi_dev_queue_ready(q, sdev))
break;
@@ -1517,7 +1488,7 @@ static void scsi_request_fn(struct request_queue *q)
* Remove the request from the request list.
*/
if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req)))
- blkdev_dequeue_request(req);
+ blk_start_request(req);
sdev->device_busy++;
spin_unlock(q->queue_lock);
@@ -2441,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_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c
index 48ba413f7f6a..10303272ba45 100644
--- a/drivers/scsi/scsi_tgt_lib.c
+++ b/drivers/scsi/scsi_tgt_lib.c
@@ -387,7 +387,7 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd,
* we use REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the
* length for us.
*/
- cmd->sdb.length = rq->data_len;
+ cmd->sdb.length = blk_rq_bytes(rq);
return 0;
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/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 50988cbf7b2d..d606452297cf 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -163,12 +163,10 @@ static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost,
int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *);
while (!blk_queue_plugged(q)) {
- req = elv_next_request(q);
+ req = blk_fetch_request(q);
if (!req)
break;
- blkdev_dequeue_request(req);
-
spin_unlock_irq(q->queue_lock);
handler = to_sas_internal(shost->transportt)->f->smp_handler;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 84044233b637..878b17a9af30 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -384,9 +384,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
struct scsi_device *sdp = q->queuedata;
struct gendisk *disk = rq->rq_disk;
struct scsi_disk *sdkp;
- sector_t block = rq->sector;
+ sector_t block = blk_rq_pos(rq);
sector_t threshold;
- unsigned int this_count = rq->nr_sectors;
+ unsigned int this_count = blk_rq_sectors(rq);
int ret, host_dif;
if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
@@ -413,10 +413,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
this_count));
if (!sdp || !scsi_device_online(sdp) ||
- block + rq->nr_sectors > get_capacity(disk)) {
+ block + blk_rq_sectors(rq) > get_capacity(disk)) {
SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
- "Finishing %ld sectors\n",
- rq->nr_sectors));
+ "Finishing %u sectors\n",
+ blk_rq_sectors(rq)));
SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
"Retry with 0x%p\n", SCpnt));
goto out;
@@ -463,7 +463,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
* for this.
*/
if (sdp->sector_size == 1024) {
- if ((block & 1) || (rq->nr_sectors & 1)) {
+ if ((block & 1) || (blk_rq_sectors(rq) & 1)) {
scmd_printk(KERN_ERR, SCpnt,
"Bad block number requested\n");
goto out;
@@ -473,7 +473,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
}
}
if (sdp->sector_size == 2048) {
- if ((block & 3) || (rq->nr_sectors & 3)) {
+ if ((block & 3) || (blk_rq_sectors(rq) & 3)) {
scmd_printk(KERN_ERR, SCpnt,
"Bad block number requested\n");
goto out;
@@ -483,7 +483,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
}
}
if (sdp->sector_size == 4096) {
- if ((block & 7) || (rq->nr_sectors & 7)) {
+ if ((block & 7) || (blk_rq_sectors(rq) & 7)) {
scmd_printk(KERN_ERR, SCpnt,
"Bad block number requested\n");
goto out;
@@ -512,10 +512,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
}
SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
- "%s %d/%ld 512 byte blocks.\n",
+ "%s %d/%u 512 byte blocks.\n",
(rq_data_dir(rq) == WRITE) ?
"writing" : "reading", this_count,
- rq->nr_sectors));
+ blk_rq_sectors(rq)));
/* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */
host_dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type);
@@ -971,8 +971,8 @@ static struct block_device_operations sd_fops = {
static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
{
- u64 start_lba = scmd->request->sector;
- u64 end_lba = scmd->request->sector + (scsi_bufflen(scmd) / 512);
+ u64 start_lba = blk_rq_pos(scmd->request);
+ u64 end_lba = blk_rq_pos(scmd->request) + (scsi_bufflen(scmd) / 512);
u64 bad_lba;
int info_valid;
@@ -1510,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];
@@ -1902,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);
@@ -1954,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);
}
/**
@@ -2026,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;
@@ -2055,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/sd_dif.c b/drivers/scsi/sd_dif.c
index 184dff492797..82f14a9482d0 100644
--- a/drivers/scsi/sd_dif.c
+++ b/drivers/scsi/sd_dif.c
@@ -507,7 +507,7 @@ void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
sector_sz = scmd->device->sector_size;
sectors = good_bytes / sector_sz;
- phys = scmd->request->sector & 0xffffffff;
+ phys = blk_rq_pos(scmd->request) & 0xffffffff;
if (sector_sz == 4096)
phys >>= 3;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index e1716f14cd47..8201387b4daa 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -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;
@@ -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);
@@ -1260,7 +1261,7 @@ static void sg_rq_end_io(struct request *rq, int uptodate)
sense = rq->sense;
result = rq->errors;
- resid = rq->data_len;
+ resid = rq->resid_len;
SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n",
sdp->disk->disk_name, srp->header.pack_id, result));
@@ -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);
@@ -2055,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 0e1a0f2d2ad5..cd350dfc1216 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -292,7 +292,8 @@ static int sr_done(struct scsi_cmnd *SCpnt)
if (cd->device->sector_size == 2048)
error_sector <<= 2;
error_sector &= ~(block_sectors - 1);
- good_bytes = (error_sector - SCpnt->request->sector) << 9;
+ good_bytes = (error_sector -
+ blk_rq_pos(SCpnt->request)) << 9;
if (good_bytes < 0 || good_bytes >= this_count)
good_bytes = 0;
/*
@@ -349,8 +350,8 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq)
cd->disk->disk_name, block));
if (!cd->device || !scsi_device_online(cd->device)) {
- SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
- rq->nr_sectors));
+ SCSI_LOG_HLQUEUE(2, printk("Finishing %u sectors\n",
+ blk_rq_sectors(rq)));
SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
goto out;
}
@@ -413,7 +414,7 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq)
/*
* request doesn't start on hw block boundary, add scatter pads
*/
- if (((unsigned int)rq->sector % (s_size >> 9)) ||
+ if (((unsigned int)blk_rq_pos(rq) % (s_size >> 9)) ||
(scsi_bufflen(SCpnt) % s_size)) {
scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n");
goto out;
@@ -422,14 +423,14 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq)
this_count = (scsi_bufflen(SCpnt) >> 9) / (s_size >> 9);
- SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
+ SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%u 512 byte blocks.\n",
cd->cdi.name,
(rq_data_dir(rq) == WRITE) ?
"writing" : "reading",
- this_count, rq->nr_sectors));
+ this_count, blk_rq_sectors(rq)));
SCpnt->cmnd[1] = 0;
- block = (unsigned int)rq->sector / (s_size >> 9);
+ block = (unsigned int)blk_rq_pos(rq) / (s_size >> 9);
if (this_count > 0xffff) {
this_count = 0xffff;
@@ -726,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/st.c b/drivers/scsi/st.c
index eb24efea8f14..b33d04250bbc 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -463,7 +463,7 @@ static void st_scsi_execute_end(struct request *req, int uptodate)
struct scsi_tape *STp = SRpnt->stp;
STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
- STp->buffer->cmdstat.residual = req->data_len;
+ STp->buffer->cmdstat.residual = req->resid_len;
if (SRpnt->waiting)
complete(SRpnt->waiting);
@@ -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/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 601e95141cbe..54023d41fd15 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1306,7 +1306,7 @@ static int u14_34f_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scs
if (linked_comm && SCpnt->device->queue_depth > 2
&& TLDEV(SCpnt->device->type)) {
HD(j)->cp_stat[i] = READY;
- flush_dev(SCpnt->device, SCpnt->request->sector, j, FALSE);
+ flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, FALSE);
return 0;
}
@@ -1610,11 +1610,13 @@ static int reorder(unsigned int j, unsigned long cursec,
if (!(cpp->xdir == DTD_IN)) input_only = FALSE;
- if (SCpnt->request->sector < minsec) minsec = SCpnt->request->sector;
- if (SCpnt->request->sector > maxsec) maxsec = SCpnt->request->sector;
+ if (blk_rq_pos(SCpnt->request) < minsec)
+ minsec = blk_rq_pos(SCpnt->request);
+ if (blk_rq_pos(SCpnt->request) > maxsec)
+ maxsec = blk_rq_pos(SCpnt->request);
- sl[n] = SCpnt->request->sector;
- ioseek += SCpnt->request->nr_sectors;
+ sl[n] = blk_rq_pos(SCpnt->request);
+ ioseek += blk_rq_sectors(SCpnt->request);
if (!n) continue;
@@ -1642,7 +1644,7 @@ static int reorder(unsigned int j, unsigned long cursec,
if (!input_only) for (n = 0; n < n_ready; n++) {
k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
- ll[n] = SCpnt->request->nr_sectors; pl[n] = SCpnt->serial_number;
+ ll[n] = blk_rq_sectors(SCpnt->request); pl[n] = SCpnt->serial_number;
if (!n) continue;
@@ -1666,12 +1668,12 @@ static int reorder(unsigned int j, unsigned long cursec,
if (link_statistics && (overlap || !(flushcount % link_statistics)))
for (n = 0; n < n_ready; n++) {
k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
- printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+ printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %u"\
" cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
(ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
SCpnt->lun, SCpnt->serial_number, k, flushcount, n_ready,
- SCpnt->request->sector, SCpnt->request->nr_sectors, cursec,
- YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
+ blk_rq_pos(SCpnt->request), blk_rq_sectors(SCpnt->request),
+ cursec, YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
YESNO(overlap), cpp->xdir);
}
#endif
@@ -1799,7 +1801,7 @@ static irqreturn_t ihdlr(unsigned int j)
if (linked_comm && SCpnt->device->queue_depth > 2
&& TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, SCpnt->request->sector, j, TRUE);
+ flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, TRUE);
tstatus = status_byte(spp->target_status);
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..1132c5cae7ab 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.
@@ -860,7 +861,7 @@ config SERIAL_UARTLITE
Say Y here if you want to use the Xilinx uartlite serial controller.
To compile this driver as a module, choose M here: the
- module will be called uartlite.ko.
+ module will be called uartlite.
config SERIAL_UARTLITE_CONSOLE
bool "Support for console on Xilinx uartlite serial port"
@@ -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..58a4879c7e48 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;
@@ -686,7 +686,7 @@ static int pl010_probe(struct amba_device *dev, void *id)
goto out;
}
- base = ioremap(dev->res.start, PAGE_SIZE);
+ base = ioremap(dev->res.start, resource_size(&dev->res));
if (!base) {
ret = -ENOMEM;
goto free;
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index 8b2b9700f3e4..bf82e28770a9 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -70,6 +70,23 @@ struct uart_amba_port {
struct clk *clk;
unsigned int im; /* interrupt mask */
unsigned int old_status;
+ unsigned int ifls; /* vendor-specific */
+};
+
+/* There is by now at least one vendor with differing details, so handle it */
+struct vendor_data {
+ unsigned int ifls;
+ unsigned int fifosize;
+};
+
+static struct vendor_data vendor_arm = {
+ .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+ .fifosize = 16,
+};
+
+static struct vendor_data vendor_st = {
+ .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
+ .fifosize = 64,
};
static void pl011_stop_tx(struct uart_port *port)
@@ -360,8 +377,7 @@ static int pl011_startup(struct uart_port *port)
if (retval)
goto clk_dis;
- writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
- uap->port.membase + UART011_IFLS);
+ writew(uap->ifls, uap->port.membase + UART011_IFLS);
/*
* Provoke TX FIFO interrupt into asserting.
@@ -729,9 +745,10 @@ 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;
+ struct vendor_data *vendor = id->data;
void __iomem *base;
int i, ret;
@@ -750,7 +767,7 @@ static int pl011_probe(struct amba_device *dev, void *id)
goto out;
}
- base = ioremap(dev->res.start, PAGE_SIZE);
+ base = ioremap(dev->res.start, resource_size(&dev->res));
if (!base) {
ret = -ENOMEM;
goto free;
@@ -762,12 +779,13 @@ static int pl011_probe(struct amba_device *dev, void *id)
goto unmap;
}
+ uap->ifls = vendor->ifls;
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
uap->port.iotype = UPIO_MEM;
uap->port.irq = dev->irq[0];
- uap->port.fifosize = 16;
+ uap->port.fifosize = vendor->fifosize;
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
@@ -812,6 +830,12 @@ static struct amba_id pl011_ids[] __initdata = {
{
.id = 0x00041011,
.mask = 0x000fffff,
+ .data = &vendor_arm,
+ },
+ {
+ .id = 0x00380802,
+ .mask = 0x00ffffff,
+ .data = &vendor_st,
},
{ 0, 0 },
};
@@ -845,7 +869,11 @@ static void __exit pl011_exit(void)
uart_unregister_driver(&amba_reg);
}
-module_init(pl011_init);
+/*
+ * While this can be a module, if builtin it's most likely the console
+ * So let's leave module_exit but move module_init to an earlier place
+ */
+arch_initcall(pl011_init);
module_exit(pl011_exit);
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
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/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..285b414f3054 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>
@@ -66,7 +71,7 @@
#define ONEMS 0xb0 /* One Millisecond register */
#define UTS 0xb4 /* UART Test Register */
#endif
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
+#ifdef CONFIG_ARCH_MX1
#define BIPR1 0xb0 /* Incremental Preset Register 1 */
#define BIPR2 0xb4 /* Incremental Preset Register 2 */
#define BIPR3 0xb8 /* Incremental Preset Register 3 */
@@ -96,7 +101,7 @@
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
+#ifdef CONFIG_ARCH_MX1
#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
#endif
#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
@@ -127,7 +132,7 @@
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
-#ifdef CONFIG_ARCH_IMX
+#ifdef CONFIG_ARCH_MX1
#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
#endif
@@ -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 */
@@ -180,13 +186,6 @@
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
-#ifdef CONFIG_ARCH_IMX
-#define SERIAL_IMX_MAJOR 204
-#define MINOR_START 41
-#define DEV_NAME "ttySMX"
-#define MAX_INTERNAL_IRQ IMX_IRQS
-#endif
-
#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
@@ -211,10 +210,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 +277,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 +353,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 +374,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 +469,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 +544,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 +607,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 +626,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 +659,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 +685,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 +735,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 +762,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 +782,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 +795,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 +809,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 +907,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 +1178,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 +1219,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 +1290,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 +1350,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 c0a3e2734e24..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
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/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..e8aae227b5e0 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -118,7 +118,7 @@ config SPI_GPIO
config SPI_IMX
tristate "Freescale iMX SPI controller"
- depends on ARCH_IMX && EXPERIMENTAL
+ depends on ARCH_MX1 && EXPERIMENTAL
help
This enables using the Freescale iMX SPI controller in master
mode.
@@ -171,6 +171,15 @@ config SPI_ORION
help
This enables using the SPI master controller on the Orion chips.
+config SPI_PL022
+ tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
+ depends on ARM_AMBA && EXPERIMENTAL
+ default y if MACH_U300
+ help
+ This selects the ARM(R) AMBA(R) PrimeCell PL022 SSP
+ controller. If you have an embedded system with an AMBA(R)
+ bus and a PL022 controller, say Y or M here.
+
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
depends on ARCH_PXA && EXPERIMENTAL
@@ -212,7 +221,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/Makefile b/drivers/spi/Makefile
index 5d0451936d86..ecfadb180482 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_ORION) += orion_spi.o
+obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
new file mode 100644
index 000000000000..da76797ce8b9
--- /dev/null
+++ b/drivers/spi/amba-pl022.c
@@ -0,0 +1,1866 @@
+/*
+ * drivers/spi/amba-pl022.c
+ *
+ * A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
+ *
+ * Copyright (C) 2008-2009 ST-Ericsson AB
+ * Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
+ *
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * Initial version inspired by:
+ * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c
+ * Initial adoption to PL022 by:
+ * Sachin Verma <sachin.verma@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * TODO:
+ * - add timeout on polled transfers
+ * - add generic DMA framework support
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl022.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+/*
+ * This macro is used to define some register default values.
+ * reg is masked with mask, the OR:ed with an (again masked)
+ * val shifted sb steps to the left.
+ */
+#define SSP_WRITE_BITS(reg, val, mask, sb) \
+ ((reg) = (((reg) & ~(mask)) | (((val)<<(sb)) & (mask))))
+
+/*
+ * This macro is also used to define some default values.
+ * It will just shift val by sb steps to the left and mask
+ * the result with mask.
+ */
+#define GEN_MASK_BITS(val, mask, sb) \
+ (((val)<<(sb)) & (mask))
+
+#define DRIVE_TX 0
+#define DO_NOT_DRIVE_TX 1
+
+#define DO_NOT_QUEUE_DMA 0
+#define QUEUE_DMA 1
+
+#define RX_TRANSFER 1
+#define TX_TRANSFER 2
+
+/*
+ * Macros to access SSP Registers with their offsets
+ */
+#define SSP_CR0(r) (r + 0x000)
+#define SSP_CR1(r) (r + 0x004)
+#define SSP_DR(r) (r + 0x008)
+#define SSP_SR(r) (r + 0x00C)
+#define SSP_CPSR(r) (r + 0x010)
+#define SSP_IMSC(r) (r + 0x014)
+#define SSP_RIS(r) (r + 0x018)
+#define SSP_MIS(r) (r + 0x01C)
+#define SSP_ICR(r) (r + 0x020)
+#define SSP_DMACR(r) (r + 0x024)
+#define SSP_ITCR(r) (r + 0x080)
+#define SSP_ITIP(r) (r + 0x084)
+#define SSP_ITOP(r) (r + 0x088)
+#define SSP_TDR(r) (r + 0x08C)
+
+#define SSP_PID0(r) (r + 0xFE0)
+#define SSP_PID1(r) (r + 0xFE4)
+#define SSP_PID2(r) (r + 0xFE8)
+#define SSP_PID3(r) (r + 0xFEC)
+
+#define SSP_CID0(r) (r + 0xFF0)
+#define SSP_CID1(r) (r + 0xFF4)
+#define SSP_CID2(r) (r + 0xFF8)
+#define SSP_CID3(r) (r + 0xFFC)
+
+/*
+ * SSP Control Register 0 - SSP_CR0
+ */
+#define SSP_CR0_MASK_DSS (0x1FUL << 0)
+#define SSP_CR0_MASK_HALFDUP (0x1UL << 5)
+#define SSP_CR0_MASK_SPO (0x1UL << 6)
+#define SSP_CR0_MASK_SPH (0x1UL << 7)
+#define SSP_CR0_MASK_SCR (0xFFUL << 8)
+#define SSP_CR0_MASK_CSS (0x1FUL << 16)
+#define SSP_CR0_MASK_FRF (0x3UL << 21)
+
+/*
+ * SSP Control Register 0 - SSP_CR1
+ */
+#define SSP_CR1_MASK_LBM (0x1UL << 0)
+#define SSP_CR1_MASK_SSE (0x1UL << 1)
+#define SSP_CR1_MASK_MS (0x1UL << 2)
+#define SSP_CR1_MASK_SOD (0x1UL << 3)
+#define SSP_CR1_MASK_RENDN (0x1UL << 4)
+#define SSP_CR1_MASK_TENDN (0x1UL << 5)
+#define SSP_CR1_MASK_MWAIT (0x1UL << 6)
+#define SSP_CR1_MASK_RXIFLSEL (0x7UL << 7)
+#define SSP_CR1_MASK_TXIFLSEL (0x7UL << 10)
+
+/*
+ * SSP Data Register - SSP_DR
+ */
+#define SSP_DR_MASK_DATA 0xFFFFFFFF
+
+/*
+ * SSP Status Register - SSP_SR
+ */
+#define SSP_SR_MASK_TFE (0x1UL << 0) /* Transmit FIFO empty */
+#define SSP_SR_MASK_TNF (0x1UL << 1) /* Transmit FIFO not full */
+#define SSP_SR_MASK_RNE (0x1UL << 2) /* Receive FIFO not empty */
+#define SSP_SR_MASK_RFF (0x1UL << 3) /* Receive FIFO full */
+#define SSP_SR_MASK_BSY (0x1UL << 4) /* Busy Flag */
+
+/*
+ * SSP Clock Prescale Register - SSP_CPSR
+ */
+#define SSP_CPSR_MASK_CPSDVSR (0xFFUL << 0)
+
+/*
+ * SSP Interrupt Mask Set/Clear Register - SSP_IMSC
+ */
+#define SSP_IMSC_MASK_RORIM (0x1UL << 0) /* Receive Overrun Interrupt mask */
+#define SSP_IMSC_MASK_RTIM (0x1UL << 1) /* Receive timeout Interrupt mask */
+#define SSP_IMSC_MASK_RXIM (0x1UL << 2) /* Receive FIFO Interrupt mask */
+#define SSP_IMSC_MASK_TXIM (0x1UL << 3) /* Transmit FIFO Interrupt mask */
+
+/*
+ * SSP Raw Interrupt Status Register - SSP_RIS
+ */
+/* Receive Overrun Raw Interrupt status */
+#define SSP_RIS_MASK_RORRIS (0x1UL << 0)
+/* Receive Timeout Raw Interrupt status */
+#define SSP_RIS_MASK_RTRIS (0x1UL << 1)
+/* Receive FIFO Raw Interrupt status */
+#define SSP_RIS_MASK_RXRIS (0x1UL << 2)
+/* Transmit FIFO Raw Interrupt status */
+#define SSP_RIS_MASK_TXRIS (0x1UL << 3)
+
+/*
+ * SSP Masked Interrupt Status Register - SSP_MIS
+ */
+/* Receive Overrun Masked Interrupt status */
+#define SSP_MIS_MASK_RORMIS (0x1UL << 0)
+/* Receive Timeout Masked Interrupt status */
+#define SSP_MIS_MASK_RTMIS (0x1UL << 1)
+/* Receive FIFO Masked Interrupt status */
+#define SSP_MIS_MASK_RXMIS (0x1UL << 2)
+/* Transmit FIFO Masked Interrupt status */
+#define SSP_MIS_MASK_TXMIS (0x1UL << 3)
+
+/*
+ * SSP Interrupt Clear Register - SSP_ICR
+ */
+/* Receive Overrun Raw Clear Interrupt bit */
+#define SSP_ICR_MASK_RORIC (0x1UL << 0)
+/* Receive Timeout Clear Interrupt bit */
+#define SSP_ICR_MASK_RTIC (0x1UL << 1)
+
+/*
+ * SSP DMA Control Register - SSP_DMACR
+ */
+/* Receive DMA Enable bit */
+#define SSP_DMACR_MASK_RXDMAE (0x1UL << 0)
+/* Transmit DMA Enable bit */
+#define SSP_DMACR_MASK_TXDMAE (0x1UL << 1)
+
+/*
+ * SSP Integration Test control Register - SSP_ITCR
+ */
+#define SSP_ITCR_MASK_ITEN (0x1UL << 0)
+#define SSP_ITCR_MASK_TESTFIFO (0x1UL << 1)
+
+/*
+ * SSP Integration Test Input Register - SSP_ITIP
+ */
+#define ITIP_MASK_SSPRXD (0x1UL << 0)
+#define ITIP_MASK_SSPFSSIN (0x1UL << 1)
+#define ITIP_MASK_SSPCLKIN (0x1UL << 2)
+#define ITIP_MASK_RXDMAC (0x1UL << 3)
+#define ITIP_MASK_TXDMAC (0x1UL << 4)
+#define ITIP_MASK_SSPTXDIN (0x1UL << 5)
+
+/*
+ * SSP Integration Test output Register - SSP_ITOP
+ */
+#define ITOP_MASK_SSPTXD (0x1UL << 0)
+#define ITOP_MASK_SSPFSSOUT (0x1UL << 1)
+#define ITOP_MASK_SSPCLKOUT (0x1UL << 2)
+#define ITOP_MASK_SSPOEn (0x1UL << 3)
+#define ITOP_MASK_SSPCTLOEn (0x1UL << 4)
+#define ITOP_MASK_RORINTR (0x1UL << 5)
+#define ITOP_MASK_RTINTR (0x1UL << 6)
+#define ITOP_MASK_RXINTR (0x1UL << 7)
+#define ITOP_MASK_TXINTR (0x1UL << 8)
+#define ITOP_MASK_INTR (0x1UL << 9)
+#define ITOP_MASK_RXDMABREQ (0x1UL << 10)
+#define ITOP_MASK_RXDMASREQ (0x1UL << 11)
+#define ITOP_MASK_TXDMABREQ (0x1UL << 12)
+#define ITOP_MASK_TXDMASREQ (0x1UL << 13)
+
+/*
+ * SSP Test Data Register - SSP_TDR
+ */
+#define TDR_MASK_TESTDATA (0xFFFFFFFF)
+
+/*
+ * Message State
+ * we use the spi_message.state (void *) pointer to
+ * hold a single state value, that's why all this
+ * (void *) casting is done here.
+ */
+#define STATE_START ((void *) 0)
+#define STATE_RUNNING ((void *) 1)
+#define STATE_DONE ((void *) 2)
+#define STATE_ERROR ((void *) -1)
+
+/*
+ * Queue State
+ */
+#define QUEUE_RUNNING (0)
+#define QUEUE_STOPPED (1)
+/*
+ * SSP State - Whether Enabled or Disabled
+ */
+#define SSP_DISABLED (0)
+#define SSP_ENABLED (1)
+
+/*
+ * SSP DMA State - Whether DMA Enabled or Disabled
+ */
+#define SSP_DMA_DISABLED (0)
+#define SSP_DMA_ENABLED (1)
+
+/*
+ * SSP Clock Defaults
+ */
+#define NMDK_SSP_DEFAULT_CLKRATE 0x2
+#define NMDK_SSP_DEFAULT_PRESCALE 0x40
+
+/*
+ * SSP Clock Parameter ranges
+ */
+#define CPSDVR_MIN 0x02
+#define CPSDVR_MAX 0xFE
+#define SCR_MIN 0x00
+#define SCR_MAX 0xFF
+
+/*
+ * SSP Interrupt related Macros
+ */
+#define DEFAULT_SSP_REG_IMSC 0x0UL
+#define DISABLE_ALL_INTERRUPTS DEFAULT_SSP_REG_IMSC
+#define ENABLE_ALL_INTERRUPTS (~DEFAULT_SSP_REG_IMSC)
+
+#define CLEAR_ALL_INTERRUPTS 0x3
+
+
+/*
+ * The type of reading going on on this chip
+ */
+enum ssp_reading {
+ READING_NULL,
+ READING_U8,
+ READING_U16,
+ READING_U32
+};
+
+/**
+ * The type of writing going on on this chip
+ */
+enum ssp_writing {
+ WRITING_NULL,
+ WRITING_U8,
+ WRITING_U16,
+ WRITING_U32
+};
+
+/**
+ * struct vendor_data - vendor-specific config parameters
+ * for PL022 derivates
+ * @fifodepth: depth of FIFOs (both)
+ * @max_bpw: maximum number of bits per word
+ * @unidir: supports unidirection transfers
+ */
+struct vendor_data {
+ int fifodepth;
+ int max_bpw;
+ bool unidir;
+};
+
+/**
+ * struct pl022 - This is the private SSP driver data structure
+ * @adev: AMBA device model hookup
+ * @phybase: The physical memory where the SSP device resides
+ * @virtbase: The virtual memory where the SSP is mapped
+ * @master: SPI framework hookup
+ * @master_info: controller-specific data from machine setup
+ * @regs: SSP controller register's virtual address
+ * @pump_messages: Work struct for scheduling work to the workqueue
+ * @lock: spinlock to syncronise access to driver data
+ * @workqueue: a workqueue on which any spi_message request is queued
+ * @busy: workqueue is busy
+ * @run: workqueue is running
+ * @pump_transfers: Tasklet used in Interrupt Transfer mode
+ * @cur_msg: Pointer to current spi_message being processed
+ * @cur_transfer: Pointer to current spi_transfer
+ * @cur_chip: pointer to current clients chip(assigned from controller_state)
+ * @tx: current position in TX buffer to be read
+ * @tx_end: end position in TX buffer to be read
+ * @rx: current position in RX buffer to be written
+ * @rx_end: end position in RX buffer to be written
+ * @readingtype: the type of read currently going on
+ * @writingtype: the type or write currently going on
+ */
+struct pl022 {
+ struct amba_device *adev;
+ struct vendor_data *vendor;
+ resource_size_t phybase;
+ void __iomem *virtbase;
+ struct clk *clk;
+ struct spi_master *master;
+ struct pl022_ssp_controller *master_info;
+ /* Driver message queue */
+ struct workqueue_struct *workqueue;
+ struct work_struct pump_messages;
+ spinlock_t queue_lock;
+ struct list_head queue;
+ int busy;
+ int run;
+ /* Message transfer pump */
+ struct tasklet_struct pump_transfers;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct chip_data *cur_chip;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ enum ssp_reading read;
+ enum ssp_writing write;
+};
+
+/**
+ * struct chip_data - To maintain runtime state of SSP for each client chip
+ * @cr0: Value of control register CR0 of SSP
+ * @cr1: Value of control register CR1 of SSP
+ * @dmacr: Value of DMA control Register of SSP
+ * @cpsr: Value of Clock prescale register
+ * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
+ * @enable_dma: Whether to enable DMA or not
+ * @write: function ptr to be used to write when doing xfer for this chip
+ * @read: function ptr to be used to read when doing xfer for this chip
+ * @cs_control: chip select callback provided by chip
+ * @xfer_type: polling/interrupt/DMA
+ *
+ * Runtime state of the SSP controller, maintained per chip,
+ * This would be set according to the current message that would be served
+ */
+struct chip_data {
+ u16 cr0;
+ u16 cr1;
+ u16 dmacr;
+ u16 cpsr;
+ u8 n_bytes;
+ u8 enable_dma:1;
+ enum ssp_reading read;
+ enum ssp_writing write;
+ void (*cs_control) (u32 command);
+ int xfer_type;
+};
+
+/**
+ * null_cs_control - Dummy chip select function
+ * @command: select/delect the chip
+ *
+ * If no chip select function is provided by client this is used as dummy
+ * chip select
+ */
+static void null_cs_control(u32 command)
+{
+ pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
+}
+
+/**
+ * giveback - current spi_message is over, schedule next message and call
+ * callback of this message. Assumes that caller already
+ * set message->status; dma and pio irqs are blocked
+ * @pl022: SSP driver private data structure
+ */
+static void giveback(struct pl022 *pl022)
+{
+ struct spi_transfer *last_transfer;
+ unsigned long flags;
+ struct spi_message *msg;
+ void (*curr_cs_control) (u32 command);
+
+ /*
+ * This local reference to the chip select function
+ * is needed because we set curr_chip to NULL
+ * as a step toward termininating the message.
+ */
+ curr_cs_control = pl022->cur_chip->cs_control;
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ msg = pl022->cur_msg;
+ pl022->cur_msg = NULL;
+ pl022->cur_transfer = NULL;
+ pl022->cur_chip = NULL;
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ last_transfer = list_entry(msg->transfers.prev,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Delay if requested before any change in chip select */
+ if (last_transfer->delay_usecs)
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ udelay(last_transfer->delay_usecs);
+
+ /*
+ * Drop chip select UNLESS cs_change is true or we are returning
+ * a message with an error, or next message is for another chip
+ */
+ if (!last_transfer->cs_change)
+ curr_cs_control(SSP_CHIP_DESELECT);
+ else {
+ struct spi_message *next_msg;
+
+ /* Holding of cs was hinted, but we need to make sure
+ * the next message is for the same chip. Don't waste
+ * time with the following tests unless this was hinted.
+ *
+ * We cannot postpone this until pump_messages, because
+ * after calling msg->complete (below) the driver that
+ * sent the current message could be unloaded, which
+ * could invalidate the cs_control() callback...
+ */
+
+ /* get a pointer to the next message, if any */
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ if (list_empty(&pl022->queue))
+ next_msg = NULL;
+ else
+ next_msg = list_entry(pl022->queue.next,
+ struct spi_message, queue);
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ /* see if the next and current messages point
+ * to the same chip
+ */
+ if (next_msg && next_msg->spi != msg->spi)
+ next_msg = NULL;
+ if (!next_msg || msg->state == STATE_ERROR)
+ curr_cs_control(SSP_CHIP_DESELECT);
+ }
+ msg->state = NULL;
+ if (msg->complete)
+ msg->complete(msg->context);
+ /* This message is completed, so let's turn off the clock! */
+ clk_disable(pl022->clk);
+}
+
+/**
+ * flush - flush the FIFO to reach a clean state
+ * @pl022: SSP driver private data structure
+ */
+static int flush(struct pl022 *pl022)
+{
+ unsigned long limit = loops_per_jiffy << 1;
+
+ dev_dbg(&pl022->adev->dev, "flush\n");
+ do {
+ while (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ readw(SSP_DR(pl022->virtbase));
+ } while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_BSY) && limit--);
+ return limit;
+}
+
+/**
+ * restore_state - Load configuration of current chip
+ * @pl022: SSP driver private data structure
+ */
+static void restore_state(struct pl022 *pl022)
+{
+ struct chip_data *chip = pl022->cur_chip;
+
+ writew(chip->cr0, SSP_CR0(pl022->virtbase));
+ writew(chip->cr1, SSP_CR1(pl022->virtbase));
+ writew(chip->dmacr, SSP_DMACR(pl022->virtbase));
+ writew(chip->cpsr, SSP_CPSR(pl022->virtbase));
+ writew(DISABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+}
+
+/**
+ * load_ssp_default_config - Load default configuration for SSP
+ * @pl022: SSP driver private data structure
+ */
+
+/*
+ * Default SSP Register Values
+ */
+#define DEFAULT_SSP_REG_CR0 ( \
+ GEN_MASK_BITS(SSP_DATA_BITS_12, SSP_CR0_MASK_DSS, 0) | \
+ GEN_MASK_BITS(SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, SSP_CR0_MASK_HALFDUP, 5) | \
+ GEN_MASK_BITS(SSP_CLK_POL_IDLE_LOW, SSP_CR0_MASK_SPO, 6) | \
+ GEN_MASK_BITS(SSP_CLK_FALLING_EDGE, SSP_CR0_MASK_SPH, 7) | \
+ GEN_MASK_BITS(NMDK_SSP_DEFAULT_CLKRATE, SSP_CR0_MASK_SCR, 8) | \
+ GEN_MASK_BITS(SSP_BITS_8, SSP_CR0_MASK_CSS, 16) | \
+ GEN_MASK_BITS(SSP_INTERFACE_MOTOROLA_SPI, SSP_CR0_MASK_FRF, 21) \
+)
+
+#define DEFAULT_SSP_REG_CR1 ( \
+ GEN_MASK_BITS(LOOPBACK_DISABLED, SSP_CR1_MASK_LBM, 0) | \
+ GEN_MASK_BITS(SSP_DISABLED, SSP_CR1_MASK_SSE, 1) | \
+ GEN_MASK_BITS(SSP_MASTER, SSP_CR1_MASK_MS, 2) | \
+ GEN_MASK_BITS(DO_NOT_DRIVE_TX, SSP_CR1_MASK_SOD, 3) | \
+ GEN_MASK_BITS(SSP_RX_MSB, SSP_CR1_MASK_RENDN, 4) | \
+ GEN_MASK_BITS(SSP_TX_MSB, SSP_CR1_MASK_TENDN, 5) | \
+ GEN_MASK_BITS(SSP_MWIRE_WAIT_ZERO, SSP_CR1_MASK_MWAIT, 6) |\
+ GEN_MASK_BITS(SSP_RX_1_OR_MORE_ELEM, SSP_CR1_MASK_RXIFLSEL, 7) | \
+ GEN_MASK_BITS(SSP_TX_1_OR_MORE_EMPTY_LOC, SSP_CR1_MASK_TXIFLSEL, 10) \
+)
+
+#define DEFAULT_SSP_REG_CPSR ( \
+ GEN_MASK_BITS(NMDK_SSP_DEFAULT_PRESCALE, SSP_CPSR_MASK_CPSDVSR, 0) \
+)
+
+#define DEFAULT_SSP_REG_DMACR (\
+ GEN_MASK_BITS(SSP_DMA_DISABLED, SSP_DMACR_MASK_RXDMAE, 0) | \
+ GEN_MASK_BITS(SSP_DMA_DISABLED, SSP_DMACR_MASK_TXDMAE, 1) \
+)
+
+
+static void load_ssp_default_config(struct pl022 *pl022)
+{
+ writew(DEFAULT_SSP_REG_CR0, SSP_CR0(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_CR1, SSP_CR1(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_DMACR, SSP_DMACR(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_CPSR, SSP_CPSR(pl022->virtbase));
+ writew(DISABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+}
+
+/**
+ * This will write to TX and read from RX according to the parameters
+ * set in pl022.
+ */
+static void readwriter(struct pl022 *pl022)
+{
+
+ /*
+ * The FIFO depth is different inbetween primecell variants.
+ * I believe filling in too much in the FIFO might cause
+ * errons in 8bit wide transfers on ARM variants (just 8 words
+ * FIFO, means only 8x8 = 64 bits in FIFO) at least.
+ *
+ * FIXME: currently we have no logic to account for this.
+ * perhaps there is even something broken in HW regarding
+ * 8bit transfers (it doesn't fail on 16bit) so this needs
+ * more investigation...
+ */
+ dev_dbg(&pl022->adev->dev,
+ "%s, rx: %p, rxend: %p, tx: %p, txend: %p\n",
+ __func__, pl022->rx, pl022->rx_end, pl022->tx, pl022->tx_end);
+
+ /* Read as much as you can */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ && (pl022->rx < pl022->rx_end)) {
+ switch (pl022->read) {
+ case READING_NULL:
+ readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U8:
+ *(u8 *) (pl022->rx) =
+ readw(SSP_DR(pl022->virtbase)) & 0xFFU;
+ break;
+ case READING_U16:
+ *(u16 *) (pl022->rx) =
+ (u16) readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U32:
+ *(u32 *) (pl022->rx) =
+ readl(SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->rx += (pl022->cur_chip->n_bytes);
+ }
+ /*
+ * Write as much as you can, while keeping an eye on the RX FIFO!
+ */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
+ && (pl022->tx < pl022->tx_end)) {
+ switch (pl022->write) {
+ case WRITING_NULL:
+ writew(0x0, SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U8:
+ writew(*(u8 *) (pl022->tx), SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U16:
+ writew((*(u16 *) (pl022->tx)), SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U32:
+ writel(*(u32 *) (pl022->tx), SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->tx += (pl022->cur_chip->n_bytes);
+ /*
+ * This inner reader takes care of things appearing in the RX
+ * FIFO as we're transmitting. This will happen a lot since the
+ * clock starts running when you put things into the TX FIFO,
+ * and then things are continously clocked into the RX FIFO.
+ */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ && (pl022->rx < pl022->rx_end)) {
+ switch (pl022->read) {
+ case READING_NULL:
+ readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U8:
+ *(u8 *) (pl022->rx) =
+ readw(SSP_DR(pl022->virtbase)) & 0xFFU;
+ break;
+ case READING_U16:
+ *(u16 *) (pl022->rx) =
+ (u16) readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U32:
+ *(u32 *) (pl022->rx) =
+ readl(SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->rx += (pl022->cur_chip->n_bytes);
+ }
+ }
+ /*
+ * When we exit here the TX FIFO should be full and the RX FIFO
+ * should be empty
+ */
+}
+
+
+/**
+ * next_transfer - Move to the Next transfer in the current spi message
+ * @pl022: SSP driver private data structure
+ *
+ * This function moves though the linked list of spi transfers in the
+ * current spi message and returns with the state of current spi
+ * message i.e whether its last transfer is done(STATE_DONE) or
+ * Next transfer is ready(STATE_RUNNING)
+ */
+static void *next_transfer(struct pl022 *pl022)
+{
+ struct spi_message *msg = pl022->cur_msg;
+ struct spi_transfer *trans = pl022->cur_transfer;
+
+ /* Move to next transfer */
+ if (trans->transfer_list.next != &msg->transfers) {
+ pl022->cur_transfer =
+ list_entry(trans->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ return STATE_RUNNING;
+ }
+ return STATE_DONE;
+}
+/**
+ * pl022_interrupt_handler - Interrupt handler for SSP controller
+ *
+ * This function handles interrupts generated for an interrupt based transfer.
+ * If a receive overrun (ROR) interrupt is there then we disable SSP, flag the
+ * current message's state as STATE_ERROR and schedule the tasklet
+ * pump_transfers which will do the postprocessing of the current message by
+ * calling giveback(). Otherwise it reads data from RX FIFO till there is no
+ * more data, and writes data in TX FIFO till it is not full. If we complete
+ * the transfer we move to the next transfer and schedule the tasklet.
+ */
+static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
+{
+ struct pl022 *pl022 = dev_id;
+ struct spi_message *msg = pl022->cur_msg;
+ u16 irq_status = 0;
+ u16 flag = 0;
+
+ if (unlikely(!msg)) {
+ dev_err(&pl022->adev->dev,
+ "bad message state in interrupt handler");
+ /* Never fail */
+ return IRQ_HANDLED;
+ }
+
+ /* Read the Interrupt Status Register */
+ irq_status = readw(SSP_MIS(pl022->virtbase));
+
+ if (unlikely(!irq_status))
+ return IRQ_NONE;
+
+ /* This handles the error code interrupts */
+ if (unlikely(irq_status & SSP_MIS_MASK_RORMIS)) {
+ /*
+ * Overrun interrupt - bail out since our Data has been
+ * corrupted
+ */
+ dev_err(&pl022->adev->dev,
+ "FIFO overrun\n");
+ if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
+ dev_err(&pl022->adev->dev,
+ "RXFIFO is full\n");
+ if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
+ dev_err(&pl022->adev->dev,
+ "TXFIFO is full\n");
+
+ /*
+ * Disable and clear interrupts, disable SSP,
+ * mark message with bad status so it can be
+ * retried.
+ */
+ writew(DISABLE_ALL_INTERRUPTS,
+ SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+ msg->state = STATE_ERROR;
+
+ /* Schedule message queue handler */
+ tasklet_schedule(&pl022->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ readwriter(pl022);
+
+ if ((pl022->tx == pl022->tx_end) && (flag == 0)) {
+ flag = 1;
+ /* Disable Transmit interrupt */
+ writew(readw(SSP_IMSC(pl022->virtbase)) &
+ (~SSP_IMSC_MASK_TXIM),
+ SSP_IMSC(pl022->virtbase));
+ }
+
+ /*
+ * Since all transactions must write as much as shall be read,
+ * we can conclude the entire transaction once RX is complete.
+ * At this point, all TX will always be finished.
+ */
+ if (pl022->rx >= pl022->rx_end) {
+ writew(DISABLE_ALL_INTERRUPTS,
+ SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+ if (unlikely(pl022->rx > pl022->rx_end)) {
+ dev_warn(&pl022->adev->dev, "read %u surplus "
+ "bytes (did you request an odd "
+ "number of bytes on a 16bit bus?)\n",
+ (u32) (pl022->rx - pl022->rx_end));
+ }
+ /* Update total bytes transfered */
+ msg->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->
+ cs_control(SSP_CHIP_DESELECT);
+ /* Move to next transfer */
+ msg->state = next_transfer(pl022);
+ tasklet_schedule(&pl022->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * This sets up the pointers to memory for the next message to
+ * send out on the SPI bus.
+ */
+static int set_up_next_transfer(struct pl022 *pl022,
+ struct spi_transfer *transfer)
+{
+ int residue;
+
+ /* Sanity check the message for this bus width */
+ residue = pl022->cur_transfer->len % pl022->cur_chip->n_bytes;
+ if (unlikely(residue != 0)) {
+ dev_err(&pl022->adev->dev,
+ "message of %u bytes to transmit but the current "
+ "chip bus has a data width of %u bytes!\n",
+ pl022->cur_transfer->len,
+ pl022->cur_chip->n_bytes);
+ dev_err(&pl022->adev->dev, "skipping this message\n");
+ return -EIO;
+ }
+ pl022->tx = (void *)transfer->tx_buf;
+ pl022->tx_end = pl022->tx + pl022->cur_transfer->len;
+ pl022->rx = (void *)transfer->rx_buf;
+ pl022->rx_end = pl022->rx + pl022->cur_transfer->len;
+ pl022->write =
+ pl022->tx ? pl022->cur_chip->write : WRITING_NULL;
+ pl022->read = pl022->rx ? pl022->cur_chip->read : READING_NULL;
+ return 0;
+}
+
+/**
+ * pump_transfers - Tasklet function which schedules next interrupt transfer
+ * when running in interrupt transfer mode.
+ * @data: SSP driver private data structure
+ *
+ */
+static void pump_transfers(unsigned long data)
+{
+ struct pl022 *pl022 = (struct pl022 *) data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+
+ /* Get current state information */
+ message = pl022->cur_msg;
+ transfer = pl022->cur_transfer;
+
+ /* Handle for abort */
+ if (message->state == STATE_ERROR) {
+ message->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+
+ /* Handle end of message */
+ if (message->state == STATE_DONE) {
+ message->status = 0;
+ giveback(pl022);
+ return;
+ }
+
+ /* Delay if requested at end of transfer before CS change */
+ if (message->state == STATE_RUNNING) {
+ previous = list_entry(transfer->transfer_list.prev,
+ struct spi_transfer,
+ transfer_list);
+ if (previous->delay_usecs)
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ udelay(previous->delay_usecs);
+
+ /* Drop chip select only if cs_change is requested */
+ if (previous->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ } else {
+ /* STATE_START */
+ message->state = STATE_RUNNING;
+ }
+
+ if (set_up_next_transfer(pl022, transfer)) {
+ message->state = STATE_ERROR;
+ message->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Flush the FIFOs and let's go! */
+ flush(pl022);
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+}
+
+/**
+ * NOT IMPLEMENTED
+ * configure_dma - It configures the DMA pipes for DMA transfers
+ * @data: SSP driver's private data structure
+ *
+ */
+static int configure_dma(void *data)
+{
+ struct pl022 *pl022 = data;
+ dev_dbg(&pl022->adev->dev, "configure DMA\n");
+ return -ENOTSUPP;
+}
+
+/**
+ * do_dma_transfer - It handles transfers of the current message
+ * if it is DMA xfer.
+ * NOT FULLY IMPLEMENTED
+ * @data: SSP driver's private data structure
+ */
+static void do_dma_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+
+ if (configure_dma(data)) {
+ dev_dbg(&pl022->adev->dev, "configuration of DMA Failed!\n");
+ goto err_config_dma;
+ }
+
+ /* TODO: Implememt DMA setup of pipes here */
+
+ /* Enable target chip, set up transfer */
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
+ /* Error path */
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Enable SSP */
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+
+ /* TODO: Enable the DMA transfer here */
+ return;
+
+ err_config_dma:
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+}
+
+static void do_interrupt_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+
+ /* Enable target chip */
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
+ /* Error path */
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Enable SSP, turn on interrupts */
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+}
+
+static void do_polling_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ struct chip_data *chip;
+
+ chip = pl022->cur_chip;
+ message = pl022->cur_msg;
+
+ while (message->state != STATE_DONE) {
+ /* Handle for abort */
+ if (message->state == STATE_ERROR)
+ break;
+ transfer = pl022->cur_transfer;
+
+ /* Delay if requested at end of transfer */
+ if (message->state == STATE_RUNNING) {
+ previous =
+ list_entry(transfer->transfer_list.prev,
+ struct spi_transfer, transfer_list);
+ if (previous->delay_usecs)
+ udelay(previous->delay_usecs);
+ if (previous->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ } else {
+ /* STATE_START */
+ message->state = STATE_RUNNING;
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ }
+
+ /* Configuration Changing Per Transfer */
+ if (set_up_next_transfer(pl022, transfer)) {
+ /* Error path */
+ message->state = STATE_ERROR;
+ break;
+ }
+ /* Flush FIFOs and enable SSP */
+ flush(pl022);
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+
+ dev_dbg(&pl022->adev->dev, "POLLING TRANSFER ONGOING ... \n");
+ /* FIXME: insert a timeout so we don't hang here indefinately */
+ while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end)
+ readwriter(pl022);
+
+ /* Update total byte transfered */
+ message->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
+ /* Move to next transfer */
+ message->state = next_transfer(pl022);
+ }
+
+ /* Handle end of message */
+ if (message->state == STATE_DONE)
+ message->status = 0;
+ else
+ message->status = -EIO;
+
+ giveback(pl022);
+ return;
+}
+
+/**
+ * pump_messages - Workqueue function which processes spi message queue
+ * @data: pointer to private data of SSP driver
+ *
+ * This function checks if there is any spi message in the queue that
+ * needs processing and delegate control to appropriate function
+ * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * based on the kind of the transfer
+ *
+ */
+static void pump_messages(struct work_struct *work)
+{
+ struct pl022 *pl022 =
+ container_of(work, struct pl022, pump_messages);
+ unsigned long flags;
+
+ /* Lock queue and check for queue work */
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ if (list_empty(&pl022->queue) || pl022->run == QUEUE_STOPPED) {
+ pl022->busy = 0;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return;
+ }
+ /* Make sure we are not already running a message */
+ if (pl022->cur_msg) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return;
+ }
+ /* Extract head of queue */
+ pl022->cur_msg =
+ list_entry(pl022->queue.next, struct spi_message, queue);
+
+ list_del_init(&pl022->cur_msg->queue);
+ pl022->busy = 1;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ /* Initial message state */
+ pl022->cur_msg->state = STATE_START;
+ pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Setup the SPI using the per chip configuration */
+ pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
+ /*
+ * We enable the clock here, then the clock will be disabled when
+ * giveback() is called in each method (poll/interrupt/DMA)
+ */
+ clk_enable(pl022->clk);
+ restore_state(pl022);
+ flush(pl022);
+
+ if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
+ do_polling_transfer(pl022);
+ else if (pl022->cur_chip->xfer_type == INTERRUPT_TRANSFER)
+ do_interrupt_transfer(pl022);
+ else
+ do_dma_transfer(pl022);
+}
+
+
+static int __init init_queue(struct pl022 *pl022)
+{
+ INIT_LIST_HEAD(&pl022->queue);
+ spin_lock_init(&pl022->queue_lock);
+
+ pl022->run = QUEUE_STOPPED;
+ pl022->busy = 0;
+
+ tasklet_init(&pl022->pump_transfers,
+ pump_transfers, (unsigned long)pl022);
+
+ INIT_WORK(&pl022->pump_messages, pump_messages);
+ pl022->workqueue = create_singlethread_workqueue(
+ dev_name(pl022->master->dev.parent));
+ if (pl022->workqueue == NULL)
+ return -EBUSY;
+
+ return 0;
+}
+
+
+static int start_queue(struct pl022 *pl022)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ if (pl022->run == QUEUE_RUNNING || pl022->busy) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return -EBUSY;
+ }
+
+ pl022->run = QUEUE_RUNNING;
+ pl022->cur_msg = NULL;
+ pl022->cur_transfer = NULL;
+ pl022->cur_chip = NULL;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+
+ return 0;
+}
+
+
+static int stop_queue(struct pl022 *pl022)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+ int status = 0;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ /* This is a bit lame, but is optimized for the common execution path.
+ * A wait_queue on the pl022->busy could be used, but then the common
+ * execution path (pump_messages) would be required to call wake_up or
+ * friends on every SPI message. Do this instead */
+ pl022->run = QUEUE_STOPPED;
+ while (!list_empty(&pl022->queue) && pl022->busy && limit--) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ }
+
+ if (!list_empty(&pl022->queue) || pl022->busy)
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ return status;
+}
+
+static int destroy_queue(struct pl022 *pl022)
+{
+ int status;
+
+ status = stop_queue(pl022);
+ /* we are unloading the module or failing to load (only two calls
+ * to this routine), and neither call can handle a return value.
+ * However, destroy_workqueue calls flush_workqueue, and that will
+ * block until all work is done. If the reason that stop_queue
+ * timed out is that the work will never finish, then it does no
+ * good to call destroy_workqueue, so return anyway. */
+ if (status != 0)
+ return status;
+
+ destroy_workqueue(pl022->workqueue);
+
+ return 0;
+}
+
+static int verify_controller_parameters(struct pl022 *pl022,
+ struct pl022_config_chip *chip_info)
+{
+ if ((chip_info->lbm != LOOPBACK_ENABLED)
+ && (chip_info->lbm != LOOPBACK_DISABLED)) {
+ dev_err(chip_info->dev,
+ "loopback Mode is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI)
+ || (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) {
+ dev_err(chip_info->dev,
+ "interface is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->iface == SSP_INTERFACE_UNIDIRECTIONAL) &&
+ (!pl022->vendor->unidir)) {
+ dev_err(chip_info->dev,
+ "unidirectional mode not supported in this "
+ "hardware version\n");
+ return -EINVAL;
+ }
+ if ((chip_info->hierarchy != SSP_MASTER)
+ && (chip_info->hierarchy != SSP_SLAVE)) {
+ dev_err(chip_info->dev,
+ "hierarchy is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN)
+ || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) {
+ dev_err(chip_info->dev,
+ "cpsdvsr is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->endian_rx != SSP_RX_MSB)
+ && (chip_info->endian_rx != SSP_RX_LSB)) {
+ dev_err(chip_info->dev,
+ "RX FIFO endianess is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->endian_tx != SSP_TX_MSB)
+ && (chip_info->endian_tx != SSP_TX_LSB)) {
+ dev_err(chip_info->dev,
+ "TX FIFO endianess is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->data_size < SSP_DATA_BITS_4)
+ || (chip_info->data_size > SSP_DATA_BITS_32)) {
+ dev_err(chip_info->dev,
+ "DATA Size is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->com_mode != INTERRUPT_TRANSFER)
+ && (chip_info->com_mode != DMA_TRANSFER)
+ && (chip_info->com_mode != POLLING_TRANSFER)) {
+ dev_err(chip_info->dev,
+ "Communication mode is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM)
+ || (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) {
+ dev_err(chip_info->dev,
+ "RX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC)
+ || (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) {
+ dev_err(chip_info->dev,
+ "TX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) {
+ if ((chip_info->clk_phase != SSP_CLK_RISING_EDGE)
+ && (chip_info->clk_phase != SSP_CLK_FALLING_EDGE)) {
+ dev_err(chip_info->dev,
+ "Clock Phase is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->clk_pol != SSP_CLK_POL_IDLE_LOW)
+ && (chip_info->clk_pol != SSP_CLK_POL_IDLE_HIGH)) {
+ dev_err(chip_info->dev,
+ "Clock Polarity is configured incorrectly\n");
+ return -EINVAL;
+ }
+ }
+ if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) {
+ if ((chip_info->ctrl_len < SSP_BITS_4)
+ || (chip_info->ctrl_len > SSP_BITS_32)) {
+ dev_err(chip_info->dev,
+ "CTRL LEN is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->wait_state != SSP_MWIRE_WAIT_ZERO)
+ && (chip_info->wait_state != SSP_MWIRE_WAIT_ONE)) {
+ dev_err(chip_info->dev,
+ "Wait State is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
+ && (chip_info->duplex !=
+ SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) {
+ dev_err(chip_info->dev,
+ "DUPLEX is configured incorrectly\n");
+ return -EINVAL;
+ }
+ }
+ if (chip_info->cs_control == NULL) {
+ dev_warn(chip_info->dev,
+ "Chip Select Function is NULL for this chip\n");
+ chip_info->cs_control = null_cs_control;
+ }
+ return 0;
+}
+
+/**
+ * pl022_transfer - transfer function registered to SPI master framework
+ * @spi: spi device which is requesting transfer
+ * @msg: spi message which is to handled is queued to driver queue
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will queue the spi_message in the queue of driver if
+ * the queue is not stopped and return.
+ */
+static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct pl022 *pl022 = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ if (pl022->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return -ESHUTDOWN;
+ }
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = STATE_START;
+
+ list_add_tail(&msg->queue, &pl022->queue);
+ if (pl022->run == QUEUE_RUNNING && !pl022->busy)
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return 0;
+}
+
+static int calculate_effective_freq(struct pl022 *pl022,
+ int freq,
+ struct ssp_clock_params *clk_freq)
+{
+ /* Lets calculate the frequency parameters */
+ u16 cpsdvsr = 2;
+ u16 scr = 0;
+ bool freq_found = false;
+ u32 rate;
+ u32 max_tclk;
+ u32 min_tclk;
+
+ rate = clk_get_rate(pl022->clk);
+ /* cpsdvscr = 2 & scr 0 */
+ max_tclk = (rate / (CPSDVR_MIN * (1 + SCR_MIN)));
+ /* cpsdvsr = 254 & scr = 255 */
+ min_tclk = (rate / (CPSDVR_MAX * (1 + SCR_MAX)));
+
+ if ((freq <= max_tclk) && (freq >= min_tclk)) {
+ while (cpsdvsr <= CPSDVR_MAX && !freq_found) {
+ while (scr <= SCR_MAX && !freq_found) {
+ if ((rate /
+ (cpsdvsr * (1 + scr))) > freq)
+ scr += 1;
+ else {
+ /*
+ * This bool is made true when
+ * effective frequency >=
+ * target frequency is found
+ */
+ freq_found = true;
+ if ((rate /
+ (cpsdvsr * (1 + scr))) != freq) {
+ if (scr == SCR_MIN) {
+ cpsdvsr -= 2;
+ scr = SCR_MAX;
+ } else
+ scr -= 1;
+ }
+ }
+ }
+ if (!freq_found) {
+ cpsdvsr += 2;
+ scr = SCR_MIN;
+ }
+ }
+ if (cpsdvsr != 0) {
+ dev_dbg(&pl022->adev->dev,
+ "SSP Effective Frequency is %u\n",
+ (rate / (cpsdvsr * (1 + scr))));
+ clk_freq->cpsdvsr = (u8) (cpsdvsr & 0xFF);
+ clk_freq->scr = (u8) (scr & 0xFF);
+ dev_dbg(&pl022->adev->dev,
+ "SSP cpsdvsr = %d, scr = %d\n",
+ clk_freq->cpsdvsr, clk_freq->scr);
+ }
+ } else {
+ dev_err(&pl022->adev->dev,
+ "controller data is incorrect: out of range frequency");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * NOT IMPLEMENTED
+ * process_dma_info - Processes the DMA info provided by client drivers
+ * @chip_info: chip info provided by client device
+ * @chip: Runtime state maintained by the SSP controller for each spi device
+ *
+ * This function processes and stores DMA config provided by client driver
+ * into the runtime state maintained by the SSP controller driver
+ */
+static int process_dma_info(struct pl022_config_chip *chip_info,
+ struct chip_data *chip)
+{
+ dev_err(chip_info->dev,
+ "cannot process DMA info, DMA not implemented!\n");
+ return -ENOTSUPP;
+}
+
+/**
+ * pl022_setup - setup function registered to SPI master framework
+ * @spi: spi device which is requesting setup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. If it is the first time when setup is called by this device,
+ * this function will initialize the runtime state for this chip and save
+ * the same in the device structure. Else it will update the runtime info
+ * with the updated chip info. Nothing is really being written to the
+ * controller hardware here, that is not done until the actual transfer
+ * commence.
+ */
+
+/* FIXME: JUST GUESSING the spi->mode bits understood by this driver */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
+ | SPI_LSB_FIRST | SPI_LOOP)
+
+static int pl022_setup(struct spi_device *spi)
+{
+ struct pl022_config_chip *chip_info;
+ struct chip_data *chip;
+ int status = 0;
+ struct pl022 *pl022 = spi_master_get_devdata(spi->master);
+
+ if (spi->mode & ~MODEBITS) {
+ dev_dbg(&spi->dev, "unsupported mode bits %x\n",
+ spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ /* Get controller_state if one is supplied */
+ chip = spi_get_ctldata(spi);
+
+ if (chip == NULL) {
+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spi->dev,
+ "cannot allocate controller state\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&spi->dev,
+ "allocated memory for controller's runtime state\n");
+ }
+
+ /* Get controller data if one is supplied */
+ chip_info = spi->controller_data;
+
+ if (chip_info == NULL) {
+ /* spi_board_info.controller_data not is supplied */
+ dev_dbg(&spi->dev,
+ "using default controller_data settings\n");
+
+ chip_info =
+ kzalloc(sizeof(struct pl022_config_chip), GFP_KERNEL);
+
+ if (!chip_info) {
+ dev_err(&spi->dev,
+ "cannot allocate controller data\n");
+ status = -ENOMEM;
+ goto err_first_setup;
+ }
+
+ dev_dbg(&spi->dev, "allocated memory for controller data\n");
+
+ /* Pointer back to the SPI device */
+ chip_info->dev = &spi->dev;
+ /*
+ * Set controller data default values:
+ * Polling is supported by default
+ */
+ chip_info->lbm = LOOPBACK_DISABLED;
+ chip_info->com_mode = POLLING_TRANSFER;
+ chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI;
+ chip_info->hierarchy = SSP_SLAVE;
+ chip_info->slave_tx_disable = DO_NOT_DRIVE_TX;
+ chip_info->endian_tx = SSP_TX_LSB;
+ chip_info->endian_rx = SSP_RX_LSB;
+ chip_info->data_size = SSP_DATA_BITS_12;
+ chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
+ chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
+ chip_info->clk_phase = SSP_CLK_FALLING_EDGE;
+ chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW;
+ chip_info->ctrl_len = SSP_BITS_8;
+ chip_info->wait_state = SSP_MWIRE_WAIT_ZERO;
+ chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX;
+ chip_info->cs_control = null_cs_control;
+ } else {
+ dev_dbg(&spi->dev,
+ "using user supplied controller_data settings\n");
+ }
+
+ /*
+ * We can override with custom divisors, else we use the board
+ * frequency setting
+ */
+ if ((0 == chip_info->clk_freq.cpsdvsr)
+ && (0 == chip_info->clk_freq.scr)) {
+ status = calculate_effective_freq(pl022,
+ spi->max_speed_hz,
+ &chip_info->clk_freq);
+ if (status < 0)
+ goto err_config_params;
+ } else {
+ if ((chip_info->clk_freq.cpsdvsr % 2) != 0)
+ chip_info->clk_freq.cpsdvsr =
+ chip_info->clk_freq.cpsdvsr - 1;
+ }
+ status = verify_controller_parameters(pl022, chip_info);
+ if (status) {
+ dev_err(&spi->dev, "controller data is incorrect");
+ goto err_config_params;
+ }
+ /* Now set controller state based on controller data */
+ chip->xfer_type = chip_info->com_mode;
+ chip->cs_control = chip_info->cs_control;
+
+ if (chip_info->data_size <= 8) {
+ dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n");
+ chip->n_bytes = 1;
+ chip->read = READING_U8;
+ chip->write = WRITING_U8;
+ } else if (chip_info->data_size <= 16) {
+ dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n");
+ chip->n_bytes = 2;
+ chip->read = READING_U16;
+ chip->write = WRITING_U16;
+ } else {
+ if (pl022->vendor->max_bpw >= 32) {
+ dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n");
+ chip->n_bytes = 4;
+ chip->read = READING_U32;
+ chip->write = WRITING_U32;
+ } else {
+ dev_err(&spi->dev,
+ "illegal data size for this controller!\n");
+ dev_err(&spi->dev,
+ "a standard pl022 can only handle "
+ "1 <= n <= 16 bit words\n");
+ goto err_config_params;
+ }
+ }
+
+ /* Now Initialize all register settings required for this chip */
+ chip->cr0 = 0;
+ chip->cr1 = 0;
+ chip->dmacr = 0;
+ chip->cpsr = 0;
+ if ((chip_info->com_mode == DMA_TRANSFER)
+ && ((pl022->master_info)->enable_dma)) {
+ chip->enable_dma = 1;
+ dev_dbg(&spi->dev, "DMA mode set in controller state\n");
+ status = process_dma_info(chip_info, chip);
+ if (status < 0)
+ goto err_config_params;
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
+ SSP_DMACR_MASK_RXDMAE, 0);
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
+ SSP_DMACR_MASK_TXDMAE, 1);
+ } else {
+ chip->enable_dma = 0;
+ dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n");
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
+ SSP_DMACR_MASK_RXDMAE, 0);
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
+ SSP_DMACR_MASK_TXDMAE, 1);
+ }
+
+ chip->cpsr = chip_info->clk_freq.cpsdvsr;
+
+ SSP_WRITE_BITS(chip->cr0, chip_info->data_size, SSP_CR0_MASK_DSS, 0);
+ SSP_WRITE_BITS(chip->cr0, chip_info->duplex, SSP_CR0_MASK_HALFDUP, 5);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_pol, SSP_CR0_MASK_SPO, 6);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_phase, SSP_CR0_MASK_SPH, 7);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8);
+ SSP_WRITE_BITS(chip->cr0, chip_info->ctrl_len, SSP_CR0_MASK_CSS, 16);
+ SSP_WRITE_BITS(chip->cr0, chip_info->iface, SSP_CR0_MASK_FRF, 21);
+ SSP_WRITE_BITS(chip->cr1, chip_info->lbm, SSP_CR1_MASK_LBM, 0);
+ SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
+ SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
+ SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
+ SSP_WRITE_BITS(chip->cr1, chip_info->endian_rx, SSP_CR1_MASK_RENDN, 4);
+ SSP_WRITE_BITS(chip->cr1, chip_info->endian_tx, SSP_CR1_MASK_TENDN, 5);
+ SSP_WRITE_BITS(chip->cr1, chip_info->wait_state, SSP_CR1_MASK_MWAIT, 6);
+ SSP_WRITE_BITS(chip->cr1, chip_info->rx_lev_trig, SSP_CR1_MASK_RXIFLSEL, 7);
+ SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig, SSP_CR1_MASK_TXIFLSEL, 10);
+
+ /* Save controller_state */
+ spi_set_ctldata(spi, chip);
+ return status;
+ err_config_params:
+ err_first_setup:
+ kfree(chip);
+ return status;
+}
+
+/**
+ * pl022_cleanup - cleanup function registered to SPI master framework
+ * @spi: spi device which is requesting cleanup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will free the runtime state of chip.
+ */
+static void pl022_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata(spi);
+
+ spi_set_ctldata(spi, NULL);
+ kfree(chip);
+}
+
+
+static int __init
+pl022_probe(struct amba_device *adev, struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
+ struct spi_master *master;
+ struct pl022 *pl022 = NULL; /*Data for this driver */
+ int status = 0;
+
+ dev_info(&adev->dev,
+ "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+ if (platform_info == NULL) {
+ dev_err(&adev->dev, "probe - no platform data supplied\n");
+ status = -ENODEV;
+ goto err_no_pdata;
+ }
+
+ /* Allocate master with space for data */
+ master = spi_alloc_master(dev, sizeof(struct pl022));
+ if (master == NULL) {
+ dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
+ status = -ENOMEM;
+ goto err_no_master;
+ }
+
+ pl022 = spi_master_get_devdata(master);
+ pl022->master = master;
+ pl022->master_info = platform_info;
+ pl022->adev = adev;
+ pl022->vendor = id->data;
+
+ /*
+ * Bus Number Which has been Assigned to this SSP controller
+ * on this board
+ */
+ master->bus_num = platform_info->bus_id;
+ master->num_chipselect = platform_info->num_chipselect;
+ master->cleanup = pl022_cleanup;
+ master->setup = pl022_setup;
+ master->transfer = pl022_transfer;
+
+ dev_dbg(&adev->dev, "BUSNO: %d\n", master->bus_num);
+
+ status = amba_request_regions(adev, NULL);
+ if (status)
+ goto err_no_ioregion;
+
+ pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
+ if (pl022->virtbase == NULL) {
+ status = -ENOMEM;
+ goto err_no_ioremap;
+ }
+ printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
+ adev->res.start, pl022->virtbase);
+
+ pl022->clk = clk_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->clk)) {
+ status = PTR_ERR(pl022->clk);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+ goto err_no_clk;
+ }
+
+ /* Disable SSP */
+ clk_enable(pl022->clk);
+ writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
+ SSP_CR1(pl022->virtbase));
+ load_ssp_default_config(pl022);
+ clk_disable(pl022->clk);
+
+ status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
+ pl022);
+ if (status < 0) {
+ dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+ goto err_no_irq;
+ }
+ /* Initialize and start queue */
+ status = init_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev, "probe - problem initializing queue\n");
+ goto err_init_queue;
+ }
+ status = start_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev, "probe - problem starting queue\n");
+ goto err_start_queue;
+ }
+ /* Register with the SPI framework */
+ amba_set_drvdata(adev, pl022);
+ status = spi_register_master(master);
+ if (status != 0) {
+ dev_err(&adev->dev,
+ "probe - problem registering spi master\n");
+ goto err_spi_register;
+ }
+ dev_dbg(dev, "probe succeded\n");
+ return 0;
+
+ err_spi_register:
+ err_start_queue:
+ err_init_queue:
+ destroy_queue(pl022);
+ free_irq(adev->irq[0], pl022);
+ err_no_irq:
+ clk_put(pl022->clk);
+ err_no_clk:
+ iounmap(pl022->virtbase);
+ err_no_ioremap:
+ amba_release_regions(adev);
+ err_no_ioregion:
+ spi_master_put(master);
+ err_no_master:
+ err_no_pdata:
+ return status;
+}
+
+static int __exit
+pl022_remove(struct amba_device *adev)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+ if (!pl022)
+ return 0;
+
+ /* Remove the queue */
+ status = destroy_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev,
+ "queue remove failed (%d)\n", status);
+ return status;
+ }
+ load_ssp_default_config(pl022);
+ free_irq(adev->irq[0], pl022);
+ clk_disable(pl022->clk);
+ clk_put(pl022->clk);
+ iounmap(pl022->virtbase);
+ amba_release_regions(adev);
+ tasklet_disable(&pl022->pump_transfers);
+ spi_unregister_master(pl022->master);
+ spi_master_put(pl022->master);
+ amba_set_drvdata(adev, NULL);
+ dev_dbg(&adev->dev, "remove succeded\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pl022_suspend(struct amba_device *adev, pm_message_t state)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+
+ status = stop_queue(pl022);
+ if (status) {
+ dev_warn(&adev->dev, "suspend cannot stop queue\n");
+ return status;
+ }
+
+ clk_enable(pl022->clk);
+ load_ssp_default_config(pl022);
+ clk_disable(pl022->clk);
+ dev_dbg(&adev->dev, "suspended\n");
+ return 0;
+}
+
+static int pl022_resume(struct amba_device *adev)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+
+ /* Start the queue running */
+ status = start_queue(pl022);
+ if (status)
+ dev_err(&adev->dev, "problem starting queue (%d)\n", status);
+ else
+ dev_dbg(&adev->dev, "resumed\n");
+
+ return status;
+}
+#else
+#define pl022_suspend NULL
+#define pl022_resume NULL
+#endif /* CONFIG_PM */
+
+static struct vendor_data vendor_arm = {
+ .fifodepth = 8,
+ .max_bpw = 16,
+ .unidir = false,
+};
+
+
+static struct vendor_data vendor_st = {
+ .fifodepth = 32,
+ .max_bpw = 32,
+ .unidir = false,
+};
+
+static struct amba_id pl022_ids[] = {
+ {
+ /*
+ * ARM PL022 variant, this has a 16bit wide
+ * and 8 locations deep TX/RX FIFO
+ */
+ .id = 0x00041022,
+ .mask = 0x000fffff,
+ .data = &vendor_arm,
+ },
+ {
+ /*
+ * ST Micro derivative, this has 32bit wide
+ * and 32 locations deep TX/RX FIFO
+ */
+ .id = 0x00108022,
+ .mask = 0xffffffff,
+ .data = &vendor_st,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver pl022_driver = {
+ .drv = {
+ .name = "ssp-pl022",
+ },
+ .id_table = pl022_ids,
+ .probe = pl022_probe,
+ .remove = __exit_p(pl022_remove),
+ .suspend = pl022_suspend,
+ .resume = pl022_resume,
+};
+
+
+static int __init pl022_init(void)
+{
+ return amba_driver_register(&pl022_driver);
+}
+
+module_init(pl022_init);
+
+static void __exit pl022_exit(void)
+{
+ amba_driver_unregister(&pl022_driver);
+}
+
+module_exit(pl022_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("PL022 SSP Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
index f2447a5476bb..bbf9371cd284 100644
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ b/drivers/spi/spi_s3c24xx_gpio.c
@@ -17,6 +17,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
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/go7007/go7007.txt b/drivers/staging/go7007/go7007.txt
index 9f6772bc68c2..1c2907c1dc81 100644
--- a/drivers/staging/go7007/go7007.txt
+++ b/drivers/staging/go7007/go7007.txt
@@ -2,7 +2,7 @@ This is a driver for the WIS GO7007SB multi-format video encoder.
Pete Eberlein <pete@sensoray.com>
-The driver was orignally released under the GPL and is currently hosted at:
+The driver was originally released under the GPL and is currently hosted at:
http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver
The go7007 firmware can be acquired from the package on the site above.
@@ -24,7 +24,7 @@ These should be used instead of the non-standard GO7007 ioctls described
below.
-The README files from the orignal package appear below:
+The README files from the original package appears below:
---------------------------------------------------------------------------
WIS GO7007SB Public Linux Driver
diff --git a/drivers/staging/panel/lcd-panel-cgram.txt b/drivers/staging/panel/lcd-panel-cgram.txt
index f9ceef4322a3..7f82c905763d 100644
--- a/drivers/staging/panel/lcd-panel-cgram.txt
+++ b/drivers/staging/panel/lcd-panel-cgram.txt
@@ -3,7 +3,7 @@ characters 0 to 7. The escape code to define a new character is
'\e[LG' followed by one digit from 0 to 7, representing the character
number, and up to 8 couples of hex digits terminated by a semi-colon
(';'). Each couple of digits represents a line, with 1-bits for each
-illuminated pixel with LSB on the right. Lines are numberred from the
+illuminated pixel with LSB on the right. Lines are numbered from the
top of the character to the bottom. On a 5x7 matrix, only the 5 lower
bits of the 7 first bytes are used for each character. If the string
is incomplete, only complete lines will be redefined. Here are some
diff --git a/drivers/staging/rt2860/common/mlme.c b/drivers/staging/rt2860/common/mlme.c
index c00f9ab9c46c..2edf2999f5c8 100644
--- a/drivers/staging/rt2860/common/mlme.c
+++ b/drivers/staging/rt2860/common/mlme.c
@@ -5664,7 +5664,7 @@ VOID AsicUpdateProtect(
#if 0
MacReg |= (pAd->CommonCfg.RtsThreshold << 8);
#else
- // If the user want disable RtsThreshold and enbale Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
+ // If the user want disable RtsThreshold and enable Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
if ((
#ifdef DOT11_N_SUPPORT
(pAd->CommonCfg.BACapability.field.AmsduEnable) ||
diff --git a/drivers/staging/rt2870/common/mlme.c b/drivers/staging/rt2870/common/mlme.c
index 8a82cee8bf26..a26bc033337d 100644
--- a/drivers/staging/rt2870/common/mlme.c
+++ b/drivers/staging/rt2870/common/mlme.c
@@ -5561,7 +5561,7 @@ VOID AsicUpdateProtect(
#if 0
MacReg |= (pAd->CommonCfg.RtsThreshold << 8);
#else
- // If the user want disable RtsThreshold and enbale Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
+ // If the user want disable RtsThreshold and enable Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
if ((
#ifdef DOT11_N_SUPPORT
(pAd->CommonCfg.BACapability.field.AmsduEnable) ||
diff --git a/drivers/staging/rt3070/common/mlme.c b/drivers/staging/rt3070/common/mlme.c
index 0ffbfa36699e..0189bab013cf 100644
--- a/drivers/staging/rt3070/common/mlme.c
+++ b/drivers/staging/rt3070/common/mlme.c
@@ -5575,7 +5575,7 @@ VOID AsicUpdateProtect(
RTMP_IO_READ32(pAd, TX_RTS_CFG, &MacReg);
MacReg &= 0xFF0000FF;
- // If the user want disable RtsThreshold and enbale Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
+ // If the user want disable RtsThreshold and enable Amsdu/Ralink-Aggregation, set the RtsThreshold as 4096
if ((
#ifdef DOT11_N_SUPPORT
(pAd->CommonCfg.BACapability.field.AmsduEnable) ||
diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c
index 888198c9a106..824e65bdc433 100644
--- a/drivers/staging/wlan-ng/hfa384x_usb.c
+++ b/drivers/staging/wlan-ng/hfa384x_usb.c
@@ -2424,7 +2424,7 @@ int hfa384x_drvr_ramdl_write(hfa384x_t *hw, u32 daddr, void *buf, u32 len)
* 0 success
* >0 f/w reported error - f/w status code
* <0 driver reported error
-* -ETIMEOUT timout waiting for the cmd regs to become
+* -ETIMEDOUT timout waiting for the cmd regs to become
* available, or waiting for the control reg
* to indicate the Aux port is enabled.
* -ENODATA the buffer does NOT contain a valid PDA.
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 0716cdb44cd8..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/
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/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/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 7cf74f8c2db1..b0dbf4157d29 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -47,7 +47,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
struct usb_hcd *hcd;
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
- pr_debug("resource[1] is not IORESOURCE_IRQ");
+ dbg("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
@@ -65,12 +65,18 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
- pr_debug("ioremap failed");
+ dbg("ioremap failed");
retval = -ENOMEM;
goto err2;
}
- usb_host_clock = clk_get(&pdev->dev, "usb_host");
+ usb_host_clock = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usb_host_clock)) {
+ dbg("clk_get failed");
+ retval = PTR_ERR(usb_host_clock);
+ goto err3;
+ }
+
ep93xx_start_hc(&pdev->dev);
ohci_hcd_init(hcd_to_ohci(hcd));
@@ -80,6 +86,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
return retval;
ep93xx_stop_hc(&pdev->dev);
+err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
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 0ab8474b00cb..683304d60615 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -89,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;
@@ -719,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);
@@ -758,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,
@@ -1037,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)
@@ -1097,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) !=
@@ -1192,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,
@@ -1220,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;
}
@@ -1392,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;
}
@@ -1487,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);
- kref_put(&priv->kref, ftdi_sio_priv_release);
- }
+ kref_put(&priv->kref, ftdi_sio_priv_release);
return 0;
}
@@ -1521,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),
@@ -1536,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);
@@ -1565,6 +1580,30 @@ static int ftdi_open(struct tty_struct *tty,
} /* 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
@@ -1574,31 +1613,12 @@ 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_sync(&priv->rx_work);
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..db964db42d3c 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -102,7 +102,7 @@ struct edgeport_port {
__u8 shadow_mcr;
__u8 shadow_lsr;
__u8 lsr_mask;
- __u32 ump_read_timeout; /* Number of miliseconds the UMP will
+ __u32 ump_read_timeout; /* Number of milliseconds the UMP will
wait without data before completing
a read short */
int baud_rate;
@@ -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/video/Kconfig b/drivers/video/Kconfig
index 0048f1185a60..2b5a691064b7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -397,7 +397,7 @@ config FB_SA1100
config FB_IMX
tristate "Motorola i.MX LCD support"
- depends on FB && (ARCH_IMX || ARCH_MX2)
+ depends on FB && (ARCH_MX1 || ARCH_MX2)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -1759,6 +1759,16 @@ config FB_68328
Say Y here if you want to support the built-in frame buffer of
the Motorola 68328 CPU family.
+config FB_PXA168
+ tristate "PXA168/910 LCD framebuffer support"
+ depends on FB && (CPU_PXA168 || CPU_PXA910)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Frame buffer driver for the built-in LCD controller in the Marvell
+ MMP processor.
+
config FB_PXA
tristate "PXA LCD framebuffer support"
depends on FB && ARCH_PXA
@@ -1996,7 +2006,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 d8d0be5151e3..01a819f47371 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
obj-$(CONFIG_FB_PXA) += pxafb.o
+obj-$(CONFIG_FB_PXA168) += pxa168fb.o
obj-$(CONFIG_FB_W100) += w100fb.o
obj-$(CONFIG_FB_TMIO) += tmiofb.o
obj-$(CONFIG_FB_AU1100) += au1100fb.o
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 61050ab14128..fb8163d181ab 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -351,7 +351,7 @@ static int clcdfb_register(struct clcd_fb *fb)
}
fb->fb.fix.mmio_start = fb->dev->res.start;
- fb->fb.fix.mmio_len = 4096;
+ fb->fb.fix.mmio_len = resource_size(&fb->dev->res);
fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
if (!fb->regs) {
@@ -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/console/vgacon.c b/drivers/video/console/vgacon.c
index 38e86b84dce0..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);
}
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/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/mx3fb.c b/drivers/video/mx3fb.c
index 9894de1c9b9f..b7af5256e887 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -706,7 +706,7 @@ static void mx3fb_dma_done(void *arg)
dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq);
/* We only need one interrupt, it will be re-enabled as needed */
- disable_irq(ichannel->eof_irq);
+ disable_irq_nosync(ichannel->eof_irq);
complete(&mx3_fbi->flip_cmpl);
}
@@ -1366,7 +1366,7 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
mx3fb_blank(FB_BLANK_UNBLANK, fbi);
- dev_info(dev, "mx3fb: fb registered, using mode %s\n", fb_mode);
+ dev_info(dev, "registered, using mode %s\n", fb_mode);
ret = register_framebuffer(fbi);
if (ret < 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/hwa742.c b/drivers/video/omap/hwa742.c
index 8aa6e47202b9..5d4f34887a22 100644
--- a/drivers/video/omap/hwa742.c
+++ b/drivers/video/omap/hwa742.c
@@ -133,8 +133,7 @@ struct {
struct lcd_ctrl_extif *extif;
struct lcd_ctrl *int_ctrl;
- void (*power_up)(struct device *dev);
- void (*power_down)(struct device *dev);
+ struct clk *sys_ck;
} hwa742;
struct lcd_ctrl hwa742_ctrl;
@@ -915,14 +914,13 @@ static void hwa742_suspend(void)
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
/* Enable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1);
- if (hwa742.power_down != NULL)
- hwa742.power_down(hwa742.fbdev->dev);
+ clk_disable(hwa742.sys_ck);
}
static void hwa742_resume(void)
{
- if (hwa742.power_up != NULL)
- hwa742.power_up(hwa742.fbdev->dev);
+ clk_enable(hwa742.sys_ck);
+
/* Disable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 0);
while (1) {
@@ -955,14 +953,13 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
omapfb_conf = fbdev->dev->platform_data;
ctrl_conf = omapfb_conf->ctrl_platform_data;
- if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) {
+ if (ctrl_conf == NULL) {
dev_err(fbdev->dev, "HWA742: missing platform data\n");
r = -ENOENT;
goto err1;
}
- hwa742.power_down = ctrl_conf->power_down;
- hwa742.power_up = ctrl_conf->power_up;
+ hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck");
spin_lock_init(&hwa742.req_lock);
@@ -972,12 +969,11 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
if ((r = hwa742.extif->init(fbdev)) < 0)
goto err2;
- ext_clk = ctrl_conf->get_clock_rate(fbdev->dev);
+ ext_clk = clk_get_rate(hwa742.sys_ck);
if ((r = calc_extif_timings(ext_clk, &extif_mem_div)) < 0)
goto err3;
hwa742.extif->set_timings(&hwa742.reg_timings);
- if (hwa742.power_up != NULL)
- hwa742.power_up(fbdev->dev);
+ clk_enable(hwa742.sys_ck);
calc_hwa742_clk_rates(ext_clk, &sys_clk, &pix_clk);
if ((r = calc_extif_timings(sys_clk, &extif_mem_div)) < 0)
@@ -1040,8 +1036,7 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
return 0;
err4:
- if (hwa742.power_down != NULL)
- hwa742.power_down(fbdev->dev);
+ clk_disable(hwa742.sys_ck);
err3:
hwa742.extif->cleanup();
err2:
@@ -1055,8 +1050,7 @@ static void hwa742_cleanup(void)
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
hwa742.extif->cleanup();
hwa742.int_ctrl->cleanup();
- if (hwa742.power_down != NULL)
- hwa742.power_down(hwa742.fbdev->dev);
+ clk_disable(hwa742.sys_ck);
}
struct lcd_ctrl hwa742_ctrl = {
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/pxa168fb.c b/drivers/video/pxa168fb.c
new file mode 100644
index 000000000000..84d8327e47db
--- /dev/null
+++ b/drivers/video/pxa168fb.c
@@ -0,0 +1,803 @@
+/*
+ * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ * 2009-02-16 adapted from original version for PXA168/910
+ * Jun Nie <njun@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <video/pxa168fb.h>
+
+#include "pxa168fb.h"
+
+#define DEFAULT_REFRESH 60 /* Hz */
+
+static int determine_best_pix_fmt(struct fb_var_screeninfo *var)
+{
+ /*
+ * Pseudocolor mode?
+ */
+ if (var->bits_per_pixel == 8)
+ return PIX_FMT_PSEUDOCOLOR;
+
+ /*
+ * Check for 565/1555.
+ */
+ if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
+ var->green.length <= 6 && var->blue.length <= 5) {
+ if (var->transp.length == 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB565;
+ else
+ return PIX_FMT_BGR565;
+ }
+
+ if (var->transp.length == 1 && var->green.length <= 5) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB1555;
+ else
+ return PIX_FMT_BGR1555;
+ }
+
+ /* fall through */
+ }
+
+ /*
+ * Check for 888/A888.
+ */
+ if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
+ var->green.length <= 8 && var->blue.length <= 8) {
+ if (var->bits_per_pixel == 24 && var->transp.length == 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB888PACK;
+ else
+ return PIX_FMT_BGR888PACK;
+ }
+
+ if (var->bits_per_pixel == 32 && var->transp.length == 8) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGBA888;
+ else
+ return PIX_FMT_BGRA888;
+ } else {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB888UNPACK;
+ else
+ return PIX_FMT_BGR888UNPACK;
+ }
+
+ /* fall through */
+ }
+
+ return -EINVAL;
+}
+
+static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
+{
+ switch (pix_fmt) {
+ case PIX_FMT_RGB565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 11; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 0; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_BGR565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 11; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_RGB1555:
+ var->bits_per_pixel = 16;
+ var->red.offset = 10; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 5;
+ var->blue.offset = 0; var->blue.length = 5;
+ var->transp.offset = 15; var->transp.length = 1;
+ break;
+ case PIX_FMT_BGR1555:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 5;
+ var->blue.offset = 10; var->blue.length = 5;
+ var->transp.offset = 15; var->transp.length = 1;
+ break;
+ case PIX_FMT_RGB888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_BGR888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_RGBA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIX_FMT_BGRA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIX_FMT_PSEUDOCOLOR:
+ var->bits_per_pixel = 8;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ }
+}
+
+static void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
+ struct fb_videomode *mode, int pix_fmt, int ystretch)
+{
+ struct fb_info *info = fbi->info;
+
+ set_pix_fmt(var, pix_fmt);
+
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = max(var->xres, var->xres_virtual);
+ if (ystretch)
+ var->yres_virtual = info->fix.smem_len /
+ (var->xres_virtual * (var->bits_per_pixel >> 3));
+ else
+ var->yres_virtual = max(var->yres, var->yres_virtual);
+ var->grayscale = 0;
+ var->accel_flags = FB_ACCEL_NONE;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->rotate = FB_ROTATE_UR;
+}
+
+static int pxa168fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ int pix_fmt;
+
+ /*
+ * Determine which pixel format we're going to use.
+ */
+ pix_fmt = determine_best_pix_fmt(var);
+ if (pix_fmt < 0)
+ return pix_fmt;
+ set_pix_fmt(var, pix_fmt);
+ fbi->pix_fmt = pix_fmt;
+
+ /*
+ * Basic geometry sanity checks.
+ */
+ if (var->xoffset + var->xres > var->xres_virtual)
+ return -EINVAL;
+ if (var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+ if (var->xres + var->right_margin +
+ var->hsync_len + var->left_margin > 2048)
+ return -EINVAL;
+ if (var->yres + var->lower_margin +
+ var->vsync_len + var->upper_margin > 2048)
+ return -EINVAL;
+
+ /*
+ * Check size of framebuffer.
+ */
+ if (var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3) > info->fix.smem_len)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * The hardware clock divider has an integer and a fractional
+ * stage:
+ *
+ * clk2 = clk_in / integer_divider
+ * clk_out = clk2 * (1 - (fractional_divider >> 12))
+ *
+ * Calculate integer and fractional divider for given clk_in
+ * and clk_out.
+ */
+static void set_clock_divider(struct pxa168fb_info *fbi,
+ const struct fb_videomode *m)
+{
+ int divider_int;
+ int needed_pixclk;
+ u64 div_result;
+ u32 x = 0;
+
+ /*
+ * Notice: The field pixclock is used by linux fb
+ * is in pixel second. E.g. struct fb_videomode &
+ * struct fb_var_screeninfo
+ */
+
+ /*
+ * Check input values.
+ */
+ if (!m || !m->pixclock || !m->refresh) {
+ dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
+ return;
+ }
+
+ /*
+ * Using PLL/AXI clock.
+ */
+ x = 0x80000000;
+
+ /*
+ * Calc divider according to refresh rate.
+ */
+ div_result = 1000000000000ll;
+ do_div(div_result, m->pixclock);
+ needed_pixclk = (u32)div_result;
+
+ divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
+
+ /* check whether divisor is too small. */
+ if (divider_int < 2) {
+ dev_warn(fbi->dev, "Warning: clock source is too slow."
+ "Try smaller resolution\n");
+ divider_int = 2;
+ }
+
+ /*
+ * Set setting to reg.
+ */
+ x |= divider_int;
+ writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
+}
+
+static void set_dma_control0(struct pxa168fb_info *fbi)
+{
+ u32 x;
+
+ /*
+ * Set bit to enable graphics DMA.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
+ x |= fbi->active ? 0x00000100 : 0;
+ fbi->active = 0;
+
+ /*
+ * If we are in a pseudo-color mode, we need to enable
+ * palette lookup.
+ */
+ if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
+ x |= 0x10000000;
+
+ /*
+ * Configure hardware pixel format.
+ */
+ x &= ~(0xF << 16);
+ x |= (fbi->pix_fmt >> 1) << 16;
+
+ /*
+ * Check red and blue pixel swap.
+ * 1. source data swap
+ * 2. panel output data swap
+ */
+ x &= ~(1 << 12);
+ x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
+
+ writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
+}
+
+static void set_dma_control1(struct pxa168fb_info *fbi, int sync)
+{
+ u32 x;
+
+ /*
+ * Configure default bits: vsync triggers DMA, gated clock
+ * enable, power save enable, configure alpha registers to
+ * display 100% graphics, and set pixel command.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
+ x |= 0x2032ff81;
+
+ /*
+ * We trigger DMA on the falling edge of vsync if vsync is
+ * active low, or on the rising edge if vsync is active high.
+ */
+ if (!(sync & FB_SYNC_VERT_HIGH_ACT))
+ x |= 0x08000000;
+
+ writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
+}
+
+static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int pixel_offset;
+ unsigned long addr;
+
+ pixel_offset = (yoffset * var->xres_virtual) + xoffset;
+
+ addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
+ writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
+}
+
+static void set_dumb_panel_control(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
+ u32 x;
+
+ /*
+ * Preserve enable flag.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
+
+ x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
+ x |= mi->gpio_output_data << 20;
+ x |= mi->gpio_output_mask << 12;
+ x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
+ x |= mi->invert_composite_blank ? 0x00000040 : 0;
+ x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
+ x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
+ x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
+ x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
+ x |= mi->invert_pixclock ? 0x00000002 : 0;
+
+ writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+}
+
+static void set_dumb_screen_dimensions(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *v = &info->var;
+ int x;
+ int y;
+
+ x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
+ y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
+
+ writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
+}
+
+static int pxa168fb_set_par(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode mode;
+ u32 x;
+ struct pxa168fb_mach_info *mi;
+
+ mi = fbi->dev->platform_data;
+
+ /*
+ * Set additional mode info.
+ */
+ if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ info->fix.ypanstep = var->yres;
+
+ /*
+ * Disable panel output while we setup the display.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
+ writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+
+ /*
+ * Configure global panel parameters.
+ */
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_V_H_ACTIVE);
+
+ /*
+ * convet var to video mode
+ */
+ fb_var_to_videomode(&mode, &info->var);
+
+ /* Calculate clock divisor. */
+ set_clock_divider(fbi, &mode);
+
+ /* Configure dma ctrl regs. */
+ set_dma_control0(fbi);
+ set_dma_control1(fbi, info->var.sync);
+
+ /*
+ * Configure graphics DMA parameters.
+ */
+ x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
+ x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
+ writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
+
+ /*
+ * Configure dumb panel ctrl regs & timings.
+ */
+ set_dumb_panel_control(info);
+ set_dumb_screen_dimensions(info);
+
+ writel((var->left_margin << 16) | var->right_margin,
+ fbi->reg_base + LCD_SPU_H_PORCH);
+ writel((var->upper_margin << 16) | var->lower_margin,
+ fbi->reg_base + LCD_SPU_V_PORCH);
+
+ /*
+ * Re-enable panel output.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
+ writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+
+ return 0;
+}
+
+static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
+{
+ return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
+}
+
+static u32 to_rgb(u16 red, u16 green, u16 blue)
+{
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ return (red << 16) | (green << 8) | blue;
+}
+
+static int
+pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ unsigned int blue, unsigned int trans, struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ u32 val;
+
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue , &info->var.blue);
+ fbi->pseudo_palette[regno] = val;
+ }
+
+ if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
+ val = to_rgb(red, green, blue);
+ writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
+ writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
+ }
+
+ return 0;
+}
+
+static int pxa168fb_blank(int blank, struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+
+ fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
+ set_dumb_panel_control(info);
+
+ return 0;
+}
+
+static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ set_graphics_start(info, var->xoffset, var->yoffset);
+
+ return 0;
+}
+
+static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
+{
+ struct pxa168fb_info *fbi = dev_id;
+ u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
+
+ if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
+
+ writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
+ fbi->reg_base + SPU_IRQ_ISR);
+
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static struct fb_ops pxa168fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = pxa168fb_check_var,
+ .fb_set_par = pxa168fb_set_par,
+ .fb_setcolreg = pxa168fb_setcolreg,
+ .fb_blank = pxa168fb_blank,
+ .fb_pan_display = pxa168fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __init pxa168fb_init_mode(struct fb_info *info,
+ struct pxa168fb_mach_info *mi)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int ret = 0;
+ u32 total_w, total_h, refresh;
+ u64 div_result;
+ const struct fb_videomode *m;
+
+ /*
+ * Set default value
+ */
+ refresh = DEFAULT_REFRESH;
+
+ /* try to find best video mode. */
+ m = fb_find_best_mode(&info->var, &info->modelist);
+ if (m)
+ fb_videomode_to_var(&info->var, m);
+
+ /* Init settings. */
+ var->xres_virtual = var->xres;
+ var->yres_virtual = info->fix.smem_len /
+ (var->xres_virtual * (var->bits_per_pixel >> 3));
+ dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
+ var->xres, var->yres);
+
+ /* correct pixclock. */
+ total_w = var->xres + var->left_margin + var->right_margin +
+ var->hsync_len;
+ total_h = var->yres + var->upper_margin + var->lower_margin +
+ var->vsync_len;
+
+ div_result = 1000000000000ll;
+ do_div(div_result, total_w * total_h * refresh);
+ var->pixclock = (u32)div_result;
+
+ return ret;
+}
+
+static int __init pxa168fb_probe(struct platform_device *pdev)
+{
+ struct pxa168fb_mach_info *mi;
+ struct fb_info *info = 0;
+ struct pxa168fb_info *fbi = 0;
+ struct resource *res;
+ struct clk *clk;
+ int irq, ret;
+
+ mi = pdev->dev.platform_data;
+ if (mi == NULL) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ clk = clk_get(&pdev->dev, "LCDCLK");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get LCDCLK");
+ return PTR_ERR(clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no IO memory defined\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no IRQ defined\n");
+ return -ENOENT;
+ }
+
+ info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
+ if (info == NULL) {
+ clk_put(clk);
+ return -ENOMEM;
+ }
+
+ /* Initialize private data */
+ fbi = info->par;
+ fbi->info = info;
+ fbi->clk = clk;
+ fbi->dev = info->dev = &pdev->dev;
+ fbi->panel_rbswap = mi->panel_rbswap;
+ fbi->is_blanked = 0;
+ fbi->active = mi->active;
+
+ /*
+ * Initialise static fb parameters.
+ */
+ info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
+ FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
+ info->node = -1;
+ strlcpy(info->fix.id, mi->id, 16);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 0;
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+ info->fix.mmio_start = res->start;
+ info->fix.mmio_len = res->end - res->start + 1;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fbops = &pxa168fb_ops;
+ info->pseudo_palette = fbi->pseudo_palette;
+
+ /*
+ * Map LCD controller registers.
+ */
+ fbi->reg_base = ioremap_nocache(res->start, res->end - res->start);
+ if (fbi->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ /*
+ * Allocate framebuffer memory.
+ */
+ info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
+
+ info->screen_base = dma_alloc_writecombine(fbi->dev, info->fix.smem_len,
+ &fbi->fb_start_dma, GFP_KERNEL);
+ if (info->screen_base == NULL) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
+
+ /*
+ * Set video mode according to platform data.
+ */
+ set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
+
+ fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
+
+ /*
+ * init video mode data.
+ */
+ pxa168fb_init_mode(info, mi);
+
+ ret = pxa168fb_check_var(&info->var, info);
+ if (ret)
+ goto failed_free_fbmem;
+
+ /*
+ * Fill in sane defaults.
+ */
+ ret = pxa168fb_check_var(&info->var, info);
+ if (ret)
+ goto failed;
+
+ /*
+ * enable controller clock
+ */
+ clk_enable(fbi->clk);
+
+ pxa168fb_set_par(info);
+
+ /*
+ * Configure default register values.
+ */
+ writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
+ writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
+ writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
+ writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+ writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
+ writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
+ fbi->reg_base + LCD_SPU_SRAM_PARA1);
+
+ /*
+ * Allocate color map.
+ */
+ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
+ ret = -ENOMEM;
+ goto failed_free_clk;
+ }
+
+ /*
+ * Register irq handler.
+ */
+ ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED,
+ info->fix.id, fbi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to request IRQ\n");
+ ret = -ENXIO;
+ goto failed_free_cmap;
+ }
+
+ /*
+ * Enable GFX interrupt
+ */
+ writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
+
+ /*
+ * Register framebuffer.
+ */
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
+ ret = -ENXIO;
+ goto failed_free_irq;
+ }
+
+ platform_set_drvdata(pdev, fbi);
+ return 0;
+
+failed_free_irq:
+ free_irq(irq, fbi);
+failed_free_cmap:
+ fb_dealloc_cmap(&info->cmap);
+failed_free_clk:
+ clk_disable(fbi->clk);
+failed_free_fbmem:
+ dma_free_coherent(fbi->dev, info->fix.smem_len,
+ info->screen_base, fbi->fb_start_dma);
+failed:
+ kfree(info);
+ clk_put(clk);
+
+ dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
+ return ret;
+}
+
+static struct platform_driver pxa168fb_driver = {
+ .driver = {
+ .name = "pxa168-fb",
+ .owner = THIS_MODULE,
+ },
+ .probe = pxa168fb_probe,
+};
+
+static int __devinit pxa168fb_init(void)
+{
+ return platform_driver_register(&pxa168fb_driver);
+}
+module_init(pxa168fb_init);
+
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
+ "Green Wan <gwan@marvell.com>");
+MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/pxa168fb.h b/drivers/video/pxa168fb.h
new file mode 100644
index 000000000000..eee09279c524
--- /dev/null
+++ b/drivers/video/pxa168fb.h
@@ -0,0 +1,558 @@
+#ifndef __PXA168FB_H__
+#define __PXA168FB_H__
+
+/* ------------< LCD register >------------ */
+/* Video Frame 0&1 start address registers */
+#define LCD_SPU_DMA_START_ADDR_Y0 0x00C0
+#define LCD_SPU_DMA_START_ADDR_U0 0x00C4
+#define LCD_SPU_DMA_START_ADDR_V0 0x00C8
+#define LCD_CFG_DMA_START_ADDR_0 0x00CC /* Cmd address */
+#define LCD_SPU_DMA_START_ADDR_Y1 0x00D0
+#define LCD_SPU_DMA_START_ADDR_U1 0x00D4
+#define LCD_SPU_DMA_START_ADDR_V1 0x00D8
+#define LCD_CFG_DMA_START_ADDR_1 0x00DC /* Cmd address */
+
+/* YC & UV Pitch */
+#define LCD_SPU_DMA_PITCH_YC 0x00E0
+#define SPU_DMA_PITCH_C(c) ((c) << 16)
+#define SPU_DMA_PITCH_Y(y) (y)
+#define LCD_SPU_DMA_PITCH_UV 0x00E4
+#define SPU_DMA_PITCH_V(v) ((v) << 16)
+#define SPU_DMA_PITCH_U(u) (u)
+
+/* Video Starting Point on Screen Register */
+#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00E8
+#define CFG_DMA_OVSA_VLN(y) ((y) << 16) /* 0~0xfff */
+#define CFG_DMA_OVSA_HPXL(x) (x) /* 0~0xfff */
+
+/* Video Size Register */
+#define LCD_SPU_DMA_HPXL_VLN 0x00EC
+#define CFG_DMA_VLN(y) ((y) << 16)
+#define CFG_DMA_HPXL(x) (x)
+
+/* Video Size After zooming Register */
+#define LCD_SPU_DZM_HPXL_VLN 0x00F0
+#define CFG_DZM_VLN(y) ((y) << 16)
+#define CFG_DZM_HPXL(x) (x)
+
+/* Graphic Frame 0&1 Starting Address Register */
+#define LCD_CFG_GRA_START_ADDR0 0x00F4
+#define LCD_CFG_GRA_START_ADDR1 0x00F8
+
+/* Graphic Frame Pitch */
+#define LCD_CFG_GRA_PITCH 0x00FC
+
+/* Graphic Starting Point on Screen Register */
+#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100
+#define CFG_GRA_OVSA_VLN(y) ((y) << 16)
+#define CFG_GRA_OVSA_HPXL(x) (x)
+
+/* Graphic Size Register */
+#define LCD_SPU_GRA_HPXL_VLN 0x0104
+#define CFG_GRA_VLN(y) ((y) << 16)
+#define CFG_GRA_HPXL(x) (x)
+
+/* Graphic Size after Zooming Register */
+#define LCD_SPU_GZM_HPXL_VLN 0x0108
+#define CFG_GZM_VLN(y) ((y) << 16)
+#define CFG_GZM_HPXL(x) (x)
+
+/* HW Cursor Starting Point on Screen Register */
+#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010C
+#define CFG_HWC_OVSA_VLN(y) ((y) << 16)
+#define CFG_HWC_OVSA_HPXL(x) (x)
+
+/* HW Cursor Size */
+#define LCD_SPU_HWC_HPXL_VLN 0x0110
+#define CFG_HWC_VLN(y) ((y) << 16)
+#define CFG_HWC_HPXL(x) (x)
+
+/* Total Screen Size Register */
+#define LCD_SPUT_V_H_TOTAL 0x0114
+#define CFG_V_TOTAL(y) ((y) << 16)
+#define CFG_H_TOTAL(x) (x)
+
+/* Total Screen Active Size Register */
+#define LCD_SPU_V_H_ACTIVE 0x0118
+#define CFG_V_ACTIVE(y) ((y) << 16)
+#define CFG_H_ACTIVE(x) (x)
+
+/* Screen H&V Porch Register */
+#define LCD_SPU_H_PORCH 0x011C
+#define CFG_H_BACK_PORCH(b) ((b) << 16)
+#define CFG_H_FRONT_PORCH(f) (f)
+#define LCD_SPU_V_PORCH 0x0120
+#define CFG_V_BACK_PORCH(b) ((b) << 16)
+#define CFG_V_FRONT_PORCH(f) (f)
+
+/* Screen Blank Color Register */
+#define LCD_SPU_BLANKCOLOR 0x0124
+#define CFG_BLANKCOLOR_MASK 0x00FFFFFF
+#define CFG_BLANKCOLOR_R_MASK 0x000000FF
+#define CFG_BLANKCOLOR_G_MASK 0x0000FF00
+#define CFG_BLANKCOLOR_B_MASK 0x00FF0000
+
+/* HW Cursor Color 1&2 Register */
+#define LCD_SPU_ALPHA_COLOR1 0x0128
+#define CFG_HWC_COLOR1 0x00FFFFFF
+#define CFG_HWC_COLOR1_R(red) ((red) << 16)
+#define CFG_HWC_COLOR1_G(green) ((green) << 8)
+#define CFG_HWC_COLOR1_B(blue) (blue)
+#define CFG_HWC_COLOR1_R_MASK 0x000000FF
+#define CFG_HWC_COLOR1_G_MASK 0x0000FF00
+#define CFG_HWC_COLOR1_B_MASK 0x00FF0000
+#define LCD_SPU_ALPHA_COLOR2 0x012C
+#define CFG_HWC_COLOR2 0x00FFFFFF
+#define CFG_HWC_COLOR2_R_MASK 0x000000FF
+#define CFG_HWC_COLOR2_G_MASK 0x0000FF00
+#define CFG_HWC_COLOR2_B_MASK 0x00FF0000
+
+/* Video YUV Color Key Control */
+#define LCD_SPU_COLORKEY_Y 0x0130
+#define CFG_CKEY_Y2(y2) ((y2) << 24)
+#define CFG_CKEY_Y2_MASK 0xFF000000
+#define CFG_CKEY_Y1(y1) ((y1) << 16)
+#define CFG_CKEY_Y1_MASK 0x00FF0000
+#define CFG_CKEY_Y(y) ((y) << 8)
+#define CFG_CKEY_Y_MASK 0x0000FF00
+#define CFG_ALPHA_Y(y) (y)
+#define CFG_ALPHA_Y_MASK 0x000000FF
+#define LCD_SPU_COLORKEY_U 0x0134
+#define CFG_CKEY_U2(u2) ((u2) << 24)
+#define CFG_CKEY_U2_MASK 0xFF000000
+#define CFG_CKEY_U1(u1) ((u1) << 16)
+#define CFG_CKEY_U1_MASK 0x00FF0000
+#define CFG_CKEY_U(u) ((u) << 8)
+#define CFG_CKEY_U_MASK 0x0000FF00
+#define CFG_ALPHA_U(u) (u)
+#define CFG_ALPHA_U_MASK 0x000000FF
+#define LCD_SPU_COLORKEY_V 0x0138
+#define CFG_CKEY_V2(v2) ((v2) << 24)
+#define CFG_CKEY_V2_MASK 0xFF000000
+#define CFG_CKEY_V1(v1) ((v1) << 16)
+#define CFG_CKEY_V1_MASK 0x00FF0000
+#define CFG_CKEY_V(v) ((v) << 8)
+#define CFG_CKEY_V_MASK 0x0000FF00
+#define CFG_ALPHA_V(v) (v)
+#define CFG_ALPHA_V_MASK 0x000000FF
+
+/* SPI Read Data Register */
+#define LCD_SPU_SPI_RXDATA 0x0140
+
+/* Smart Panel Read Data Register */
+#define LCD_SPU_ISA_RSDATA 0x0144
+#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000FF
+#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000FF00
+#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00FF0000
+#define ISA_RXDATA_16BIT_4_DATA_MASK 0xFF000000
+#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00FFFFFF
+
+/* HWC SRAM Read Data Register */
+#define LCD_SPU_HWC_RDDAT 0x0158
+
+/* Gamma Table SRAM Read Data Register */
+#define LCD_SPU_GAMMA_RDDAT 0x015c
+#define CFG_GAMMA_RDDAT_MASK 0x000000FF
+
+/* Palette Table SRAM Read Data Register */
+#define LCD_SPU_PALETTE_RDDAT 0x0160
+#define CFG_PALETTE_RDDAT_MASK 0x00FFFFFF
+
+/* I/O Pads Input Read Only Register */
+#define LCD_SPU_IOPAD_IN 0x0178
+#define CFG_IOPAD_IN_MASK 0x0FFFFFFF
+
+/* Reserved Read Only Registers */
+#define LCD_CFG_RDREG5F 0x017C
+#define IRE_FRAME_CNT_MASK 0x000000C0
+#define IPE_FRAME_CNT_MASK 0x00000030
+#define GRA_FRAME_CNT_MASK 0x0000000C /* Graphic */
+#define DMA_FRAME_CNT_MASK 0x00000003 /* Video */
+
+/* SPI Control Register. */
+#define LCD_SPU_SPI_CTRL 0x0180
+#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */
+#define CFG_SCLKCNT_MASK 0xFF000000
+#define CFG_RXBITS(rx) ((rx) << 16) /* 0x1F~0x1 */
+#define CFG_RXBITS_MASK 0x00FF0000
+#define CFG_TXBITS(tx) ((tx) << 8) /* 0x1F~0x1 */
+#define CFG_TXBITS_MASK 0x0000FF00
+#define CFG_CLKINV(clk) ((clk) << 7)
+#define CFG_CLKINV_MASK 0x00000080
+#define CFG_KEEPXFER(transfer) ((transfer) << 6)
+#define CFG_KEEPXFER_MASK 0x00000040
+#define CFG_RXBITSTO0(rx) ((rx) << 5)
+#define CFG_RXBITSTO0_MASK 0x00000020
+#define CFG_TXBITSTO0(tx) ((tx) << 4)
+#define CFG_TXBITSTO0_MASK 0x00000010
+#define CFG_SPI_ENA(spi) ((spi) << 3)
+#define CFG_SPI_ENA_MASK 0x00000008
+#define CFG_SPI_SEL(spi) ((spi) << 2)
+#define CFG_SPI_SEL_MASK 0x00000004
+#define CFG_SPI_3W4WB(wire) ((wire) << 1)
+#define CFG_SPI_3W4WB_MASK 0x00000002
+#define CFG_SPI_START(start) (start)
+#define CFG_SPI_START_MASK 0x00000001
+
+/* SPI Tx Data Register */
+#define LCD_SPU_SPI_TXDATA 0x0184
+
+/*
+ 1. Smart Pannel 8-bit Bus Control Register.
+ 2. AHB Slave Path Data Port Register
+*/
+#define LCD_SPU_SMPN_CTRL 0x0188
+
+/* DMA Control 0 Register */
+#define LCD_SPU_DMA_CTRL0 0x0190
+#define CFG_NOBLENDING(nb) ((nb) << 31)
+#define CFG_NOBLENDING_MASK 0x80000000
+#define CFG_GAMMA_ENA(gn) ((gn) << 30)
+#define CFG_GAMMA_ENA_MASK 0x40000000
+#define CFG_CBSH_ENA(cn) ((cn) << 29)
+#define CFG_CBSH_ENA_MASK 0x20000000
+#define CFG_PALETTE_ENA(pn) ((pn) << 28)
+#define CFG_PALETTE_ENA_MASK 0x10000000
+#define CFG_ARBFAST_ENA(an) ((an) << 27)
+#define CFG_ARBFAST_ENA_MASK 0x08000000
+#define CFG_HWC_1BITMOD(mode) ((mode) << 26)
+#define CFG_HWC_1BITMOD_MASK 0x04000000
+#define CFG_HWC_1BITENA(mn) ((mn) << 25)
+#define CFG_HWC_1BITENA_MASK 0x02000000
+#define CFG_HWC_ENA(cn) ((cn) << 24)
+#define CFG_HWC_ENA_MASK 0x01000000
+#define CFG_DMAFORMAT(dmaformat) ((dmaformat) << 20)
+#define CFG_DMAFORMAT_MASK 0x00F00000
+#define CFG_GRAFORMAT(graformat) ((graformat) << 16)
+#define CFG_GRAFORMAT_MASK 0x000F0000
+/* for graphic part */
+#define CFG_GRA_FTOGGLE(toggle) ((toggle) << 15)
+#define CFG_GRA_FTOGGLE_MASK 0x00008000
+#define CFG_GRA_HSMOOTH(smooth) ((smooth) << 14)
+#define CFG_GRA_HSMOOTH_MASK 0x00004000
+#define CFG_GRA_TSTMODE(test) ((test) << 13)
+#define CFG_GRA_TSTMODE_MASK 0x00002000
+#define CFG_GRA_SWAPRB(swap) ((swap) << 12)
+#define CFG_GRA_SWAPRB_MASK 0x00001000
+#define CFG_GRA_SWAPUV(swap) ((swap) << 11)
+#define CFG_GRA_SWAPUV_MASK 0x00000800
+#define CFG_GRA_SWAPYU(swap) ((swap) << 10)
+#define CFG_GRA_SWAPYU_MASK 0x00000400
+#define CFG_YUV2RGB_GRA(cvrt) ((cvrt) << 9)
+#define CFG_YUV2RGB_GRA_MASK 0x00000200
+#define CFG_GRA_ENA(gra) ((gra) << 8)
+#define CFG_GRA_ENA_MASK 0x00000100
+/* for video part */
+#define CFG_DMA_FTOGGLE(toggle) ((toggle) << 7)
+#define CFG_DMA_FTOGGLE_MASK 0x00000080
+#define CFG_DMA_HSMOOTH(smooth) ((smooth) << 6)
+#define CFG_DMA_HSMOOTH_MASK 0x00000040
+#define CFG_DMA_TSTMODE(test) ((test) << 5)
+#define CFG_DMA_TSTMODE_MASK 0x00000020
+#define CFG_DMA_SWAPRB(swap) ((swap) << 4)
+#define CFG_DMA_SWAPRB_MASK 0x00000010
+#define CFG_DMA_SWAPUV(swap) ((swap) << 3)
+#define CFG_DMA_SWAPUV_MASK 0x00000008
+#define CFG_DMA_SWAPYU(swap) ((swap) << 2)
+#define CFG_DMA_SWAPYU_MASK 0x00000004
+#define CFG_DMA_SWAP_MASK 0x0000001C
+#define CFG_YUV2RGB_DMA(cvrt) ((cvrt) << 1)
+#define CFG_YUV2RGB_DMA_MASK 0x00000002
+#define CFG_DMA_ENA(video) (video)
+#define CFG_DMA_ENA_MASK 0x00000001
+
+/* DMA Control 1 Register */
+#define LCD_SPU_DMA_CTRL1 0x0194
+#define CFG_FRAME_TRIG(trig) ((trig) << 31)
+#define CFG_FRAME_TRIG_MASK 0x80000000
+#define CFG_VSYNC_TRIG(trig) ((trig) << 28)
+#define CFG_VSYNC_TRIG_MASK 0x70000000
+#define CFG_VSYNC_INV(inv) ((inv) << 27)
+#define CFG_VSYNC_INV_MASK 0x08000000
+#define CFG_COLOR_KEY_MODE(cmode) ((cmode) << 24)
+#define CFG_COLOR_KEY_MASK 0x07000000
+#define CFG_CARRY(carry) ((carry) << 23)
+#define CFG_CARRY_MASK 0x00800000
+#define CFG_LNBUF_ENA(lnbuf) ((lnbuf) << 22)
+#define CFG_LNBUF_ENA_MASK 0x00400000
+#define CFG_GATED_ENA(gated) ((gated) << 21)
+#define CFG_GATED_ENA_MASK 0x00200000
+#define CFG_PWRDN_ENA(power) ((power) << 20)
+#define CFG_PWRDN_ENA_MASK 0x00100000
+#define CFG_DSCALE(dscale) ((dscale) << 18)
+#define CFG_DSCALE_MASK 0x000C0000
+#define CFG_ALPHA_MODE(amode) ((amode) << 16)
+#define CFG_ALPHA_MODE_MASK 0x00030000
+#define CFG_ALPHA(alpha) ((alpha) << 8)
+#define CFG_ALPHA_MASK 0x0000FF00
+#define CFG_PXLCMD(pxlcmd) (pxlcmd)
+#define CFG_PXLCMD_MASK 0x000000FF
+
+/* SRAM Control Register */
+#define LCD_SPU_SRAM_CTRL 0x0198
+#define CFG_SRAM_INIT_WR_RD(mode) ((mode) << 14)
+#define CFG_SRAM_INIT_WR_RD_MASK 0x0000C000
+#define CFG_SRAM_ADDR_LCDID(id) ((id) << 8)
+#define CFG_SRAM_ADDR_LCDID_MASK 0x00000F00
+#define CFG_SRAM_ADDR(addr) (addr)
+#define CFG_SRAM_ADDR_MASK 0x000000FF
+
+/* SRAM Write Data Register */
+#define LCD_SPU_SRAM_WRDAT 0x019C
+
+/* SRAM RTC/WTC Control Register */
+#define LCD_SPU_SRAM_PARA0 0x01A0
+
+/* SRAM Power Down Control Register */
+#define LCD_SPU_SRAM_PARA1 0x01A4
+#define CFG_CSB_256x32(hwc) ((hwc) << 15) /* HWC */
+#define CFG_CSB_256x32_MASK 0x00008000
+#define CFG_CSB_256x24(palette) ((palette) << 14) /* Palette */
+#define CFG_CSB_256x24_MASK 0x00004000
+#define CFG_CSB_256x8(gamma) ((gamma) << 13) /* Gamma */
+#define CFG_CSB_256x8_MASK 0x00002000
+#define CFG_PDWN256x32(pdwn) ((pdwn) << 7) /* HWC */
+#define CFG_PDWN256x32_MASK 0x00000080
+#define CFG_PDWN256x24(pdwn) ((pdwn) << 6) /* Palette */
+#define CFG_PDWN256x24_MASK 0x00000040
+#define CFG_PDWN256x8(pdwn) ((pdwn) << 5) /* Gamma */
+#define CFG_PDWN256x8_MASK 0x00000020
+#define CFG_PDWN32x32(pdwn) ((pdwn) << 3)
+#define CFG_PDWN32x32_MASK 0x00000008
+#define CFG_PDWN16x66(pdwn) ((pdwn) << 2)
+#define CFG_PDWN16x66_MASK 0x00000004
+#define CFG_PDWN32x66(pdwn) ((pdwn) << 1)
+#define CFG_PDWN32x66_MASK 0x00000002
+#define CFG_PDWN64x66(pdwn) (pdwn)
+#define CFG_PDWN64x66_MASK 0x00000001
+
+/* Smart or Dumb Panel Clock Divider */
+#define LCD_CFG_SCLK_DIV 0x01A8
+#define SCLK_SOURCE_SELECT(src) ((src) << 31)
+#define SCLK_SOURCE_SELECT_MASK 0x80000000
+#define CLK_FRACDIV(frac) ((frac) << 16)
+#define CLK_FRACDIV_MASK 0x0FFF0000
+#define CLK_INT_DIV(div) (div)
+#define CLK_INT_DIV_MASK 0x0000FFFF
+
+/* Video Contrast Register */
+#define LCD_SPU_CONTRAST 0x01AC
+#define CFG_BRIGHTNESS(bright) ((bright) << 16)
+#define CFG_BRIGHTNESS_MASK 0xFFFF0000
+#define CFG_CONTRAST(contrast) (contrast)
+#define CFG_CONTRAST_MASK 0x0000FFFF
+
+/* Video Saturation Register */
+#define LCD_SPU_SATURATION 0x01B0
+#define CFG_C_MULTS(mult) ((mult) << 16)
+#define CFG_C_MULTS_MASK 0xFFFF0000
+#define CFG_SATURATION(sat) (sat)
+#define CFG_SATURATION_MASK 0x0000FFFF
+
+/* Video Hue Adjust Register */
+#define LCD_SPU_CBSH_HUE 0x01B4
+#define CFG_SIN0(sin0) ((sin0) << 16)
+#define CFG_SIN0_MASK 0xFFFF0000
+#define CFG_COS0(con0) (con0)
+#define CFG_COS0_MASK 0x0000FFFF
+
+/* Dump LCD Panel Control Register */
+#define LCD_SPU_DUMB_CTRL 0x01B8
+#define CFG_DUMBMODE(mode) ((mode) << 28)
+#define CFG_DUMBMODE_MASK 0xF0000000
+#define CFG_LCDGPIO_O(data) ((data) << 20)
+#define CFG_LCDGPIO_O_MASK 0x0FF00000
+#define CFG_LCDGPIO_ENA(gpio) ((gpio) << 12)
+#define CFG_LCDGPIO_ENA_MASK 0x000FF000
+#define CFG_BIAS_OUT(bias) ((bias) << 8)
+#define CFG_BIAS_OUT_MASK 0x00000100
+#define CFG_REVERSE_RGB(rRGB) ((rRGB) << 7)
+#define CFG_REVERSE_RGB_MASK 0x00000080
+#define CFG_INV_COMPBLANK(blank) ((blank) << 6)
+#define CFG_INV_COMPBLANK_MASK 0x00000040
+#define CFG_INV_COMPSYNC(sync) ((sync) << 5)
+#define CFG_INV_COMPSYNC_MASK 0x00000020
+#define CFG_INV_HENA(hena) ((hena) << 4)
+#define CFG_INV_HENA_MASK 0x00000010
+#define CFG_INV_VSYNC(vsync) ((vsync) << 3)
+#define CFG_INV_VSYNC_MASK 0x00000008
+#define CFG_INV_HSYNC(hsync) ((hsync) << 2)
+#define CFG_INV_HSYNC_MASK 0x00000004
+#define CFG_INV_PCLK(pclk) ((pclk) << 1)
+#define CFG_INV_PCLK_MASK 0x00000002
+#define CFG_DUMB_ENA(dumb) (dumb)
+#define CFG_DUMB_ENA_MASK 0x00000001
+
+/* LCD I/O Pads Control Register */
+#define SPU_IOPAD_CONTROL 0x01BC
+#define CFG_GRA_VM_ENA(vm) ((vm) << 15) /* gfx */
+#define CFG_GRA_VM_ENA_MASK 0x00008000
+#define CFG_DMA_VM_ENA(vm) ((vm) << 13) /* video */
+#define CFG_DMA_VM_ENA_MASK 0x00002000
+#define CFG_CMD_VM_ENA(vm) ((vm) << 13)
+#define CFG_CMD_VM_ENA_MASK 0x00000800
+#define CFG_CSC(csc) ((csc) << 8) /* csc */
+#define CFG_CSC_MASK 0x00000300
+#define CFG_AXICTRL(axi) ((axi) << 4)
+#define CFG_AXICTRL_MASK 0x000000F0
+#define CFG_IOPADMODE(iopad) (iopad)
+#define CFG_IOPADMODE_MASK 0x0000000F
+
+/* LCD Interrupt Control Register */
+#define SPU_IRQ_ENA 0x01C0
+#define DMA_FRAME_IRQ0_ENA(irq) ((irq) << 31)
+#define DMA_FRAME_IRQ0_ENA_MASK 0x80000000
+#define DMA_FRAME_IRQ1_ENA(irq) ((irq) << 30)
+#define DMA_FRAME_IRQ1_ENA_MASK 0x40000000
+#define DMA_FF_UNDERFLOW_ENA(ff) ((ff) << 29)
+#define DMA_FF_UNDERFLOW_ENA_MASK 0x20000000
+#define GRA_FRAME_IRQ0_ENA(irq) ((irq) << 27)
+#define GRA_FRAME_IRQ0_ENA_MASK 0x08000000
+#define GRA_FRAME_IRQ1_ENA(irq) ((irq) << 26)
+#define GRA_FRAME_IRQ1_ENA_MASK 0x04000000
+#define GRA_FF_UNDERFLOW_ENA(ff) ((ff) << 25)
+#define GRA_FF_UNDERFLOW_ENA_MASK 0x02000000
+#define VSYNC_IRQ_ENA(vsync_irq) ((vsync_irq) << 23)
+#define VSYNC_IRQ_ENA_MASK 0x00800000
+#define DUMB_FRAMEDONE_ENA(fdone) ((fdone) << 22)
+#define DUMB_FRAMEDONE_ENA_MASK 0x00400000
+#define TWC_FRAMEDONE_ENA(fdone) ((fdone) << 21)
+#define TWC_FRAMEDONE_ENA_MASK 0x00200000
+#define HWC_FRAMEDONE_ENA(fdone) ((fdone) << 20)
+#define HWC_FRAMEDONE_ENA_MASK 0x00100000
+#define SLV_IRQ_ENA(irq) ((irq) << 19)
+#define SLV_IRQ_ENA_MASK 0x00080000
+#define SPI_IRQ_ENA(irq) ((irq) << 18)
+#define SPI_IRQ_ENA_MASK 0x00040000
+#define PWRDN_IRQ_ENA(irq) ((irq) << 17)
+#define PWRDN_IRQ_ENA_MASK 0x00020000
+#define ERR_IRQ_ENA(irq) ((irq) << 16)
+#define ERR_IRQ_ENA_MASK 0x00010000
+#define CLEAN_SPU_IRQ_ISR(irq) (irq)
+#define CLEAN_SPU_IRQ_ISR_MASK 0x0000FFFF
+
+/* LCD Interrupt Status Register */
+#define SPU_IRQ_ISR 0x01C4
+#define DMA_FRAME_IRQ0(irq) ((irq) << 31)
+#define DMA_FRAME_IRQ0_MASK 0x80000000
+#define DMA_FRAME_IRQ1(irq) ((irq) << 30)
+#define DMA_FRAME_IRQ1_MASK 0x40000000
+#define DMA_FF_UNDERFLOW(ff) ((ff) << 29)
+#define DMA_FF_UNDERFLOW_MASK 0x20000000
+#define GRA_FRAME_IRQ0(irq) ((irq) << 27)
+#define GRA_FRAME_IRQ0_MASK 0x08000000
+#define GRA_FRAME_IRQ1(irq) ((irq) << 26)
+#define GRA_FRAME_IRQ1_MASK 0x04000000
+#define GRA_FF_UNDERFLOW(ff) ((ff) << 25)
+#define GRA_FF_UNDERFLOW_MASK 0x02000000
+#define VSYNC_IRQ(vsync_irq) ((vsync_irq) << 23)
+#define VSYNC_IRQ_MASK 0x00800000
+#define DUMB_FRAMEDONE(fdone) ((fdone) << 22)
+#define DUMB_FRAMEDONE_MASK 0x00400000
+#define TWC_FRAMEDONE(fdone) ((fdone) << 21)
+#define TWC_FRAMEDONE_MASK 0x00200000
+#define HWC_FRAMEDONE(fdone) ((fdone) << 20)
+#define HWC_FRAMEDONE_MASK 0x00100000
+#define SLV_IRQ(irq) ((irq) << 19)
+#define SLV_IRQ_MASK 0x00080000
+#define SPI_IRQ(irq) ((irq) << 18)
+#define SPI_IRQ_MASK 0x00040000
+#define PWRDN_IRQ(irq) ((irq) << 17)
+#define PWRDN_IRQ_MASK 0x00020000
+#define ERR_IRQ(irq) ((irq) << 16)
+#define ERR_IRQ_MASK 0x00010000
+/* read-only */
+#define DMA_FRAME_IRQ0_LEVEL_MASK 0x00008000
+#define DMA_FRAME_IRQ1_LEVEL_MASK 0x00004000
+#define DMA_FRAME_CNT_ISR_MASK 0x00003000
+#define GRA_FRAME_IRQ0_LEVEL_MASK 0x00000800
+#define GRA_FRAME_IRQ1_LEVEL_MASK 0x00000400
+#define GRA_FRAME_CNT_ISR_MASK 0x00000300
+#define VSYNC_IRQ_LEVEL_MASK 0x00000080
+#define DUMB_FRAMEDONE_LEVEL_MASK 0x00000040
+#define TWC_FRAMEDONE_LEVEL_MASK 0x00000020
+#define HWC_FRAMEDONE_LEVEL_MASK 0x00000010
+#define SLV_FF_EMPTY_MASK 0x00000008
+#define DMA_FF_ALLEMPTY_MASK 0x00000004
+#define GRA_FF_ALLEMPTY_MASK 0x00000002
+#define PWRDN_IRQ_LEVEL_MASK 0x00000001
+
+
+/*
+ * defined Video Memory Color format for DMA control 0 register
+ * DMA0 bit[23:20]
+ */
+#define VMODE_RGB565 0x0
+#define VMODE_RGB1555 0x1
+#define VMODE_RGB888PACKED 0x2
+#define VMODE_RGB888UNPACKED 0x3
+#define VMODE_RGBA888 0x4
+#define VMODE_YUV422PACKED 0x5
+#define VMODE_YUV422PLANAR 0x6
+#define VMODE_YUV420PLANAR 0x7
+#define VMODE_SMPNCMD 0x8
+#define VMODE_PALETTE4BIT 0x9
+#define VMODE_PALETTE8BIT 0xa
+#define VMODE_RESERVED 0xb
+
+/*
+ * defined Graphic Memory Color format for DMA control 0 register
+ * DMA0 bit[19:16]
+ */
+#define GMODE_RGB565 0x0
+#define GMODE_RGB1555 0x1
+#define GMODE_RGB888PACKED 0x2
+#define GMODE_RGB888UNPACKED 0x3
+#define GMODE_RGBA888 0x4
+#define GMODE_YUV422PACKED 0x5
+#define GMODE_YUV422PLANAR 0x6
+#define GMODE_YUV420PLANAR 0x7
+#define GMODE_SMPNCMD 0x8
+#define GMODE_PALETTE4BIT 0x9
+#define GMODE_PALETTE8BIT 0xa
+#define GMODE_RESERVED 0xb
+
+/*
+ * define for DMA control 1 register
+ */
+#define DMA1_FRAME_TRIG 31 /* bit location */
+#define DMA1_VSYNC_MODE 28
+#define DMA1_VSYNC_INV 27
+#define DMA1_CKEY 24
+#define DMA1_CARRY 23
+#define DMA1_LNBUF_ENA 22
+#define DMA1_GATED_ENA 21
+#define DMA1_PWRDN_ENA 20
+#define DMA1_DSCALE 18
+#define DMA1_ALPHA_MODE 16
+#define DMA1_ALPHA 08
+#define DMA1_PXLCMD 00
+
+/*
+ * defined for Configure Dumb Mode
+ * DUMB LCD Panel bit[31:28]
+ */
+#define DUMB16_RGB565_0 0x0
+#define DUMB16_RGB565_1 0x1
+#define DUMB18_RGB666_0 0x2
+#define DUMB18_RGB666_1 0x3
+#define DUMB12_RGB444_0 0x4
+#define DUMB12_RGB444_1 0x5
+#define DUMB24_RGB888_0 0x6
+#define DUMB_BLANK 0x7
+
+/*
+ * defined for Configure I/O Pin Allocation Mode
+ * LCD LCD I/O Pads control register bit[3:0]
+ */
+#define IOPAD_DUMB24 0x0
+#define IOPAD_DUMB18SPI 0x1
+#define IOPAD_DUMB18GPIO 0x2
+#define IOPAD_DUMB16SPI 0x3
+#define IOPAD_DUMB16GPIO 0x4
+#define IOPAD_DUMB12 0x5
+#define IOPAD_SMART18SPI 0x6
+#define IOPAD_SMART16SPI 0x7
+#define IOPAD_SMART8BOTH 0x8
+
+#endif /* __PXA168FB_H__ */
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/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/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/w1/Kconfig b/drivers/w1/Kconfig
index 9adbb4f90479..fd2c7bd9dfbe 100644
--- a/drivers/w1/Kconfig
+++ b/drivers/w1/Kconfig
@@ -8,7 +8,7 @@ menuconfig W1
If you want W1 support, you should say Y here.
This W1 support can also be built as a module. If so, the module
- will be called wire.ko.
+ will be called wire.
if W1
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 96d2f8e4c275..3195fb8b7d9a 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -12,7 +12,7 @@ config W1_MASTER_MATROX
using Matrox's G400 GPIO pins.
This support is also available as a module. If so, the module
- will be called matrox_w1.ko.
+ will be called matrox_w1.
config W1_MASTER_DS2490
tristate "DS2490 USB <-> W1 transport layer for 1-wire"
@@ -22,7 +22,7 @@ config W1_MASTER_DS2490
for example DS9490*.
This support is also available as a module. If so, the module
- will be called ds2490.ko.
+ will be called ds2490.
config W1_MASTER_DS2482
tristate "Maxim DS2482 I2C to 1-Wire bridge"
@@ -56,7 +56,7 @@ config W1_MASTER_GPIO
GPIO pins. This driver uses the GPIO API to control the wire.
This support is also available as a module. If so, the module
- will be called w1-gpio.ko.
+ will be called w1-gpio.
config HDQ_MASTER_OMAP
tristate "OMAP HDQ driver"
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5eb8f21da82e..b166f2852a64 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -231,14 +231,14 @@ config DAVINCI_WATCHDOG
NOTE: once enabled, this timer cannot be disabled.
Say N if you are unsure.
-config ORION5X_WATCHDOG
- tristate "Orion5x watchdog"
- depends on ARCH_ORION5X
+config ORION_WATCHDOG
+ tristate "Orion watchdog"
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD
help
Say Y here if to include support for the watchdog timer
- in the Orion5x ARM SoCs.
+ in the Marvell Orion5x and Kirkwood ARM SoCs.
To compile this driver as a module, choose M here: the
- module will be called orion5x_wdt.
+ module will be called orion_wdt.
# AVR32 Architecture
@@ -531,7 +531,7 @@ config SBC8360_WDT
Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com).
To compile this driver as a module, choose M here: the
- module will be called sbc8360.ko.
+ module will be called sbc8360.
Most people will say N.
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 7f8c56b14f58..c3afa14d5be1 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -40,7 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
-obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
+obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
index 96eb2cbe5874..0c9059676690 100644
--- a/drivers/watchdog/iop_wdt.c
+++ b/drivers/watchdog/iop_wdt.c
@@ -192,7 +192,7 @@ static int iop_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_ENABLED, &wdt_status))
state = wdt_disable();
- /* if the timer is not disbaled reload and notify that we are still
+ /* if the timer is not disabled reload and notify that we are still
* going down
*/
if (state != 0) {
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion_wdt.c
index 2cde568e4fb0..2d9fb96a9ee9 100644
--- a/drivers/watchdog/orion5x_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -1,7 +1,7 @@
/*
- * drivers/watchdog/orion5x_wdt.c
+ * drivers/watchdog/orion_wdt.c
*
- * Watchdog driver for Orion5x processors
+ * Watchdog driver for Orion/Kirkwood processors
*
* Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
*
@@ -23,7 +23,7 @@
#include <linux/io.h>
#include <linux/spinlock.h>
#include <mach/bridge-regs.h>
-#include <plat/orion5x_wdt.h>
+#include <plat/orion_wdt.h>
/*
* Watchdog timer block registers.
@@ -43,7 +43,7 @@ static unsigned int wdt_tclk;
static unsigned long wdt_status;
static spinlock_t wdt_lock;
-static void orion5x_wdt_ping(void)
+static void orion_wdt_ping(void)
{
spin_lock(&wdt_lock);
@@ -53,7 +53,7 @@ static void orion5x_wdt_ping(void)
spin_unlock(&wdt_lock);
}
-static void orion5x_wdt_enable(void)
+static void orion_wdt_enable(void)
{
u32 reg;
@@ -73,23 +73,23 @@ static void orion5x_wdt_enable(void)
writel(reg, TIMER_CTRL);
/* Enable reset on watchdog */
- reg = readl(CPU_RESET_MASK);
- reg |= WDT_RESET;
- writel(reg, CPU_RESET_MASK);
+ reg = readl(RSTOUTn_MASK);
+ reg |= WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
spin_unlock(&wdt_lock);
}
-static void orion5x_wdt_disable(void)
+static void orion_wdt_disable(void)
{
u32 reg;
spin_lock(&wdt_lock);
/* Disable reset on watchdog */
- reg = readl(CPU_RESET_MASK);
- reg &= ~WDT_RESET;
- writel(reg, CPU_RESET_MASK);
+ reg = readl(RSTOUTn_MASK);
+ reg &= ~WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
/* Disable watchdog timer */
reg = readl(TIMER_CTRL);
@@ -99,7 +99,7 @@ static void orion5x_wdt_disable(void)
spin_unlock(&wdt_lock);
}
-static int orion5x_wdt_get_timeleft(int *time_left)
+static int orion_wdt_get_timeleft(int *time_left)
{
spin_lock(&wdt_lock);
*time_left = readl(WDT_VAL) / wdt_tclk;
@@ -107,16 +107,16 @@ static int orion5x_wdt_get_timeleft(int *time_left)
return 0;
}
-static int orion5x_wdt_open(struct inode *inode, struct file *file)
+static int orion_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- orion5x_wdt_enable();
+ orion_wdt_enable();
return nonseekable_open(inode, file);
}
-static ssize_t orion5x_wdt_write(struct file *file, const char *data,
+static ssize_t orion_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
if (len) {
@@ -133,18 +133,18 @@ static ssize_t orion5x_wdt_write(struct file *file, const char *data,
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
- orion5x_wdt_ping();
+ orion_wdt_ping();
}
return len;
}
-static int orion5x_wdt_settimeout(int new_time)
+static int orion_wdt_settimeout(int new_time)
{
if ((new_time <= 0) || (new_time > wdt_max_duration))
return -EINVAL;
/* Set new watchdog time to be used when
- * orion5x_wdt_enable() or orion5x_wdt_ping() is called. */
+ * orion_wdt_enable() or orion_wdt_ping() is called. */
heartbeat = new_time;
return 0;
}
@@ -152,10 +152,10 @@ static int orion5x_wdt_settimeout(int new_time)
static const struct watchdog_info ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
- .identity = "Orion5x Watchdog",
+ .identity = "Orion Watchdog",
};
-static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
+static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = -ENOTTY;
@@ -173,7 +173,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_KEEPALIVE:
- orion5x_wdt_ping();
+ orion_wdt_ping();
ret = 0;
break;
@@ -182,11 +182,11 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
if (ret)
break;
- if (orion5x_wdt_settimeout(time)) {
+ if (orion_wdt_settimeout(time)) {
ret = -EINVAL;
break;
}
- orion5x_wdt_ping();
+ orion_wdt_ping();
/* Fall through */
case WDIOC_GETTIMEOUT:
@@ -194,7 +194,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_GETTIMELEFT:
- if (orion5x_wdt_get_timeleft(&time)) {
+ if (orion_wdt_get_timeleft(&time)) {
ret = -EINVAL;
break;
}
@@ -204,10 +204,10 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
return ret;
}
-static int orion5x_wdt_release(struct inode *inode, struct file *file)
+static int orion_wdt_release(struct inode *inode, struct file *file)
{
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- orion5x_wdt_disable();
+ orion_wdt_disable();
else
printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
"timer will not stop\n");
@@ -218,98 +218,98 @@ static int orion5x_wdt_release(struct inode *inode, struct file *file)
}
-static const struct file_operations orion5x_wdt_fops = {
+static const struct file_operations orion_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .write = orion5x_wdt_write,
- .unlocked_ioctl = orion5x_wdt_ioctl,
- .open = orion5x_wdt_open,
- .release = orion5x_wdt_release,
+ .write = orion_wdt_write,
+ .unlocked_ioctl = orion_wdt_ioctl,
+ .open = orion_wdt_open,
+ .release = orion_wdt_release,
};
-static struct miscdevice orion5x_wdt_miscdev = {
+static struct miscdevice orion_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
- .fops = &orion5x_wdt_fops,
+ .fops = &orion_wdt_fops,
};
-static int __devinit orion5x_wdt_probe(struct platform_device *pdev)
+static int __devinit orion_wdt_probe(struct platform_device *pdev)
{
- struct orion5x_wdt_platform_data *pdata = pdev->dev.platform_data;
+ struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
int ret;
if (pdata) {
wdt_tclk = pdata->tclk;
} else {
- printk(KERN_ERR "Orion5x Watchdog misses platform data\n");
+ printk(KERN_ERR "Orion Watchdog misses platform data\n");
return -ENODEV;
}
- if (orion5x_wdt_miscdev.parent)
+ if (orion_wdt_miscdev.parent)
return -EBUSY;
- orion5x_wdt_miscdev.parent = &pdev->dev;
+ orion_wdt_miscdev.parent = &pdev->dev;
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
- if (orion5x_wdt_settimeout(heartbeat))
+ if (orion_wdt_settimeout(heartbeat))
heartbeat = wdt_max_duration;
- ret = misc_register(&orion5x_wdt_miscdev);
+ ret = misc_register(&orion_wdt_miscdev);
if (ret)
return ret;
- printk(KERN_INFO "Orion5x Watchdog Timer: Initial timeout %d sec%s\n",
+ printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n",
heartbeat, nowayout ? ", nowayout" : "");
return 0;
}
-static int __devexit orion5x_wdt_remove(struct platform_device *pdev)
+static int __devexit orion_wdt_remove(struct platform_device *pdev)
{
int ret;
if (test_bit(WDT_IN_USE, &wdt_status)) {
- orion5x_wdt_disable();
+ orion_wdt_disable();
clear_bit(WDT_IN_USE, &wdt_status);
}
- ret = misc_deregister(&orion5x_wdt_miscdev);
+ ret = misc_deregister(&orion_wdt_miscdev);
if (!ret)
- orion5x_wdt_miscdev.parent = NULL;
+ orion_wdt_miscdev.parent = NULL;
return ret;
}
-static void orion5x_wdt_shutdown(struct platform_device *pdev)
+static void orion_wdt_shutdown(struct platform_device *pdev)
{
if (test_bit(WDT_IN_USE, &wdt_status))
- orion5x_wdt_disable();
+ orion_wdt_disable();
}
-static struct platform_driver orion5x_wdt_driver = {
- .probe = orion5x_wdt_probe,
- .remove = __devexit_p(orion5x_wdt_remove),
- .shutdown = orion5x_wdt_shutdown,
+static struct platform_driver orion_wdt_driver = {
+ .probe = orion_wdt_probe,
+ .remove = __devexit_p(orion_wdt_remove),
+ .shutdown = orion_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
- .name = "orion5x_wdt",
+ .name = "orion_wdt",
},
};
-static int __init orion5x_wdt_init(void)
+static int __init orion_wdt_init(void)
{
spin_lock_init(&wdt_lock);
- return platform_driver_register(&orion5x_wdt_driver);
+ return platform_driver_register(&orion_wdt_driver);
}
-static void __exit orion5x_wdt_exit(void)
+static void __exit orion_wdt_exit(void)
{
- platform_driver_unregister(&orion5x_wdt_driver);
+ platform_driver_unregister(&orion_wdt_driver);
}
-module_init(orion5x_wdt_init);
-module_exit(orion5x_wdt_exit);
+module_init(orion_wdt_init);
+module_exit(orion_wdt_exit);
MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
-MODULE_DESCRIPTION("Orion5x Processor Watchdog");
+MODULE_DESCRIPTION("Orion Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
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/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 },
{""},
};