Merge branch 'drm-prime-vmap' of git://people.freedesktop.org/~airlied/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 01:01:18 +0000 (18:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 01:01:18 +0000 (18:01 -0700)
Pull drm prime mmap/vmap code from Dave Airlie:
 "As mentioned previously these are the extra bits of drm that relied on
  the dma-buf pull to work, the first three just stub out the mmap
  interface, and the next set provide vmap export to i915/radeon/nouveau
  and vmap import to udl."

* 'drm-prime-vmap' of git://people.freedesktop.org/~airlied/linux:
  radeon: add radeon prime vmap support.
  nouveau: add vmap support to nouveau prime support
  udl: support vmapping imported dma-bufs
  i915: add dma-buf vmap support for exporting vmapped buffer
  radeon: add stub dma-buf mmap functionality
  nouveau: add stub dma-buf mmap functionality.
  i915: add stub dma-buf mmap callback.

267 files changed:
Documentation/ABI/testing/sysfs-block-rssd
Documentation/ABI/testing/sysfs-bus-fcoe [new file with mode: 0644]
Documentation/feature-removal-schedule.txt
Documentation/power/charger-manager.txt
Documentation/power/power_supply_class.txt
MAINTAINERS
arch/arm/Kconfig
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/lpc32xx.dtsi
arch/arm/boot/dts/phy3250.dts
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
arch/arm/boot/dts/vexpress-v2p-ca5s.dts
arch/arm/boot/dts/vexpress-v2p-ca9.dts
arch/arm/mach-exynos/Kconfig
arch/arm/mach-exynos/Makefile
arch/arm/mach-exynos/clock-exynos5.c
arch/arm/mach-exynos/cpuidle.c
arch/arm/mach-exynos/include/mach/pm-core.h
arch/arm/mach-exynos/include/mach/pmu.h
arch/arm/mach-exynos/include/mach/regs-clock.h
arch/arm/mach-exynos/include/mach/regs-pmu.h
arch/arm/mach-exynos/pm.c
arch/arm/mach-exynos/pmu.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-ixp4xx/include/mach/gpio.h
arch/arm/mach-s3c24xx/include/mach/irqs.h
arch/arm/mach-s3c24xx/irq-s3c2416.c
arch/arm/mach-s3c24xx/s3c2416.c
arch/arm/mach-s3c64xx/cpuidle.c
arch/arm/mach-s3c64xx/mach-crag6410-module.c
arch/arm/mach-s3c64xx/mach-crag6410.c
arch/arm/mach-vexpress/v2m.c
arch/arm/plat-samsung/include/plat/s3c2416.h
arch/parisc/Kconfig
arch/parisc/include/asm/smp.h
arch/parisc/include/asm/uaccess.h
arch/parisc/kernel/entry.S
arch/parisc/kernel/parisc_ksyms.c
arch/parisc/kernel/vmlinux.lds.S
arch/parisc/lib/lusercopy.S
arch/s390/Kconfig
arch/s390/include/asm/bitops.h
arch/s390/include/asm/cio.h
arch/s390/include/asm/cmpxchg.h
arch/s390/include/asm/cputime.h
arch/s390/include/asm/ctl_reg.h
arch/s390/include/asm/current.h
arch/s390/include/asm/elf.h
arch/s390/include/asm/futex.h
arch/s390/include/asm/idals.h
arch/s390/include/asm/io.h
arch/s390/include/asm/irq.h
arch/s390/include/asm/kexec.h
arch/s390/include/asm/kmap_types.h
arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/module.h
arch/s390/include/asm/os_info.h
arch/s390/include/asm/percpu.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/processor.h
arch/s390/include/asm/rwsem.h
arch/s390/include/asm/setup.h
arch/s390/include/asm/sfp-util.h
arch/s390/include/asm/string.h
arch/s390/include/asm/thread_info.h
arch/s390/include/asm/timer.h
arch/s390/include/asm/tlb.h
arch/s390/include/asm/tlbflush.h
arch/s390/include/asm/types.h
arch/s390/include/asm/uaccess.h
arch/s390/include/asm/vdso.h
arch/s390/kernel/base.S
arch/s390/kernel/early.c
arch/s390/kernel/head_kdump.S
arch/s390/kernel/ipl.c
arch/s390/kernel/irq.c
arch/s390/kernel/machine_kexec.c
arch/s390/kernel/os_info.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/kernel/setup.c
arch/s390/kernel/smp.c
arch/s390/kernel/sysinfo.c
arch/s390/lib/uaccess_mvcos.c
arch/s390/lib/uaccess_std.c
arch/s390/mm/maccess.c
arch/s390/mm/vmem.c
arch/s390/oprofile/hwsampler.c
arch/sparc/Kconfig
arch/sparc/include/asm/asi.h
arch/sparc/include/asm/asmmacro.h
arch/sparc/include/asm/dma-mapping.h
arch/sparc/include/asm/leon.h
arch/sparc/include/asm/leon_amba.h
arch/sparc/include/asm/pgtsrmmu.h
arch/sparc/include/asm/psr.h
arch/sparc/include/asm/sections.h
arch/sparc/kernel/Makefile
arch/sparc/kernel/cpu.c
arch/sparc/kernel/entry.S
arch/sparc/kernel/etrap_32.S
arch/sparc/kernel/head_32.S
arch/sparc/kernel/ioport.c
arch/sparc/kernel/irq_32.c
arch/sparc/kernel/kernel.h
arch/sparc/kernel/leon_kernel.c
arch/sparc/kernel/leon_pmc.c
arch/sparc/kernel/leon_smp.c
arch/sparc/kernel/process_32.c
arch/sparc/kernel/prom_common.c
arch/sparc/kernel/rtrap_32.S
arch/sparc/kernel/setup_32.c
arch/sparc/kernel/trampoline_32.S
arch/sparc/kernel/traps_64.c
arch/sparc/kernel/vmlinux.lds.S
arch/sparc/kernel/wof.S
arch/sparc/kernel/wuf.S
arch/sparc/math-emu/math_64.c
arch/sparc/mm/Makefile
arch/sparc/mm/leon_mm.c
arch/sparc/mm/srmmu.c
arch/sparc/mm/srmmu_access.S [new file with mode: 0644]
arch/x86/include/asm/acpi.h
arch/x86/include/asm/bitops.h
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mtrr/cleanup.c
arch/x86/kernel/hpet.c
arch/x86/kvm/mmu.c
arch/x86/mm/pat.c
arch/x86/xen/enlighten.c
block/blk-ioc.c
drivers/acpi/sleep.c
drivers/atm/solos-pci.c
drivers/block/mtip32xx/mtip32xx.c
drivers/block/mtip32xx/mtip32xx.h
drivers/edac/mce_amd.h
drivers/message/fusion/mptbase.c
drivers/net/ethernet/rdc/r6040.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/usb/asix.c
drivers/net/usb/qmi_wwan.c
drivers/net/virtio_net.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn-rs.c
drivers/net/wireless/iwlwifi/iwl-agn-sta.c
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-phy-db.c [deleted file]
drivers/net/wireless/iwlwifi/iwl-phy-db.h [deleted file]
drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
drivers/net/wireless/ti/wl1251/sdio.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/xen-netback/netback.c
drivers/nfc/pn544_hci.c
drivers/power/Kconfig
drivers/power/ab8500_btemp.c
drivers/power/ab8500_charger.c
drivers/power/ab8500_fg.c
drivers/power/charger-manager.c
drivers/power/ds2781_battery.c
drivers/power/isp1704_charger.c
drivers/power/max17042_battery.c
drivers/power/power_supply_sysfs.c
drivers/power/sbs-battery.c
drivers/power/smb347-charger.c
drivers/s390/block/dasd_int.h
drivers/s390/char/sclp_sdias.c
drivers/scsi/be2iscsi/be_mgmt.c
drivers/scsi/bfa/bfad_attr.c
drivers/scsi/bfa/bfad_im.c
drivers/scsi/bfa/bfad_im.h
drivers/scsi/bnx2fc/bnx2fc.h
drivers/scsi/bnx2fc/bnx2fc_els.c
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/bnx2fc/bnx2fc_hwi.c
drivers/scsi/bnx2fc/bnx2fc_io.c
drivers/scsi/bnx2fc/bnx2fc_tgt.c
drivers/scsi/fcoe/Makefile
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe.h
drivers/scsi/fcoe/fcoe_ctlr.c
drivers/scsi/fcoe/fcoe_sysfs.c [new file with mode: 0644]
drivers/scsi/fcoe/fcoe_transport.c
drivers/scsi/qla2xxx/Kconfig
drivers/scsi/qla2xxx/Makefile
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_bsg.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_dbg.h
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_mid.c
drivers/scsi/qla2xxx/qla_nx.c
drivers/scsi/qla2xxx/qla_nx.h
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_target.c [new file with mode: 0644]
drivers/scsi/qla2xxx/qla_target.h [new file with mode: 0644]
drivers/scsi/qla2xxx/tcm_qla2xxx.c [new file with mode: 0644]
drivers/scsi/qla2xxx/tcm_qla2xxx.h [new file with mode: 0644]
drivers/scsi/qla4xxx/ql4_attr.c
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_fw.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_init.c
drivers/scsi/qla4xxx/ql4_mbx.c
drivers/scsi/qla4xxx/ql4_nx.c
drivers/scsi/qla4xxx/ql4_nx.h
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/qla4xxx/ql4_version.h
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_pm.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_wait_scan.c
drivers/scsi/ufs/ufshcd.c
drivers/tty/tty_mutex.c
fs/direct-io.c
fs/inode.c
fs/nfs/direct.c
fs/nfs/inode.c
fs/nfs/internal.h
include/asm-generic/Kbuild
include/linux/fs.h
include/linux/genetlink.h
include/linux/net.h
include/linux/netdevice.h
include/linux/power/charger-manager.h
include/linux/power/max17042_battery.h
include/linux/power_supply.h
include/linux/skbuff.h
include/net/dst.h
include/scsi/fcoe_sysfs.h [new file with mode: 0644]
include/scsi/libfcoe.h
net/core/drop_monitor.c
net/ipv4/esp4.c
net/ipv6/esp6.c
net/ipv6/ip6_output.c
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/mac80211/mlme.c
net/mac80211/tx.c
net/mac80211/util.c
net/netlink/genetlink.c
net/rds/ib.h
net/wanrouter/Kconfig
net/xfrm/xfrm_policy.c

index d535757..679ce35 100644 (file)
@@ -6,13 +6,21 @@ Description:    This is a read-only file. Dumps below driver information and
                 hardware registers.
                     - S ACTive
                     - Command Issue
-                    - Allocated
                     - Completed
                     - PORT IRQ STAT
                     - HOST IRQ STAT
+                    - Allocated
+                    - Commands in Q
 
 What:           /sys/block/rssd*/status
 Date:           April 2012
 KernelVersion:  3.4
 Contact:        Asai Thambi S P <asamymuthupa@micron.com>
-Description:   This is a read-only file. Indicates the status of the device.
+Description:    This is a read-only file. Indicates the status of the device.
+
+What:           /sys/block/rssd*/flags
+Date:           May 2012
+KernelVersion:  3.5
+Contact:        Asai Thambi S P <asamymuthupa@micron.com>
+Description:    This is a read-only file. Dumps the flags in port and driver
+                data structure
diff --git a/Documentation/ABI/testing/sysfs-bus-fcoe b/Documentation/ABI/testing/sysfs-bus-fcoe
new file mode 100644 (file)
index 0000000..469d09c
--- /dev/null
@@ -0,0 +1,77 @@
+What:          /sys/bus/fcoe/ctlr_X
+Date:          March 2012
+KernelVersion: TBD
+Contact:       Robert Love <robert.w.love@intel.com>, devel@open-fcoe.org
+Description:   'FCoE Controller' instances on the fcoe bus
+Attributes:
+
+       fcf_dev_loss_tmo: Device loss timeout peroid (see below). Changing
+                         this value will change the dev_loss_tmo for all
+                         FCFs discovered by this controller.
+
+       lesb_link_fail:   Link Error Status Block (LESB) link failure count.
+
+       lesb_vlink_fail:  Link Error Status Block (LESB) virtual link
+                         failure count.
+
+       lesb_miss_fka:    Link Error Status Block (LESB) missed FCoE
+                         Initialization Protocol (FIP) Keep-Alives (FKA).
+
+       lesb_symb_err:    Link Error Status Block (LESB) symbolic error count.
+
+       lesb_err_block:   Link Error Status Block (LESB) block error count.
+
+       lesb_fcs_error:   Link Error Status Block (LESB) Fibre Channel
+                         Serivces error count.
+
+Notes: ctlr_X (global increment starting at 0)
+
+What:          /sys/bus/fcoe/fcf_X
+Date:          March 2012
+KernelVersion: TBD
+Contact:       Robert Love <robert.w.love@intel.com>, devel@open-fcoe.org
+Description:   'FCoE FCF' instances on the fcoe bus. A FCF is a Fibre Channel
+               Forwarder, which is a FCoE switch that can accept FCoE
+               (Ethernet) packets, unpack them, and forward the embedded
+               Fibre Channel frames into a FC fabric. It can also take
+               outbound FC frames and pack them in Ethernet packets to
+               be sent to their destination on the Ethernet segment.
+Attributes:
+
+       fabric_name: Identifies the fabric that the FCF services.
+
+       switch_name: Identifies the FCF.
+
+       priority:    The switch's priority amongst other FCFs on the same
+                    fabric.
+
+       selected:    1 indicates that the switch has been selected for use;
+                    0 indicates that the swich will not be used.
+
+       fc_map:      The Fibre Channel MAP
+
+       vfid:        The Virtual Fabric ID
+
+       mac:         The FCF's MAC address
+
+       fka_peroid:  The FIP Keep-Alive peroid
+
+       fabric_state: The internal kernel state
+                     "Unknown" - Initialization value
+                     "Disconnected" - No link to the FCF/fabric
+                     "Connected" - Host is connected to the FCF
+                     "Deleted" - FCF is being removed from the system
+
+       dev_loss_tmo: The device loss timeout peroid for this FCF.
+
+Notes: A device loss infrastructre similar to the FC Transport's
+       is present in fcoe_sysfs. It is nice to have so that a
+       link flapping adapter doesn't continually advance the count
+       used to identify the discovered FCF. FCFs will exist in a
+       "Disconnected" state until either the timer expires and the
+       FCF becomes "Deleted" or the FCF is rediscovered and becomes
+       "Connected."
+
+
+Users: The first user of this interface will be the fcoeadm application,
+       which is commonly packaged in the fcoe-utils package.
index ebaffe2..56000b3 100644 (file)
@@ -606,3 +606,9 @@ Why:        There are two mci drivers: at91-mci and atmel-mci. The PDC support
 Who:   Ludovic Desroches <ludovic.desroches@atmel.com>
 
 ----------------------------
+
+What:  net/wanrouter/
+When:  June 2013
+Why:   Unsupported/unmaintained/unused since 2.6
+
+----------------------------
index fdcca99..b4f7f4b 100644 (file)
@@ -44,6 +44,16 @@ Charger Manager supports the following:
        Normally, the platform will need to resume and suspend some devices
        that are used by Charger Manager.
 
+* Support for premature full-battery event handling
+       If the battery voltage drops by "fullbatt_vchkdrop_uV" after
+       "fullbatt_vchkdrop_ms" from the full-battery event, the framework
+       restarts charging. This check is also performed while suspended by
+       setting wakeup time accordingly and using suspend_again.
+
+* Support for uevent-notify
+       With the charger-related events, the device sends
+       notification to users with UEVENT.
+
 2. Global Charger-Manager Data related with suspend_again
 ========================================================
 In order to setup Charger Manager with suspend-again feature
@@ -55,7 +65,7 @@ if there are multiple batteries. If there are multiple batteries, the
 multiple instances of Charger Manager share the same charger_global_desc
 and it will manage in-suspend monitoring for all instances of Charger Manager.
 
-The user needs to provide all the two entries properly in order to activate
+The user needs to provide all the three entries properly in order to activate
 in-suspend monitoring:
 
 struct charger_global_desc {
@@ -74,6 +84,11 @@ bool (*rtc_only_wakeup)(void);
        same struct. If there is any other wakeup source triggered the
        wakeup, it should return false. If the "rtc" is the only wakeup
        reason, it should return true.
+
+bool assume_timer_stops_in_suspend;
+       : if true, Charger Manager assumes that
+       the timer (CM uses jiffies as timer) stops during suspend. Then, CM
+       assumes that the suspend-duration is same as the alarm length.
 };
 
 3. How to setup suspend_again
@@ -111,6 +126,16 @@ enum polling_modes polling_mode;
          CM_POLL_CHARGING_ONLY: poll this battery if and only if the
                                 battery is being charged.
 
+unsigned int fullbatt_vchkdrop_ms;
+unsigned int fullbatt_vchkdrop_uV;
+       : If both have non-zero values, Charger Manager will check the
+       battery voltage drop fullbatt_vchkdrop_ms after the battery is fully
+       charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger
+       Manager will try to recharge the battery by disabling and enabling
+       chargers. Recharge with voltage drop condition only (without delay
+       condition) is needed to be implemented with hardware interrupts from
+       fuel gauges or charger devices/chips.
+
 unsigned int fullbatt_uV;
        : If specified with a non-zero value, Charger Manager assumes
        that the battery is full (capacity = 100) if the battery is not being
@@ -122,6 +147,8 @@ unsigned int polling_interval_ms;
        this battery every polling_interval_ms or more frequently.
 
 enum data_source battery_present;
+       : CM_BATTERY_PRESENT: assume that the battery exists.
+       CM_NO_BATTERY: assume that the battery does not exists.
        CM_FUEL_GAUGE: get battery presence information from fuel gauge.
        CM_CHARGER_STAT: get battery presence from chargers.
 
@@ -151,7 +178,17 @@ bool measure_battery_temp;
        the value of measure_battery_temp.
 };
 
-5. Other Considerations
+5. Notify Charger-Manager of charger events: cm_notify_event()
+=========================================================
+If there is an charger event is required to notify
+Charger Manager, a charger device driver that triggers the event can call
+cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
+In the function, psy is the charger driver's power_supply pointer, which is
+associated with Charger-Manager. The parameter "type"
+is the same as irq's type (enum cm_event_types). The event message "msg" is
+optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
+
+6. Other Considerations
 =======================
 
 At the charger/battery-related events such as battery-pulled-out,
index 9f16c51..211831d 100644 (file)
@@ -84,6 +84,8 @@ are already charged or discharging, 'n/a' can be displayed (or
 HEALTH - represents health of the battery, values corresponds to
 POWER_SUPPLY_HEALTH_*, defined in battery.h.
 
+VOLTAGE_OCV - open circuit voltage of the battery.
+
 VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
 minimal power supply voltages. Maximal/minimal means values of voltages
 when battery considered "full"/"empty" at normal conditions. Yes, there is
index 64e675d..55f0fda 100644 (file)
@@ -5337,7 +5337,7 @@ M:        David Woodhouse <dwmw2@infradead.org>
 T:     git git://git.infradead.org/battery-2.6.git
 S:     Maintained
 F:     include/linux/power_supply.h
-F:     drivers/power/power_supply*
+F:     drivers/power/
 
 PNP SUPPORT
 M:     Adam Belay <abelay@mit.edu>
@@ -6657,7 +6657,7 @@ F:        include/linux/taskstats*
 F:     kernel/taskstats.c
 
 TC CLASSIFIER
-M:     Jamal Hadi Salim <hadi@cyberus.ca>
+M:     Jamal Hadi Salim <jhs@mojatatu.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     include/linux/pkt_cls.h
index 5e76013..b649c59 100644 (file)
@@ -525,7 +525,7 @@ config ARCH_IXP4XX
        select ARCH_HAS_DMA_SET_COHERENT_MASK
        select CLKSRC_MMIO
        select CPU_XSCALE
-       select GENERIC_GPIO
+       select ARCH_REQUIRE_GPIOLIB
        select GENERIC_CLOCKEVENTS
        select MIGHT_HAVE_PCI
        select NEED_MACH_IO_H
index 5ca0cdb..4272b29 100644 (file)
                reg = <0x10481000 0x1000>, <0x10482000 0x2000>;
        };
 
+       combiner:interrupt-controller@10440000 {
+               compatible = "samsung,exynos4210-combiner";
+               #interrupt-cells = <2>;
+               interrupt-controller;
+               samsung,combiner-nr = <32>;
+               reg = <0x10440000 0x1000>;
+               interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
+                            <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
+                            <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
+                            <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
+                            <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
+                            <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
+                            <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
+                            <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
+       };
+
        watchdog {
                compatible = "samsung,s3c2410-wdt";
                reg = <0x101D0000 0x100>;
index 2d69686..3f5dad8 100644 (file)
                        gpio: gpio@40028000 {
                                compatible = "nxp,lpc3220-gpio";
                                reg = <0x40028000 0x1000>;
-                               /* create a private address space for enumeration */
-                               #address-cells = <1>;
-                               #size-cells = <0>;
-
-                               gpio_p0: gpio-bank@0 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <0>;
-                               };
-
-                               gpio_p1: gpio-bank@1 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <1>;
-                               };
-
-                               gpio_p2: gpio-bank@2 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <2>;
-                               };
-
-                               gpio_p3: gpio-bank@3 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <3>;
-                               };
-
-                               gpi_p3: gpio-bank@4 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <4>;
-                               };
-
-                               gpo_p3: gpio-bank@5 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <5>;
-                               };
+                               gpio-controller;
+                               #gpio-cells = <3>; /* bank, pin, flags */
                        };
 
                        watchdog@4003C000 {
index 0167e86..c4ff6d1 100644 (file)
                compatible = "gpio-leds";
 
                led0 {
-                       gpios = <&gpo_p3 1 1>; /* GPO_P3 1, GPIO 80, active low */
+                       gpios = <&gpio 5 1 1>; /* GPO_P3 1, GPIO 80, active low */
                        linux,default-trigger = "heartbeat";
                        default-state = "off";
                };
 
                led1 {
-                       gpios = <&gpo_p3 14 1>; /* GPO_P3 14, GPIO 93, active low */
+                       gpios = <&gpio 5 14 1>; /* GPO_P3 14, GPIO 93, active low */
                        linux,default-trigger = "timer";
                        default-state = "off";
                };
index 941b161..7e1091d 100644 (file)
                #address-cells = <0>;
                interrupt-controller;
                reg = <0x2c001000 0x1000>,
-                     <0x2c002000 0x100>;
+                     <0x2c002000 0x1000>,
+                     <0x2c004000 0x2000>,
+                     <0x2c006000 0x2000>;
+               interrupts = <1 9 0xf04>;
        };
 
        memory-controller@7ffd0000 {
                             <0 91 4>;
        };
 
+       timer {
+               compatible = "arm,armv7-timer";
+               interrupts = <1 13 0xf08>,
+                            <1 14 0xf08>,
+                            <1 11 0xf08>,
+                            <1 10 0xf08>;
+       };
+
        pmu {
                compatible = "arm,cortex-a15-pmu", "arm,cortex-a9-pmu";
                interrupts = <0 68 4>,
index 6905e66..18917a0 100644 (file)
 
        timer@2c000600 {
                compatible = "arm,cortex-a5-twd-timer";
-               reg = <0x2c000600 0x38>;
-               interrupts = <1 2 0x304>,
-                            <1 3 0x304>;
+               reg = <0x2c000600 0x20>;
+               interrupts = <1 13 0x304>;
+       };
+
+       watchdog@2c000620 {
+               compatible = "arm,cortex-a5-twd-wdt";
+               reg = <0x2c000620 0x20>;
+               interrupts = <1 14 0x304>;
        };
 
        gic: interrupt-controller@2c001000 {
-               compatible = "arm,corex-a5-gic", "arm,cortex-a9-gic";
+               compatible = "arm,cortex-a5-gic", "arm,cortex-a9-gic";
                #interrupt-cells = <3>;
                #address-cells = <0>;
                interrupt-controller;
index da77869..3f0c736 100644 (file)
        timer@1e000600 {
                compatible = "arm,cortex-a9-twd-timer";
                reg = <0x1e000600 0x20>;
-               interrupts = <1 2 0xf04>,
-                            <1 3 0xf04>;
+               interrupts = <1 13 0xf04>;
+       };
+
+       watchdog@1e000620 {
+               compatible = "arm,cortex-a9-twd-wdt";
+               reg = <0x1e000620 0x20>;
+               interrupts = <1 14 0xf04>;
        };
 
        gic: interrupt-controller@1e001000 {
index 43ebe90..573be57 100644 (file)
@@ -62,6 +62,8 @@ config SOC_EXYNOS5250
        default y
        depends on ARCH_EXYNOS5
        select SAMSUNG_DMADEV
+       select S5P_PM if PM
+       select S5P_SLEEP if PM
        help
          Enable EXYNOS5250 SoC support
 
index 440a637..9b58024 100644 (file)
@@ -22,7 +22,7 @@ obj-$(CONFIG_PM)              += pm.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 
-obj-$(CONFIG_ARCH_EXYNOS4)     += pmu.o
+obj-$(CONFIG_ARCH_EXYNOS     += pmu.o
 
 obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
 
index 5aa460b..fefa336 100644 (file)
 
 #ifdef CONFIG_PM_SLEEP
 static struct sleep_save exynos5_clock_save[] = {
-       /* will be implemented */
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_TOP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_DISP1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_MFC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_G3D),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GEN),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_BLOCK),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKDIV_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GEN),
+       SAVE_ITEM(EXYNOS5_CLKDIV_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC4),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC5),
+       SAVE_ITEM(EXYNOS5_SCLK_DIV_ISP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP2),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP3),
+       SAVE_ITEM(EXYNOS5_CLKSRC_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC1),
+       SAVE_ITEM(EXYNOS5_SCLK_SRC_ISP),
+       SAVE_ITEM(EXYNOS5_EPLL_CON0),
+       SAVE_ITEM(EXYNOS5_EPLL_CON1),
+       SAVE_ITEM(EXYNOS5_EPLL_CON2),
+       SAVE_ITEM(EXYNOS5_VPLL_CON0),
+       SAVE_ITEM(EXYNOS5_VPLL_CON1),
+       SAVE_ITEM(EXYNOS5_VPLL_CON2),
 };
 #endif
 
index 26dac28..cff0595 100644 (file)
@@ -100,7 +100,7 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
        exynos4_set_wakeupmask();
 
        /* Set value of power down register for aftr mode */
-       exynos4_sys_powerdown_conf(SYS_AFTR);
+       exynos_sys_powerdown_conf(SYS_AFTR);
 
        __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR);
        __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
index 9d8da51..a67ecfa 100644 (file)
@@ -33,7 +33,7 @@ static inline void s3c_pm_arch_prepare_irqs(void)
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
        __raw_writel(s3c_irqwake_intmask, S5P_WAKEUP_MASK);
-       __raw_writel(s3c_irqwake_eintmask, S5P_EINT_WAKEUP_MASK);
+       __raw_writel(s3c_irqwake_eintmask & 0xFFFFFFFE, S5P_EINT_WAKEUP_MASK);
 }
 
 static inline void s3c_pm_arch_stop_clocks(void)
index e76b7fa..7c27c2d 100644 (file)
@@ -23,12 +23,12 @@ enum sys_powerdown {
 };
 
 extern unsigned long l2x0_regs_phys;
-struct exynos4_pmu_conf {
+struct exynos_pmu_conf {
        void __iomem *reg;
        unsigned int val[NUM_SYS_POWERDOWN];
 };
 
-extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode);
+extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
 extern void s3c_cpu_resume(void);
 
 #endif /* __ASM_ARCH_PMU_H */
index b78b5f3..8c9b38c 100644 (file)
 
 #define EXYNOS5_CLKDIV_ACP                     EXYNOS_CLKREG(0x08500)
 
-#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_EPLL_CON0                      EXYNOS_CLKREG(0x10130)
 #define EXYNOS5_EPLL_CON1                      EXYNOS_CLKREG(0x10134)
+#define EXYNOS5_EPLL_CON2                      EXYNOS_CLKREG(0x10138)
 #define EXYNOS5_VPLL_CON0                      EXYNOS_CLKREG(0x10140)
 #define EXYNOS5_VPLL_CON1                      EXYNOS_CLKREG(0x10144)
+#define EXYNOS5_VPLL_CON2                      EXYNOS_CLKREG(0x10148)
 #define EXYNOS5_CPLL_CON0                      EXYNOS_CLKREG(0x10120)
 
 #define EXYNOS5_CLKSRC_TOP0                    EXYNOS_CLKREG(0x10210)
+#define EXYNOS5_CLKSRC_TOP1                    EXYNOS_CLKREG(0x10214)
+#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_CLKSRC_TOP3                    EXYNOS_CLKREG(0x1021C)
 #define EXYNOS5_CLKSRC_GSCL                    EXYNOS_CLKREG(0x10220)
 #define EXYNOS5_CLKSRC_DISP1_0                 EXYNOS_CLKREG(0x1022C)
+#define EXYNOS5_CLKSRC_MAUDIO                  EXYNOS_CLKREG(0x10240)
 #define EXYNOS5_CLKSRC_FSYS                    EXYNOS_CLKREG(0x10244)
 #define EXYNOS5_CLKSRC_PERIC0                  EXYNOS_CLKREG(0x10250)
+#define EXYNOS5_CLKSRC_PERIC1                  EXYNOS_CLKREG(0x10254)
+#define EXYNOS5_SCLK_SRC_ISP                   EXYNOS_CLKREG(0x10270)
 
 #define EXYNOS5_CLKSRC_MASK_TOP                        EXYNOS_CLKREG(0x10310)
 #define EXYNOS5_CLKSRC_MASK_GSCL               EXYNOS_CLKREG(0x10320)
 #define EXYNOS5_CLKSRC_MASK_DISP1_0            EXYNOS_CLKREG(0x1032C)
+#define EXYNOS5_CLKSRC_MASK_MAUDIO             EXYNOS_CLKREG(0x10334)
 #define EXYNOS5_CLKSRC_MASK_FSYS               EXYNOS_CLKREG(0x10340)
 #define EXYNOS5_CLKSRC_MASK_PERIC0             EXYNOS_CLKREG(0x10350)
+#define EXYNOS5_CLKSRC_MASK_PERIC1             EXYNOS_CLKREG(0x10354)
 
 #define EXYNOS5_CLKDIV_TOP0                    EXYNOS_CLKREG(0x10510)
 #define EXYNOS5_CLKDIV_TOP1                    EXYNOS_CLKREG(0x10514)
 #define EXYNOS5_CLKDIV_GSCL                    EXYNOS_CLKREG(0x10520)
 #define EXYNOS5_CLKDIV_DISP1_0                 EXYNOS_CLKREG(0x1052C)
 #define EXYNOS5_CLKDIV_GEN                     EXYNOS_CLKREG(0x1053C)
+#define EXYNOS5_CLKDIV_MAUDIO                  EXYNOS_CLKREG(0x10544)
 #define EXYNOS5_CLKDIV_FSYS0                   EXYNOS_CLKREG(0x10548)
 #define EXYNOS5_CLKDIV_FSYS1                   EXYNOS_CLKREG(0x1054C)
 #define EXYNOS5_CLKDIV_FSYS2                   EXYNOS_CLKREG(0x10550)
 #define EXYNOS5_CLKDIV_FSYS3                   EXYNOS_CLKREG(0x10554)
 #define EXYNOS5_CLKDIV_PERIC0                  EXYNOS_CLKREG(0x10558)
+#define EXYNOS5_CLKDIV_PERIC1                  EXYNOS_CLKREG(0x1055C)
+#define EXYNOS5_CLKDIV_PERIC2                  EXYNOS_CLKREG(0x10560)
+#define EXYNOS5_CLKDIV_PERIC3                  EXYNOS_CLKREG(0x10564)
+#define EXYNOS5_CLKDIV_PERIC4                  EXYNOS_CLKREG(0x10568)
+#define EXYNOS5_CLKDIV_PERIC5                  EXYNOS_CLKREG(0x1056C)
+#define EXYNOS5_SCLK_DIV_ISP                   EXYNOS_CLKREG(0x10580)
 
 #define EXYNOS5_CLKGATE_IP_ACP                 EXYNOS_CLKREG(0x08800)
 #define EXYNOS5_CLKGATE_IP_ISP0                        EXYNOS_CLKREG(0x0C800)
 #define EXYNOS5_CLKGATE_IP_GSCL                        EXYNOS_CLKREG(0x10920)
 #define EXYNOS5_CLKGATE_IP_DISP1               EXYNOS_CLKREG(0x10928)
 #define EXYNOS5_CLKGATE_IP_MFC                 EXYNOS_CLKREG(0x1092C)
+#define EXYNOS5_CLKGATE_IP_G3D                 EXYNOS_CLKREG(0x10930)
 #define EXYNOS5_CLKGATE_IP_GEN                 EXYNOS_CLKREG(0x10934)
 #define EXYNOS5_CLKGATE_IP_FSYS                        EXYNOS_CLKREG(0x10944)
 #define EXYNOS5_CLKGATE_IP_GPS                 EXYNOS_CLKREG(0x1094C)
index 4dbb862..43a99e6 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/include/mach/regs-pmu.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4 - Power management unit definition
+ * EXYNOS - Power management unit definition
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #define S5P_DIS_IRQ_CORE3                      S5P_PMUREG(0x1034)
 #define S5P_DIS_IRQ_CENTRAL3                   S5P_PMUREG(0x1038)
 
+/* For EXYNOS5 */
+
+#define EXYNOS5_USB_CFG                                                S5P_PMUREG(0x0230)
+
+#define EXYNOS5_ARM_CORE0_SYS_PWR_REG                          S5P_PMUREG(0x1000)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG            S5P_PMUREG(0x1004)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG          S5P_PMUREG(0x1008)
+#define EXYNOS5_ARM_CORE1_SYS_PWR_REG                          S5P_PMUREG(0x1010)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG            S5P_PMUREG(0x1014)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG          S5P_PMUREG(0x1018)
+#define EXYNOS5_FSYS_ARM_SYS_PWR_REG                           S5P_PMUREG(0x1040)
+#define EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG           S5P_PMUREG(0x1048)
+#define EXYNOS5_ISP_ARM_SYS_PWR_REG                            S5P_PMUREG(0x1050)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG              S5P_PMUREG(0x1054)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG            S5P_PMUREG(0x1058)
+#define EXYNOS5_ARM_COMMON_SYS_PWR_REG                         S5P_PMUREG(0x1080)
+#define EXYNOS5_ARM_L2_SYS_PWR_REG                             S5P_PMUREG(0x10C0)
+#define EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG                       S5P_PMUREG(0x1100)
+#define EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG                       S5P_PMUREG(0x1104)
+#define EXYNOS5_CMU_RESET_SYS_PWR_REG                          S5P_PMUREG(0x110C)
+#define EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG                        S5P_PMUREG(0x1120)
+#define EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG                        S5P_PMUREG(0x1124)
+#define EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG                   S5P_PMUREG(0x112C)
+#define EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG                     S5P_PMUREG(0x1130)
+#define EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG                      S5P_PMUREG(0x1134)
+#define EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG                     S5P_PMUREG(0x1138)
+#define EXYNOS5_APLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1140)
+#define EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1144)
+#define EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1148)
+#define EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x114C)
+#define EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1150)
+#define EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1154)
+#define EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG                    S5P_PMUREG(0x1164)
+#define EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG                    S5P_PMUREG(0x1170)
+#define EXYNOS5_TOP_BUS_SYS_PWR_REG                            S5P_PMUREG(0x1180)
+#define EXYNOS5_TOP_RETENTION_SYS_PWR_REG                      S5P_PMUREG(0x1184)
+#define EXYNOS5_TOP_PWR_SYS_PWR_REG                            S5P_PMUREG(0x1188)
+#define EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG                     S5P_PMUREG(0x1190)
+#define EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG               S5P_PMUREG(0x1194)
+#define EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG                     S5P_PMUREG(0x1198)
+#define EXYNOS5_LOGIC_RESET_SYS_PWR_REG                                S5P_PMUREG(0x11A0)
+#define EXYNOS5_OSCCLK_GATE_SYS_PWR_REG                                S5P_PMUREG(0x11A4)
+#define EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG                 S5P_PMUREG(0x11B0)
+#define EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG                 S5P_PMUREG(0x11B4)
+#define EXYNOS5_USBOTG_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11C0)
+#define EXYNOS5_G2D_MEM_SYS_PWR_REG                            S5P_PMUREG(0x11C8)
+#define EXYNOS5_USBDRD_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11CC)
+#define EXYNOS5_SDMMC_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D0)
+#define EXYNOS5_CSSYS_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D4)
+#define EXYNOS5_SECSS_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D8)
+#define EXYNOS5_ROTATOR_MEM_SYS_PWR_REG                                S5P_PMUREG(0x11DC)
+#define EXYNOS5_INTRAM_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11E0)
+#define EXYNOS5_INTROM_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11E4)
+#define EXYNOS5_JPEG_MEM_SYS_PWR_REG                           S5P_PMUREG(0x11E8)
+#define EXYNOS5_HSI_MEM_SYS_PWR_REG                            S5P_PMUREG(0x11EC)
+#define EXYNOS5_MCUIOP_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11F4)
+#define EXYNOS5_SATA_MEM_SYS_PWR_REG                           S5P_PMUREG(0x11FC)
+#define EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG                 S5P_PMUREG(0x1200)
+#define EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG                  S5P_PMUREG(0x1204)
+#define EXYNOS5_PAD_RETENTION_EFNAND_SYS_PWR_REG               S5P_PMUREG(0x1208)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG                 S5P_PMUREG(0x1220)
+#define EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG                 S5P_PMUREG(0x1224)
+#define EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG                 S5P_PMUREG(0x1228)
+#define EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG                 S5P_PMUREG(0x122C)
+#define EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG                 S5P_PMUREG(0x1230)
+#define EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG                 S5P_PMUREG(0x1234)
+#define EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG                  S5P_PMUREG(0x1238)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG          S5P_PMUREG(0x123C)
+#define EXYNOS5_PAD_ISOLATION_SYS_PWR_REG                      S5P_PMUREG(0x1240)
+#define EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG               S5P_PMUREG(0x1250)
+#define EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG                                S5P_PMUREG(0x1260)
+#define EXYNOS5_XUSBXTI_SYS_PWR_REG                            S5P_PMUREG(0x1280)
+#define EXYNOS5_XXTI_SYS_PWR_REG                               S5P_PMUREG(0x1284)
+#define EXYNOS5_EXT_REGULATOR_SYS_PWR_REG                      S5P_PMUREG(0x12C0)
+#define EXYNOS5_GPIO_MODE_SYS_PWR_REG                          S5P_PMUREG(0x1300)
+#define EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG                   S5P_PMUREG(0x1320)
+#define EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG                      S5P_PMUREG(0x1340)
+#define EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG                      S5P_PMUREG(0x1344)
+#define EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG                  S5P_PMUREG(0x1348)
+#define EXYNOS5_GSCL_SYS_PWR_REG                               S5P_PMUREG(0x1400)
+#define EXYNOS5_ISP_SYS_PWR_REG                                        S5P_PMUREG(0x1404)
+#define EXYNOS5_MFC_SYS_PWR_REG                                        S5P_PMUREG(0x1408)
+#define EXYNOS5_G3D_SYS_PWR_REG                                        S5P_PMUREG(0x140C)
+#define EXYNOS5_DISP1_SYS_PWR_REG                              S5P_PMUREG(0x1414)
+#define EXYNOS5_MAU_SYS_PWR_REG                                        S5P_PMUREG(0x1418)
+#define EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG                   S5P_PMUREG(0x1480)
+#define EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG                    S5P_PMUREG(0x1484)
+#define EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG                    S5P_PMUREG(0x1488)
+#define EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG                    S5P_PMUREG(0x148C)
+#define EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG                  S5P_PMUREG(0x1494)
+#define EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG                    S5P_PMUREG(0x1498)
+#define EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG                    S5P_PMUREG(0x14C0)
+#define EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG                     S5P_PMUREG(0x14C4)
+#define EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG                     S5P_PMUREG(0x14C8)
+#define EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG                     S5P_PMUREG(0x14CC)
+#define EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG                   S5P_PMUREG(0x14D4)
+#define EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG                     S5P_PMUREG(0x14D8)
+#define EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG                     S5P_PMUREG(0x1580)
+#define EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG                      S5P_PMUREG(0x1584)
+#define EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG                      S5P_PMUREG(0x1588)
+#define EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG                      S5P_PMUREG(0x158C)
+#define EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG                    S5P_PMUREG(0x1594)
+#define EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG                      S5P_PMUREG(0x1598)
+
+#define EXYNOS5_ARM_CORE0_OPTION                               S5P_PMUREG(0x2008)
+#define EXYNOS5_ARM_CORE1_OPTION                               S5P_PMUREG(0x2088)
+#define EXYNOS5_FSYS_ARM_OPTION                                        S5P_PMUREG(0x2208)
+#define EXYNOS5_ISP_ARM_OPTION                                 S5P_PMUREG(0x2288)
+#define EXYNOS5_ARM_COMMON_OPTION                              S5P_PMUREG(0x2408)
+#define EXYNOS5_TOP_PWR_OPTION                                 S5P_PMUREG(0x2C48)
+#define EXYNOS5_TOP_PWR_SYSMEM_OPTION                          S5P_PMUREG(0x2CC8)
+#define EXYNOS5_JPEG_MEM_OPTION                                        S5P_PMUREG(0x2F48)
+#define EXYNOS5_GSCL_STATUS                                    S5P_PMUREG(0x4004)
+#define EXYNOS5_ISP_STATUS                                     S5P_PMUREG(0x4024)
+#define EXYNOS5_GSCL_OPTION                                    S5P_PMUREG(0x4008)
+#define EXYNOS5_ISP_OPTION                                     S5P_PMUREG(0x4028)
+#define EXYNOS5_MFC_OPTION                                     S5P_PMUREG(0x4048)
+#define EXYNOS5_G3D_CONFIGURATION                              S5P_PMUREG(0x4060)
+#define EXYNOS5_G3D_STATUS                                     S5P_PMUREG(0x4064)
+#define EXYNOS5_G3D_OPTION                                     S5P_PMUREG(0x4068)
+#define EXYNOS5_DISP1_OPTION                                   S5P_PMUREG(0x40A8)
+#define EXYNOS5_MAU_OPTION                                     S5P_PMUREG(0x40C8)
+
+#define EXYNOS5_USE_SC_FEEDBACK                                        (1 << 1)
+#define EXYNOS5_USE_SC_COUNTER                                 (1 << 0)
+
+#define EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL                    (1 << 2)
+#define EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN                 (1 << 7)
+
+#define EXYNOS5_OPTION_USE_STANDBYWFE                          (1 << 24)
+#define EXYNOS5_OPTION_USE_STANDBYWFI                          (1 << 16)
+
+#define EXYNOS5_OPTION_USE_RETENTION                           (1 << 4)
+
 #endif /* __ASM_ARCH_REGS_PMU_H */
index 563dea9..c06c992 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/pm.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4210 - Power Management support
+ * EXYNOS - Power Management support
  *
  * Based on arch/arm/mach-s3c2410/pm.c
  * Copyright (c) 2006 Simtec Electronics
@@ -63,90 +62,7 @@ static struct sleep_save exynos4_vpll_save[] = {
        SAVE_ITEM(EXYNOS4_VPLL_CON1),
 };
 
-static struct sleep_save exynos4_core_save[] = {
-       /* GIC side */
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x008),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x00C),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x014),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x018),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x100),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x104),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x108),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x300),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x304),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x308),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x400),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x404),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x408),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x40C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x410),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x414),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x418),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x41C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x420),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x424),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x428),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x42C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x430),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x434),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x438),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x43C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x440),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x444),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x448),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x44C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x450),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x454),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x458),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x45C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x800),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x804),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x808),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x80C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x810),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x814),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x818),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x81C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x820),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x824),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x828),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x82C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x830),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x834),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x838),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x83C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x840),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x844),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x848),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x84C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x850),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x854),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x858),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x85C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC00),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC04),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC08),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC0C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC10),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC14),
-
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x000),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x010),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x020),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x030),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x040),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x050),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x060),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090),
-
+static struct sleep_save exynos_core_save[] = {
        /* SROM side */
        SAVE_ITEM(S5P_SROM_BW),
        SAVE_ITEM(S5P_SROM_BC0),
@@ -159,9 +75,11 @@ static struct sleep_save exynos4_core_save[] = {
 /* For Cortex-A9 Diagnostic and Power control register */
 static unsigned int save_arm_register[2];
 
-static int exynos4_cpu_suspend(unsigned long arg)
+static int exynos_cpu_suspend(unsigned long arg)
 {
+#ifdef CONFIG_CACHE_L2X0
        outer_flush_all();
+#endif
 
        /* issue the standby signal into the pm unit. */
        cpu_do_idle();
@@ -170,19 +88,25 @@ static int exynos4_cpu_suspend(unsigned long arg)
        panic("sleep resumed to originator?");
 }
 
-static void exynos4_pm_prepare(void)
+static void exynos_pm_prepare(void)
 {
-       u32 tmp;
+       unsigned int tmp;
 
-       s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
-       s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
-       s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
+       s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       tmp = __raw_readl(S5P_INFORM1);
+       if (!soc_is_exynos5250()) {
+               s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
+               s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
+       } else {
+               /* Disable USE_RETENTION of JPEG_MEM_OPTION */
+               tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
+               tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
+               __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
+       }
 
        /* Set value of power down register for sleep mode */
 
-       exynos4_sys_powerdown_conf(SYS_SLEEP);
+       exynos_sys_powerdown_conf(SYS_SLEEP);
        __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
 
        /* ensure at least INFORM0 has the resume address */
@@ -191,17 +115,18 @@ static void exynos4_pm_prepare(void)
 
        /* Before enter central sequence mode, clock src register have to set */
 
-       s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
+       if (!soc_is_exynos5250())
+               s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
 
        if (soc_is_exynos4210())
                s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc));
 
 }
 
-static int exynos4_pm_add(struct device *dev, struct subsys_interface *sif)
+static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
 {
-       pm_cpu_prep = exynos4_pm_prepare;
-       pm_cpu_sleep = exynos4_cpu_suspend;
+       pm_cpu_prep = exynos_pm_prepare;
+       pm_cpu_sleep = exynos_cpu_suspend;
 
        return 0;
 }
@@ -273,13 +198,13 @@ static void exynos4_restore_pll(void)
        } while (epll_wait || vpll_wait);
 }
 
-static struct subsys_interface exynos4_pm_interface = {
-       .name           = "exynos4_pm",
+static struct subsys_interface exynos_pm_interface = {
+       .name           = "exynos_pm",
        .subsys         = &exynos_subsys,
-       .add_dev        = exynos4_pm_add,
+       .add_dev        = exynos_pm_add,
 };
 
-static __init int exynos4_pm_drvinit(void)
+static __init int exynos_pm_drvinit(void)
 {
        struct clk *pll_base;
        unsigned int tmp;
@@ -292,18 +217,20 @@ static __init int exynos4_pm_drvinit(void)
        tmp |= ((0xFF << 8) | (0x1F << 1));
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
-       pll_base = clk_get(NULL, "xtal");
+       if (!soc_is_exynos5250()) {
+               pll_base = clk_get(NULL, "xtal");
 
-       if (!IS_ERR(pll_base)) {
-               pll_base_rate = clk_get_rate(pll_base);
-               clk_put(pll_base);
+               if (!IS_ERR(pll_base)) {
+                       pll_base_rate = clk_get_rate(pll_base);
+                       clk_put(pll_base);
+               }
        }
 
-       return subsys_interface_register(&exynos4_pm_interface);
+       return subsys_interface_register(&exynos_pm_interface);
 }
-arch_initcall(exynos4_pm_drvinit);
+arch_initcall(exynos_pm_drvinit);
 
-static int exynos4_pm_suspend(void)
+static int exynos_pm_suspend(void)
 {
        unsigned long tmp;
 
@@ -313,27 +240,27 @@ static int exynos4_pm_suspend(void)
        tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
        __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
 
-       if (soc_is_exynos4212() || soc_is_exynos4412()) {
-               tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION);
-               tmp &= ~(S5P_USE_STANDBYWFI_ISP_ARM |
-                        S5P_USE_STANDBYWFE_ISP_ARM);
-               __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
-       }
+       /* Setting SEQ_OPTION register */
+
+       tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
+       __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
 
-       /* Save Power control register */
-       asm ("mrc p15, 0, %0, c15, c0, 0"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[0] = tmp;
+       if (!soc_is_exynos5250()) {
+               /* Save Power control register */
+               asm ("mrc p15, 0, %0, c15, c0, 0"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[0] = tmp;
 
-       /* Save Diagnostic register */
-       asm ("mrc p15, 0, %0, c15, c0, 1"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[1] = tmp;
+               /* Save Diagnostic register */
+               asm ("mrc p15, 0, %0, c15, c0, 1"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[1] = tmp;
+       }
 
        return 0;
 }
 
-static void exynos4_pm_resume(void)
+static void exynos_pm_resume(void)
 {
        unsigned long tmp;
 
@@ -350,17 +277,19 @@ static void exynos4_pm_resume(void)
                /* No need to perform below restore code */
                goto early_wakeup;
        }
-       /* Restore Power control register */
-       tmp = save_arm_register[0];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 0"
-                     : : "r" (tmp)
-                     : "cc");
-
-       /* Restore Diagnostic register */
-       tmp = save_arm_register[1];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 1"
-                     : : "r" (tmp)
-                     : "cc");
+       if (!soc_is_exynos5250()) {
+               /* Restore Power control register */
+               tmp = save_arm_register[0];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 0"
+                             : : "r" (tmp)
+                             : "cc");
+
+               /* Restore Diagnostic register */
+               tmp = save_arm_register[1];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 1"
+                             : : "r" (tmp)
+                             : "cc");
+       }
 
        /* For release retention */
 
@@ -372,26 +301,28 @@ static void exynos4_pm_resume(void)
        __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
        __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);
 
-       s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
+       s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       exynos4_restore_pll();
+       if (!soc_is_exynos5250()) {
+               exynos4_restore_pll();
 
 #ifdef CONFIG_SMP
-       scu_enable(S5P_VA_SCU);
+               scu_enable(S5P_VA_SCU);
 #endif
+       }
 
 early_wakeup:
        return;
 }
 
-static struct syscore_ops exynos4_pm_syscore_ops = {
-       .suspend        = exynos4_pm_suspend,
-       .resume         = exynos4_pm_resume,
+static struct syscore_ops exynos_pm_syscore_ops = {
+       .suspend        = exynos_pm_suspend,
+       .resume         = exynos_pm_resume,
 };
 
-static __init int exynos4_pm_syscore_init(void)
+static __init int exynos_pm_syscore_init(void)
 {
-       register_syscore_ops(&exynos4_pm_syscore_ops);
+       register_syscore_ops(&exynos_pm_syscore_ops);
        return 0;
 }
-arch_initcall(exynos4_pm_syscore_init);
+arch_initcall(exynos_pm_syscore_init);
index 77c6815..4aacb66 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/pmu.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
  *
- * EXYNOS4210 - CPU PMU(Power Management Unit) support
+ * EXYNOS - CPU PMU(Power Management Unit) support
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/bug.h>
 
 #include <mach/regs-clock.h>
 #include <mach/pmu.h>
 
-static struct exynos4_pmu_conf *exynos4_pmu_config;
+static struct exynos_pmu_conf *exynos_pmu_config;
 
-static struct exynos4_pmu_conf exynos4210_pmu_config[] = {
+static struct exynos_pmu_conf exynos4210_pmu_config[] = {
        /* { .reg = address, .val = { AFTR, LPA, SLEEP } */
        { S5P_ARM_CORE0_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE0,                    { 0x0, 0x0, 0x0 } },
@@ -94,7 +94,7 @@ static struct exynos4_pmu_conf exynos4210_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-static struct exynos4_pmu_conf exynos4x12_pmu_config[] = {
+static struct exynos_pmu_conf exynos4x12_pmu_config[] = {
        { S5P_ARM_CORE0_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE0,                    { 0x0, 0x0, 0x0 } },
        { S5P_DIS_IRQ_CENTRAL0,                 { 0x0, 0x0, 0x0 } },
@@ -202,7 +202,7 @@ static struct exynos4_pmu_conf exynos4x12_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-static struct exynos4_pmu_conf exynos4412_pmu_config[] = {
+static struct exynos_pmu_conf exynos4412_pmu_config[] = {
        { S5P_ARM_CORE2_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE2,                    { 0x0, 0x0, 0x0 } },
        { S5P_DIS_IRQ_CENTRAL2,                 { 0x0, 0x0, 0x0 } },
@@ -212,13 +212,174 @@ static struct exynos4_pmu_conf exynos4412_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
+static struct exynos_pmu_conf exynos5250_pmu_config[] = {
+       /* { .reg = address, .val = { AFTR, LPA, SLEEP } */
+       { EXYNOS5_ARM_CORE0_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_CORE1_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_FSYS_ARM_SYS_PWR_REG,                 { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG, { 0x1, 0x1, 0x1} },
+       { EXYNOS5_ISP_ARM_SYS_PWR_REG,                  { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_COMMON_SYS_PWR_REG,               { 0x0, 0x0, 0x2} },
+       { EXYNOS5_ARM_L2_SYS_PWR_REG,                   { 0x3, 0x3, 0x3} },
+       { EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYS_PWR_REG,                { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG,         { 0x1, 0x1, 0x0} },
+       { EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_APLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_TOP_BUS_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYS_PWR_REG,            { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYS_PWR_REG,                  { 0x3, 0x0, 0x3} },
+       { EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x3} },
+       { EXYNOS5_LOGIC_RESET_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYS_PWR_REG,              { 0x1, 0x0, 0x1} },
+       { EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG,       { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG,       { 0x1, 0x0, 0x1} },
+       { EXYNOS5_USBOTG_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_G2D_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_USBDRD_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SDMMC_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_CSSYS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SECSS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_ROTATOR_MEM_SYS_PWR_REG,              { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTRAM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTROM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_JPEG_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_HSI_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_MCUIOP_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SATA_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG,        { 0x1, 0x1, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_XUSBXTI_SYS_PWR_REG,                  { 0x1, 0x1, 0x1} },
+       { EXYNOS5_XXTI_SYS_PWR_REG,                     { 0x1, 0x1, 0x0} },
+       { EXYNOS5_EXT_REGULATOR_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYS_PWR_REG,                { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG,        { 0x1, 0x0, 0x1} },
+       { EXYNOS5_GSCL_SYS_PWR_REG,                     { 0x7, 0x0, 0x0} },
+       { EXYNOS5_ISP_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MFC_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_G3D_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_DISP1_SYS_PWR_REG,                    { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MAU_SYS_PWR_REG,                      { 0x7, 0x7, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG,          { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG,           { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { PMU_TABLE_END,},
+};
+
+void __iomem *exynos5_list_both_cnt_feed[] = {
+       EXYNOS5_ARM_CORE0_OPTION,
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_ARM_COMMON_OPTION,
+       EXYNOS5_GSCL_OPTION,
+       EXYNOS5_ISP_OPTION,
+       EXYNOS5_MFC_OPTION,
+       EXYNOS5_G3D_OPTION,
+       EXYNOS5_DISP1_OPTION,
+       EXYNOS5_MAU_OPTION,
+       EXYNOS5_TOP_PWR_OPTION,
+       EXYNOS5_TOP_PWR_SYSMEM_OPTION,
+};
+
+void __iomem *exynos5_list_diable_wfi_wfe[] = {
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_FSYS_ARM_OPTION,
+       EXYNOS5_ISP_ARM_OPTION,
+};
+
+static void exynos5_init_pmu(void)
 {
        unsigned int i;
+       unsigned int tmp;
+
+       /*
+        * Enable both SC_FEEDBACK and SC_COUNTER
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) {
+               tmp = __raw_readl(exynos5_list_both_cnt_feed[i]);
+               tmp |= (EXYNOS5_USE_SC_FEEDBACK |
+                       EXYNOS5_USE_SC_COUNTER);
+               __raw_writel(tmp, exynos5_list_both_cnt_feed[i]);
+       }
+
+       /*
+        * SKIP_DEACTIVATE_ACEACP_IN_PWDN_BITFIELD Enable
+        * MANUAL_L2RSTDISABLE_CONTROL_BITFIELD Enable
+        */
+       tmp = __raw_readl(EXYNOS5_ARM_COMMON_OPTION);
+       tmp |= (EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL |
+               EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN);
+       __raw_writel(tmp, EXYNOS5_ARM_COMMON_OPTION);
+
+       /*
+        * Disable WFI/WFE on XXX_OPTION
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) {
+               tmp = __raw_readl(exynos5_list_diable_wfi_wfe[i]);
+               tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
+                        EXYNOS5_OPTION_USE_STANDBYWFI);
+               __raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
+       }
+}
+
+void exynos_sys_powerdown_conf(enum sys_powerdown mode)
+{
+       unsigned int i;
+
+       if (soc_is_exynos5250())
+               exynos5_init_pmu();
 
-       for (i = 0; (exynos4_pmu_config[i].reg != PMU_TABLE_END) ; i++)
-               __raw_writel(exynos4_pmu_config[i].val[mode],
-                               exynos4_pmu_config[i].reg);
+       for (i = 0; (exynos_pmu_config[i].reg != PMU_TABLE_END) ; i++)
+               __raw_writel(exynos_pmu_config[i].val[mode],
+                               exynos_pmu_config[i].reg);
 
        if (soc_is_exynos4412()) {
                for (i = 0; exynos4412_pmu_config[i].reg != PMU_TABLE_END ; i++)
@@ -227,20 +388,23 @@ void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
        }
 }
 
-static int __init exynos4_pmu_init(void)
+static int __init exynos_pmu_init(void)
 {
-       exynos4_pmu_config = exynos4210_pmu_config;
+       exynos_pmu_config = exynos4210_pmu_config;
 
        if (soc_is_exynos4210()) {
-               exynos4_pmu_config = exynos4210_pmu_config;
+               exynos_pmu_config = exynos4210_pmu_config;
                pr_info("EXYNOS4210 PMU Initialize\n");
        } else if (soc_is_exynos4212() || soc_is_exynos4412()) {
-               exynos4_pmu_config = exynos4x12_pmu_config;
+               exynos_pmu_config = exynos4x12_pmu_config;
                pr_info("EXYNOS4x12 PMU Initialize\n");
+       } else if (soc_is_exynos5250()) {
+               exynos_pmu_config = exynos5250_pmu_config;
+               pr_info("EXYNOS5250 PMU Initialize\n");
        } else {
-               pr_info("EXYNOS4: PMU not supported\n");
+               pr_info("EXYNOS: PMU not supported\n");
        }
 
        return 0;
 }
-arch_initcall(exynos4_pmu_init);
+arch_initcall(exynos_pmu_init);
index ebbd7fc..a9f8094 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/clockchips.h>
 #include <linux/io.h>
 #include <linux/export.h>
+#include <linux/gpio.h>
 
 #include <mach/udc.h>
 #include <mach/hardware.h>
@@ -107,7 +108,7 @@ static signed char irq2gpio[32] = {
         7,  8,  9, 10, 11, 12, -1, -1,
 };
 
-int gpio_to_irq(int gpio)
+static int ixp4xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
 {
        int irq;
 
@@ -117,7 +118,6 @@ int gpio_to_irq(int gpio)
        }
        return -EINVAL;
 }
-EXPORT_SYMBOL(gpio_to_irq);
 
 int irq_to_gpio(unsigned int irq)
 {
@@ -383,12 +383,56 @@ static struct platform_device *ixp46x_devices[] __initdata = {
 unsigned long ixp4xx_exp_bus_size;
 EXPORT_SYMBOL(ixp4xx_exp_bus_size);
 
+static int ixp4xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+       gpio_line_config(gpio, IXP4XX_GPIO_IN);
+
+       return 0;
+}
+
+static int ixp4xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+                                       int level)
+{
+       gpio_line_set(gpio, level);
+       gpio_line_config(gpio, IXP4XX_GPIO_OUT);
+
+       return 0;
+}
+
+static int ixp4xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+       int value;
+
+       gpio_line_get(gpio, &value);
+
+       return value;
+}
+
+static void ixp4xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
+                                 int value)
+{
+       gpio_line_set(gpio, value);
+}
+
+static struct gpio_chip ixp4xx_gpio_chip = {
+       .label                  = "IXP4XX_GPIO_CHIP",
+       .direction_input        = ixp4xx_gpio_direction_input,
+       .direction_output       = ixp4xx_gpio_direction_output,
+       .get                    = ixp4xx_gpio_get_value,
+       .set                    = ixp4xx_gpio_set_value,
+       .to_irq                 = ixp4xx_gpio_to_irq,
+       .base                   = 0,
+       .ngpio                  = 16,
+};
+
 void __init ixp4xx_sys_init(void)
 {
        ixp4xx_exp_bus_size = SZ_16M;
 
        platform_add_devices(ixp4xx_devices, ARRAY_SIZE(ixp4xx_devices));
 
+       gpiochip_add(&ixp4xx_gpio_chip);
+
        if (cpu_is_ixp46x()) {
                int region;
 
index 83d6b4e..ef37f26 100644 (file)
@@ -1,79 +1,2 @@
-/*
- * arch/arm/mach-ixp4xx/include/mach/gpio.h
- *
- * IXP4XX GPIO wrappers for arch-neutral GPIO calls
- *
- * Written by Milan Svoboda <msvoboda@ra.rockwell.com>
- * Based on PXA implementation by Philipp Zabel <philipp.zabel@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef __ASM_ARCH_IXP4XX_GPIO_H
-#define __ASM_ARCH_IXP4XX_GPIO_H
-
-#include <linux/kernel.h>
-#include <mach/hardware.h>
-
-#define __ARM_GPIOLIB_COMPLEX
-
-static inline int gpio_request(unsigned gpio, const char *label)
-{
-       return 0;
-}
-
-static inline void gpio_free(unsigned gpio)
-{
-       might_sleep();
-
-       return;
-}
-
-static inline int gpio_direction_input(unsigned gpio)
-{
-       gpio_line_config(gpio, IXP4XX_GPIO_IN);
-       return 0;
-}
-
-static inline int gpio_direction_output(unsigned gpio, int level)
-{
-       gpio_line_set(gpio, level);
-       gpio_line_config(gpio, IXP4XX_GPIO_OUT);
-       return 0;
-}
-
-static inline int gpio_get_value(unsigned gpio)
-{
-       int value;
-
-       gpio_line_get(gpio, &value);
-
-       return value;
-}
-
-static inline void gpio_set_value(unsigned gpio, int value)
-{
-       gpio_line_set(gpio, value);
-}
-
-#include <asm-generic/gpio.h>                  /* cansleep wrappers */
-
-extern int gpio_to_irq(int gpio);
-#define gpio_to_irq gpio_to_irq
-extern int irq_to_gpio(unsigned int irq);
-
-#endif
+/* empty */
 
index e53b217..b7a9f4d 100644 (file)
 #define IRQ_S32416_WDT         S3C2410_IRQSUB(27)
 #define IRQ_S32416_AC97                S3C2410_IRQSUB(28)
 
+/* second interrupt-register of s3c2416/s3c2450 */
+
+#define S3C2416_IRQ(x)         S3C2410_IRQ((x) + 54 + 29)
+#define IRQ_S3C2416_2D         S3C2416_IRQ(0)
+#define IRQ_S3C2416_IIC1       S3C2416_IRQ(1)
+#define IRQ_S3C2416_RESERVED2  S3C2416_IRQ(2)
+#define IRQ_S3C2416_RESERVED3  S3C2416_IRQ(3)
+#define IRQ_S3C2416_PCM0       S3C2416_IRQ(4)
+#define IRQ_S3C2416_PCM1       S3C2416_IRQ(5)
+#define IRQ_S3C2416_I2S0       S3C2416_IRQ(6)
+#define IRQ_S3C2416_I2S1       S3C2416_IRQ(7)
 
 /* extra irqs for s3c2440 */
 
 #define IRQ_S3C2443_WDT                S3C2410_IRQSUB(27)
 #define IRQ_S3C2443_AC97       S3C2410_IRQSUB(28)
 
-#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416)
+#if defined(CONFIG_CPU_S3C2416)
+#define NR_IRQS (IRQ_S3C2416_I2S1 + 1)
+#elif defined(CONFIG_CPU_S3C2443)
 #define NR_IRQS (IRQ_S3C2443_AC97+1)
 #else
 #define NR_IRQS (IRQ_S3C2440_AC97+1)
index fd49f35..23ec973 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/io.h>
+#include <linux/syscore_ops.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
@@ -192,6 +193,43 @@ static struct irq_chip s3c2416_irq_uart3 = {
        .irq_ack        = s3c2416_irq_uart3_ack,
 };
 
+/* second interrupt register */
+
+static inline void s3c2416_irq_ack_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+
+       __raw_writel(bitval, S3C2416_SRCPND2);
+       __raw_writel(bitval, S3C2416_INTPND2);
+}
+
+static void s3c2416_irq_mask_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+       unsigned long mask;
+
+       mask = __raw_readl(S3C2416_INTMSK2);
+       mask |= bitval;
+       __raw_writel(mask, S3C2416_INTMSK2);
+}
+
+static void s3c2416_irq_unmask_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+       unsigned long mask;
+
+       mask = __raw_readl(S3C2416_INTMSK2);
+       mask &= ~bitval;
+       __raw_writel(mask, S3C2416_INTMSK2);
+}
+
+struct irq_chip s3c2416_irq_second = {
+       .irq_ack        = s3c2416_irq_ack_second,
+       .irq_mask       = s3c2416_irq_mask_second,
+       .irq_unmask     = s3c2416_irq_unmask_second,
+};
+
+
 /* IRQ initialisation code */
 
 static int __init s3c2416_add_sub(unsigned int base,
@@ -213,6 +251,42 @@ static int __init s3c2416_add_sub(unsigned int base,
        return 0;
 }
 
+static void __init s3c2416_irq_add_second(void)
+{
+       unsigned long pend;
+       unsigned long last;
+       int irqno;
+       int i;
+
+       /* first, clear all interrupts pending... */
+       last = 0;
+       for (i = 0; i < 4; i++) {
+               pend = __raw_readl(S3C2416_INTPND2);
+
+               if (pend == 0 || pend == last)
+                       break;
+
+               __raw_writel(pend, S3C2416_SRCPND2);
+               __raw_writel(pend, S3C2416_INTPND2);
+               printk(KERN_INFO "irq: clearing pending status %08x\n",
+                      (int)pend);
+               last = pend;
+       }
+
+       for (irqno = IRQ_S3C2416_2D; irqno <= IRQ_S3C2416_I2S1; irqno++) {
+               switch (irqno) {
+               case IRQ_S3C2416_RESERVED2:
+               case IRQ_S3C2416_RESERVED3:
+                       /* no IRQ here */
+                       break;
+               default:
+                       irq_set_chip_and_handler(irqno, &s3c2416_irq_second,
+                                                handle_edge_irq);
+                       set_irq_flags(irqno, IRQF_VALID);
+               }
+       }
+}
+
 static int __init s3c2416_irq_add(struct device *dev,
                                  struct subsys_interface *sif)
 {
@@ -232,6 +306,8 @@ static int __init s3c2416_irq_add(struct device *dev,
                        &s3c2416_irq_wdtac97,
                        IRQ_S3C2443_WDT, IRQ_S3C2443_AC97);
 
+       s3c2416_irq_add_second();
+
        return 0;
 }
 
@@ -248,3 +324,25 @@ static int __init s3c2416_irq_init(void)
 
 arch_initcall(s3c2416_irq_init);
 
+#ifdef CONFIG_PM
+static struct sleep_save irq_save[] = {
+       SAVE_ITEM(S3C2416_INTMSK2),
+};
+
+int s3c2416_irq_suspend(void)
+{
+       s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
+
+       return 0;
+}
+
+void s3c2416_irq_resume(void)
+{
+       s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
+}
+
+struct syscore_ops s3c2416_irq_syscore_ops = {
+       .suspend        = s3c2416_irq_suspend,
+       .resume         = s3c2416_irq_resume,
+};
+#endif
index 7743fad..ed5a95e 100644 (file)
@@ -106,6 +106,7 @@ int __init s3c2416_init(void)
        register_syscore_ops(&s3c2416_pm_syscore_ops);
 #endif
        register_syscore_ops(&s3c24xx_irq_syscore_ops);
+       register_syscore_ops(&s3c2416_irq_syscore_ops);
 
        return device_register(&s3c2416_dev);
 }
index 179460f..acb197c 100644 (file)
@@ -27,12 +27,7 @@ static int s3c64xx_enter_idle(struct cpuidle_device *dev,
                              struct cpuidle_driver *drv,
                              int index)
 {
-       struct timeval before, after;
        unsigned long tmp;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
 
        /* Setup PWRCFG to enter idle mode */
        tmp = __raw_readl(S3C64XX_PWR_CFG);
@@ -42,42 +37,32 @@ static int s3c64xx_enter_idle(struct cpuidle_device *dev,
 
        cpu_do_idle();
 
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                   (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
        return index;
 }
 
-static struct cpuidle_state s3c64xx_cpuidle_set[] = {
-       [0] = {
-               .enter                  = s3c64xx_enter_idle,
-               .exit_latency           = 1,
-               .target_residency       = 1,
-               .flags                  = CPUIDLE_FLAG_TIME_VALID,
-               .name                   = "IDLE",
-               .desc                   = "System active, ARM gated",
-       },
-};
+static DEFINE_PER_CPU(struct cpuidle_device, s3c64xx_cpuidle_device);
 
 static struct cpuidle_driver s3c64xx_cpuidle_driver = {
-       .name           = "s3c64xx_cpuidle",
-       .owner          = THIS_MODULE,
-       .state_count    = ARRAY_SIZE(s3c64xx_cpuidle_set),
-};
-
-static struct cpuidle_device s3c64xx_cpuidle_device = {
-       .state_count    = ARRAY_SIZE(s3c64xx_cpuidle_set),
+       .name   = "s3c64xx_cpuidle",
+       .owner  = THIS_MODULE,
+       .en_core_tk_irqen = 1,
+       .states = {
+               {
+                       .enter            = s3c64xx_enter_idle,
+                       .exit_latency     = 1,
+                       .target_residency = 1,
+                       .flags            = CPUIDLE_FLAG_TIME_VALID,
+                       .name             = "IDLE",
+                       .desc             = "System active, ARM gated",
+               },
+       },
+       .state_count = 1,
 };
 
 static int __init s3c64xx_init_cpuidle(void)
 {
        int ret;
 
-       memcpy(s3c64xx_cpuidle_driver.states, s3c64xx_cpuidle_set,
-              sizeof(s3c64xx_cpuidle_set));
        cpuidle_register_driver(&s3c64xx_cpuidle_driver);
 
        ret = cpuidle_register_device(&s3c64xx_cpuidle_device);
index 0ace108..7a27f56 100644 (file)
@@ -182,6 +182,11 @@ static const struct i2c_board_info wm1277_devs[] = {
        },
 };
 
+static const struct i2c_board_info wm6230_i2c_devs[] = {
+       { I2C_BOARD_INFO("wm9081", 0x6c),
+         .platform_data = &wm9081_pdata, },
+};
+
 static __devinitdata const struct {
        u8 id;
        const char *name;
@@ -195,7 +200,9 @@ static __devinitdata const struct {
        { .id = 0x03, .name = "1252-EV1 Glenlivet" },
        { .id = 0x11, .name = "6249-EV2 Glenfarclas", },
        { .id = 0x14, .name = "6271-EV1 Lochnagar" },
-       { .id = 0x15, .name = "XXXX-EV1 Bells" },
+       { .id = 0x15, .name = "6320-EV1 Bells",
+         .i2c_devs = wm6230_i2c_devs,
+         .num_i2c_devs = ARRAY_SIZE(wm6230_i2c_devs) },
        { .id = 0x21, .name = "1275-EV1 Mortlach" },
        { .id = 0x25, .name = "1274-EV1 Glencadam" },
        { .id = 0x31, .name = "1253-EV1 Tomatin",
index eda5e02..6b20a71 100644 (file)
@@ -671,6 +671,7 @@ static struct i2c_board_info i2c_devs1[] __initdata = {
          .irq = S3C_EINT(0),
          .platform_data = &glenfarclas_pmic_pdata },
 
+       { I2C_BOARD_INFO("wlf-gf-module", 0x22) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x24) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x25) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x26) },
index 04dd092..fde26ad 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/ata_platform.h>
 #include <linux/smsc911x.h>
 #include <linux/spinlock.h>
-#include <linux/device.h>
 #include <linux/usb/isp1760.h>
 #include <linux/clkdev.h>
 #include <linux/mtd/physmap.h>
@@ -31,7 +30,6 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/timer-sp.h>
 #include <asm/hardware/sp810.h>
-#include <asm/hardware/gic.h>
 
 #include <mach/ct-ca9x4.h>
 #include <mach/motherboard.h>
index de2b5bd..7178e33 100644 (file)
@@ -24,6 +24,9 @@ extern void s3c2416_init_clocks(int xtal);
 extern  int s3c2416_baseclk_add(void);
 
 extern void s3c2416_restart(char mode, const char *cmd);
+
+extern struct syscore_ops s3c2416_irq_syscore_ops;
+
 #else
 #define s3c2416_init_clocks NULL
 #define s3c2416_init_uarts NULL
index ddb8b24..3ff21b5 100644 (file)
@@ -18,6 +18,7 @@ config PARISC
        select IRQ_PER_CPU
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select GENERIC_SMP_IDLE_THREAD
+       select GENERIC_STRNCPY_FROM_USER
 
        help
          The PA-RISC microprocessor is designed by Hewlett-Packard and used
index e8f8037..a5dc906 100644 (file)
@@ -25,7 +25,6 @@ typedef unsigned long address_t;
 #define cpu_number_map(cpu)    (cpu)
 #define cpu_logical_map(cpu)   (cpu)
 
-extern void smp_send_reschedule(int cpu);
 extern void smp_send_all_nop(void);
 
 extern void arch_send_call_function_single_ipi(int cpu);
@@ -50,6 +49,5 @@ static inline void __cpu_die (unsigned int cpu) {
   while(1)
     ;
 }
-extern int __cpu_up (unsigned int cpu);
 
 #endif /*  __ASM_SMP_H */
index 9ac0660..4ba2c93 100644 (file)
@@ -218,15 +218,14 @@ struct exception_data {
 extern unsigned long lcopy_to_user(void __user *, const void *, unsigned long);
 extern unsigned long lcopy_from_user(void *, const void __user *, unsigned long);
 extern unsigned long lcopy_in_user(void __user *, const void __user *, unsigned long);
-extern long lstrncpy_from_user(char *, const char __user *, long);
+extern long strncpy_from_user(char *, const char __user *, long);
 extern unsigned lclear_user(void __user *,unsigned long);
 extern long lstrnlen_user(const char __user *,long);
-
 /*
  * Complex access routines -- macros
  */
+#define user_addr_max() (~0UL)
 
-#define strncpy_from_user lstrncpy_from_user
 #define strnlen_user lstrnlen_user
 #define strlen_user(str) lstrnlen_user(str, 0x7fffffffL)
 #define clear_user lclear_user
index 5350342..07ef351 100644 (file)
         * entry (identifying the physical page) and %r23 up with
         * the from tlb entry (or nothing if only a to entry---for
         * clear_user_page_asm) */
-       .macro          do_alias        spc,tmp,tmp1,va,pte,prot,fault
+       .macro          do_alias        spc,tmp,tmp1,va,pte,prot,fault,patype
        cmpib,COND(<>),n 0,\spc,\fault
        ldil            L%(TMPALIAS_MAP_START),\tmp
 #if defined(CONFIG_64BIT) && (TMPALIAS_MAP_START >= 0x80000000)
         */
        cmpiclr,=       0x01,\tmp,%r0
        ldi             (_PAGE_DIRTY|_PAGE_READ|_PAGE_WRITE),\prot
-#ifdef CONFIG_64BIT
+.ifc \patype,20
        depd,z          \prot,8,7,\prot
-#else
+.else
+.ifc \patype,11
        depw,z          \prot,8,7,\prot
-#endif
+.else
+       .error "undefined PA type to do_alias"
+.endif
+.endif
        /*
         * OK, it is in the temp alias region, check whether "from" or "to".
         * Check "subtle" note in pacache.S re: r23/r26.
@@ -1189,7 +1193,7 @@ dtlb_miss_20w:
        nop
 
 dtlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,20
 
        idtlbt          pte,prot
 
@@ -1213,7 +1217,7 @@ nadtlb_miss_20w:
        nop
 
 nadtlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,20
 
        idtlbt          pte,prot
 
@@ -1245,7 +1249,7 @@ dtlb_miss_11:
        nop
 
 dtlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,11
 
        idtlba          pte,(va)
        idtlbp          prot,(va)
@@ -1277,7 +1281,7 @@ nadtlb_miss_11:
        nop
 
 nadtlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,11
 
        idtlba          pte,(va)
        idtlbp          prot,(va)
@@ -1304,7 +1308,7 @@ dtlb_miss_20:
        nop
 
 dtlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,20
        
        idtlbt          pte,prot
 
@@ -1330,7 +1334,7 @@ nadtlb_miss_20:
        nop
 
 nadtlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,20
 
        idtlbt          pte,prot
 
@@ -1457,7 +1461,7 @@ naitlb_miss_20w:
        nop
 
 naitlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault,20
 
        iitlbt          pte,prot
 
@@ -1511,7 +1515,7 @@ naitlb_miss_11:
        nop
 
 naitlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,itlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,itlb_fault,11
 
        iitlba          pte,(%sr0, va)
        iitlbp          prot,(%sr0, va)
@@ -1557,7 +1561,7 @@ naitlb_miss_20:
        nop
 
 naitlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault,20
 
        iitlbt          pte,prot
 
index a7bb757..ceec85d 100644 (file)
@@ -44,7 +44,6 @@ EXPORT_SYMBOL(__cmpxchg_u64);
 #endif
 
 #include <asm/uaccess.h>
-EXPORT_SYMBOL(lstrncpy_from_user);
 EXPORT_SYMBOL(lclear_user);
 EXPORT_SYMBOL(lstrnlen_user);
 
index fa6f2b8..64a9998 100644 (file)
@@ -50,8 +50,10 @@ SECTIONS
        . = KERNEL_BINARY_TEXT_START;
 
        _text = .;              /* Text and read-only data */
-       .text ALIGN(16) : {
+       .head ALIGN(16) : {
                HEAD_TEXT
+       } = 0
+       .text ALIGN(16) : {
                TEXT_TEXT
                SCHED_TEXT
                LOCK_TEXT
@@ -65,7 +67,7 @@ SECTIONS
                *(.fixup)
                *(.lock.text)           /* out-of-line lock text */
                *(.gnu.warning)
-       } = 0
+       }
        /* End of text section */
        _etext = .;
 
index 1bd23cc..6f2d935 100644 (file)
        bv          %r0(%r1)
        .endm
 
-       /*
-        * long lstrncpy_from_user(char *dst, const char *src, long n)
-        *
-        * Returns -EFAULT if exception before terminator,
-        *         N if the entire buffer filled,
-        *         otherwise strlen (i.e. excludes zero byte)
-        */
-
-ENTRY(lstrncpy_from_user)
-       .proc
-       .callinfo NO_CALLS
-       .entry
-       comib,=     0,%r24,$lsfu_done
-       copy        %r24,%r23
-       get_sr
-1:      ldbs,ma     1(%sr1,%r25),%r1
-$lsfu_loop:
-       stbs,ma     %r1,1(%r26)
-       comib,=,n   0,%r1,$lsfu_done
-       addib,<>,n  -1,%r24,$lsfu_loop
-2:      ldbs,ma     1(%sr1,%r25),%r1
-$lsfu_done:
-       sub         %r23,%r24,%r28
-$lsfu_exit:
-       bv          %r0(%r2)
-       nop
-       .exit
-ENDPROC(lstrncpy_from_user)
-
-       .section .fixup,"ax"
-3:      fixup_branch $lsfu_exit
-       ldi         -EFAULT,%r28
-       .previous
-
-       .section __ex_table,"aw"
-       ASM_ULONG_INSN 1b,3b
-       ASM_ULONG_INSN 2b,3b
-       .previous
-
-       .procend
-
        /*
         * unsigned long lclear_user(void *to, unsigned long n)
         *
index b403c53..a39b469 100644 (file)
@@ -87,6 +87,7 @@ config S390
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
+       select HAVE_CMPXCHG_LOCAL
        select ARCH_DISCARD_MEMBLOCK
        select ARCH_INLINE_SPIN_TRYLOCK
        select ARCH_INLINE_SPIN_TRYLOCK_BH
index e5beb49..a6ff5a8 100644 (file)
@@ -13,8 +13,6 @@
  *
  */
 
-#ifdef __KERNEL__
-
 #ifndef _LINUX_BITOPS_H
 #error only <linux/bitops.h> can be included directly
 #endif
@@ -63,7 +61,7 @@ extern const char _ni_bitmap[];
 extern const char _zb_findmap[];
 extern const char _sb_findmap[];
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define __BITOPS_ALIGN         3
 #define __BITOPS_WORDSIZE      32
@@ -83,7 +81,7 @@ extern const char _sb_findmap[];
                : "d" (__val), "Q" (*(unsigned long *) __addr)  \
                : "cc");
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define __BITOPS_ALIGN         7
 #define __BITOPS_WORDSIZE      64
@@ -103,7 +101,7 @@ extern const char _sb_findmap[];
                : "d" (__val), "Q" (*(unsigned long *) __addr)  \
                : "cc");
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define __BITOPS_WORDS(bits) (((bits)+__BITOPS_WORDSIZE-1)/__BITOPS_WORDSIZE)
 #define __BITOPS_BARRIER() asm volatile("" : : : "memory")
@@ -412,7 +410,7 @@ static inline unsigned long __ffz_word_loop(const unsigned long *addr,
        unsigned long bytes = 0;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       ahi     %1,-1\n"
                "       sra     %1,5\n"
                "       jz      1f\n"
@@ -449,7 +447,7 @@ static inline unsigned long __ffs_word_loop(const unsigned long *addr,
        unsigned long bytes = 0;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       ahi     %1,-1\n"
                "       sra     %1,5\n"
                "       jz      1f\n"
@@ -481,7 +479,7 @@ static inline unsigned long __ffs_word_loop(const unsigned long *addr,
  */
 static inline unsigned long __ffz_word(unsigned long nr, unsigned long word)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((word & 0xffffffff) == 0xffffffff) {
                word >>= 32;
                nr += 32;
@@ -505,7 +503,7 @@ static inline unsigned long __ffz_word(unsigned long nr, unsigned long word)
  */
 static inline unsigned long __ffs_word(unsigned long nr, unsigned long word)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((word & 0xffffffff) == 0) {
                word >>= 32;
                nr += 32;
@@ -546,7 +544,7 @@ static inline unsigned long __load_ulong_le(const unsigned long *p,
        unsigned long word;
 
        p = (unsigned long *)((unsigned long) p + offset);
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       ic      %0,%O1(%R1)\n"
                "       icm     %0,2,%O1+1(%R1)\n"
@@ -834,7 +832,4 @@ static inline int find_next_bit_le(void *vaddr, unsigned long size,
 
 #include <asm-generic/bitops/ext2-atomic-setbit.h>
 
-
-#endif /* __KERNEL__ */
-
 #endif /* _S390_BITOPS_H */
index fc50a33..4c8d4d5 100644 (file)
@@ -10,8 +10,6 @@
 #include <linux/spinlock.h>
 #include <asm/types.h>
 
-#ifdef __KERNEL__
-
 #define LPM_ANYPATH 0xff
 #define __MAX_CSSID 0
 
@@ -291,5 +289,3 @@ int chsc_sstpc(void *page, unsigned int op, u16 ctrl);
 int chsc_sstpi(void *page, void *result, size_t size);
 
 #endif
-
-#endif
index 81d7908..8d798e9 100644 (file)
@@ -29,7 +29,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(255 << shift)),
+                       : "d" ((x & 0xff) << shift), "d" (~(0xff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 2:
@@ -44,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(65535 << shift)),
+                       : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 4:
@@ -113,9 +113,10 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       nr      %1,%5\n"
                        "       jnz     0b\n"
                        "1:"
-                       : "=&d" (prev), "=&d" (tmp), "=Q" (*(int *) ptr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(255 << shift)), "Q" (*(int *) ptr)
+                       : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
+                       : "d" ((old & 0xff) << shift),
+                         "d" ((new & 0xff) << shift),
+                         "d" (~(0xff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 2:
@@ -134,9 +135,10 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       nr      %1,%5\n"
                        "       jnz     0b\n"
                        "1:"
-                       : "=&d" (prev), "=&d" (tmp), "=Q" (*(int *) ptr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(65535 << shift)), "Q" (*(int *) ptr)
+                       : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
+                       : "d" ((old & 0xffff) << shift),
+                         "d" ((new & 0xffff) << shift),
+                         "d" (~(0xffff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 4:
@@ -160,9 +162,14 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
        return old;
 }
 
-#define cmpxchg(ptr, o, n)                                             \
-       ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o),       \
-                                      (unsigned long)(n), sizeof(*(ptr))))
+#define cmpxchg(ptr, o, n)                                              \
+({                                                                      \
+       __typeof__(*(ptr)) __ret;                                        \
+       __ret = (__typeof__(*(ptr)))                                     \
+               __cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), \
+                         sizeof(*(ptr)));                               \
+       __ret;                                                           \
+})
 
 #ifdef CONFIG_64BIT
 #define cmpxchg64(ptr, o, n)                                           \
@@ -181,13 +188,19 @@ static inline unsigned long long __cmpxchg64(void *ptr,
                "       cds     %0,%2,%1"
                : "+&d" (rp_old), "=Q" (ptr)
                : "d" (rp_new), "Q" (ptr)
-               : "cc");
+               : "memory", "cc");
        return rp_old.pair;
 }
-#define cmpxchg64(ptr, o, n)                                           \
-       ((__typeof__(*(ptr)))__cmpxchg64((ptr),                         \
-                                        (unsigned long long)(o),       \
-                                        (unsigned long long)(n)))
+
+#define cmpxchg64(ptr, o, n)                           \
+({                                                     \
+       __typeof__(*(ptr)) __ret;                       \
+       __ret = (__typeof__(*(ptr)))                    \
+               __cmpxchg64((ptr),                      \
+                           (unsigned long long)(o),    \
+                           (unsigned long long)(n));   \
+       __ret;                                          \
+})
 #endif /* CONFIG_64BIT */
 
 #include <asm-generic/cmpxchg-local.h>
@@ -216,8 +229,13 @@ static inline unsigned long __cmpxchg_local(void *ptr,
  * them available.
  */
 #define cmpxchg_local(ptr, o, n)                                       \
-       ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \
-                       (unsigned long)(n), sizeof(*(ptr))))
+({                                                                     \
+       __typeof__(*(ptr)) __ret;                                       \
+       __ret = (__typeof__(*(ptr)))                                    \
+               __cmpxchg_local((ptr), (unsigned long)(o),              \
+                               (unsigned long)(n), sizeof(*(ptr)));    \
+       __ret;                                                          \
+})
 
 #define cmpxchg64_local(ptr, o, n)     cmpxchg64((ptr), (o), (n))
 
index 24ef186..718374d 100644 (file)
@@ -21,15 +21,15 @@ typedef unsigned long long __nocast cputime64_t;
 
 static inline unsigned long __div(unsigned long long n, unsigned long base)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = n >> 1;
        asm ("dr %0,%1" : "+d" (rp) : "d" (base >> 1));
        return rp.subreg.odd;
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        return n / base;
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 }
 
 #define cputime_one_jiffy              jiffies_to_cputime(1)
@@ -100,7 +100,7 @@ static inline void cputime_to_timespec(const cputime_t cputime,
                                       struct timespec *value)
 {
        unsigned long long __cputime = (__force unsigned long long) cputime;
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = __cputime >> 1;
@@ -128,7 +128,7 @@ static inline void cputime_to_timeval(const cputime_t cputime,
                                      struct timeval *value)
 {
        unsigned long long __cputime = (__force unsigned long long) cputime;
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = __cputime >> 1;
index ecde941..debfda3 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef __ASM_CTL_REG_H
 #define __ASM_CTL_REG_H
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 
 #define __ctl_load(array, low, high) ({                                \
        typedef struct { char _[sizeof(array)]; } addrtype;     \
@@ -25,7 +25,7 @@
                : "i" (low), "i" (high));                       \
        })
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define __ctl_load(array, low, high) ({                                \
        typedef struct { char _[sizeof(array)]; } addrtype;     \
@@ -43,7 +43,7 @@
                : "i" (low), "i" (high));                       \
        })
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define __ctl_set_bit(cr, bit) ({      \
        unsigned long __dummy;          \
index 83cf36c..7a68084 100644 (file)
 #ifndef _S390_CURRENT_H
 #define _S390_CURRENT_H
 
-#ifdef __KERNEL__
 #include <asm/lowcore.h>
 
 struct task_struct;
 
 #define current ((struct task_struct *const)S390_lowcore.current_task)
 
-#endif
-
 #endif /* !(_S390_CURRENT_H) */
index c4ee39f..06151e6 100644 (file)
 /*
  * These are used to set parameters in the core dumps.
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define ELF_CLASS      ELFCLASS32
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define ELF_CLASS      ELFCLASS64
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define ELF_DATA       ELFDATA2MSB
 #define ELF_ARCH       EM_S390
 
@@ -181,9 +181,9 @@ extern unsigned long elf_hwcap;
 extern char elf_platform[];
 #define ELF_PLATFORM (elf_platform)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define SET_PERSONALITY(ex) set_personality(PER_LINUX)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define SET_PERSONALITY(ex)                                    \
 do {                                                           \
        if (personality(current->personality) != PER_LINUX32)   \
@@ -194,7 +194,7 @@ do {                                                                \
        else                                                    \
                clear_thread_flag(TIF_31BIT);                   \
 } while (0)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define STACK_RND_MASK 0x7ffUL
 
index 81cf36b..96bc83e 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _ASM_S390_FUTEX_H
 #define _ASM_S390_FUTEX_H
 
-#ifdef __KERNEL__
-
 #include <linux/futex.h>
 #include <linux/uaccess.h>
 #include <asm/errno.h>
@@ -48,5 +46,4 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
        return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval);
 }
 
-#endif /* __KERNEL__ */
 #endif /* _ASM_S390_FUTEX_H */
index aae276d..aef0dde 100644 (file)
@@ -20,7 +20,7 @@
 #include <asm/cio.h>
 #include <asm/uaccess.h>
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
 #else
 #define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
@@ -33,7 +33,7 @@
 static inline int
 idal_is_needed(void *vaddr, unsigned int length)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        return ((__pa(vaddr) + length - 1) >> 31) != 0;
 #else
        return 0;
@@ -78,7 +78,7 @@ static inline unsigned long *idal_create_words(unsigned long *idaws,
 static inline int
 set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        unsigned int nridaws;
        unsigned long *idal;
 
@@ -105,7 +105,7 @@ set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 static inline void
 clear_normalized_cda(struct ccw1 * ccw)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (ccw->flags & CCW_FLAG_IDA) {
                kfree((void *)(unsigned long) ccw->cda);
                ccw->flags &= ~CCW_FLAG_IDA;
@@ -182,7 +182,7 @@ idal_buffer_free(struct idal_buffer *ib)
 static inline int
 __idal_buffer_is_needed(struct idal_buffer *ib)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        return ib->size > (4096ul << ib->page_order) ||
                idal_is_needed(ib->data[0], ib->size);
 #else
index 27216d3..f81a097 100644 (file)
@@ -11,8 +11,6 @@
 #ifndef _S390_IO_H
 #define _S390_IO_H
 
-#ifdef __KERNEL__
-
 #include <asm/page.h>
 
 #define IO_SPACE_LIMIT 0xffffffff
@@ -46,6 +44,4 @@ void unxlate_dev_mem_ptr(unsigned long phys, void *addr);
  */
 #define xlate_dev_kmem_ptr(p)  p
 
-#endif /* __KERNEL__ */
-
 #endif
index 5289cac..2b9d418 100644 (file)
@@ -17,7 +17,8 @@ enum interruption_class {
        EXTINT_VRT,
        EXTINT_SCP,
        EXTINT_IUC,
-       EXTINT_CPM,
+       EXTINT_CMS,
+       EXTINT_CMC,
        IOINT_CIO,
        IOINT_QAI,
        IOINT_DAS,
index 3f30dac..f4f3882 100644 (file)
 #ifndef _S390_KEXEC_H
 #define _S390_KEXEC_H
 
-#ifdef __KERNEL__
-#include <asm/page.h>
-#endif
 #include <asm/processor.h>
+#include <asm/page.h>
 /*
  * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
  * I.e. Maximum page that is mapped directly into kernel memory,
index 94ec3ee..0a88622 100644 (file)
@@ -1,8 +1,6 @@
-#ifdef __KERNEL__
 #ifndef _ASM_KMAP_TYPES_H
 #define _ASM_KMAP_TYPES_H
 
 #include <asm-generic/kmap_types.h>
 
 #endif
-#endif /* __KERNEL__ */
index 5d09e40..69bdf72 100644 (file)
@@ -49,7 +49,7 @@ static inline int init_new_context(struct task_struct *tsk,
 
 #define destroy_context(mm)             do { } while (0)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define LCTL_OPCODE "lctl"
 #else
 #define LCTL_OPCODE "lctlg"
index 1cc1c5a..f0b6b26 100644 (file)
@@ -28,7 +28,7 @@ struct mod_arch_specific
        struct mod_arch_syminfo *syminfo;
 };
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define ElfW(x) Elf64_ ## x
 #define ELFW(x) ELF64_ ## x
 #else
index d07518a..295f2c4 100644 (file)
@@ -13,7 +13,6 @@
 
 #define OS_INFO_VMCOREINFO     0
 #define OS_INFO_REIPL_BLOCK    1
-#define OS_INFO_INIT_FN                2
 
 struct os_info_entry {
        u64     addr;
@@ -28,8 +27,8 @@ struct os_info {
        u16     version_minor;
        u64     crashkernel_addr;
        u64     crashkernel_size;
-       struct os_info_entry entry[3];
-       u8      reserved[4004];
+       struct os_info_entry entry[2];
+       u8      reserved[4024];
 } __packed;
 
 void os_info_init(void);
index 0fbd189..6537e72 100644 (file)
@@ -15,7 +15,7 @@
  * per cpu area, use weak definitions to force the compiler to
  * generate external references.
  */
-#if defined(CONFIG_SMP) && defined(__s390x__) && defined(MODULE)
+#if defined(CONFIG_SMP) && defined(CONFIG_64BIT) && defined(MODULE)
 #define ARCH_NEEDS_WEAK_PER_CPU
 #endif
 
index 78e3041..43078c1 100644 (file)
@@ -48,7 +48,7 @@ static inline void crst_table_init(unsigned long *crst, unsigned long entry)
        clear_table(crst, entry, sizeof(unsigned long)*2048);
 }
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
@@ -64,7 +64,7 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 #define pgd_populate(mm, pgd, pud)             BUG()
 #define pud_populate(mm, pud, pmd)             BUG()
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
@@ -106,7 +106,7 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
        pud_val(*pud) = _REGION3_ENTRY | __pa(pmd);
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
index 011358c..b322741 100644 (file)
@@ -74,15 +74,15 @@ static inline int is_zero_pfn(unsigned long pfn)
  * table can map
  * PGDIR_SHIFT determines what a third-level page table entry can map
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 # define PMD_SHIFT     20
 # define PUD_SHIFT     20
 # define PGDIR_SHIFT   20
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 # define PMD_SHIFT     20
 # define PUD_SHIFT     31
 # define PGDIR_SHIFT   42
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define PMD_SIZE        (1UL << PMD_SHIFT)
 #define PMD_MASK        (~(PMD_SIZE-1))
@@ -98,13 +98,13 @@ static inline int is_zero_pfn(unsigned long pfn)
  * that leads to 1024 pte per pgd
  */
 #define PTRS_PER_PTE   256
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define PTRS_PER_PMD   1
 #define PTRS_PER_PUD   1
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define PTRS_PER_PMD   2048
 #define PTRS_PER_PUD   2048
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define PTRS_PER_PGD   2048
 
 #define FIRST_USER_ADDRESS  0
@@ -276,7 +276,7 @@ extern struct page *vmemmap;
  * swap pte is 1011 and 0001, 0011, 0101, 0111 are invalid.
  */
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 /* Bits in the segment table address-space-control-element */
 #define _ASCE_SPACE_SWITCH     0x80000000UL    /* space switch event       */
@@ -308,7 +308,7 @@ extern struct page *vmemmap;
 #define KVM_UR_BIT     0x00008000UL
 #define KVM_UC_BIT     0x00004000UL
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 /* Bits in the segment/region table address-space-control-element */
 #define _ASCE_ORIGIN           ~0xfffUL/* segment table origin             */
@@ -363,7 +363,7 @@ extern struct page *vmemmap;
 #define KVM_UR_BIT     0x0000800000000000UL
 #define KVM_UC_BIT     0x0000400000000000UL
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 /*
  * A user page table pointer has the space-switch-event bit, the
@@ -424,7 +424,7 @@ static inline int mm_has_pgste(struct mm_struct *mm)
 /*
  * pgd/pmd/pte query functions
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 static inline int pgd_present(pgd_t pgd) { return 1; }
 static inline int pgd_none(pgd_t pgd)    { return 0; }
@@ -434,7 +434,7 @@ static inline int pud_present(pud_t pud) { return 1; }
 static inline int pud_none(pud_t pud)   { return 0; }
 static inline int pud_bad(pud_t pud)    { return 0; }
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 static inline int pgd_present(pgd_t pgd)
 {
@@ -490,7 +490,7 @@ static inline int pud_bad(pud_t pud)
        return (pud_val(pud) & mask) != 0;
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 static inline int pmd_present(pmd_t pmd)
 {
@@ -741,7 +741,7 @@ static inline int pte_young(pte_t pte)
 
 static inline void pgd_clear(pgd_t *pgd)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
                pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
 #endif
@@ -749,7 +749,7 @@ static inline void pgd_clear(pgd_t *pgd)
 
 static inline void pud_clear(pud_t *pud)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
                pud_val(*pud) = _REGION3_ENTRY_EMPTY;
 #endif
@@ -921,7 +921,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
 static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
 {
        if (!(pte_val(*ptep) & _PAGE_INVALID)) {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                /* pto must point to the start of the segment table */
                pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
 #else
@@ -1116,7 +1116,7 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 #define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
 #define pud_deref(pmd) ({ BUG(); 0UL; })
@@ -1125,7 +1125,7 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 #define pud_offset(pgd, address) ((pud_t *) pgd)
 #define pmd_offset(pud, address) ((pmd_t *) pud + pmd_index(address))
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
 #define pud_deref(pud) (pud_val(pud) & _REGION_ENTRY_ORIGIN)
@@ -1147,7 +1147,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
        return pmd + pmd_index(address);
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define pfn_pte(pfn,pgprot) mk_pte_phys(__pa((pfn) << PAGE_SHIFT),(pgprot))
 #define pte_pfn(x) (pte_val(x) >> PAGE_SHIFT)
@@ -1196,7 +1196,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
  *  0000000000111111111122222222223333333333444444444455 5555 5 55566 66
  *  0123456789012345678901234567890123456789012345678901 2345 6 78901 23
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define __SWP_OFFSET_MASK (~0UL >> 12)
 #else
 #define __SWP_OFFSET_MASK (~0UL >> 11)
@@ -1217,11 +1217,11 @@ static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
 #define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) })
 #define __swp_entry_to_pte(x)  ((pte_t) { (x).val })
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 # define PTE_FILE_MAX_BITS     26
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 # define PTE_FILE_MAX_BITS     59
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define pte_to_pgoff(__pte) \
        ((((__pte).pte >> 12) << 7) + (((__pte).pte >> 1) & 0x7f))
index 6cbf313..20d0585 100644 (file)
@@ -20,7 +20,6 @@
 #include <asm/ptrace.h>
 #include <asm/setup.h>
 
-#ifdef __KERNEL__
 /*
  * Default implementation of macro that returns current
  * instruction pointer ("program counter").
@@ -33,39 +32,33 @@ static inline void get_cpu_id(struct cpuid *ptr)
 }
 
 extern void s390_adjust_jiffies(void);
-extern int get_cpu_capability(unsigned int *);
 extern const struct seq_operations cpuinfo_op;
 extern int sysctl_ieee_emulation_warnings;
 
 /*
  * User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit.
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define TASK_SIZE              (1UL << 31)
 #define TASK_UNMAPPED_BASE     (1UL << 30)
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define TASK_SIZE_OF(tsk)      ((tsk)->mm->context.asce_limit)
 #define TASK_UNMAPPED_BASE     (test_thread_flag(TIF_31BIT) ? \
                                        (1UL << 30) : (1UL << 41))
 #define TASK_SIZE              TASK_SIZE_OF(current)
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
-#ifdef __KERNEL__
-
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define STACK_TOP              (1UL << 31)
 #define STACK_TOP_MAX          (1UL << 31)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define STACK_TOP              (1UL << (test_thread_flag(TIF_31BIT) ? 31:42))
 #define STACK_TOP_MAX          (1UL << 42)
-#endif /* __s390x__ */
-
-
-#endif
+#endif /* CONFIG_64BIT */
 
 #define HAVE_ARCH_PICK_MMAP_LAYOUT
 
@@ -182,7 +175,7 @@ static inline void psw_set_key(unsigned int key)
  */
 static inline void __load_psw(psw_t psw)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile("lpsw  %0" : : "Q" (psw) : "cc");
 #else
        asm volatile("lpswe %0" : : "Q" (psw) : "cc");
@@ -200,7 +193,7 @@ static inline void __load_psw_mask (unsigned long mask)
 
        psw.mask = mask;
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       basr    %0,0\n"
                "0:     ahi     %0,1f-0b\n"
@@ -208,14 +201,14 @@ static inline void __load_psw_mask (unsigned long mask)
                "       lpsw    %1\n"
                "1:"
                : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        asm volatile(
                "       larl    %0,1f\n"
                "       stg     %0,%O1+8(%R1)\n"
                "       lpswe   %1\n"
                "1:"
                : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 }
 
 /*
@@ -223,7 +216,7 @@ static inline void __load_psw_mask (unsigned long mask)
  */
 static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        if (psw.addr & PSW_ADDR_AMODE)
                /* 31 bit mode */
                return (psw.addr - ilc) | PSW_ADDR_AMODE;
@@ -253,7 +246,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
          * Store status and then load disabled wait psw,
          * the processor is dead afterwards
          */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       stctl   0,0,0(%2)\n"
                "       ni      0(%2),0xef\n"   /* switch off protection */
@@ -272,7 +265,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
                "       lpsw    0(%1)"
                : "=m" (ctl_buf)
                : "a" (&dw_psw), "a" (&ctl_buf), "m" (dw_psw) : "cc");
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        asm volatile(
                "       stctg   0,0,0(%2)\n"
                "       ni      4(%2),0xef\n"   /* switch off protection */
@@ -305,7 +298,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
                "       lpswe   0(%1)"
                : "=m" (ctl_buf)
                : "a" (&dw_psw), "a" (&ctl_buf), "m" (dw_psw) : "cc", "0", "1");
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
        while (1);
 }
 
@@ -338,12 +331,10 @@ extern void (*s390_base_ext_handler_fn)(void);
 
 #define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
 
-#endif
-
 /*
  * Helper macro for exception table entries
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define EX_TABLE(_fault,_target)                       \
        ".section __ex_table,\"a\"\n"                   \
        "       .align 4\n"                             \
index d0eb465..1ceee10 100644 (file)
 #error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead"
 #endif
 
-#ifdef __KERNEL__
-
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define RWSEM_UNLOCKED_VALUE   0x00000000
 #define RWSEM_ACTIVE_BIAS      0x00000001
 #define RWSEM_ACTIVE_MASK      0x0000ffff
 #define RWSEM_WAITING_BIAS     (-0x00010000)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define RWSEM_UNLOCKED_VALUE   0x0000000000000000L
 #define RWSEM_ACTIVE_BIAS      0x0000000000000001L
 #define RWSEM_ACTIVE_MASK      0x00000000ffffffffL
 #define RWSEM_WAITING_BIAS     (-0x0000000100000000L)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
 #define RWSEM_ACTIVE_WRITE_BIAS        (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
 
@@ -65,19 +63,19 @@ static inline void __down_read(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ahi     %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       aghi    %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -93,7 +91,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     ltr     %1,%0\n"
                "       jm      1f\n"
@@ -101,7 +99,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
                "       cs      %0,%1,%2\n"
                "       jl      0b\n"
                "1:"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     ltgr    %1,%0\n"
                "       jm      1f\n"
@@ -109,7 +107,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
                "       csg     %0,%1,%2\n"
                "       jl      0b\n"
                "1:"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -125,19 +123,19 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
 
        tmp = RWSEM_ACTIVE_WRITE_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -158,19 +156,19 @@ static inline int __down_write_trylock(struct rw_semaphore *sem)
        signed long old;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%1\n"
                "0:     ltr     %0,%0\n"
                "       jnz     1f\n"
                "       cs      %0,%3,%1\n"
                "       jl      0b\n"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%1\n"
                "0:     ltgr    %0,%0\n"
                "       jnz     1f\n"
                "       csg     %0,%3,%1\n"
                "       jl      0b\n"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                "1:"
                : "=&d" (old), "=Q" (sem->count)
                : "Q" (sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS)
@@ -186,19 +184,19 @@ static inline void __up_read(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ahi     %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       aghi    %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (-RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -216,19 +214,19 @@ static inline void __up_write(struct rw_semaphore *sem)
 
        tmp = -RWSEM_ACTIVE_WRITE_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -246,19 +244,19 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
 
        tmp = -RWSEM_WAITING_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -274,19 +272,19 @@ static inline void rwsem_atomic_add(long delta, struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ar      %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       agr     %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "d" (delta)
                : "cc", "memory");
@@ -300,24 +298,23 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ar      %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       agr     %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "d" (delta)
                : "cc", "memory");
        return new;
 }
 
-#endif /* __KERNEL__ */
 #endif /* _S390_RWSEM_H */
index 7244e1f..40eb2ff 100644 (file)
 #include <asm/lowcore.h>
 #include <asm/types.h>
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define IPL_DEVICE        (*(unsigned long *)  (0x10404))
 #define INITRD_START      (*(unsigned long *)  (0x1040C))
 #define INITRD_SIZE       (*(unsigned long *)  (0x10414))
 #define OLDMEM_BASE      (*(unsigned long *)  (0x1041C))
 #define OLDMEM_SIZE      (*(unsigned long *)  (0x10424))
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define IPL_DEVICE        (*(unsigned long *)  (0x10400))
 #define INITRD_START      (*(unsigned long *)  (0x10408))
 #define INITRD_SIZE       (*(unsigned long *)  (0x10410))
 #define OLDMEM_BASE      (*(unsigned long *)  (0x10418))
 #define OLDMEM_SIZE      (*(unsigned long *)  (0x10420))
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define COMMAND_LINE      ((char *)            (0x10480))
 
 #define CHUNK_READ_WRITE 0
@@ -89,7 +89,7 @@ extern unsigned int user_mode;
 
 #define MACHINE_HAS_DIAG9C     (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define MACHINE_HAS_IEEE       (S390_lowcore.machine_flags & MACHINE_FLAG_IEEE)
 #define MACHINE_HAS_CSP                (S390_lowcore.machine_flags & MACHINE_FLAG_CSP)
 #define MACHINE_HAS_IDTE       (0)
@@ -100,7 +100,7 @@ extern unsigned int user_mode;
 #define MACHINE_HAS_PFMF       (0)
 #define MACHINE_HAS_SPP                (0)
 #define MACHINE_HAS_TOPOLOGY   (0)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define MACHINE_HAS_IEEE       (1)
 #define MACHINE_HAS_CSP                (1)
 #define MACHINE_HAS_IDTE       (S390_lowcore.machine_flags & MACHINE_FLAG_IDTE)
@@ -111,7 +111,7 @@ extern unsigned int user_mode;
 #define MACHINE_HAS_PFMF       (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF)
 #define MACHINE_HAS_SPP                (S390_lowcore.machine_flags & MACHINE_FLAG_SPP)
 #define MACHINE_HAS_TOPOLOGY   (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define ZFCPDUMP_HSA_SIZE      (32UL<<20)
 #define ZFCPDUMP_HSA_SIZE_MAX  (64UL<<20)
@@ -153,19 +153,19 @@ extern void (*_machine_power_off)(void);
 
 #else /* __ASSEMBLY__ */
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define IPL_DEVICE        0x10404
 #define INITRD_START      0x1040C
 #define INITRD_SIZE       0x10414
 #define OLDMEM_BASE      0x1041C
 #define OLDMEM_SIZE      0x10424
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define IPL_DEVICE        0x10400
 #define INITRD_START      0x10408
 #define INITRD_SIZE       0x10410
 #define OLDMEM_BASE      0x10418
 #define OLDMEM_SIZE      0x10420
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define COMMAND_LINE      0x10480
 
 #endif /* __ASSEMBLY__ */
index ca3f881..5959bfb 100644 (file)
@@ -51,7 +51,7 @@
        wl = __wl;                                      \
 })
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define udiv_qrnnd(q, r, n1, n0, d)                    \
   do { unsigned long __n;                              \
        unsigned int __r, __d;                          \
index cd0241d..8cc160c 100644 (file)
@@ -9,8 +9,6 @@
 #ifndef _S390_STRING_H_
 #define _S390_STRING_H_
 
-#ifdef __KERNEL__
-
 #ifndef _LINUX_TYPES_H
 #include <linux/types.h>
 #endif
@@ -152,6 +150,4 @@ size_t strlen(const char *s);
 size_t strnlen(const char * s, size_t n);
 #endif /* !IN_ARCH_STRING_C */
 
-#endif /* __KERNEL__ */
-
 #endif /* __S390_STRING_H_ */
index 003b04e..4e40b25 100644 (file)
@@ -9,15 +9,13 @@
 #ifndef _ASM_THREAD_INFO_H
 #define _ASM_THREAD_INFO_H
 
-#ifdef __KERNEL__
-
 /*
  * Size of kernel stack for each process
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define THREAD_ORDER 1
 #define ASYNC_ORDER  1
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #ifndef __SMALL_STACK
 #define THREAD_ORDER 2
 #define ASYNC_ORDER  2
@@ -25,7 +23,7 @@
 #define THREAD_ORDER 1
 #define ASYNC_ORDER  1
 #endif
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER)
 #define ASYNC_SIZE  (PAGE_SIZE << ASYNC_ORDER)
@@ -123,8 +121,6 @@ static inline struct thread_info *current_thread_info(void)
 #define is_32bit_task()                (1)
 #endif
 
-#endif /* __KERNEL__ */
-
 #define PREEMPT_ACTIVE         0x4000000
 
 #endif /* _ASM_THREAD_INFO_H */
index e63069b..15d6479 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef _ASM_S390_TIMER_H
 #define _ASM_S390_TIMER_H
 
-#ifdef __KERNEL__
-
 #include <linux/timer.h>
 
 #define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
@@ -50,6 +48,4 @@ extern void vtime_init(void);
 extern void vtime_stop_cpu(void);
 extern void vtime_start_leave(void);
 
-#endif /* __KERNEL__ */
-
 #endif /* _ASM_S390_TIMER_H */
index 775a5ee..06e5acb 100644 (file)
@@ -106,7 +106,7 @@ static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
 static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
                                unsigned long address)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (tlb->mm->context.asce_limit <= (1UL << 31))
                return;
        if (!tlb->fullmm)
@@ -125,7 +125,7 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
 static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
                                unsigned long address)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (tlb->mm->context.asce_limit <= (1UL << 42))
                return;
        if (!tlb->fullmm)
index 1d8648c..9fde315 100644 (file)
@@ -27,12 +27,12 @@ static inline void __tlb_flush_global(void)
        register unsigned long reg4 asm("4");
        long dummy;
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        if (!MACHINE_HAS_CSP) {
                smp_ptlb_all();
                return;
        }
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
        dummy = 0;
        reg2 = reg3 = 0;
index 05ebbcd..6c8c35f 100644 (file)
@@ -28,7 +28,7 @@ typedef __signed__ long saddr_t;
 
 #ifndef __ASSEMBLY__
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 typedef union {
        unsigned long long pair;
        struct {
@@ -37,7 +37,7 @@ typedef union {
        } subreg;
 } register_pair;
 
-#endif /* ! __s390x__   */
+#endif /* ! CONFIG_64BIT   */
 #endif /* __ASSEMBLY__  */
 #endif /* __KERNEL__    */
 #endif /* _S390_TYPES_H */
index 8f2cada..1f3a79b 100644 (file)
 
 #define segment_eq(a,b) ((a).ar4 == (b).ar4)
 
-#define __access_ok(addr, size)        \
-({                             \
-       __chk_user_ptr(addr);   \
-       1;                      \
+static inline int __range_ok(unsigned long addr, unsigned long size)
+{
+       return 1;
+}
+
+#define __access_ok(addr, size)                                \
+({                                                     \
+       __chk_user_ptr(addr);                           \
+       __range_ok((unsigned long)(addr), (size));      \
 })
 
 #define access_ok(type, addr, size) __access_ok(addr, size)
@@ -377,7 +382,7 @@ clear_user(void __user *to, unsigned long n)
 }
 
 extern int memcpy_real(void *, void *, size_t);
-extern void copy_to_absolute_zero(void *dest, void *src, size_t count);
+extern void memcpy_absolute(void *, void *, size_t);
 extern int copy_to_user_real(void __user *dest, void *src, size_t count);
 extern int copy_from_user_real(void *dest, void __user *src, size_t count);
 
index c4a11cf..a73eb2e 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef __S390_VDSO_H__
 #define __S390_VDSO_H__
 
-#ifdef __KERNEL__
-
 /* Default link addresses for the vDSOs */
 #define VDSO32_LBASE   0
 #define VDSO64_LBASE   0
@@ -45,7 +43,4 @@ void vdso_free_per_cpu(struct _lowcore *lowcore);
 #endif
 
 #endif /* __ASSEMBLY__ */
-
-#endif /* __KERNEL__ */
-
 #endif /* __S390_VDSO_H__ */
index 3aa4d00..c880ff7 100644 (file)
@@ -88,6 +88,9 @@ ENTRY(diag308_reset)
        stctg   %c0,%c15,0(%r4)
        larl    %r4,.Lfpctl             # Floating point control register
        stfpc   0(%r4)
+       larl    %r4,.Lcontinue_psw      # Save PSW flags
+       epsw    %r2,%r3
+       stm     %r2,%r3,0(%r4)
        larl    %r4,.Lrestart_psw       # Setup restart PSW at absolute 0
        lghi    %r3,0
        lg      %r4,0(%r4)              # Save PSW
@@ -103,11 +106,20 @@ ENTRY(diag308_reset)
        lctlg   %c0,%c15,0(%r4)
        larl    %r4,.Lfpctl             # Restore floating point ctl register
        lfpc    0(%r4)
+       larl    %r4,.Lcontinue_psw      # Restore PSW flags
+       lpswe   0(%r4)
+.Lcontinue:
        br      %r14
 .align 16
 .Lrestart_psw:
        .long   0x00080000,0x80000000 + .Lrestart_part2
 
+       .section .data..nosave,"aw",@progbits
+.align 8
+.Lcontinue_psw:
+       .quad   0,.Lcontinue
+       .previous
+
        .section .bss
 .align 8
 .Lctlregs:
index d84181f..6684fff 100644 (file)
@@ -237,7 +237,7 @@ static noinline __init void detect_machine_type(void)
                S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
 }
 
-static __init void early_pgm_check_handler(void)
+static void early_pgm_check_handler(void)
 {
        unsigned long addr;
        const struct exception_table_entry *fixup;
index e1ac389..796c976 100644 (file)
@@ -85,11 +85,6 @@ startup_kdump_relocated:
        basr    %r13,0
 0:
        mvc     0(8,%r0),.Lrestart_psw-0b(%r13) # Setup restart PSW
-       mvc     464(16,%r0),.Lpgm_psw-0b(%r13)  # Setup pgm check PSW
-       lhi     %r1,1                           # Start new kernel
-       diag    %r1,%r1,0x308                   # with diag 308
-
-.Lno_diag308:                                  # No diag 308
        sam31                                   # Switch to 31 bit addr mode
        sr      %r1,%r1                         # Erase register r1
        sr      %r2,%r2                         # Erase register r2
@@ -98,8 +93,6 @@ startup_kdump_relocated:
 .align 8
 .Lrestart_psw:
        .long   0x00080000,0x80000000 + startup
-.Lpgm_psw:
-       .quad   0x0000000180000000,0x0000000000000000 + .Lno_diag308
 #else
 .align 2
 .Lep_startup_kdump:
index 8342e65..2f6cfd4 100644 (file)
@@ -1528,12 +1528,15 @@ static struct shutdown_action __refdata dump_action = {
 
 static void dump_reipl_run(struct shutdown_trigger *trigger)
 {
-       u32 csum;
-
-       csum = csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0);
-       copy_to_absolute_zero(&S390_lowcore.ipib_checksum, &csum, sizeof(csum));
-       copy_to_absolute_zero(&S390_lowcore.ipib, &reipl_block_actual,
-                             sizeof(reipl_block_actual));
+       struct {
+               void    *addr;
+               __u32   csum;
+       } __packed ipib;
+
+       ipib.csum = csum_partial(reipl_block_actual,
+                                reipl_block_actual->hdr.len, 0);
+       ipib.addr = reipl_block_actual;
+       memcpy_absolute(&S390_lowcore.ipib, &ipib, sizeof(ipib));
        dump_run(trigger);
 }
 
@@ -1750,6 +1753,7 @@ static struct kobj_attribute on_restart_attr =
 
 static void __do_restart(void *ignore)
 {
+       __arch_local_irq_stosm(0x04); /* enable DAT */
        smp_send_stop();
 #ifdef CONFIG_CRASH_DUMP
        crash_kexec(NULL);
index 8a22c27..b4f4a71 100644 (file)
@@ -42,7 +42,8 @@ static const struct irq_class intrclass_names[] = {
        {.name = "VRT", .desc = "[EXT] Virtio" },
        {.name = "SCP", .desc = "[EXT] Service Call" },
        {.name = "IUC", .desc = "[EXT] IUCV" },
-       {.name = "CPM", .desc = "[EXT] CPU Measurement" },
+       {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling" },
+       {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter" },
        {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
        {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
        {.name = "DAS", .desc = "[I/O] DASD" },
index bdad47d..cdacf8f 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/ipl.h>
 #include <asm/diag.h>
 #include <asm/asm-offsets.h>
+#include <asm/os_info.h>
 
 typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long);
 
@@ -79,8 +80,8 @@ static void __do_machine_kdump(void *image)
 #ifdef CONFIG_CRASH_DUMP
        int (*start_kdump)(int) = (void *)((struct kimage *) image)->start;
 
-       __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
        setup_regs();
+       __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
        start_kdump(1);
 #endif
 }
@@ -114,8 +115,13 @@ static void crash_map_pages(int enable)
               size % KEXEC_CRASH_MEM_ALIGN);
        if (enable)
                vmem_add_mapping(crashk_res.start, size);
-       else
+       else {
                vmem_remove_mapping(crashk_res.start, size);
+               if (size)
+                       os_info_crashkernel_add(crashk_res.start, size);
+               else
+                       os_info_crashkernel_add(0, 0);
+       }
 }
 
 /*
@@ -208,6 +214,7 @@ static void __machine_kexec(void *data)
 {
        struct kimage *image = data;
 
+       __arch_local_irq_stosm(0x04); /* enable DAT */
        pfault_fini();
        tracing_off();
        debug_locks_off();
index e8d6c21..95fa5ac 100644 (file)
@@ -60,7 +60,7 @@ void __init os_info_init(void)
        os_info.version_minor = OS_INFO_VERSION_MINOR;
        os_info.magic = OS_INFO_MAGIC;
        os_info.csum = os_info_csum(&os_info);
-       copy_to_absolute_zero(&S390_lowcore.os_info, &ptr, sizeof(ptr));
+       memcpy_absolute(&S390_lowcore.os_info, &ptr, sizeof(ptr));
 }
 
 #ifdef CONFIG_CRASH_DUMP
@@ -138,7 +138,6 @@ static void os_info_old_init(void)
                goto fail_free;
        os_info_old_alloc(OS_INFO_VMCOREINFO, 1);
        os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1);
-       os_info_old_alloc(OS_INFO_INIT_FN, PAGE_SIZE);
        pr_info("crashkernel: addr=0x%lx size=%lu\n",
                (unsigned long) os_info_old->crashkernel_addr,
                (unsigned long) os_info_old->crashkernel_size);
index cb019f4..9871b19 100644 (file)
@@ -225,7 +225,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
        if (!(alert & CPU_MF_INT_CF_MASK))
                return;
 
-       kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
+       kstat_cpu(smp_processor_id()).irqs[EXTINT_CMC]++;
        cpuhw = &__get_cpu_var(cpu_hw_events);
 
        /* Measurement alerts are shared and might happen when the PMU
index 06264ae..489d1d8 100644 (file)
@@ -428,10 +428,12 @@ static void __init setup_lowcore(void)
        lc->restart_fn = (unsigned long) do_restart;
        lc->restart_data = 0;
        lc->restart_source = -1UL;
-       memcpy(&S390_lowcore.restart_stack, &lc->restart_stack,
-              4*sizeof(unsigned long));
-       copy_to_absolute_zero(&S390_lowcore.restart_psw,
-                             &lc->restart_psw, sizeof(psw_t));
+
+       /* Setup absolute zero lowcore */
+       memcpy_absolute(&S390_lowcore.restart_stack, &lc->restart_stack,
+                       4 * sizeof(unsigned long));
+       memcpy_absolute(&S390_lowcore.restart_psw, &lc->restart_psw,
+                       sizeof(lc->restart_psw));
 
        set_prefix((u32)(unsigned long) lc);
        lowcore_ptr[0] = lc;
@@ -598,7 +600,7 @@ static void __init setup_vmcoreinfo(void)
 #ifdef CONFIG_KEXEC
        unsigned long ptr = paddr_vmcoreinfo_note();
 
-       copy_to_absolute_zero(&S390_lowcore.vmcore_info, &ptr, sizeof(ptr));
+       memcpy_absolute(&S390_lowcore.vmcore_info, &ptr, sizeof(ptr));
 #endif
 }
 
index 647ba94..15cca26 100644 (file)
@@ -297,26 +297,27 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
 static void pcpu_delegate(struct pcpu *pcpu, void (*func)(void *),
                          void *data, unsigned long stack)
 {
-       struct _lowcore *lc = pcpu->lowcore;
-       unsigned short this_cpu;
+       struct _lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
+       struct {
+               unsigned long   stack;
+               void            *func;
+               void            *data;
+               unsigned long   source;
+       } restart = { stack, func, data, stap() };
 
        __load_psw_mask(psw_kernel_bits);
-       this_cpu = stap();
-       if (pcpu->address == this_cpu)
+       if (pcpu->address == restart.source)
                func(data);     /* should not return */
        /* Stop target cpu (if func returns this stops the current cpu). */
        pcpu_sigp_retry(pcpu, sigp_stop, 0);
        /* Restart func on the target cpu and stop the current cpu. */
-       lc->restart_stack = stack;
-       lc->restart_fn = (unsigned long) func;
-       lc->restart_data = (unsigned long) data;
-       lc->restart_source = (unsigned long) this_cpu;
+       memcpy_absolute(&lc->restart_stack, &restart, sizeof(restart));
        asm volatile(
                "0:     sigp    0,%0,6  # sigp restart to target cpu\n"
                "       brc     2,0b    # busy, try again\n"
                "1:     sigp    0,%1,5  # sigp stop to current cpu\n"
                "       brc     2,1b    # busy, try again\n"
-               : : "d" (pcpu->address), "d" (this_cpu) : "0", "1", "cc");
+               : : "d" (pcpu->address), "d" (restart.source) : "0", "1", "cc");
        for (;;) ;
 }
 
@@ -800,17 +801,6 @@ void __noreturn cpu_die(void)
 
 #endif /* CONFIG_HOTPLUG_CPU */
 
-static void smp_call_os_info_init_fn(void)
-{
-       int (*init_fn)(void);
-       unsigned long size;
-
-       init_fn = os_info_old_entry(OS_INFO_INIT_FN, &size);
-       if (!init_fn)
-               return;
-       init_fn();
-}
-
 void __init smp_prepare_cpus(unsigned int max_cpus)
 {
        /* request the 0x1201 emergency signal external interrupt */
@@ -819,7 +809,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
        /* request the 0x1202 external call external interrupt */
        if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0)
                panic("Couldn't request external interrupt 0x1202");
-       smp_call_os_info_init_fn();
        smp_detect_cpus();
 }
 
@@ -943,19 +932,6 @@ static struct attribute_group cpu_common_attr_group = {
        .attrs = cpu_common_attrs,
 };
 
-static ssize_t show_capability(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       unsigned int capability;
-       int rc;
-
-       rc = get_cpu_capability(&capability);
-       if (rc)
-               return rc;
-       return sprintf(buf, "%u\n", capability);
-}
-static DEVICE_ATTR(capability, 0444, show_capability, NULL);
-
 static ssize_t show_idle_count(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -993,7 +969,6 @@ static ssize_t show_idle_time(struct device *dev,
 static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
 
 static struct attribute *cpu_online_attrs[] = {
-       &dev_attr_capability.attr,
        &dev_attr_idle_count.attr,
        &dev_attr_idle_time_us.attr,
        NULL,
index 2a94b77..fa0eb23 100644 (file)
@@ -392,27 +392,6 @@ static __init int create_proc_service_level(void)
 }
 subsys_initcall(create_proc_service_level);
 
-/*
- * Bogomips calculation based on cpu capability.
- */
-int get_cpu_capability(unsigned int *capability)
-{
-       struct sysinfo_1_2_2 *info;
-       int rc;
-
-       info = (void *) get_zeroed_page(GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-       rc = stsi(info, 1, 2, 2);
-       if (rc == -ENOSYS)
-               goto out;
-       rc = 0;
-       *capability = info->capability;
-out:
-       free_page((unsigned long) info);
-       return rc;
-}
-
 /*
  * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
  */
index 60455f1..58a75a8 100644 (file)
@@ -14,7 +14,7 @@
 #include <asm/futex.h>
 #include "uaccess.h"
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define AHI    "ahi"
 #define ALR    "alr"
 #define CLR    "clr"
index bb1a7ee..57e9429 100644 (file)
@@ -15,7 +15,7 @@
 #include <asm/futex.h>
 #include "uaccess.h"
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define AHI    "ahi"
 #define ALR    "alr"
 #define CLR    "clr"
index 795a0a9..921fa54 100644 (file)
@@ -101,19 +101,27 @@ int memcpy_real(void *dest, void *src, size_t count)
 }
 
 /*
- * Copy memory to absolute zero
+ * Copy memory in absolute mode (kernel to kernel)
  */
-void copy_to_absolute_zero(void *dest, void *src, size_t count)
+void memcpy_absolute(void *dest, void *src, size_t count)
 {
-       unsigned long cr0;
+       unsigned long cr0, flags, prefix;
 
-       BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
-       preempt_disable();
+       flags = arch_local_irq_save();
        __ctl_store(cr0, 0, 0);
        __ctl_clear_bit(0, 28); /* disable lowcore protection */
-       memcpy_real(dest + store_prefix(), src, count);
+       prefix = store_prefix();
+       if (prefix) {
+               local_mcck_disable();
+               set_prefix(0);
+               memcpy(dest, src, count);
+               set_prefix(prefix);
+               local_mcck_enable();
+       } else {
+               memcpy(dest, src, count);
+       }
        __ctl_load(cr0, 0, 0);
-       preempt_enable();
+       arch_local_irq_restore(flags);
 }
 
 /*
@@ -187,20 +195,6 @@ static int is_swapped(unsigned long addr)
        return 0;
 }
 
-/*
- * Return swapped prefix or zero page address
- */
-static unsigned long get_swapped(unsigned long addr)
-{
-       unsigned long prefix = store_prefix();
-
-       if (addr < sizeof(struct _lowcore))
-               return addr + prefix;
-       if (addr >= prefix && addr < prefix + sizeof(struct _lowcore))
-               return addr - prefix;
-       return addr;
-}
-
 /*
  * Convert a physical pointer for /dev/mem access
  *
@@ -218,7 +212,7 @@ void *xlate_dev_mem_ptr(unsigned long addr)
                size = PAGE_SIZE - (addr & ~PAGE_MASK);
                bounce = (void *) __get_free_page(GFP_ATOMIC);
                if (bounce)
-                       memcpy_real(bounce, (void *) get_swapped(addr), size);
+                       memcpy_absolute(bounce, (void *) addr, size);
        }
        preempt_enable();
        put_online_cpus();
index 4799383..71ae20d 100644 (file)
@@ -109,7 +109,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
                pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
                pm_dir = pmd_offset(pu_dir, address);
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
                if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
                    (address + HPAGE_SIZE <= start + size) &&
                    (address >= HPAGE_SIZE)) {
index c6646de..a4a89fa 100644 (file)
@@ -235,7 +235,7 @@ static void hws_ext_handler(struct ext_code ext_code,
        if (!(param32 & CPU_MF_INT_SF_MASK))
                return;
 
-       kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
+       kstat_cpu(smp_processor_id()).irqs[EXTINT_CMS]++;
        atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);
 
        if (hws_wq)
index 83bd051..e74ff13 100644 (file)
@@ -41,7 +41,6 @@ config SPARC32
        def_bool !64BIT
        select GENERIC_ATOMIC64
        select CLZ_TAB
-       select ARCH_USES_GETTIMEOFFSET
 
 config SPARC64
        def_bool 64BIT
index cbb93e5..61ebe74 100644 (file)
 #define ASI_M_UNA01         0x01   /* Same here... */
 #define ASI_M_MXCC          0x02   /* Access to TI VIKING MXCC registers */
 #define ASI_M_FLUSH_PROBE   0x03   /* Reference MMU Flush/Probe; rw, ss */
-#ifndef CONFIG_SPARC_LEON
 #define ASI_M_MMUREGS       0x04   /* MMU Registers; rw, ss */
-#else
-#define ASI_M_MMUREGS       0x19
-#endif /* CONFIG_SPARC_LEON */
 #define ASI_M_TLBDIAG       0x05   /* MMU TLB only Diagnostics */
 #define ASI_M_DIAGS         0x06   /* Reference MMU Diagnostics */
 #define ASI_M_IODIAG        0x07   /* MMU I/O TLB only Diagnostics */
index 02a172f..a0e28ef 100644 (file)
 /* All traps low-level code here must end with this macro. */
 #define RESTORE_ALL b ret_trap_entry; clr %l6;
 
+/* Support for run-time patching of single instructions.
+ * This is used to handle the differences in the ASI for
+ * MMUREGS for LEON and SUN.
+ *
+ * Sample:
+ * LEON_PI(lda [%g0] ASI_LEON_MMUREGS, %o0
+ * SUN_PI_(lda [%g0] ASI_M_MMUREGS, %o0
+ * PI == Patch Instruction
+ *
+ * For LEON we will use the first variant,
+ * and for all other we will use the SUN variant.
+ * The order is important.
+ */
+#define LEON_PI(...)                           \
+662:   __VA_ARGS__
+
+#define SUN_PI_(...)                           \
+       .section .leon_1insn_patch, "ax";       \
+       .word 662b;                             \
+       __VA_ARGS__;                            \
+       .previous
+
 #endif /* !(_SPARC_ASMMACRO_H) */
index 48a7c65..8493fd3 100644 (file)
@@ -12,13 +12,18 @@ extern int dma_supported(struct device *dev, u64 mask);
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
-extern struct dma_map_ops *dma_ops, pci32_dma_ops;
+extern struct dma_map_ops *dma_ops;
+extern struct dma_map_ops *leon_dma_ops;
+extern struct dma_map_ops pci32_dma_ops;
+
 extern struct bus_type pci_bus_type;
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 {
 #if defined(CONFIG_SPARC32) && defined(CONFIG_PCI)
-       if (dev->bus == &pci_bus_type)
+       if (sparc_cpu_model == sparc_leon)
+               return leon_dma_ops;
+       else if (dev->bus == &pci_bus_type)
                return &pci32_dma_ops;
 #endif
        return dma_ops;
index 0765912..3375c62 100644 (file)
@@ -8,8 +8,6 @@
 #ifndef LEON_H_INCLUDE
 #define LEON_H_INCLUDE
 
-#ifdef CONFIG_SPARC_LEON
-
 /* mmu register access, ASI_LEON_MMUREGS */
 #define LEON_CNR_CTRL          0x000
 #define LEON_CNR_CTXP          0x100
 
 #ifndef __ASSEMBLY__
 
-/* do a virtual address read without cache */
-static inline unsigned long leon_readnobuffer_reg(unsigned long paddr)
-{
-       unsigned long retval;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r"(retval) : "r"(paddr), "i"(ASI_LEON_NOCACHE));
-       return retval;
-}
-
 /* do a physical address bypass write, i.e. for 0x80000000 */
 static inline void leon_store_reg(unsigned long paddr, unsigned long value)
 {
@@ -87,47 +76,16 @@ static inline unsigned long leon_load_reg(unsigned long paddr)
        return retval;
 }
 
-static inline void leon_srmmu_disabletlb(void)
-{
-       unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %2, %0\n\t" : "=r"(retval) : "r"(0),
-                            "i"(ASI_LEON_MMUREGS));
-       retval |= LEON_CNR_CTRL_TLBDIS;
-       __asm__ __volatile__("sta %0, [%%g0] %2\n\t" : : "r"(retval), "r"(0),
-                            "i"(ASI_LEON_MMUREGS) : "memory");
-}
-
-static inline void leon_srmmu_enabletlb(void)
-{
-       unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %2, %0\n\t" : "=r"(retval) : "r"(0),
-                            "i"(ASI_LEON_MMUREGS));
-       retval = retval & ~LEON_CNR_CTRL_TLBDIS;
-       __asm__ __volatile__("sta %0, [%%g0] %2\n\t" : : "r"(retval), "r"(0),
-                            "i"(ASI_LEON_MMUREGS) : "memory");
-}
-
 /* macro access for leon_load_reg() and leon_store_reg() */
 #define LEON3_BYPASS_LOAD_PA(x)            (leon_load_reg((unsigned long)(x)))
 #define LEON3_BYPASS_STORE_PA(x, v) (leon_store_reg((unsigned long)(x), (unsigned long)(v)))
-#define LEON3_BYPASS_ANDIN_PA(x, v) LEON3_BYPASS_STORE_PA(x, LEON3_BYPASS_LOAD_PA(x) & v)
-#define LEON3_BYPASS_ORIN_PA(x, v)  LEON3_BYPASS_STORE_PA(x, LEON3_BYPASS_LOAD_PA(x) | v)
 #define LEON_BYPASS_LOAD_PA(x)      leon_load_reg((unsigned long)(x))
 #define LEON_BYPASS_STORE_PA(x, v)  leon_store_reg((unsigned long)(x), (unsigned long)(v))
-#define LEON_REGLOAD_PA(x)          leon_load_reg((unsigned long)(x)+LEON_PREGS)
-#define LEON_REGSTORE_PA(x, v)      leon_store_reg((unsigned long)(x)+LEON_PREGS, (unsigned long)(v))
-#define LEON_REGSTORE_OR_PA(x, v)   LEON_REGSTORE_PA(x, LEON_REGLOAD_PA(x) | (unsigned long)(v))
-#define LEON_REGSTORE_AND_PA(x, v)  LEON_REGSTORE_PA(x, LEON_REGLOAD_PA(x) & (unsigned long)(v))
-
-/* macro access for leon_readnobuffer_reg() */
-#define LEON_BYPASSCACHE_LOAD_VA(x) leon_readnobuffer_reg((unsigned long)(x))
 
 extern void leon_init(void);
 extern void leon_switch_mm(void);
 extern void leon_init_IRQ(void);
 
-extern unsigned long last_valid_pfn;
-
 static inline unsigned long sparc_leon3_get_dcachecfg(void)
 {
        unsigned int retval;
@@ -230,9 +188,6 @@ static inline int sparc_leon3_cpuid(void)
 #error cannot determine LEON_PAGE_SIZE_LEON
 #endif
 
-#define PAGE_MIN_SHIFT   (12)
-#define PAGE_MIN_SIZE    (1UL << PAGE_MIN_SHIFT)
-
 #define LEON3_XCCR_SETS_MASK  0x07000000UL
 #define LEON3_XCCR_SSIZE_MASK 0x00f00000UL
 
@@ -242,7 +197,7 @@ static inline int sparc_leon3_cpuid(void)
 #ifndef __ASSEMBLY__
 struct vm_area_struct;
 
-extern unsigned long srmmu_swprobe(unsigned long vaddr, unsigned long *paddr);
+extern unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr);
 extern void leon_flush_icache_all(void);
 extern void leon_flush_dcache_all(void);
 extern void leon_flush_cache_all(void);
@@ -258,15 +213,7 @@ struct leon3_cacheregs {
        unsigned long dccr;     /* 0x0c - Data Cache Configuration Register */
 };
 
-/* struct that hold LEON2 cache configuration register
- * & configuration register
- */
-struct leon2_cacheregs {
-       unsigned long ccr, cfg;
-};
-
-#ifdef __KERNEL__
-
+#include <linux/irq.h>
 #include <linux/interrupt.h>
 
 struct device_node;
@@ -292,24 +239,15 @@ extern void leon_smp_done(void);
 extern void leon_boot_cpus(void);
 extern int leon_boot_one_cpu(int i, struct task_struct *);
 void leon_init_smp(void);
-extern void cpu_idle(void);
-extern void init_IRQ(void);
-extern void cpu_panic(void);
-extern int __leon_processor_id(void);
 void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu);
 extern irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused);
 
-extern unsigned int real_irq_entry[];
 extern unsigned int smpleon_ipi[];
-extern unsigned int patchme_maybe_smp_msg[];
-extern unsigned int t_nmi[], linux_trap_ipi15_leon[];
-extern unsigned int linux_trap_ipi15_sun4m[];
+extern unsigned int linux_trap_ipi15_leon[];
 extern int leon_ipi_irq;
 
 #endif /* CONFIG_SMP */
 
-#endif /* __KERNEL__ */
-
 #endif /* __ASSEMBLY__ */
 
 /* macros used in leon_mm.c */
@@ -317,18 +255,4 @@ extern int leon_ipi_irq;
 #define _pfn_valid(pfn)         ((pfn < last_valid_pfn) && (pfn >= PFN(phys_base)))
 #define _SRMMU_PTE_PMASK_LEON 0xffffffff
 
-#else /* defined(CONFIG_SPARC_LEON) */
-
-/* nop definitions for !LEON case */
-#define leon_init() do {} while (0)
-#define leon_switch_mm() do {} while (0)
-#define leon_init_IRQ() do {} while (0)
-#define init_leon() do {} while (0)
-#define leon_smp_done() do {} while (0)
-#define leon_boot_cpus() do {} while (0)
-#define leon_boot_one_cpu(i, t) 1
-#define leon_init_smp() do {} while (0)
-
-#endif /* !defined(CONFIG_SPARC_LEON) */
-
 #endif
index e50f326..f3034ed 100644 (file)
@@ -87,8 +87,6 @@ struct amba_prom_registers {
 #define LEON3_GPTIMER_CONFIG_NRTIMERS(c) ((c)->config & 0x7)
 #define LEON3_GPTIMER_CTRL_ISPENDING(r)  (((r)&LEON3_GPTIMER_CTRL_PENDING) ? 1 : 0)
 
-#ifdef CONFIG_SPARC_LEON
-
 #ifndef __ASSEMBLY__
 
 struct leon3_irqctrl_regs_map {
@@ -264,6 +262,4 @@ extern unsigned int sparc_leon_eirq;
 
 #define amba_device(x) (((x) >> 12) & 0xfff)
 
-#endif /* !defined(CONFIG_SPARC_LEON) */
-
 #endif
index cb82870..79da178 100644 (file)
         restore %g0, %g0, %g0;
 
 #ifndef __ASSEMBLY__
+extern unsigned long last_valid_pfn;
 
 /* This makes sense. Honest it does - Anton */
 /* XXX Yes but it's ugly as sin.  FIXME. -KMW */
@@ -148,67 +149,13 @@ extern void *srmmu_nocache_pool;
 #define __nocache_fix(VADDR) __va(__nocache_pa(VADDR))
 
 /* Accessing the MMU control register. */
-static inline unsigned int srmmu_get_mmureg(void)
-{
-        unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %1, %0\n\t" :
-                            "=r" (retval) :
-                            "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline void srmmu_set_mmureg(unsigned long regval)
-{
-       __asm__ __volatile__("sta %0, [%%g0] %1\n\t" : :
-                            "r" (regval), "i" (ASI_M_MMUREGS) : "memory");
-
-}
-
-static inline void srmmu_set_ctable_ptr(unsigned long paddr)
-{
-       paddr = ((paddr >> 4) & SRMMU_CTX_PMASK);
-       __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-                            "r" (paddr), "r" (SRMMU_CTXTBL_PTR),
-                            "i" (ASI_M_MMUREGS) :
-                            "memory");
-}
-
-static inline void srmmu_set_context(int context)
-{
-       __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-                            "r" (context), "r" (SRMMU_CTX_REG),
-                            "i" (ASI_M_MMUREGS) : "memory");
-}
-
-static inline int srmmu_get_context(void)
-{
-       register int retval;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_CTX_REG),
-                            "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline unsigned int srmmu_get_fstatus(void)
-{
-       unsigned int retval;
-
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline unsigned int srmmu_get_faddr(void)
-{
-       unsigned int retval;
-
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_FAULT_ADDR), "i" (ASI_M_MMUREGS));
-       return retval;
-}
+unsigned int srmmu_get_mmureg(void);
+void srmmu_set_mmureg(unsigned long regval);
+void srmmu_set_ctable_ptr(unsigned long paddr);
+void srmmu_set_context(int context);
+int srmmu_get_context(void);
+unsigned int srmmu_get_fstatus(void);
+unsigned int srmmu_get_faddr(void);
 
 /* This is guaranteed on all SRMMU's. */
 static inline void srmmu_flush_whole_tlb(void)
@@ -219,23 +166,6 @@ static inline void srmmu_flush_whole_tlb(void)
 
 }
 
-/* These flush types are not available on all chips... */
-#ifndef CONFIG_SPARC_LEON
-static inline unsigned long srmmu_hwprobe(unsigned long vaddr)
-{
-       unsigned long retval;
-
-       vaddr &= PAGE_MASK;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
-
-       return retval;
-}
-#else
-#define srmmu_hwprobe(addr) srmmu_swprobe(addr, 0)
-#endif
-
 static inline int
 srmmu_get_pte (unsigned long addr)
 {
index b8c0e5f..cee7ed9 100644 (file)
 #define PSR_VERS    0x0f000000         /* cpu-version field          */
 #define PSR_IMPL    0xf0000000         /* cpu-implementation field   */
 
+#define PSR_VERS_SHIFT         24
+#define PSR_IMPL_SHIFT         28
+#define PSR_VERS_SHIFTED_MASK  0xf
+#define PSR_IMPL_SHIFTED_MASK  0xf
+
+#define PSR_IMPL_TI            0x4
+#define PSR_IMPL_LEON          0xf
+
 #ifdef __KERNEL__
 
 #ifndef __ASSEMBLY__
index 0b0553b..f300d1a 100644 (file)
@@ -7,4 +7,7 @@
 /* sparc entry point */
 extern char _start[];
 
+extern char __leon_1insn_patch[];
+extern char __leon_1insn_patch_end[];
+
 #endif
index 72308f9..6cf591b 100644 (file)
@@ -51,8 +51,8 @@ obj-y                   += of_device_common.o
 obj-y                   += of_device_$(BITS).o
 obj-$(CONFIG_SPARC64)   += prom_irqtrans.o
 
-obj-$(CONFIG_SPARC_LEON)+= leon_kernel.o
-obj-$(CONFIG_SPARC_LEON)+= leon_pmc.o
+obj-$(CONFIG_SPARC32)   += leon_kernel.o
+obj-$(CONFIG_SPARC32)   += leon_pmc.o
 
 obj-$(CONFIG_SPARC64)   += reboot.o
 obj-$(CONFIG_SPARC64)   += sysfs.o
index 2d18196..a6c94a2 100644 (file)
@@ -121,7 +121,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
                FPU(-1, NULL)
        }
 },{
-       4,
+       PSR_IMPL_TI,
        .cpu_info = {
                CPU(0, "Texas Instruments, Inc. - SuperSparc-(II)"),
                /* SparcClassic  --  borned STP1010TAB-50*/
@@ -191,7 +191,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
                FPU(-1, NULL)
        }
 },{
-       0xF,            /* Aeroflex Gaisler */
+       PSR_IMPL_LEON,          /* Aeroflex Gaisler */
        .cpu_info = {
                CPU(3, "LEON"),
                CPU(-1, NULL)
@@ -440,16 +440,16 @@ static int __init cpu_type_probe(void)
        int psr_impl, psr_vers, fpu_vers;
        int psr;
 
-       psr_impl = ((get_psr() >> 28) & 0xf);
-       psr_vers = ((get_psr() >> 24) & 0xf);
+       psr_impl = ((get_psr() >> PSR_IMPL_SHIFT) & PSR_IMPL_SHIFTED_MASK);
+       psr_vers = ((get_psr() >> PSR_VERS_SHIFT) & PSR_VERS_SHIFTED_MASK);
 
        psr = get_psr();
        put_psr(psr | PSR_EF);
-#ifdef CONFIG_SPARC_LEON
-       fpu_vers = get_psr() & PSR_EF ? ((get_fsr() >> 17) & 0x7) : 7;
-#else
-       fpu_vers = ((get_fsr() >> 17) & 0x7);
-#endif
+
+       if (psr_impl == PSR_IMPL_LEON)
+               fpu_vers = get_psr() & PSR_EF ? ((get_fsr() >> 17) & 0x7) : 7;
+       else
+               fpu_vers = ((get_fsr() >> 17) & 0x7);
 
        put_psr(psr);
 
index 2dbe180..dcaa1cf 100644 (file)
@@ -393,7 +393,6 @@ linux_trap_ipi15_sun4d:
        /* FIXME */
 1:     b,a     1b
 
-#ifdef CONFIG_SPARC_LEON
        .globl  smpleon_ipi
        .extern leon_ipi_interrupt
        /* SMP per-cpu IPI interrupts are handled specially. */
@@ -424,8 +423,6 @@ linux_trap_ipi15_leon:
        b       ret_trap_lockless_ipi
         clr    %l6
 
-#endif /* CONFIG_SPARC_LEON */
-
 #endif /* CONFIG_SMP */
 
        /* This routine handles illegal instructions and privileged
@@ -770,8 +767,11 @@ srmmu_fault:
        mov     0x400, %l5
        mov     0x300, %l4
 
-       lda     [%l5] ASI_M_MMUREGS, %l6        ! read sfar first
-       lda     [%l4] ASI_M_MMUREGS, %l5        ! read sfsr last
+LEON_PI(lda    [%l5] ASI_LEON_MMUREGS, %l6)    ! read sfar first
+SUN_PI_(lda    [%l5] ASI_M_MMUREGS, %l6)       ! read sfar first
+
+LEON_PI(lda    [%l4] ASI_LEON_MMUREGS, %l5)    ! read sfsr last
+SUN_PI_(lda    [%l4] ASI_M_MMUREGS, %l5)       ! read sfsr last
 
        andn    %l6, 0xfff, %l6
        srl     %l5, 6, %l5                     ! and encode all info into l7
index 84b5f0d..e3e80d6 100644 (file)
@@ -234,7 +234,8 @@ tsetup_srmmu_stackchk:
 
        cmp     %glob_tmp, %sp
        bleu,a  1f
-        lda    [%g0] ASI_M_MMUREGS, %glob_tmp          ! read MMU control
+LEON_PI( lda   [%g0] ASI_LEON_MMUREGS, %glob_tmp)      ! read MMU control
+SUN_PI_( lda   [%g0] ASI_M_MMUREGS, %glob_tmp)         ! read MMU control
 
 trap_setup_user_stack_is_bolixed:
        /* From user/kernel into invalid window w/bad user
@@ -249,18 +250,25 @@ trap_setup_user_stack_is_bolixed:
 1:
        /* Clear the fault status and turn on the no_fault bit. */
        or      %glob_tmp, 0x2, %glob_tmp               ! or in no_fault bit
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS          ! set it
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)              ! set it
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)         ! set it
 
        /* Dump the registers and cross fingers. */
        STORE_WINDOW(sp)
 
        /* Clear the no_fault bit and check the status. */
        andn    %glob_tmp, 0x2, %glob_tmp
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)
+
        mov     AC_M_SFAR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)
+
        mov     AC_M_SFSR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %glob_tmp    ! save away status of winstore
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %glob_tmp)! save away status of winstore
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %glob_tmp)   ! save away status of winstore
+
        andcc   %glob_tmp, 0x2, %g0                     ! did we fault?
        bne     trap_setup_user_stack_is_bolixed        ! failure
         nop
index a0f5c20..afeb1d7 100644 (file)
  * the cpu-type
  */
        .align 4
-cputyp:
-        .word   1
-
-       .align 4
        .globl cputypval
 cputypval:
        .asciz "sun4m"
@@ -46,8 +42,8 @@ cputypvar:
 
        .align 4
 
-sun4c_notsup:
-       .asciz  "Sparc-Linux sun4/sun4c support does no longer exist.\n\n"
+notsup:
+       .asciz  "Sparc-Linux sun4/sun4c or MMU-less not supported\n\n"
        .align 4
 
 sun4e_notsup:
@@ -123,7 +119,7 @@ current_pc:
                tst     %o0
                be      no_sun4u_here
                 mov    %g4, %o7                /* Previous %o7. */
-       
+
                mov     %o0, %l0                ! stash away romvec
                mov     %o0, %g7                ! put it here too
                mov     %o1, %l1                ! stash away debug_vec too
@@ -132,7 +128,7 @@ current_pc:
                set     current_pc, %g5
                cmp     %g3, %g5
                be      already_mapped
-                nop 
+                nop
 
                /* %l6 will hold the offset we have to subtract
                 * from absolute symbols in order to access areas
@@ -192,9 +188,9 @@ copy_prom_done:
                bne     not_a_sun4
                 nop
 
-halt_sun4_or_sun4c:
+halt_notsup:
                ld      [%g7 + 0x68], %o1
-               set     sun4c_notsup, %o0
+               set     notsup, %o0
                sub     %o0, %l6, %o0
                call    %o1
                 nop
@@ -202,18 +198,31 @@ halt_sun4_or_sun4c:
                 nop
 
 not_a_sun4:
+               /* It looks like this is a machine we support.
+                * Now find out what MMU we are dealing with
+                * LEON - identified by the psr.impl field
+                * Viking - identified by the psr.impl field
+                * In all other cases a sun4m srmmu.
+                * We check that the MMU is enabled in all cases.
+                */
+
+               /* Check if this is a LEON CPU */
+               rd      %psr, %g3
+               srl     %g3, PSR_IMPL_SHIFT, %g3
+               and     %g3, PSR_IMPL_SHIFTED_MASK, %g3
+               cmp     %g3, PSR_IMPL_LEON
+               be      leon_remap              /* It is a LEON - jump */
+                nop
+
+               /* Sanity-check, is MMU enabled */
                lda     [%g0] ASI_M_MMUREGS, %g1
                andcc   %g1, 1, %g0
-               be      halt_sun4_or_sun4c
+               be      halt_notsup
                 nop
 
-srmmu_remap:
-               /* First, check for a viking (TI) module. */
-               set     0x40000000, %g2
-               rd      %psr, %g3
-               and     %g2, %g3, %g3
-               subcc   %g3, 0x0, %g0
-               bz      srmmu_nviking
+               /* Check for a viking (TI) module. */
+               cmp     %g3, PSR_IMPL_TI
+               bne     srmmu_not_viking
                 nop
 
                /* Figure out what kind of viking we are on.
@@ -228,14 +237,14 @@ srmmu_remap:
                lda     [%g0] ASI_M_MMUREGS, %g3        ! peek in the control reg
                and     %g2, %g3, %g3
                subcc   %g3, 0x0, %g0
-               bnz     srmmu_nviking                   ! is in mbus mode
+               bnz     srmmu_not_viking                        ! is in mbus mode
                 nop
-               
+
                rd      %psr, %g3                       ! DO NOT TOUCH %g3
                andn    %g3, PSR_ET, %g2
                wr      %g2, 0x0, %psr
                WRITE_PAUSE
-               
+
                /* Get context table pointer, then convert to
                 * a physical address, which is 36 bits.
                 */
@@ -258,12 +267,12 @@ srmmu_remap:
                lda     [%g4] ASI_M_BYPASS, %o1         ! This is a level 1 ptr
                srl     %o1, 0x4, %o1                   ! Clear low 4 bits
                sll     %o1, 0x8, %o1                   ! Make physical
-               
+
                /* Ok, pull in the PTD. */
                lda     [%o1] ASI_M_BYPASS, %o2         ! This is the 0x0 16MB pgd
 
                /* Calculate to KERNBASE entry. */
-               add     %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3           
+               add     %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3
 
                /* Poke the entry into the calculated address. */
                sta     %o2, [%o3] ASI_M_BYPASS
@@ -293,12 +302,12 @@ srmmu_remap:
                b       go_to_highmem
                 nop
 
+srmmu_not_viking:
                /* This works on viking's in Mbus mode and all
                 * other MBUS modules.  It is virtually the same as
                 * the above madness sans turning traps off and flipping
                 * the AC bit.
                 */
-srmmu_nviking:
                set     AC_M_CTPR, %g1
                lda     [%g1] ASI_M_MMUREGS, %g1        ! get ctx table ptr
                sll     %g1, 0x4, %g1                   ! make physical addr
@@ -313,6 +322,29 @@ srmmu_nviking:
                 nop                                    ! wheee....
 
 
+leon_remap:
+               /* Sanity-check, is MMU enabled */
+               lda     [%g0] ASI_LEON_MMUREGS, %g1
+               andcc   %g1, 1, %g0
+               be      halt_notsup
+                nop
+
+               /* Same code as in the srmmu_not_viking case,
+                * with the LEON ASI for mmuregs
+                */
+               set     AC_M_CTPR, %g1
+               lda     [%g1] ASI_LEON_MMUREGS, %g1     ! get ctx table ptr
+               sll     %g1, 0x4, %g1                   ! make physical addr
+               lda     [%g1] ASI_M_BYPASS, %g1         ! ptr to level 1 pg_table
+               srl     %g1, 0x4, %g1
+               sll     %g1, 0x8, %g1                   ! make phys addr for l1 tbl
+
+               lda     [%g1] ASI_M_BYPASS, %g2         ! get level1 entry for 0x0
+               add     %g1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %g3
+               sta     %g2, [%g3] ASI_M_BYPASS         ! place at KERNBASE entry
+               b       go_to_highmem
+                nop                                    ! wheee....
+
 /* Now do a non-relative jump so that PC is in high-memory */
 go_to_highmem:
                set     execute_in_high_mem, %g1
@@ -336,8 +368,9 @@ execute_in_high_mem:
                sethi   %hi(linux_dbvec), %g1
                st      %o1, [%g1 + %lo(linux_dbvec)]
 
-/* Get the machine type via the mysterious romvec node operations. */
-
+               /* Get the machine type via the romvec
+                * getprops node operation
+                */
                add     %g7, 0x1c, %l1
                ld      [%l1], %l0
                ld      [%l0], %l0
@@ -356,9 +389,42 @@ execute_in_high_mem:
                                                ! to a buf where above string
                                                ! will get stored by the prom.
 
-#ifdef CONFIG_SPARC_LEON
-               /* no cpu-type check is needed, it is a SPARC-LEON */
 
+               /* Check value of "compatible" property.
+                * "value" => "model"
+                * leon => sparc_leon
+                * sun4m => sun4m
+                * sun4s => sun4m
+                * sun4d => sun4d
+                * sun4e => "no_sun4e_here"
+                * '*'   => "no_sun4u_here"
+                * Check single letters only
+                */
+
+               set     cputypval, %o2
+               /* If cputypval[0] == 'l' (lower case letter L) this is leon */
+               ldub    [%o2], %l1
+               cmp     %l1, 'l'
+               be      leon_init
+                nop
+
+               /* Check cputypval[4] to find the sun model */
+               ldub    [%o2 + 0x4], %l1
+
+               cmp     %l1, 'm'
+               be      sun4m_init
+                cmp    %l1, 's'
+               be      sun4m_init
+                cmp    %l1, 'd'
+               be      sun4d_init
+                cmp    %l1, 'e'
+               be      no_sun4e_here           ! Could be a sun4e.
+                nop
+               b       no_sun4u_here           ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
+                nop
+
+leon_init:
+               /* LEON CPU - set boot_cpu_id */
                sethi   %hi(boot_cpu_id), %g2   ! boot-cpu index
 
 #ifdef CONFIG_SMP
@@ -376,26 +442,6 @@ execute_in_high_mem:
 
                ba continue_boot
                 nop
-#endif
-
-/* Check to cputype. We may be booted on a sun4u (64 bit box),
- * and sun4d needs special treatment.
- */
-
-               set     cputypval, %o2
-               ldub    [%o2 + 0x4], %l1
-
-               cmp     %l1, 'm'
-               be      sun4m_init
-                cmp    %l1, 's'
-               be      sun4m_init
-                cmp    %l1, 'd'
-               be      sun4d_init
-                cmp    %l1, 'e'
-               be      no_sun4e_here           ! Could be a sun4e.
-                nop
-               b       no_sun4u_here           ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
-                nop
 
 /* CPUID in bootbus can be found at PA 0xff0140000 */
 #define SUN4D_BOOTBUS_CPUID     0xf0140000
@@ -431,9 +477,9 @@ sun4m_init:
 /* This sucks, apparently this makes Vikings call prom panic, will fix later */
 2:
                rd      %psr, %o1
-               srl     %o1, 28, %o1            ! Get a type of the CPU
+               srl     %o1, PSR_IMPL_SHIFT, %o1        ! Get a type of the CPU
 
-               subcc   %o1, 4, %g0             ! TI: Viking or MicroSPARC
+               subcc   %o1, PSR_IMPL_TI, %g0           ! TI: Viking or MicroSPARC
                be      continue_boot
                 nop
 
@@ -459,10 +505,6 @@ continue_boot:
 /* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
  * show-time!
  */
-
-               sethi   %hi(cputyp), %o0
-               st      %g4, [%o0 + %lo(cputyp)]
-
                /* Turn on Supervisor, EnableFloating, and all the PIL bits.
                 * Also puts us in register window zero with traps off.
                 */
@@ -480,7 +522,7 @@ continue_boot:
                set     __bss_start , %o0       ! First address of BSS
                set     _end , %o1              ! Last address of BSS
                add     %o0, 0x1, %o0
-1:     
+1:
                stb     %g0, [%o0]
                subcc   %o0, %o1, %g0
                bl      1b
@@ -546,7 +588,7 @@ continue_boot:
                set     dest, %g2; \
                ld      [%g5], %g4; \
                st      %g4, [%g2];
-       
+
                /* Patch for window spills... */
                PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
                PATCH_INSN(spnwin_patch2_7win, spnwin_patch2)
@@ -597,7 +639,7 @@ continue_boot:
                st      %g4, [%g5 + 0x18]
                st      %g4, [%g5 + 0x1c]
 
-2:             
+2:
                sethi   %hi(nwindows), %g4
                st      %g3, [%g4 + %lo(nwindows)]      ! store final value
                sub     %g3, 0x1, %g3
@@ -617,18 +659,12 @@ continue_boot:
                wr      %g3, PSR_ET, %psr
                WRITE_PAUSE
 
-               /* First we call prom_init() to set up PROMLIB, then
-                * off to start_kernel().
-                */
-
+               /* Call sparc32_start_kernel(struct linux_romvec *rp) */
                sethi   %hi(prom_vector_p), %g5
                ld      [%g5 + %lo(prom_vector_p)], %o0
-               call    prom_init
+               call    sparc32_start_kernel
                 nop
 
-               call    start_kernel
-                nop
-       
                /* We should not get here. */
                call    halt_me
                 nop
@@ -659,7 +695,7 @@ sun4u_5:
                .asciz "write"
                .align  4
 sun4u_6:
-               .asciz  "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
+               .asciz  "\n\rOn sun4u you have to use sparc64 kernel\n\rand not a sparc32 version\n\r\n\r"
 sun4u_6e:
                .align  4
 sun4u_7:
index a2846f5..0f094db 100644 (file)
@@ -55,17 +55,13 @@ const struct sparc32_dma_ops *sparc32_dma_ops;
 /* This function must make sure that caches and memory are coherent after DMA
  * On LEON systems without cache snooping it flushes the entire D-CACHE.
  */
-#ifndef CONFIG_SPARC_LEON
 static inline void dma_make_coherent(unsigned long pa, unsigned long len)
 {
+       if (sparc_cpu_model == sparc_leon) {
+               if (!sparc_leon3_snooping_enabled())
+                       leon_flush_dcache_all();
+       }
 }
-#else
-static inline void dma_make_coherent(unsigned long pa, unsigned long len)
-{
-       if (!sparc_leon3_snooping_enabled())
-               leon_flush_dcache_all();
-}
-#endif
 
 static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
 static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
@@ -427,9 +423,6 @@ arch_initcall(sparc_register_ioport);
 #endif /* CONFIG_SBUS */
 
 
-/* LEON reuses PCI DMA ops */
-#if defined(CONFIG_PCI) || defined(CONFIG_SPARC_LEON)
-
 /* Allocate and map kernel buffer using consistent mode DMA for a device.
  * hwdev should be valid struct pci_dev pointer for PCI devices.
  */
@@ -657,14 +650,11 @@ struct dma_map_ops pci32_dma_ops = {
 };
 EXPORT_SYMBOL(pci32_dma_ops);
 
-#endif /* CONFIG_PCI || CONFIG_SPARC_LEON */
+/* leon re-uses pci32_dma_ops */
+struct dma_map_ops *leon_dma_ops = &pci32_dma_ops;
+EXPORT_SYMBOL(leon_dma_ops);
 
-#ifdef CONFIG_SPARC_LEON
-struct dma_map_ops *dma_ops = &pci32_dma_ops;
-#elif defined(CONFIG_SBUS)
 struct dma_map_ops *dma_ops = &sbus_dma_ops;
-#endif
-
 EXPORT_SYMBOL(dma_ops);
 
 
index ae04914..c145f6f 100644 (file)
@@ -241,9 +241,6 @@ int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
        unsigned int cpu_irq;
        int err;
 
-#if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
-       struct tt_entry *trap_table;
-#endif
 
        err = request_irq(irq, irq_handler, 0, "floppy", NULL);
        if (err)
@@ -264,13 +261,18 @@ int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
 
        INSTANTIATE(sparc_ttable)
-#if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
-       trap_table = &trapbase_cpu1;
-       INSTANTIATE(trap_table)
-       trap_table = &trapbase_cpu2;
-       INSTANTIATE(trap_table)
-       trap_table = &trapbase_cpu3;
-       INSTANTIATE(trap_table)
+
+#if defined CONFIG_SMP
+       if (sparc_cpu_model != sparc_leon) {
+               struct tt_entry *trap_table;
+
+               trap_table = &trapbase_cpu1;
+               INSTANTIATE(trap_table)
+               trap_table = &trapbase_cpu2;
+               INSTANTIATE(trap_table)
+               trap_table = &trapbase_cpu3;
+               INSTANTIATE(trap_table)
+       }
 #endif
 #undef INSTANTIATE
        /*
index a86372d..291bb5d 100644 (file)
@@ -26,6 +26,9 @@ static inline unsigned long kimage_addr_to_ra(const char *p)
 #endif
 
 #ifdef CONFIG_SPARC32
+/* setup_32.c */
+void sparc32_start_kernel(struct linux_romvec *rp);
+
 /* cpu.c */
 extern void cpu_probe(void);
 
index 77c1b91..e34e2c4 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/smp.h>
 #include <asm/setup.h>
 
+#include "kernel.h"
 #include "prom.h"
 #include "irq.h"
 
index 519ca92..4e17432 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/pm.h>
 
 #include <asm/leon_amba.h>
+#include <asm/cpu_type.h>
 #include <asm/leon.h>
 
 /* List of Systems that need fixup instructions around power-down instruction */
@@ -65,13 +66,15 @@ void pmc_leon_idle(void)
 /* Install LEON Power Down function */
 static int __init leon_pmc_install(void)
 {
-       /* Assign power management IDLE handler */
-       if (pmc_leon_need_fixup())
-               pm_idle = pmc_leon_idle_fixup;
-       else
-               pm_idle = pmc_leon_idle;
+       if (sparc_cpu_model == sparc_leon) {
+               /* Assign power management IDLE handler */
+               if (pmc_leon_need_fixup())
+                       pm_idle = pmc_leon_idle_fixup;
+               else
+                       pm_idle = pmc_leon_idle;
 
-       printk(KERN_INFO "leon: power management initialized\n");
+               printk(KERN_INFO "leon: power management initialized\n");
+       }
 
        return 0;
 }
index a469090..0f3fb6d 100644 (file)
 
 #include "kernel.h"
 
-#ifdef CONFIG_SPARC_LEON
-
 #include "irq.h"
 
 extern ctxd_t *srmmu_ctx_table_phys;
 static int smp_processors_ready;
 extern volatile unsigned long cpu_callin_map[NR_CPUS];
 extern cpumask_t smp_commenced_mask;
-void __init leon_configure_cache_smp(void);
+void __cpuinit leon_configure_cache_smp(void);
 static void leon_ipi_init(void);
 
 /* IRQ number of LEON IPIs */
@@ -123,7 +121,7 @@ void __cpuinit leon_callin(void)
 
 extern struct linux_prom_registers smp_penguin_ctable;
 
-void __init leon_configure_cache_smp(void)
+void __cpuinit leon_configure_cache_smp(void)
 {
        unsigned long cfg = sparc_leon3_get_dcachecfg();
        int me = smp_processor_id();
@@ -507,5 +505,3 @@ void __init leon_init_smp(void)
 
        sparc32_ipi_ops = &leon_ipi_ops;
 }
-
-#endif /* CONFIG_SPARC_LEON */
index fe6787c..cb36e82 100644 (file)
@@ -65,50 +65,25 @@ extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
 struct task_struct *last_task_used_math = NULL;
 struct thread_info *current_set[NR_CPUS];
 
-#ifndef CONFIG_SMP
-
 /*
  * the idle loop on a Sparc... ;)
  */
 void cpu_idle(void)
 {
-       /* endless idle loop with no priority at all */
-       for (;;) {
-               if (pm_idle) {
-                       while (!need_resched())
-                               (*pm_idle)();
-               } else {
-                       while (!need_resched())
-                               cpu_relax();
-               }
-               schedule_preempt_disabled();
-       }
-}
-
-#else
+       set_thread_flag(TIF_POLLING_NRFLAG);
 
-/* This is being executed in task 0 'user space'. */
-void cpu_idle(void)
-{
-        set_thread_flag(TIF_POLLING_NRFLAG);
        /* endless idle loop with no priority at all */
-       while(1) {
-#ifdef CONFIG_SPARC_LEON
-               if (pm_idle) {
-                       while (!need_resched())
+       for (;;) {
+               while (!need_resched()) {
+                       if (pm_idle)
                                (*pm_idle)();
-               } else
-#endif
-               {
-                       while (!need_resched())
+                       else
                                cpu_relax();
                }
                schedule_preempt_disabled();
        }
 }
 
-#endif
-
 /* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
 void machine_halt(void)
 {
index 741df91..1303021 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/of_pdt.h>
 #include <asm/prom.h>
 #include <asm/oplib.h>
-#include <asm/leon.h>
 
 #include "prom.h"
 
index 7abc24e..6c34de0 100644 (file)
@@ -231,11 +231,14 @@ srmmu_rett_stackchk:
        cmp     %g1, %fp
        bleu    ret_trap_user_stack_is_bolixed
         mov    AC_M_SFSR, %g1
-       lda     [%g1] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%g1] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%g1] ASI_M_MMUREGS, %g0)
 
-       lda     [%g0] ASI_M_MMUREGS, %g1
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %g1)
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %g1)
        or      %g1, 0x2, %g1
-       sta     %g1, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %g1, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g0] ASI_M_MMUREGS)
 
        restore %g0, %g0, %g0
 
@@ -244,13 +247,16 @@ srmmu_rett_stackchk:
        save    %g0, %g0, %g0
 
        andn    %g1, 0x2, %g1
-       sta     %g1, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %g1, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g0] ASI_M_MMUREGS)
 
        mov     AC_M_SFAR, %g2
-       lda     [%g2] ASI_M_MMUREGS, %g2
+LEON_PI(lda    [%g2] ASI_LEON_MMUREGS, %g2)
+SUN_PI_(lda    [%g2] ASI_M_MMUREGS, %g2)
 
        mov     AC_M_SFSR, %g1
-       lda     [%g1] ASI_M_MMUREGS, %g1
+LEON_PI(lda    [%g1] ASI_LEON_MMUREGS, %g1)
+SUN_PI_(lda    [%g1] ASI_M_MMUREGS, %g1)
        andcc   %g1, 0x2, %g0
        be      ret_trap_userwins_ok
         nop
index c052313..efe3e64 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/cpu.h>
 #include <linux/kdebug.h>
 #include <linux/export.h>
+#include <linux/start_kernel.h>
 
 #include <asm/io.h>
 #include <asm/processor.h>
@@ -45,6 +46,7 @@
 #include <asm/cpudata.h>
 #include <asm/setup.h>
 #include <asm/cacheflush.h>
+#include <asm/sections.h>
 
 #include "kernel.h"
 
@@ -237,28 +239,42 @@ static void __init per_cpu_patch(void)
        }
 }
 
+struct leon_1insn_patch_entry {
+       unsigned int addr;
+       unsigned int insn;
+};
+
 enum sparc_cpu sparc_cpu_model;
 EXPORT_SYMBOL(sparc_cpu_model);
 
-struct tt_entry *sparc_ttable;
+static __init void leon_patch(void)
+{
+       struct leon_1insn_patch_entry *start = (void *)__leon_1insn_patch;
+       struct leon_1insn_patch_entry *end = (void *)__leon_1insn_patch_end;
 
-struct pt_regs fake_swapper_regs;
+       /* Default instruction is leon - no patching */
+       if (sparc_cpu_model == sparc_leon)
+               return;
 
-void __init setup_arch(char **cmdline_p)
-{
-       int i;
-       unsigned long highest_paddr;
+       while (start < end) {
+               unsigned long addr = start->addr;
 
-       sparc_ttable = (struct tt_entry *) &trapbase;
+               *(unsigned int *)(addr) = start->insn;
+               flushi(addr);
 
-       /* Initialize PROM console and command line. */
-       *cmdline_p = prom_getbootargs();
-       strcpy(boot_command_line, *cmdline_p);
-       parse_early_param();
+               start++;
+       }
+}
 
-       boot_flags_init(*cmdline_p);
+struct tt_entry *sparc_ttable;
+struct pt_regs fake_swapper_regs;
 
-       register_console(&prom_early_console);
+/* Called from head_32.S - before we have setup anything
+ * in the kernel. Be very careful with what you do here.
+ */
+void __init sparc32_start_kernel(struct linux_romvec *rp)
+{
+       prom_init(rp);
 
        /* Set sparc_cpu_model */
        sparc_cpu_model = sun_unknown;
@@ -275,6 +291,26 @@ void __init setup_arch(char **cmdline_p)
        if (!strncmp(&cputypval[0], "leon" , 4))
                sparc_cpu_model = sparc_leon;
 
+       leon_patch();
+       start_kernel();
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+       int i;
+       unsigned long highest_paddr;
+
+       sparc_ttable = (struct tt_entry *) &trapbase;
+
+       /* Initialize PROM console and command line. */
+       *cmdline_p = prom_getbootargs();
+       strcpy(boot_command_line, *cmdline_p);
+       parse_early_param();
+
+       boot_flags_init(*cmdline_p);
+
+       register_console(&prom_early_console);
+
        printk("ARCH: ");
        switch(sparc_cpu_model) {
        case sun4m:
index 7364ddc..af27aca 100644 (file)
@@ -149,8 +149,6 @@ sun4d_cpu_startup:
 
        b,a     smp_do_cpu_idle
 
-#ifdef CONFIG_SPARC_LEON
-
        __CPUINIT
        .align  4
         .global leon_smp_cpu_startup, smp_penguin_ctable
@@ -161,7 +159,7 @@ leon_smp_cpu_startup:
         ld [%g1+4],%g1
         srl %g1,4,%g1
         set 0x00000100,%g5 /* SRMMU_CTXTBL_PTR */
-       sta %g1, [%g5] ASI_M_MMUREGS
+       sta %g1, [%g5] ASI_LEON_MMUREGS
 
        /* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
        set     (PSR_PIL | PSR_S | PSR_PS), %g1
@@ -207,5 +205,3 @@ leon_smp_cpu_startup:
         nop
 
        b,a     smp_do_cpu_idle
-
-#endif
index c72fdf5..3b05e66 100644 (file)
@@ -2054,7 +2054,7 @@ void do_fpieee(struct pt_regs *regs)
        do_fpe_common(regs);
 }
 
-extern int do_mathemu(struct pt_regs *, struct fpustate *);
+extern int do_mathemu(struct pt_regs *, struct fpustate *, bool);
 
 void do_fpother(struct pt_regs *regs)
 {
@@ -2068,7 +2068,7 @@ void do_fpother(struct pt_regs *regs)
        switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
        case (2 << 14): /* unfinished_FPop */
        case (3 << 14): /* unimplemented_FPop */
-               ret = do_mathemu(regs, f);
+               ret = do_mathemu(regs, f, false);
                break;
        }
        if (ret)
@@ -2308,10 +2308,12 @@ void do_illegal_instruction(struct pt_regs *regs)
                        } else {
                                struct fpustate *f = FPUSTATE;
 
-                               /* XXX maybe verify XFSR bits like
-                                * XXX do_fpother() does?
+                               /* On UltraSPARC T2 and later, FPU insns which
+                                * are not implemented in HW signal an illegal
+                                * instruction trap and do not set the FP Trap
+                                * Trap in the %fsr to unimplemented_FPop.
                                 */
-                               if (do_mathemu(regs, f))
+                               if (do_mathemu(regs, f, true))
                                        return;
                        }
                }
index 0e16056..89c2c29 100644 (file)
@@ -107,6 +107,11 @@ SECTIONS
                *(.sun4v_2insn_patch)
                __sun4v_2insn_patch_end = .;
        }
+       .leon_1insn_patch : {
+               __leon_1insn_patch = .;
+               *(.leon_1insn_patch)
+               __leon_1insn_patch_end = .;
+       }
        .swapper_tsb_phys_patch : {
                __swapper_tsb_phys_patch = .;
                *(.swapper_tsb_phys_patch)
index 4c2de3c..28a7bc6 100644 (file)
@@ -332,24 +332,30 @@ spwin_srmmu_stackchk:
         mov    AC_M_SFSR, %glob_tmp
 
        /* Clear the fault status and turn on the no_fault bit. */
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0          ! eat SFSR
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)      ! eat SFSR
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)         ! eat SFSR
 
-       lda     [%g0] ASI_M_MMUREGS, %glob_tmp          ! read MMU control
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %glob_tmp)      ! read MMU control
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %glob_tmp)         ! read MMU control
        or      %glob_tmp, 0x2, %glob_tmp               ! or in no_fault bit
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS          ! set it
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)      ! set it
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)         ! set it
 
        /* Dump the registers and cross fingers. */
        STORE_WINDOW(sp)
 
        /* Clear the no_fault bit and check the status. */
        andn    %glob_tmp, 0x2, %glob_tmp
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)
 
        mov     AC_M_SFAR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)
 
        mov     AC_M_SFSR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %glob_tmp
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %glob_tmp)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %glob_tmp)
        andcc   %glob_tmp, 0x2, %g0                     ! did we fault?
        be,a    spwin_finish_up + 0x4                   ! cool beans, success
         restore %g0, %g0, %g0
index 9fde91a..2c21cc5 100644 (file)
@@ -254,16 +254,19 @@ srmmu_fwin_stackchk:
        mov     AC_M_SFSR, %l4
        cmp     %l5, %sp
        bleu    fwin_user_stack_is_bolixed
-        lda    [%l4] ASI_M_MMUREGS, %g0        ! clear fault status
+LEON_PI( lda   [%l4] ASI_LEON_MMUREGS, %g0)    ! clear fault status
+SUN_PI_( lda   [%l4] ASI_M_MMUREGS, %g0)       ! clear fault status
 
        /* The technique is, turn off faults on this processor,
         * just let the load rip, then check the sfsr to see if
         * a fault did occur.  Then we turn on fault traps again
         * and branch conditionally based upon what happened.
         */
-       lda     [%g0] ASI_M_MMUREGS, %l5        ! read mmu-ctrl reg
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %l5)    ! read mmu-ctrl reg
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %l5)       ! read mmu-ctrl reg
        or      %l5, 0x2, %l5                   ! turn on no-fault bit
-       sta     %l5, [%g0] ASI_M_MMUREGS        ! store it
+LEON_PI(sta    %l5, [%g0] ASI_LEON_MMUREGS)    ! store it
+SUN_PI_(sta    %l5, [%g0] ASI_M_MMUREGS)       ! store it
 
        /* Cross fingers and go for it. */
        LOAD_WINDOW(sp)
@@ -275,18 +278,22 @@ srmmu_fwin_stackchk:
 
        /* LOCATION: Window 'T' */
 
-       lda     [%g0] ASI_M_MMUREGS, %twin_tmp1 ! load mmu-ctrl again
-       andn    %twin_tmp1, 0x2, %twin_tmp1     ! clear no-fault bit
-       sta     %twin_tmp1, [%g0] ASI_M_MMUREGS ! store it
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %twin_tmp1)     ! load mmu-ctrl again
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %twin_tmp1)        ! load mmu-ctrl again
+       andn    %twin_tmp1, 0x2, %twin_tmp1             ! clear no-fault bit
+LEON_PI(sta    %twin_tmp1, [%g0] ASI_LEON_MMUREGS)     ! store it
+SUN_PI_(sta    %twin_tmp1, [%g0] ASI_M_MMUREGS)        ! store it
 
        mov     AC_M_SFAR, %twin_tmp2
-       lda     [%twin_tmp2] ASI_M_MMUREGS, %g0 ! read fault address
+LEON_PI(lda    [%twin_tmp2] ASI_LEON_MMUREGS, %g0)     ! read fault address
+SUN_PI_(lda    [%twin_tmp2] ASI_M_MMUREGS, %g0)        ! read fault address
 
        mov     AC_M_SFSR, %twin_tmp2
-       lda     [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2  ! read fault status
-       andcc   %twin_tmp2, 0x2, %g0                    ! did fault occur?
+LEON_PI(lda    [%twin_tmp2] ASI_LEON_MMUREGS, %twin_tmp2) ! read fault status
+SUN_PI_(lda    [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2)    ! read fault status
+       andcc   %twin_tmp2, 0x2, %g0                       ! did fault occur?
 
-       bne     1f                                      ! yep, cleanup
+       bne     1f                                         ! yep, cleanup
         nop
 
        wr      %t_psr, 0x0, %psr
index 2bbe2f2..1704068 100644 (file)
@@ -163,7 +163,7 @@ typedef union {
        u64 q[2];
 } *argp;
 
-int do_mathemu(struct pt_regs *regs, struct fpustate *f)
+int do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap)
 {
        unsigned long pc = regs->tpc;
        unsigned long tstate = regs->tstate;
@@ -218,7 +218,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
                        case FSQRTS: {
                                unsigned long x = current_thread_info()->xfsr[0];
 
-                               x = (x >> 14) & 0xf;
+                               x = (x >> 14) & 0x7;
                                TYPE(x,1,1,1,1,0,0);
                                break;
                        }
@@ -226,7 +226,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
                        case FSQRTD: {
                                unsigned long x = current_thread_info()->xfsr[0];
 
-                               x = (x >> 14) & 0xf;
+                               x = (x >> 14) & 0x7;
                                TYPE(x,2,1,2,1,0,0);
                                break;
                        }
@@ -357,9 +357,17 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
        if (type) {
                argp rs1 = NULL, rs2 = NULL, rd = NULL;
                
-               freg = (current_thread_info()->xfsr[0] >> 14) & 0xf;
-               if (freg != (type >> 9))
-                       goto err;
+               /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
+                * Type field in the %fsr to unimplemented_FPop.  Nor does it
+                * use the fp_exception_other trap.  Instead it signals an
+                * illegal instruction and leaves the FP trap type field of
+                * the %fsr unchanged.
+                */
+               if (!illegal_insn_trap) {
+                       int ftt = (current_thread_info()->xfsr[0] >> 14) & 0x7;
+                       if (ftt != (type >> 9))
+                               goto err;
+               }
                current_thread_info()->xfsr[0] &= ~0x1c000;
                freg = ((insn >> 14) & 0x1f);
                switch (type & 0x3) {
index 69ffd31..30c3ecc 100644 (file)
@@ -8,8 +8,9 @@ obj-$(CONFIG_SPARC64)   += ultra.o tlb.o tsb.o gup.o
 obj-y                   += fault_$(BITS).o
 obj-y                   += init_$(BITS).o
 obj-$(CONFIG_SPARC32)   += extable.o srmmu.o iommu.o io-unit.o
+obj-$(CONFIG_SPARC32)   += srmmu_access.o
 obj-$(CONFIG_SPARC32)   += hypersparc.o viking.o tsunami.o swift.o
-obj-$(CONFIG_SPARC_LEON)+= leon_mm.o
+obj-$(CONFIG_SPARC32)   += leon_mm.o
 
 # Only used by sparc64
 obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
index 4c67ae6..5bed085 100644 (file)
@@ -32,7 +32,7 @@ static inline unsigned long leon_get_ctable_ptr(void)
 }
 
 
-unsigned long srmmu_swprobe(unsigned long vaddr, unsigned long *paddr)
+unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr)
 {
 
        unsigned int ctxtbl;
index 256db6b..62e3f57 100644 (file)
@@ -646,6 +646,23 @@ static void __init srmmu_allocate_ptable_skeleton(unsigned long start,
        }
 }
 
+/* These flush types are not available on all chips... */
+static inline unsigned long srmmu_probe(unsigned long vaddr)
+{
+       unsigned long retval;
+
+       if (sparc_cpu_model != sparc_leon) {
+
+               vaddr &= PAGE_MASK;
+               __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+                                    "=r" (retval) :
+                                    "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
+       } else {
+               retval = leon_swprobe(vaddr, 0);
+       }
+       return retval;
+}
+
 /*
  * This is much cleaner than poking around physical address space
  * looking at the prom's page table directly which is what most
@@ -665,7 +682,7 @@ static void __init srmmu_inherit_prom_mappings(unsigned long start,
                        break; /* probably wrap around */
                if(start == 0xfef00000)
                        start = KADB_DEBUGGER_BEGVM;
-               if(!(prompte = srmmu_hwprobe(start))) {
+               if(!(prompte = srmmu_probe(start))) {
                        start += PAGE_SIZE;
                        continue;
                }
@@ -674,12 +691,12 @@ static void __init srmmu_inherit_prom_mappings(unsigned long start,
                what = 0;
     
                if(!(start & ~(SRMMU_REAL_PMD_MASK))) {
-                       if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_REAL_PMD_SIZE) == prompte)
+                       if(srmmu_probe((start-PAGE_SIZE) + SRMMU_REAL_PMD_SIZE) == prompte)
                                what = 1;
                }
     
                if(!(start & ~(SRMMU_PGDIR_MASK))) {
-                       if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
+                       if(srmmu_probe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
                           prompte)
                                what = 2;
                }
@@ -1156,7 +1173,7 @@ static void turbosparc_flush_page_to_ram(unsigned long page)
 #ifdef TURBOSPARC_WRITEBACK
        volatile unsigned long clear;
 
-       if (srmmu_hwprobe(page))
+       if (srmmu_probe(page))
                turbosparc_flush_page_cache(page);
        clear = srmmu_get_fstatus();
 #endif
diff --git a/arch/sparc/mm/srmmu_access.S b/arch/sparc/mm/srmmu_access.S
new file mode 100644 (file)
index 0000000..d0a67b2
--- /dev/null
@@ -0,0 +1,82 @@
+/* Assembler variants of srmmu access functions.
+ * Implemented in assembler to allow run-time patching.
+ * LEON uses a different ASI for MMUREGS than SUN.
+ *
+ * The leon_1insn_patch infrastructure is used
+ * for the run-time patching.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asmmacro.h>
+#include <asm/pgtsrmmu.h>
+#include <asm/asi.h>
+
+/* unsigned int srmmu_get_mmureg(void) */
+ENTRY(srmmu_get_mmureg)
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_mmureg)
+
+/* void srmmu_set_mmureg(unsigned long regval) */
+ENTRY(srmmu_set_mmureg)
+LEON_PI(sta    %o0, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %o0, [%g0] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_mmureg)
+
+/* void srmmu_set_ctable_ptr(unsigned long paddr) */
+ENTRY(srmmu_set_ctable_ptr)
+       /* paddr = ((paddr >> 4) & SRMMU_CTX_PMASK); */
+       srl     %o0, 4, %g1
+       and     %g1, SRMMU_CTX_PMASK, %g1
+
+       mov     SRMMU_CTXTBL_PTR, %g2
+LEON_PI(sta    %g1, [%g2] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g2] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_ctable_ptr)
+
+
+/* void srmmu_set_context(int context) */
+ENTRY(srmmu_set_context)
+       mov     SRMMU_CTX_REG, %g1
+LEON_PI(sta    %o0, [%g1] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %o0, [%g1] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_context)
+
+
+/* int srmmu_get_context(void) */
+ENTRY(srmmu_get_context)
+       mov     SRMMU_CTX_REG, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_context)
+
+
+/* unsigned int srmmu_get_fstatus(void) */
+ENTRY(srmmu_get_fstatus)
+       mov     SRMMU_FAULT_STATUS, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_fstatus)
+
+
+/* unsigned int srmmu_get_faddr(void) */
+ENTRY(srmmu_get_faddr)
+       mov     SRMMU_FAULT_ADDR, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_faddr)
index 724aa44..0c44630 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/processor.h>
 #include <asm/mmu.h>
 #include <asm/mpspec.h>
+#include <asm/realmode.h>
 
 #define COMPILER_DEPENDENT_INT64   long long
 #define COMPILER_DEPENDENT_UINT64  unsigned long long
@@ -116,10 +117,8 @@ static inline void acpi_disable_pci(void)
 /* Low-level suspend routine. */
 extern int acpi_suspend_lowlevel(void);
 
-extern const unsigned char acpi_wakeup_code[];
-
-/* early initialization routine */
-extern void acpi_reserve_wakeup_memory(void);
+/* Physical address to resume after wakeup */
+#define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start))
 
 /*
  * Check if the CPU can handle C2 and deeper
index b97596e..a6983b2 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/compiler.h>
 #include <asm/alternative.h>
 
+#define BIT_64(n)                      (U64_C(1) << (n))
+
 /*
  * These have to be done with inline assembly: that way the bit-setting
  * is guaranteed to be atomic. All bit operations return 0 if the bit
index b772dd6..0a687fd 100644 (file)
@@ -1251,15 +1251,15 @@ void mce_log_therm_throt_event(__u64 status)
  * poller finds an MCE, poll 2x faster.  When the poller finds no more
  * errors, poll 2x slower (up to check_interval seconds).
  */
-static int check_interval = 5 * 60; /* 5 minutes */
+static unsigned long check_interval = 5 * 60; /* 5 minutes */
 
-static DEFINE_PER_CPU(int, mce_next_interval); /* in jiffies */
+static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */
 static DEFINE_PER_CPU(struct timer_list, mce_timer);
 
-static void mce_start_timer(unsigned long data)
+static void mce_timer_fn(unsigned long data)
 {
-       struct timer_list *t = &per_cpu(mce_timer, data);
-       int *n;
+       struct timer_list *t = &__get_cpu_var(mce_timer);
+       unsigned long iv;
 
        WARN_ON(smp_processor_id() != data);
 
@@ -1272,13 +1272,14 @@ static void mce_start_timer(unsigned long data)
         * Alert userspace if needed.  If we logged an MCE, reduce the
         * polling interval, otherwise increase the polling interval.
         */
-       n = &__get_cpu_var(mce_next_interval);
+       iv = __this_cpu_read(mce_next_interval);
        if (mce_notify_irq())
-               *n = max(*n/2, HZ/100);
+               iv = max(iv, (unsigned long) HZ/100);
        else
-               *n = min(*n*2, (int)round_jiffies_relative(check_interval*HZ));
+               iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
+       __this_cpu_write(mce_next_interval, iv);
 
-       t->expires = jiffies + *n;
+       t->expires = jiffies + iv;
        add_timer_on(t, smp_processor_id());
 }
 
@@ -1472,9 +1473,9 @@ static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
                                 rdmsrl(msrs[i], val);
 
                                 /* CntP bit set? */
-                                if (val & BIT(62)) {
-                                        val &= ~BIT(62);
-                                        wrmsrl(msrs[i], val);
+                                if (val & BIT_64(62)) {
+                                       val &= ~BIT_64(62);
+                                       wrmsrl(msrs[i], val);
                                 }
                         }
 
@@ -1556,17 +1557,17 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
 static void __mcheck_cpu_init_timer(void)
 {
        struct timer_list *t = &__get_cpu_var(mce_timer);
-       int *n = &__get_cpu_var(mce_next_interval);
+       unsigned long iv = __this_cpu_read(mce_next_interval);
 
-       setup_timer(t, mce_start_timer, smp_processor_id());
+       setup_timer(t, mce_timer_fn, smp_processor_id());
 
        if (mce_ignore_ce)
                return;
 
-       *n = check_interval * HZ;
-       if (!*n)
+       __this_cpu_write(mce_next_interval, iv);
+       if (!iv)
                return;
-       t->expires = round_jiffies(jiffies + *n);
+       t->expires = round_jiffies(jiffies + iv);
        add_timer_on(t, smp_processor_id());
 }
 
@@ -2276,7 +2277,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
        case CPU_DOWN_FAILED_FROZEN:
                if (!mce_ignore_ce && check_interval) {
                        t->expires = round_jiffies(jiffies +
-                                          __get_cpu_var(mce_next_interval));
+                                       per_cpu(mce_next_interval, cpu));
                        add_timer_on(t, cpu);
                }
                smp_call_function_single(cpu, mce_reenable_cpu, &action, 1);
index ac140c7..bdda2e6 100644 (file)
@@ -266,7 +266,7 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk,
                if (align > max_align)
                        align = max_align;
 
-               sizek = 1 << align;
+               sizek = 1UL << align;
                if (debug_print) {
                        char start_factor = 'K', size_factor = 'K';
                        unsigned long start_base, size_base;
index 9cc7b43..1460a5d 100644 (file)
@@ -870,7 +870,7 @@ int __init hpet_enable(void)
        else
                pr_warn("HPET initial state will not be saved\n");
        cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
-       hpet_writel(cfg, HPET_Tn_CFG(i));
+       hpet_writel(cfg, HPET_CFG);
        if (cfg)
                pr_warn("HPET: Unrecognized bits %#x set in global cfg\n",
                        cfg);
index 72102e0..be3cea4 100644 (file)
@@ -2595,8 +2595,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
                        *gfnp = gfn;
                        kvm_release_pfn_clean(pfn);
                        pfn &= ~mask;
-                       if (!get_page_unless_zero(pfn_to_page(pfn)))
-                               BUG();
+                       kvm_get_pfn(pfn);
                        *pfnp = pfn;
                }
        }
index f11729f..3d68ef6 100644 (file)
@@ -158,31 +158,47 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end, unsigned long req_type)
        return req_type;
 }
 
+struct pagerange_state {
+       unsigned long           cur_pfn;
+       int                     ram;
+       int                     not_ram;
+};
+
+static int
+pagerange_is_ram_callback(unsigned long initial_pfn, unsigned long total_nr_pages, void *arg)
+{
+       struct pagerange_state *state = arg;
+
+       state->not_ram  |= initial_pfn > state->cur_pfn;
+       state->ram      |= total_nr_pages > 0;
+       state->cur_pfn   = initial_pfn + total_nr_pages;
+
+       return state->ram && state->not_ram;
+}
+
 static int pat_pagerange_is_ram(resource_size_t start, resource_size_t end)
 {
-       int ram_page = 0, not_rampage = 0;
-       unsigned long page_nr;
+       int ret = 0;
+       unsigned long start_pfn = start >> PAGE_SHIFT;
+       unsigned long end_pfn = (end + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       struct pagerange_state state = {start_pfn, 0, 0};
 
-       for (page_nr = (start >> PAGE_SHIFT); page_nr < (end >> PAGE_SHIFT);
-            ++page_nr) {
-               /*
-                * For legacy reasons, physical address range in the legacy ISA
-                * region is tracked as non-RAM. This will allow users of
-                * /dev/mem to map portions of legacy ISA region, even when
-                * some of those portions are listed(or not even listed) with
-                * different e820 types(RAM/reserved/..)
-                */
-               if (page_nr >= (ISA_END_ADDRESS >> PAGE_SHIFT) &&
-                   page_is_ram(page_nr))
-                       ram_page = 1;
-               else
-                       not_rampage = 1;
-
-               if (ram_page == not_rampage)
-                       return -1;
+       /*
+        * For legacy reasons, physical address range in the legacy ISA
+        * region is tracked as non-RAM. This will allow users of
+        * /dev/mem to map portions of legacy ISA region, even when
+        * some of those portions are listed(or not even listed) with
+        * different e820 types(RAM/reserved/..)
+        */
+       if (start_pfn < ISA_END_ADDRESS >> PAGE_SHIFT)
+               start_pfn = ISA_END_ADDRESS >> PAGE_SHIFT;
+
+       if (start_pfn < end_pfn) {
+               ret = walk_system_ram_range(start_pfn, end_pfn - start_pfn,
+                               &state, pagerange_is_ram_callback);
        }
 
-       return ram_page;
+       return (ret > 0) ? -1 : (state.ram ? 1 : 0);
 }
 
 /*
index 75f33b2..e74df95 100644 (file)
@@ -1116,7 +1116,10 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
        .wbinvd = native_wbinvd,
 
        .read_msr = native_read_msr_safe,
+       .rdmsr_regs = native_rdmsr_safe_regs,
        .write_msr = xen_write_msr_safe,
+       .wrmsr_regs = native_wrmsr_safe_regs,
+
        .read_tsc = native_read_tsc,
        .read_pmc = native_read_pmc,
 
index 1e2d53b..893b800 100644 (file)
@@ -235,6 +235,7 @@ void ioc_clear_queue(struct request_queue *q)
 int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node)
 {
        struct io_context *ioc;
+       int ret;
 
        ioc = kmem_cache_alloc_node(iocontext_cachep, gfp_flags | __GFP_ZERO,
                                    node);
@@ -262,9 +263,12 @@ int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node)
                task->io_context = ioc;
        else
                kmem_cache_free(iocontext_cachep, ioc);
+
+       ret = task->io_context ? 0 : -EBUSY;
+
        task_unlock(task);
 
-       return 0;
+       return ret;
 }
 
 /**
index ebaa045..74ee4ab 100644 (file)
@@ -25,8 +25,6 @@
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
-#include <asm/realmode.h>
-
 #include "internal.h"
 #include "sleep.h"
 
@@ -93,13 +91,11 @@ static struct notifier_block tts_notifier = {
 static int acpi_sleep_prepare(u32 acpi_state)
 {
 #ifdef CONFIG_ACPI_SLEEP
-       unsigned long wakeup_pa = real_mode_header->wakeup_start;
        /* do we have a wakeup address for S2 and S3? */
        if (acpi_state == ACPI_STATE_S3) {
-               if (!wakeup_pa)
+               if (!acpi_wakeup_address)
                        return -EFAULT;
-               acpi_set_firmware_waking_vector(
-                               (acpi_physical_address)wakeup_pa);
+               acpi_set_firmware_waking_vector(acpi_wakeup_address);
 
        }
        ACPI_FLUSH_CPU_CACHE();
index e8cd652..9851093 100644 (file)
@@ -984,6 +984,7 @@ static uint32_t fpga_tx(struct solos_card *card)
                        } else if (skb && card->using_dma) {
                                SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
                                                                       skb->len, PCI_DMA_TODEVICE);
+                               card->tx_skb[port] = skb;
                                iowrite32(SKB_CB(skb)->dma_addr,
                                          card->config_regs + TX_DMA_ADDR(port));
                        }
@@ -1152,7 +1153,8 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
                db_fpga_upgrade = db_firmware_upgrade = 0;
        }
 
-       if (card->fpga_version >= DMA_SUPPORTED){
+       if (card->fpga_version >= DMA_SUPPORTED) {
+               pci_set_master(dev);
                card->using_dma = 1;
        } else {
                card->using_dma = 0;
index 304000c..264bc77 100644 (file)
@@ -294,18 +294,16 @@ static int hba_reset_nosleep(struct driver_data *dd)
  */
 static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag)
 {
-       unsigned long flags = 0;
-
        atomic_set(&port->commands[tag].active, 1);
 
-       spin_lock_irqsave(&port->cmd_issue_lock, flags);
+       spin_lock(&port->cmd_issue_lock);
 
        writel((1 << MTIP_TAG_BIT(tag)),
                        port->s_active[MTIP_TAG_INDEX(tag)]);
        writel((1 << MTIP_TAG_BIT(tag)),
                        port->cmd_issue[MTIP_TAG_INDEX(tag)]);
 
-       spin_unlock_irqrestore(&port->cmd_issue_lock, flags);
+       spin_unlock(&port->cmd_issue_lock);
 
        /* Set the command's timeout value.*/
        port->commands[tag].comp_time = jiffies + msecs_to_jiffies(
@@ -436,8 +434,7 @@ static void mtip_init_port(struct mtip_port *port)
                writel(0xFFFFFFFF, port->completed[i]);
 
        /* Clear any pending interrupts for this port */
-       writel(readl(port->dd->mmio + PORT_IRQ_STAT),
-                                       port->dd->mmio + PORT_IRQ_STAT);
+       writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT);
 
        /* Clear any pending interrupts on the HBA. */
        writel(readl(port->dd->mmio + HOST_IRQ_STAT),
@@ -782,13 +779,24 @@ static void mtip_handle_tfe(struct driver_data *dd)
 
        /* Stop the timer to prevent command timeouts. */
        del_timer(&port->cmd_timer);
+       set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
+
+       if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) &&
+                       test_bit(MTIP_TAG_INTERNAL, port->allocated)) {
+               cmd = &port->commands[MTIP_TAG_INTERNAL];
+               dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n");
+
+               atomic_inc(&cmd->active); /* active > 1 indicates error */
+               if (cmd->comp_data && cmd->comp_func) {
+                       cmd->comp_func(port, MTIP_TAG_INTERNAL,
+                                       cmd->comp_data, PORT_IRQ_TF_ERR);
+               }
+               goto handle_tfe_exit;
+       }
 
        /* clear the tag accumulator */
        memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long));
 
-       /* Set eh_active */
-       set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
-
        /* Loop through all the groups */
        for (group = 0; group < dd->slot_groups; group++) {
                completed = readl(port->completed[group]);
@@ -940,6 +948,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
        }
        print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt);
 
+handle_tfe_exit:
        /* clear eh_active */
        clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
        wake_up_interruptible(&port->svc_wait);
@@ -961,6 +970,8 @@ static inline void mtip_process_sdbf(struct driver_data *dd)
        /* walk all bits in all slot groups */
        for (group = 0; group < dd->slot_groups; group++) {
                completed = readl(port->completed[group]);
+               if (!completed)
+                       continue;
 
                /* clear completed status register in the hardware.*/
                writel(completed, port->completed[group]);
@@ -1329,22 +1340,6 @@ static int mtip_exec_internal_command(struct mtip_port *port,
                        }
                        rv = -EAGAIN;
                }
-
-               if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
-                       & (1 << MTIP_TAG_INTERNAL)) {
-                       dev_warn(&port->dd->pdev->dev,
-                               "Retiring internal command but CI is 1.\n");
-                       if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
-                                               &port->dd->dd_flag)) {
-                               hba_reset_nosleep(port->dd);
-                               rv = -ENXIO;
-                       } else {
-                               mtip_restart_port(port);
-                               rv = -EAGAIN;
-                       }
-                       goto exec_ic_exit;
-               }
-
        } else {
                /* Spin for <timeout> checking if command still outstanding */
                timeout = jiffies + msecs_to_jiffies(timeout);
@@ -1361,21 +1356,25 @@ static int mtip_exec_internal_command(struct mtip_port *port,
                                rv = -ENXIO;
                                goto exec_ic_exit;
                        }
+                       if (readl(port->mmio + PORT_IRQ_STAT) & PORT_IRQ_ERR) {
+                               atomic_inc(&int_cmd->active); /* error */
+                               break;
+                       }
                }
+       }
 
-               if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
+       if (atomic_read(&int_cmd->active) > 1) {
+               dev_err(&port->dd->pdev->dev,
+                       "Internal command [%02X] failed\n", fis->command);
+               rv = -EIO;
+       }
+       if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
                        & (1 << MTIP_TAG_INTERNAL)) {
-                       dev_err(&port->dd->pdev->dev,
-                               "Internal command did not complete [atomic]\n");
+               rv = -ENXIO;
+               if (!test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
+                                       &port->dd->dd_flag)) {
+                       mtip_restart_port(port);
                        rv = -EAGAIN;
-                       if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
-                                               &port->dd->dd_flag)) {
-                               hba_reset_nosleep(port->dd);
-                               rv = -ENXIO;
-                       } else {
-                               mtip_restart_port(port);
-                               rv = -EAGAIN;
-                       }
                }
        }
 exec_ic_exit:
@@ -1893,13 +1892,33 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                                void __user *user_buffer)
 {
        struct host_to_dev_fis  fis;
-       struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG);
+       struct host_to_dev_fis *reply;
+       u8 *buf = NULL;
+       dma_addr_t dma_addr = 0;
+       int rv = 0, xfer_sz = command[3];
+
+       if (xfer_sz) {
+               if (user_buffer)
+                       return -EFAULT;
+
+               buf = dmam_alloc_coherent(&port->dd->pdev->dev,
+                               ATA_SECT_SIZE * xfer_sz,
+                               &dma_addr,
+                               GFP_KERNEL);
+               if (!buf) {
+                       dev_err(&port->dd->pdev->dev,
+                               "Memory allocation failed (%d bytes)\n",
+                               ATA_SECT_SIZE * xfer_sz);
+                       return -ENOMEM;
+               }
+               memset(buf, 0, ATA_SECT_SIZE * xfer_sz);
+       }
 
        /* Build the FIS. */
        memset(&fis, 0, sizeof(struct host_to_dev_fis));
-       fis.type                = 0x27;
-       fis.opts                = 1 << 7;
-       fis.command             = command[0];
+       fis.type        = 0x27;
+       fis.opts        = 1 << 7;
+       fis.command     = command[0];
        fis.features    = command[2];
        fis.sect_count  = command[3];
        if (fis.command == ATA_CMD_SMART) {
@@ -1908,6 +1927,11 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                fis.cyl_hi      = 0xC2;
        }
 
+       if (xfer_sz)
+               reply = (port->rxfis + RX_FIS_PIO_SETUP);
+       else
+               reply = (port->rxfis + RX_FIS_D2H_REG);
+
        dbg_printk(MTIP_DRV_NAME
                " %s: User Command: cmd %x, sect %x, "
                "feat %x, sectcnt %x\n",
@@ -1917,43 +1941,46 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                command[2],
                command[3]);
 
-       memset(port->sector_buffer, 0x00, ATA_SECT_SIZE);
-
        /* Execute the command. */
        if (mtip_exec_internal_command(port,
                                &fis,
                                 5,
-                                port->sector_buffer_dma,
-                                (command[3] != 0) ? ATA_SECT_SIZE : 0,
+                                (xfer_sz ? dma_addr : 0),
+                                (xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0),
                                 0,
                                 GFP_KERNEL,
                                 MTIP_IOCTL_COMMAND_TIMEOUT_MS)
                                 < 0) {
-               return -1;
+               rv = -EFAULT;
+               goto exit_drive_command;
        }
 
        /* Collect the completion status. */
        command[0] = reply->command; /* Status*/
        command[1] = reply->features; /* Error*/
-       command[2] = command[3];
+       command[2] = reply->sect_count;
 
        dbg_printk(MTIP_DRV_NAME
                " %s: Completion Status: stat %x, "
-               "err %x, cmd %x\n",
+               "err %x, nsect %x\n",
                __func__,
                command[0],
                command[1],
                command[2]);
 
-       if (user_buffer && command[3]) {
+       if (xfer_sz) {
                if (copy_to_user(user_buffer,
-                                port->sector_buffer,
+                                buf,
                                 ATA_SECT_SIZE * command[3])) {
-                       return -EFAULT;
+                       rv = -EFAULT;
+                       goto exit_drive_command;
                }
        }
-
-       return 0;
+exit_drive_command:
+       if (buf)
+               dmam_free_coherent(&port->dd->pdev->dev,
+                               ATA_SECT_SIZE * xfer_sz, buf, dma_addr);
+       return rv;
 }
 
 /*
@@ -2003,6 +2030,32 @@ static unsigned int implicit_sector(unsigned char command,
        return rv;
 }
 
+static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
+{
+       switch (fis->command) {
+       case ATA_CMD_DOWNLOAD_MICRO:
+               *timeout = 120000; /* 2 minutes */
+               break;
+       case ATA_CMD_SEC_ERASE_UNIT:
+       case 0xFC:
+               *timeout = 240000; /* 4 minutes */
+               break;
+       case ATA_CMD_STANDBYNOW1:
+               *timeout = 10000;  /* 10 seconds */
+               break;
+       case 0xF7:
+       case 0xFA:
+               *timeout = 60000;  /* 60 seconds */
+               break;
+       case ATA_CMD_SMART:
+               *timeout = 15000;  /* 15 seconds */
+               break;
+       default:
+               *timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
+               break;
+       }
+}
+
 /*
  * Executes a taskfile
  * See ide_taskfile_ioctl() for derivation
@@ -2023,7 +2076,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
        unsigned int taskin = 0;
        unsigned int taskout = 0;
        u8 nsect = 0;
-       unsigned int timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
+       unsigned int timeout;
        unsigned int force_single_sector;
        unsigned int transfer_size;
        unsigned long task_file_data;
@@ -2153,32 +2206,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
                fis.lba_hi,
                fis.device);
 
-       switch (fis.command) {
-       case ATA_CMD_DOWNLOAD_MICRO:
-               /* Change timeout for Download Microcode to 2 minutes */
-               timeout = 120000;
-               break;
-       case ATA_CMD_SEC_ERASE_UNIT:
-               /* Change timeout for Security Erase Unit to 4 minutes.*/
-               timeout = 240000;
-               break;
-       case ATA_CMD_STANDBYNOW1:
-               /* Change timeout for standby immediate to 10 seconds.*/
-               timeout = 10000;
-               break;
-       case 0xF7:
-       case 0xFA:
-               /* Change timeout for vendor unique command to 10 secs */
-               timeout = 10000;
-               break;
-       case ATA_CMD_SMART:
-               /* Change timeout for vendor unique command to 15 secs */
-               timeout = 15000;
-               break;
-       default:
-               timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
-               break;
-       }
+       mtip_set_timeout(&fis, &timeout);
 
        /* Determine the correct transfer size.*/
        if (force_single_sector)
@@ -2295,13 +2323,12 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
 {
        switch (cmd) {
        case HDIO_GET_IDENTITY:
-               if (mtip_get_identify(dd->port, (void __user *) arg) < 0) {
-                       dev_warn(&dd->pdev->dev,
-                               "Unable to read identity\n");
-                       return -EIO;
-               }
-
+       {
+               if (copy_to_user((void __user *)arg, dd->port->identify,
+                                               sizeof(u16) * ATA_ID_WORDS))
+                       return -EFAULT;
                break;
+       }
        case HDIO_DRIVE_CMD:
        {
                u8 drive_command[4];
@@ -2537,40 +2564,58 @@ static ssize_t mtip_hw_show_registers(struct device *dev,
        int size = 0;
        int n;
 
-       size += sprintf(&buf[size], "S ACTive:\n");
+       size += sprintf(&buf[size], "Hardware\n--------\n");
+       size += sprintf(&buf[size], "S ACTive      : [ 0x");
 
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
                                         readl(dd->port->s_active[n]));
 
-       size += sprintf(&buf[size], "Command Issue:\n");
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "Command Issue : [ 0x");
 
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
                                        readl(dd->port->cmd_issue[n]));
 
-       size += sprintf(&buf[size], "Allocated:\n");
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "Completed     : [ 0x");
+
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
+                               readl(dd->port->completed[n]));
+
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "PORT IRQ STAT : [ 0x%08X ]\n",
+                               readl(dd->port->mmio + PORT_IRQ_STAT));
+       size += sprintf(&buf[size], "HOST IRQ STAT : [ 0x%08X ]\n",
+                               readl(dd->mmio + HOST_IRQ_STAT));
+       size += sprintf(&buf[size], "\n");
 
-       for (n = 0; n < dd->slot_groups; n++) {
+       size += sprintf(&buf[size], "Local\n-----\n");
+       size += sprintf(&buf[size], "Allocated    : [ 0x");
+
+       for (n = dd->slot_groups-1; n >= 0; n--) {
                if (sizeof(long) > sizeof(u32))
                        group_allocated =
                                dd->port->allocated[n/2] >> (32*(n&1));
                else
                        group_allocated = dd->port->allocated[n];
-               size += sprintf(&buf[size], "0x%08x\n",
-                                group_allocated);
+               size += sprintf(&buf[size], "%08X ", group_allocated);
        }
+       size += sprintf(&buf[size], "]\n");
 
-       size += sprintf(&buf[size], "Completed:\n");
-
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
-                               readl(dd->port->completed[n]));
+       size += sprintf(&buf[size], "Commands in Q: [ 0x");
 
-       size += sprintf(&buf[size], "PORT IRQ STAT : 0x%08x\n",
-                               readl(dd->port->mmio + PORT_IRQ_STAT));
-       size += sprintf(&buf[size], "HOST IRQ STAT : 0x%08x\n",
-                               readl(dd->mmio + HOST_IRQ_STAT));
+       for (n = dd->slot_groups-1; n >= 0; n--) {
+               if (sizeof(long) > sizeof(u32))
+                       group_allocated =
+                               dd->port->cmds_to_issue[n/2] >> (32*(n&1));
+               else
+                       group_allocated = dd->port->cmds_to_issue[n];
+               size += sprintf(&buf[size], "%08X ", group_allocated);
+       }
+       size += sprintf(&buf[size], "]\n");
 
        return size;
 }
@@ -2592,8 +2637,24 @@ static ssize_t mtip_hw_show_status(struct device *dev,
        return size;
 }
 
+static ssize_t mtip_hw_show_flags(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct driver_data *dd = dev_to_disk(dev)->private_data;
+       int size = 0;
+
+       size += sprintf(&buf[size], "Flag in port struct : [ %08lX ]\n",
+                                                       dd->port->flags);
+       size += sprintf(&buf[size], "Flag in dd struct   : [ %08lX ]\n",
+                                                       dd->dd_flag);
+
+       return size;
+}
+
 static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL);
 static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL);
+static DEVICE_ATTR(flags, S_IRUGO, mtip_hw_show_flags, NULL);
 
 /*
  * Create the sysfs related attributes.
@@ -2616,6 +2677,9 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj)
        if (sysfs_create_file(kobj, &dev_attr_status.attr))
                dev_warn(&dd->pdev->dev,
                        "Error creating 'status' sysfs entry\n");
+       if (sysfs_create_file(kobj, &dev_attr_flags.attr))
+               dev_warn(&dd->pdev->dev,
+                       "Error creating 'flags' sysfs entry\n");
        return 0;
 }
 
@@ -2636,6 +2700,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj)
 
        sysfs_remove_file(kobj, &dev_attr_registers.attr);
        sysfs_remove_file(kobj, &dev_attr_status.attr);
+       sysfs_remove_file(kobj, &dev_attr_flags.attr);
 
        return 0;
 }
@@ -3634,7 +3699,10 @@ skip_create_disk:
        set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags);
        blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
        blk_queue_physical_block_size(dd->queue, 4096);
+       blk_queue_max_hw_sectors(dd->queue, 0xffff);
+       blk_queue_max_segment_size(dd->queue, 0x400000);
        blk_queue_io_min(dd->queue, 4096);
+
        /*
         * write back cache is not supported in the device. FUA depends on
         * write back cache support, hence setting flush support to zero.
index 4ef5833..b2c88da 100644 (file)
 
 #define __force_bit2int (unsigned int __force)
 
-/* below are bit numbers in 'flags' defined in mtip_port */
-#define MTIP_PF_IC_ACTIVE_BIT          0 /* pio/ioctl */
-#define MTIP_PF_EH_ACTIVE_BIT          1 /* error handling */
-#define MTIP_PF_SE_ACTIVE_BIT          2 /* secure erase */
-#define MTIP_PF_DM_ACTIVE_BIT          3 /* download microcde */
-#define MTIP_PF_PAUSE_IO       ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
+enum {
+       /* below are bit numbers in 'flags' defined in mtip_port */
+       MTIP_PF_IC_ACTIVE_BIT       = 0, /* pio/ioctl */
+       MTIP_PF_EH_ACTIVE_BIT       = 1, /* error handling */
+       MTIP_PF_SE_ACTIVE_BIT       = 2, /* secure erase */
+       MTIP_PF_DM_ACTIVE_BIT       = 3, /* download microcde */
+       MTIP_PF_PAUSE_IO      = ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
                                (1 << MTIP_PF_EH_ACTIVE_BIT) | \
                                (1 << MTIP_PF_SE_ACTIVE_BIT) | \
-                               (1 << MTIP_PF_DM_ACTIVE_BIT))
-
-#define MTIP_PF_SVC_THD_ACTIVE_BIT     4
-#define MTIP_PF_ISSUE_CMDS_BIT         5
-#define MTIP_PF_REBUILD_BIT            6
-#define MTIP_PF_SVC_THD_STOP_BIT       8
-
-/* below are bit numbers in 'dd_flag' defined in driver_data */
-#define MTIP_DDF_REMOVE_PENDING_BIT    1
-#define MTIP_DDF_OVER_TEMP_BIT         2
-#define MTIP_DDF_WRITE_PROTECT_BIT     3
-#define MTIP_DDF_STOP_IO       ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+                               (1 << MTIP_PF_DM_ACTIVE_BIT)),
+
+       MTIP_PF_SVC_THD_ACTIVE_BIT  = 4,
+       MTIP_PF_ISSUE_CMDS_BIT      = 5,
+       MTIP_PF_REBUILD_BIT         = 6,
+       MTIP_PF_SVC_THD_STOP_BIT    = 8,
+
+       /* below are bit numbers in 'dd_flag' defined in driver_data */
+       MTIP_DDF_REMOVE_PENDING_BIT = 1,
+       MTIP_DDF_OVER_TEMP_BIT      = 2,
+       MTIP_DDF_WRITE_PROTECT_BIT  = 3,
+       MTIP_DDF_STOP_IO      = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
                                (1 << MTIP_DDF_OVER_TEMP_BIT) | \
-                               (1 << MTIP_DDF_WRITE_PROTECT_BIT))
+                               (1 << MTIP_DDF_WRITE_PROTECT_BIT)),
 
-#define MTIP_DDF_CLEANUP_BIT           5
-#define MTIP_DDF_RESUME_BIT            6
-#define MTIP_DDF_INIT_DONE_BIT         7
-#define MTIP_DDF_REBUILD_FAILED_BIT    8
+       MTIP_DDF_CLEANUP_BIT        = 5,
+       MTIP_DDF_RESUME_BIT         = 6,
+       MTIP_DDF_INIT_DONE_BIT      = 7,
+       MTIP_DDF_REBUILD_FAILED_BIT = 8,
+};
 
 __packed struct smart_attr{
        u8 attr_id;
index c6074c5..8c87a5e 100644 (file)
@@ -5,8 +5,6 @@
 
 #include <asm/mce.h>
 
-#define BIT_64(n)                      (U64_C(1) << (n))
-
 #define EC(x)                          ((x) & 0xffff)
 #define XEC(x, mask)                   (((x) >> 16) & mask)
 
index a5c591f..444143e 100644 (file)
@@ -6483,6 +6483,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
                                printk(MYIOC_s_INFO_FMT "%s: host reset in"
                                        " progress mpt_config timed out.!!\n",
                                        __func__, ioc->name);
+                               mutex_unlock(&ioc->mptbase_cmds.mutex);
                                return -EFAULT;
                        }
                        spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
index 4de7364..d1827e8 100644 (file)
@@ -1096,20 +1096,20 @@ static int __devinit r6040_init_one(struct pci_dev *pdev,
        if (err) {
                dev_err(&pdev->dev, "32-bit PCI DMA addresses"
                                "not supported by the card\n");
-               goto err_out;
+               goto err_out_disable_dev;
        }
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
                dev_err(&pdev->dev, "32-bit PCI DMA addresses"
                                "not supported by the card\n");
-               goto err_out;
+               goto err_out_disable_dev;
        }
 
        /* IO Size check */
        if (pci_resource_len(pdev, bar) < io_size) {
                dev_err(&pdev->dev, "Insufficient PCI resources, aborting\n");
                err = -EIO;
-               goto err_out;
+               goto err_out_disable_dev;
        }
 
        pci_set_master(pdev);
@@ -1117,7 +1117,7 @@ static int __devinit r6040_init_one(struct pci_dev *pdev,
        dev = alloc_etherdev(sizeof(struct r6040_private));
        if (!dev) {
                err = -ENOMEM;
-               goto err_out;
+               goto err_out_disable_dev;
        }
        SET_NETDEV_DEV(dev, &pdev->dev);
        lp = netdev_priv(dev);
@@ -1233,11 +1233,15 @@ err_out_mdio_irq:
 err_out_mdio:
        mdiobus_free(lp->mii_bus);
 err_out_unmap:
+       netif_napi_del(&lp->napi);
+       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
 err_out_free_dev:
        free_netdev(dev);
+err_out_disable_dev:
+       pci_disable_device(pdev);
 err_out:
        return err;
 }
@@ -1251,6 +1255,9 @@ static void __devexit r6040_remove_one(struct pci_dev *pdev)
        mdiobus_unregister(lp->mii_bus);
        kfree(lp->mii_bus->irq);
        mdiobus_free(lp->mii_bus);
+       netif_napi_del(&lp->napi);
+       pci_set_drvdata(pdev, NULL);
+       pci_iounmap(pdev, lp->base);
        pci_release_regions(pdev);
        free_netdev(dev);
        pci_disable_device(pdev);
index be3c221..667169b 100644 (file)
@@ -1101,8 +1101,12 @@ static int sh_eth_rx(struct net_device *ndev)
 
        /* Restart Rx engine if stopped. */
        /* If we don't need to check status, don't. -KDU */
-       if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R))
+       if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R)) {
+               /* fix the values for the next receiving */
+               mdp->cur_rx = mdp->dirty_rx = (sh_eth_read(ndev, RDFAR) -
+                                              sh_eth_read(ndev, RDLAR)) >> 4;
                sh_eth_write(ndev, EDRRR_R, EDRRR);
+       }
 
        return 0;
 }
@@ -1199,8 +1203,6 @@ static void sh_eth_error(struct net_device *ndev, int intr_status)
                /* Receive Descriptor Empty int */
                ndev->stats.rx_over_errors++;
 
-               if (sh_eth_read(ndev, EDRRR) ^ EDRRR_R)
-                       sh_eth_write(ndev, EDRRR_R, EDRRR);
                if (netif_msg_rx_err(mdp))
                        dev_err(&ndev->dev, "Receive Descriptor Empty\n");
        }
index dab9c6f..1466e5d 100644 (file)
@@ -2390,11 +2390,11 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
 
        retval = smsc911x_request_resources(pdev);
        if (retval)
-               goto out_return_resources;
+               goto out_request_resources_fail;
 
        retval = smsc911x_enable_resources(pdev);
        if (retval)
-               goto out_disable_resources;
+               goto out_enable_resources_fail;
 
        if (pdata->ioaddr == NULL) {
                SMSC_WARN(pdata, probe, "Error smsc911x base address invalid");
@@ -2501,8 +2501,9 @@ out_free_irq:
        free_irq(dev->irq, dev);
 out_disable_resources:
        (void)smsc911x_disable_resources(pdev);
-out_return_resources:
+out_enable_resources_fail:
        smsc911x_free_resources(pdev);
+out_request_resources_fail:
        platform_set_drvdata(pdev, NULL);
        iounmap(pdata->ioaddr);
        free_netdev(dev);
index 71e2b05..3ae80ec 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/crc32.h>
 #include <linux/usb/usbnet.h>
 #include <linux/slab.h>
+#include <linux/if_vlan.h>
 
 #define DRIVER_VERSION "22-Dec-2011"
 #define DRIVER_NAME "asix"
@@ -321,7 +322,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        return 0;
                }
 
-               if ((size > dev->net->mtu + ETH_HLEN) ||
+               if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) ||
                    (size + offset > skb->len)) {
                        netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
                                   size);
index 380dbea..3b20678 100644 (file)
@@ -547,6 +547,8 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x16d8, 0x8002)},      /* CMDTech Gobi 2000 Modem device (VU922) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9205)},      /* Gobi 2000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x9013)},      /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+       {QMI_GOBI_DEVICE(0x1199, 0x9015)},      /* Sierra Wireless Gobi 3000 Modem device */
+       {QMI_GOBI_DEVICE(0x1199, 0x9019)},      /* Sierra Wireless Gobi 3000 Modem device */
        { }                                     /* END */
 };
 MODULE_DEVICE_TABLE(usb, products);
index 9ce6995..5214b1e 100644 (file)
@@ -1231,11 +1231,6 @@ static int virtnet_freeze(struct virtio_device *vdev)
        vi->config_enable = false;
        mutex_unlock(&vi->config_lock);
 
-       virtqueue_disable_cb(vi->rvq);
-       virtqueue_disable_cb(vi->svq);
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
-               virtqueue_disable_cb(vi->cvq);
-
        netif_device_detach(vi->dev);
        cancel_delayed_work_sync(&vi->refill);
 
index 0ba81a6..fbaa309 100644 (file)
@@ -2415,6 +2415,22 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
 * Initialization routines *
 \*************************/
 
+static const struct ieee80211_iface_limit if_limits[] = {
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) },
+       { .max = 4,     .types =
+#ifdef CONFIG_MAC80211_MESH
+                                BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                                BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination if_comb = {
+       .limits = if_limits,
+       .n_limits = ARRAY_SIZE(if_limits),
+       .max_interfaces = 2048,
+       .num_different_channels = 1,
+};
+
 int __devinit
 ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
 {
@@ -2436,6 +2452,9 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_MESH_POINT);
 
+       hw->wiphy->iface_combinations = &if_comb;
+       hw->wiphy->n_iface_combinations = 1;
+
        /* SW support for IBSS_RSN is provided by mac80211 */
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
index ac53d90..dfb0441 100644 (file)
@@ -3809,7 +3809,7 @@ static bool is_pmu_set(struct ath_hw *ah, u32 pmu_reg, int pmu_set)
        return true;
 }
 
-static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
+void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
 {
        int internal_regulator =
                ath9k_hw_ar9300_get_eeprom(ah, EEP_INTERNAL_REGULATOR);
index 2505ac4..8396d15 100644 (file)
@@ -334,4 +334,7 @@ u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
 
 unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah,
                                           struct ath9k_channel *chan);
+
+void ar9003_hw_internal_regulator_apply(struct ath_hw *ah);
+
 #endif
index f11d9b2..1bd3a3d 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +19,7 @@
 #define INITVALS_9330_1P1_H
 
 static const u32 ar9331_1p1_baseband_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005},
        {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e},
        {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
@@ -27,10 +28,10 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
        {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c},
        {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044},
        {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4},
-       {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020},
+       {0x00009e04, 0x00202020, 0x00202020, 0x00202020, 0x00202020},
        {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
        {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e},
-       {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+       {0x00009e14, 0x31365d5e, 0x3136605e, 0x3136605e, 0x31365d5e},
        {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
        {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
@@ -55,7 +56,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
        {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
-       {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981},
+       {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982},
        {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020},
@@ -63,7 +64,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
 };
 
 static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -155,7 +156,7 @@ static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52},
        {0x0000a2e0, 0xffb31c84, 0xffb31c84, 0xffb31c84, 0xffb31c84},
@@ -245,7 +246,7 @@ static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -377,14 +378,14 @@ static const u32 ar9331_1p1_radio_core[][2] = {
        {0x000160b4, 0x92480040},
        {0x000160c0, 0x006db6db},
        {0x000160c4, 0x0186db60},
-       {0x000160c8, 0x6db6db6c},
+       {0x000160c8, 0x6db4db6c},
        {0x000160cc, 0x6de6c300},
        {0x000160d0, 0x14500820},
        {0x00016100, 0x04cb0001},
        {0x00016104, 0xfff80015},
        {0x00016108, 0x00080010},
        {0x0001610c, 0x00170000},
-       {0x00016140, 0x10804000},
+       {0x00016140, 0x10800000},
        {0x00016144, 0x01884080},
        {0x00016148, 0x000080c0},
        {0x00016280, 0x01000015},
@@ -417,7 +418,7 @@ static const u32 ar9331_1p1_radio_core[][2] = {
 };
 
 static const u32 ar9331_1p1_soc_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022},
 };
 
@@ -691,7 +692,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = {
 };
 
 static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -783,7 +784,7 @@ static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_1p1_mac_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
        {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
        {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
@@ -973,26 +974,27 @@ static const u32 ar9331_1p1_mac_core[][2] = {
 
 static const u32 ar9331_common_rx_gain_1p1[][2] = {
        /* Addr      allmodes  */
-       {0x0000a000, 0x00010000},
-       {0x0000a004, 0x00030002},
-       {0x0000a008, 0x00050004},
-       {0x0000a00c, 0x00810080},
-       {0x0000a010, 0x00830082},
-       {0x0000a014, 0x01810180},
-       {0x0000a018, 0x01830182},
-       {0x0000a01c, 0x01850184},
-       {0x0000a020, 0x01890188},
-       {0x0000a024, 0x018b018a},
-       {0x0000a028, 0x018d018c},
-       {0x0000a02c, 0x01910190},
-       {0x0000a030, 0x01930192},
-       {0x0000a034, 0x01950194},
-       {0x0000a038, 0x038a0196},
-       {0x0000a03c, 0x038c038b},
-       {0x0000a040, 0x0390038d},
-       {0x0000a044, 0x03920391},
-       {0x0000a048, 0x03940393},
-       {0x0000a04c, 0x03960395},
+       {0x00009e18, 0x05000000},
+       {0x0000a000, 0x00060005},
+       {0x0000a004, 0x00810080},
+       {0x0000a008, 0x00830082},
+       {0x0000a00c, 0x00850084},
+       {0x0000a010, 0x01820181},
+       {0x0000a014, 0x01840183},
+       {0x0000a018, 0x01880185},
+       {0x0000a01c, 0x018a0189},
+       {0x0000a020, 0x02850284},
+       {0x0000a024, 0x02890288},
+       {0x0000a028, 0x028b028a},
+       {0x0000a02c, 0x03850384},
+       {0x0000a030, 0x03890388},
+       {0x0000a034, 0x038b038a},
+       {0x0000a038, 0x038d038c},
+       {0x0000a03c, 0x03910390},
+       {0x0000a040, 0x03930392},
+       {0x0000a044, 0x03950394},
+       {0x0000a048, 0x00000396},
+       {0x0000a04c, 0x00000000},
        {0x0000a050, 0x00000000},
        {0x0000a054, 0x00000000},
        {0x0000a058, 0x00000000},
@@ -1005,15 +1007,15 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a074, 0x00000000},
        {0x0000a078, 0x00000000},
        {0x0000a07c, 0x00000000},
-       {0x0000a080, 0x22222229},
-       {0x0000a084, 0x1d1d1d1d},
-       {0x0000a088, 0x1d1d1d1d},
-       {0x0000a08c, 0x1d1d1d1d},
-       {0x0000a090, 0x171d1d1d},
-       {0x0000a094, 0x11111717},
-       {0x0000a098, 0x00030311},
-       {0x0000a09c, 0x00000000},
-       {0x0000a0a0, 0x00000000},
+       {0x0000a080, 0x28282828},
+       {0x0000a084, 0x28282828},
+       {0x0000a088, 0x28282828},
+       {0x0000a08c, 0x28282828},
+       {0x0000a090, 0x28282828},
+       {0x0000a094, 0x24242428},
+       {0x0000a098, 0x171e1e1e},
+       {0x0000a09c, 0x02020b0b},
+       {0x0000a0a0, 0x02020202},
        {0x0000a0a4, 0x00000000},
        {0x0000a0a8, 0x00000000},
        {0x0000a0ac, 0x00000000},
@@ -1021,27 +1023,27 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a0b4, 0x00000000},
        {0x0000a0b8, 0x00000000},
        {0x0000a0bc, 0x00000000},
-       {0x0000a0c0, 0x001f0000},
-       {0x0000a0c4, 0x01000101},
-       {0x0000a0c8, 0x011e011f},
-       {0x0000a0cc, 0x011c011d},
-       {0x0000a0d0, 0x02030204},
-       {0x0000a0d4, 0x02010202},
-       {0x0000a0d8, 0x021f0200},
-       {0x0000a0dc, 0x0302021e},
-       {0x0000a0e0, 0x03000301},
-       {0x0000a0e4, 0x031e031f},
-       {0x0000a0e8, 0x0402031d},
-       {0x0000a0ec, 0x04000401},
-       {0x0000a0f0, 0x041e041f},
-       {0x0000a0f4, 0x0502041d},
-       {0x0000a0f8, 0x05000501},
-       {0x0000a0fc, 0x051e051f},
-       {0x0000a100, 0x06010602},
-       {0x0000a104, 0x061f0600},
-       {0x0000a108, 0x061d061e},
-       {0x0000a10c, 0x07020703},
-       {0x0000a110, 0x07000701},
+       {0x0000a0c0, 0x22072208},
+       {0x0000a0c4, 0x22052206},
+       {0x0000a0c8, 0x22032204},
+       {0x0000a0cc, 0x22012202},
+       {0x0000a0d0, 0x221f2200},
+       {0x0000a0d4, 0x221d221e},
+       {0x0000a0d8, 0x33023303},
+       {0x0000a0dc, 0x33003301},
+       {0x0000a0e0, 0x331e331f},
+       {0x0000a0e4, 0x4402331d},
+       {0x0000a0e8, 0x44004401},
+       {0x0000a0ec, 0x441e441f},
+       {0x0000a0f0, 0x55025503},
+       {0x0000a0f4, 0x55005501},
+       {0x0000a0f8, 0x551e551f},
+       {0x0000a0fc, 0x6602551d},
+       {0x0000a100, 0x66006601},
+       {0x0000a104, 0x661e661f},
+       {0x0000a108, 0x7703661d},
+       {0x0000a10c, 0x77017702},
+       {0x0000a110, 0x00007700},
        {0x0000a114, 0x00000000},
        {0x0000a118, 0x00000000},
        {0x0000a11c, 0x00000000},
@@ -1054,26 +1056,26 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a138, 0x00000000},
        {0x0000a13c, 0x00000000},
        {0x0000a140, 0x001f0000},
-       {0x0000a144, 0x01000101},
-       {0x0000a148, 0x011e011f},
-       {0x0000a14c, 0x011c011d},
-       {0x0000a150, 0x02030204},
-       {0x0000a154, 0x02010202},
-       {0x0000a158, 0x021f0200},
-       {0x0000a15c, 0x0302021e},
-       {0x0000a160, 0x03000301},
-       {0x0000a164, 0x031e031f},
-       {0x0000a168, 0x0402031d},
-       {0x0000a16c, 0x04000401},
-       {0x0000a170, 0x041e041f},
-       {0x0000a174, 0x0502041d},
-       {0x0000a178, 0x05000501},
-       {0x0000a17c, 0x051e051f},
-       {0x0000a180, 0x06010602},
-       {0x0000a184, 0x061f0600},
-       {0x0000a188, 0x061d061e},
-       {0x0000a18c, 0x07020703},
-       {0x0000a190, 0x07000701},
+       {0x0000a144, 0x111f1100},
+       {0x0000a148, 0x111d111e},
+       {0x0000a14c, 0x111b111c},
+       {0x0000a150, 0x22032204},
+       {0x0000a154, 0x22012202},
+       {0x0000a158, 0x221f2200},
+       {0x0000a15c, 0x221d221e},
+       {0x0000a160, 0x33013302},
+       {0x0000a164, 0x331f3300},
+       {0x0000a168, 0x4402331e},
+       {0x0000a16c, 0x44004401},
+       {0x0000a170, 0x441e441f},
+       {0x0000a174, 0x55015502},
+       {0x0000a178, 0x551f5500},
+       {0x0000a17c, 0x6602551e},
+       {0x0000a180, 0x66006601},
+       {0x0000a184, 0x661e661f},
+       {0x0000a188, 0x7703661d},
+       {0x0000a18c, 0x77017702},
+       {0x0000a190, 0x00007700},
        {0x0000a194, 0x00000000},
        {0x0000a198, 0x00000000},
        {0x0000a19c, 0x00000000},
@@ -1100,14 +1102,14 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a1f0, 0x00000396},
        {0x0000a1f4, 0x00000396},
        {0x0000a1f8, 0x00000396},
-       {0x0000a1fc, 0x00000196},
+       {0x0000a1fc, 0x00000296},
 };
 
 static const u32 ar9331_common_tx_gain_offset1_1[][1] = {
-       {0},
-       {3},
-       {0},
-       {0},
+       {0x00000000},
+       {0x00000003},
+       {0x00000000},
+       {0x00000000},
 };
 
 static const u32 ar9331_1p1_chansel_xtal_25M[] = {
index abe05ec..7db1890 100644 (file)
@@ -1468,6 +1468,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
                return false;
 
        ah->chip_fullsleep = false;
+
+       if (AR_SREV_9330(ah))
+               ar9003_hw_internal_regulator_apply(ah);
        ath9k_hw_init_pll(ah, chan);
        ath9k_hw_set_rfmode(ah, chan);
 
index dfa78e8..4de4473 100644 (file)
@@ -239,7 +239,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       bool ret;
+       bool ret = true;
 
        ieee80211_stop_queues(sc->hw);
 
@@ -250,11 +250,12 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
        ath9k_debug_samp_bb_mac(sc);
        ath9k_hw_disable_interrupts(ah);
 
-       ret = ath_drain_all_txq(sc, retry_tx);
-
        if (!ath_stoprecv(sc))
                ret = false;
 
+       if (!ath_drain_all_txq(sc, retry_tx))
+               ret = false;
+
        if (!flush) {
                if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                        ath_rx_tasklet(sc, 1, true);
index 23eaa1b..d59dd01 100644 (file)
@@ -64,7 +64,8 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
 static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
                                           struct ath_txq *txq,
                                           struct ath_atx_tid *tid,
-                                          struct sk_buff *skb);
+                                          struct sk_buff *skb,
+                                          bool dequeue);
 
 enum {
        MCS_HT20,
@@ -811,7 +812,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                fi = get_frame_info(skb);
                bf = fi->bf;
                if (!fi->bf)
-                       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+                       bf = ath_tx_setup_buffer(sc, txq, tid, skb, true);
 
                if (!bf)
                        continue;
@@ -1726,7 +1727,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
                return;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false);
        if (!bf)
                return;
 
@@ -1753,7 +1754,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
 
        bf = fi->bf;
        if (!bf)
-               bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+               bf = ath_tx_setup_buffer(sc, txq, tid, skb, false);
 
        if (!bf)
                return;
@@ -1814,7 +1815,8 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
 static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
                                           struct ath_txq *txq,
                                           struct ath_atx_tid *tid,
-                                          struct sk_buff *skb)
+                                          struct sk_buff *skb,
+                                          bool dequeue)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_frame_info *fi = get_frame_info(skb);
@@ -1863,6 +1865,8 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 
 error:
+       if (dequeue)
+               __skb_unlink(skb, &tid->buf_q);
        dev_kfree_skb_any(skb);
        return NULL;
 }
@@ -1893,7 +1897,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb,
                 */
                ath_tx_send_ampdu(sc, tid, skb, txctl);
        } else {
-               bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+               bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false);
                if (!bf)
                        return;
 
index c5a34ff..a299d42 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/uaccess.h>
 #include <linux/firmware.h>
 #include <linux/usb.h>
+#include <linux/vmalloc.h>
 #include <net/cfg80211.h>
 
 #include <defs.h>
@@ -1239,7 +1240,7 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
                return -EINVAL;
        }
 
-       devinfo->image = kmalloc(fw->size, GFP_ATOMIC); /* plus nvram */
+       devinfo->image = vmalloc(fw->size); /* plus nvram */
        if (!devinfo->image)
                return -ENOMEM;
 
@@ -1603,7 +1604,7 @@ static struct usb_driver brcmf_usbdrvr = {
 void brcmf_usb_exit(void)
 {
        usb_deregister(&brcmf_usbdrvr);
-       kfree(g_image.data);
+       vfree(g_image.data);
        g_image.data = NULL;
        g_image.len = 0;
 }
index db6c6e5..2463c06 100644 (file)
@@ -137,11 +137,3 @@ config IWLWIFI_EXPERIMENTAL_MFP
          even if the microcode doesn't advertise it.
 
          Say Y only if you want to experiment with MFP.
-
-config IWLWIFI_UCODE16
-       bool "support uCode 16.0"
-       depends on IWLWIFI
-       help
-         This option enables support for uCode version 16.0.
-
-         Say Y if you want to use 16.0 microcode.
index 406f297..d615eac 100644 (file)
@@ -18,7 +18,6 @@ iwlwifi-objs          += iwl-notif-wait.o
 iwlwifi-objs           += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o
 
 
-iwlwifi-$(CONFIG_IWLWIFI_UCODE16) += iwl-phy-db.o
 iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o
index 7f79341..8133105 100644 (file)
@@ -79,7 +79,7 @@ static const struct iwl_base_params iwl2000_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
        .hd_v2 = true,
 };
 
@@ -97,7 +97,7 @@ static const struct iwl_base_params iwl2030_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
        .hd_v2 = true,
 };
 
index 381b02c..19f7ee8 100644 (file)
@@ -86,7 +86,7 @@ static const struct iwl_base_params iwl6000_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_base_params iwl6050_base_params = {
@@ -102,7 +102,7 @@ static const struct iwl_base_params iwl6050_base_params = {
        .chain_noise_scale = 1500,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 1024,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_base_params iwl6000_g2_base_params = {
@@ -118,7 +118,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_ht_params iwl6000_ht_params = {
index 51e1a69..8cebd7c 100644 (file)
@@ -884,6 +884,7 @@ static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        if ((priv->bt_traffic_load != priv->last_bt_traffic_load) ||
            (priv->bt_full_concurrent != full_concurrent)) {
                priv->bt_full_concurrent = full_concurrent;
+               priv->last_bt_traffic_load = priv->bt_traffic_load;
 
                /* Update uCode's rate table. */
                tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
index b31584e..aea07aa 100644 (file)
@@ -772,7 +772,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                                                ~IWL_STA_DRIVER_ACTIVE;
                                priv->stations[i].used &=
                                                ~IWL_STA_UCODE_INPROGRESS;
-                               spin_unlock_bh(&priv->sta_lock);
+                               continue;
                        }
                        /*
                         * Rate scaling has already been initialized, send
index 3c72bad..d742900 100644 (file)
@@ -657,17 +657,17 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        return -EINVAL;
 }
 
-static int alloc_pci_desc(struct iwl_drv *drv,
-                         struct iwl_firmware_pieces *pieces,
-                         enum iwl_ucode_type type)
+static int iwl_alloc_ucode(struct iwl_drv *drv,
+                          struct iwl_firmware_pieces *pieces,
+                          enum iwl_ucode_type type)
 {
        int i;
        for (i = 0;
             i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i);
             i++)
                if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]),
-                                               get_sec(pieces, type, i)))
-                       return -1;
+                                     get_sec(pieces, type, i)))
+                       return -ENOMEM;
        return 0;
 }
 
@@ -825,8 +825,8 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
         * 1) unmodified from disk
         * 2) backup cache for save/restore during power-downs */
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
-               if (alloc_pci_desc(drv, &pieces, i))
-                       goto err_pci_alloc;
+               if (iwl_alloc_ucode(drv, &pieces, i))
+                       goto out_free_fw;
 
        /* Now that we can no longer fail, copy information */
 
@@ -866,7 +866,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
        drv->op_mode = iwl_dvm_ops.start(drv->trans, drv->cfg, &drv->fw);
 
        if (!drv->op_mode)
-               goto out_unbind;
+               goto out_free_fw;
 
        return;
 
@@ -877,7 +877,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
                goto out_unbind;
        return;
 
err_pci_alloc:
out_free_fw:
        IWL_ERR(drv, "failed to allocate pci memory\n");
        iwl_dealloc_ucode(drv);
        release_firmware(ucode_raw);
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
deleted file mode 100644 (file)
index f166955..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include "iwl-debug.h"
-#include "iwl-dev.h"
-
-#include "iwl-phy-db.h"
-
-#define CHANNEL_NUM_SIZE       4       /* num of channels in calib_ch size */
-
-struct iwl_phy_db *iwl_phy_db_init(struct device *dev)
-{
-       struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
-                                           GFP_KERNEL);
-
-       if (!phy_db)
-               return phy_db;
-
-       phy_db->dev = dev;
-
-       /* TODO: add default values of the phy db. */
-       return phy_db;
-}
-
-/*
- * get phy db section: returns a pointer to a phy db section specified by
- * type and channel group id.
- */
-static struct iwl_phy_db_entry *
-iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
-                      enum iwl_phy_db_section_type type,
-                      u16 chg_id)
-{
-       if (!phy_db || type < 0 || type >= IWL_PHY_DB_MAX)
-               return NULL;
-
-       switch (type) {
-       case IWL_PHY_DB_CFG:
-               return &phy_db->cfg;
-       case IWL_PHY_DB_CALIB_NCH:
-               return &phy_db->calib_nch;
-       case IWL_PHY_DB_CALIB_CH:
-               return &phy_db->calib_ch;
-       case IWL_PHY_DB_CALIB_CHG_PAPD:
-               if (chg_id < 0 || chg_id >= IWL_NUM_PAPD_CH_GROUPS)
-                       return NULL;
-               return &phy_db->calib_ch_group_papd[chg_id];
-       case IWL_PHY_DB_CALIB_CHG_TXP:
-               if (chg_id < 0 || chg_id >= IWL_NUM_TXP_CH_GROUPS)
-                       return NULL;
-               return &phy_db->calib_ch_group_txp[chg_id];
-       default:
-               return NULL;
-       }
-       return NULL;
-}
-
-static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
-                                   enum iwl_phy_db_section_type type,
-                                   u16 chg_id)
-{
-       struct iwl_phy_db_entry *entry =
-                               iwl_phy_db_get_section(phy_db, type, chg_id);
-       if (!entry)
-               return;
-
-       kfree(entry->data);
-       entry->data = NULL;
-       entry->size = 0;
-}
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db)
-{
-       int i;
-
-       if (!phy_db)
-               return;
-
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
-       for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
-               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
-       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
-               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
-
-       kfree(phy_db);
-}
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
-                          enum iwl_phy_db_section_type type, u8 *data,
-                          u16 size, gfp_t alloc_ctx)
-{
-       struct iwl_phy_db_entry *entry;
-       u16 chg_id = 0;
-
-       if (!phy_db)
-               return -EINVAL;
-
-       if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
-           type == IWL_PHY_DB_CALIB_CHG_TXP)
-               chg_id = le16_to_cpup((__le16 *)data);
-
-       entry = iwl_phy_db_get_section(phy_db, type, chg_id);
-       if (!entry)
-               return -EINVAL;
-
-       kfree(entry->data);
-       entry->data = kmemdup(data, size, alloc_ctx);
-       if (!entry->data) {
-               entry->size = 0;
-               return -ENOMEM;
-       }
-
-       entry->size = size;
-
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               phy_db->channel_num = le32_to_cpup((__le32 *)data);
-               phy_db->channel_size =
-                     (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
-       }
-
-       return 0;
-}
-
-static int is_valid_channel(u16 ch_id)
-{
-       if (ch_id <= 14 ||
-           (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
-           (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
-           (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
-               return 1;
-       return 0;
-}
-
-static u8 ch_id_to_ch_index(u16 ch_id)
-{
-       if (WARN_ON(!is_valid_channel(ch_id)))
-               return 0xff;
-
-       if (ch_id <= 14)
-               return ch_id - 1;
-       if (ch_id <= 64)
-               return (ch_id + 20) / 4;
-       if (ch_id <= 140)
-               return (ch_id - 12) / 4;
-       return (ch_id - 13) / 4;
-}
-
-
-static u16 channel_id_to_papd(u16 ch_id)
-{
-       if (WARN_ON(!is_valid_channel(ch_id)))
-               return 0xff;
-
-       if (1 <= ch_id && ch_id <= 14)
-               return 0;
-       if (36 <= ch_id && ch_id <= 64)
-               return 1;
-       if (100 <= ch_id && ch_id <= 140)
-               return 2;
-       return 3;
-}
-
-static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
-{
-       struct iwl_phy_db_chg_txp *txp_chg;
-       int i;
-       u8 ch_index = ch_id_to_ch_index(ch_id);
-       if (ch_index == 0xff)
-               return 0xff;
-
-       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
-               txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
-               if (!txp_chg)
-                       return 0xff;
-               /*
-                * Looking for the first channel group that its max channel is
-                * higher then wanted channel.
-                */
-               if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
-                       return i;
-       }
-       return 0xff;
-}
-
-int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
-                               enum iwl_phy_db_section_type type, u8 **data,
-                               u16 *size, u16 ch_id)
-{
-       struct iwl_phy_db_entry *entry;
-       u32 channel_num;
-       u32 channel_size;
-       u16 ch_group_id = 0;
-       u16 index;
-
-       if (!phy_db)
-               return -EINVAL;
-
-       /* find wanted channel group */
-       if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
-               ch_group_id = channel_id_to_papd(ch_id);
-       else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
-               ch_group_id = channel_id_to_txp(phy_db, ch_id);
-
-       entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
-       if (!entry)
-               return -EINVAL;
-
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               index = ch_id_to_ch_index(ch_id);
-               channel_num = phy_db->channel_num;
-               channel_size = phy_db->channel_size;
-               if (index >= channel_num) {
-                       IWL_ERR(phy_db, "Wrong channel number %d", ch_id);
-                       return -EINVAL;
-               }
-               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
-               *size = channel_size;
-       } else {
-               *data = entry->data;
-               *size = entry->size;
-       }
-       return 0;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
deleted file mode 100644 (file)
index c34c6a9..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __IWL_PHYDB_H__
-#define __IWL_PHYDB_H__
-
-#include <linux/types.h>
-
-#define IWL_NUM_PAPD_CH_GROUPS 4
-#define IWL_NUM_TXP_CH_GROUPS  8
-
-struct iwl_phy_db_entry {
-       u16     size;
-       u8      *data;
-};
-
-struct iwl_shared;
-
-/**
- * struct iwl_phy_db - stores phy configuration and calibration data.
- *
- * @cfg: phy configuration.
- * @calib_nch: non channel specific calibration data.
- * @calib_ch: channel specific calibration data.
- * @calib_ch_group_papd: calibration data related to papd channel group.
- * @calib_ch_group_txp: calibration data related to tx power chanel group.
- */
-struct iwl_phy_db {
-       struct iwl_phy_db_entry cfg;
-       struct iwl_phy_db_entry calib_nch;
-       struct iwl_phy_db_entry calib_ch;
-       struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
-       struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
-
-       u32 channel_num;
-       u32 channel_size;
-
-       /* for an access to the logger */
-       struct device *dev;
-};
-
-enum iwl_phy_db_section_type {
-       IWL_PHY_DB_CFG = 1,
-       IWL_PHY_DB_CALIB_NCH,
-       IWL_PHY_DB_CALIB_CH,
-       IWL_PHY_DB_CALIB_CHG_PAPD,
-       IWL_PHY_DB_CALIB_CHG_TXP,
-       IWL_PHY_DB_MAX
-};
-
-/* for parsing of tx power channel group data that comes from the firmware*/
-struct iwl_phy_db_chg_txp {
-       __le32 space;
-       __le16 max_channel_idx;
-} __packed;
-
-struct iwl_phy_db *iwl_phy_db_init(struct device *dev);
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db);
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
-                          enum iwl_phy_db_section_type type, u8 *data,
-                          u16 size, gfp_t alloc_ctx);
-
-int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
-                               enum iwl_phy_db_section_type type, u8 **data,
-                               u16 *size, u16 ch_id);
-
-#endif /* __IWL_PHYDB_H__ */
index 6213c05..e959207 100644 (file)
@@ -347,7 +347,7 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
 void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo,
                                 int sta_id, int tid, int frame_limit, u16 ssn);
 void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
-       int index, enum dma_data_direction dma_dir);
+                        enum dma_data_direction dma_dir);
 int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
                         struct sk_buff_head *skbs);
 int iwl_queue_space(const struct iwl_queue *q);
index 21a8a67..a875023 100644 (file)
@@ -204,33 +204,39 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
        for (i = 1; i < num_tbs; i++)
                dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i),
                                iwl_tfd_tb_get_len(tfd, i), dma_dir);
+
+       tfd->num_tbs = 0;
 }
 
 /**
  * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
  * @trans - transport private data
  * @txq - tx queue
- * @index - the index of the TFD to be freed
- *@dma_dir - the direction of the DMA mapping
+ * @dma_dir - the direction of the DMA mapping
  *
  * Does NOT advance any TFD circular buffer read/write indexes
  * Does NOT free the TFD itself (which is within circular buffer)
  */
 void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
-       int index, enum dma_data_direction dma_dir)
+                        enum dma_data_direction dma_dir)
 {
        struct iwl_tfd *tfd_tmp = txq->tfds;
 
+       /* rd_ptr is bounded by n_bd and idx is bounded by n_window */
+       int rd_ptr = txq->q.read_ptr;
+       int idx = get_cmd_index(&txq->q, rd_ptr);
+
        lockdep_assert_held(&txq->lock);
 
-       iwlagn_unmap_tfd(trans, &txq->entries[index].meta,
-                        &tfd_tmp[index], dma_dir);
+       /* We have only q->n_window txq->entries, but we use q->n_bd tfds */
+       iwlagn_unmap_tfd(trans, &txq->entries[idx].meta,
+                        &tfd_tmp[rd_ptr], dma_dir);
 
        /* free SKB */
        if (txq->entries) {
                struct sk_buff *skb;
 
-               skb = txq->entries[index].skb;
+               skb = txq->entries[idx].skb;
 
                /* Can be called from irqs-disabled context
                 * If skb is not NULL, it means that the whole queue is being
@@ -238,7 +244,7 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
                 */
                if (skb) {
                        iwl_op_mode_free_skb(trans->op_mode, skb);
-                       txq->entries[index].skb = NULL;
+                       txq->entries[idx].skb = NULL;
                }
        }
 }
@@ -973,7 +979,7 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
 
                iwlagn_txq_inval_byte_cnt_tbl(trans, txq);
 
-               iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE);
+               iwlagn_txq_free_tfd(trans, txq, DMA_TO_DEVICE);
                freed++;
        }
 
index 2e57161..ec6fb39 100644 (file)
@@ -435,9 +435,7 @@ static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id)
 
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
-               /* The read_ptr needs to bound by q->n_window */
-               iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr),
-                                   dma_dir);
+               iwlagn_txq_free_tfd(trans, txq, dma_dir);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
        }
        spin_unlock_bh(&txq->lock);
index 1b851f6..e2750a1 100644 (file)
@@ -260,6 +260,7 @@ static int wl1251_sdio_probe(struct sdio_func *func,
        }
 
        if (wl->irq) {
+               irq_set_status_flags(wl->irq, IRQ_NOAUTOEN);
                ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl);
                if (ret < 0) {
                        wl1251_error("request_irq() failed: %d", ret);
@@ -267,7 +268,6 @@ static int wl1251_sdio_probe(struct sdio_func *func,
                }
 
                irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
-               disable_irq(wl->irq);
 
                wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq;
                wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq;
index 6248c35..87f6305 100644 (file)
@@ -281,6 +281,7 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
 
        wl->use_eeprom = pdata->use_eeprom;
 
+       irq_set_status_flags(wl->irq, IRQ_NOAUTOEN);
        ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
        if (ret < 0) {
                wl1251_error("request_irq() failed: %d", ret);
@@ -289,8 +290,6 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
 
        irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
 
-       disable_irq(wl->irq);
-
        ret = wl1251_init_ieee80211(wl);
        if (ret)
                goto out_irq;
index 509aa88..f3d6fa5 100644 (file)
@@ -1715,6 +1715,7 @@ out:
 
 }
 
+#ifdef CONFIG_PM
 /* Set the global behaviour of RX filters - On/Off + default action */
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
                                        enum rx_filter_action action)
@@ -1794,3 +1795,4 @@ out:
        kfree(acx);
        return ret;
 }
+#endif /* CONFIG_PM */
index 8106b2e..e6a7486 100644 (file)
@@ -1330,9 +1330,11 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
+
+#ifdef CONFIG_PM
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
                                        enum rx_filter_action action);
 int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable,
                             struct wl12xx_rx_filter *filter);
-
+#endif /* CONFIG_PM */
 #endif /* __WL1271_ACX_H__ */
index 1f1d948..d6a3c6b 100644 (file)
@@ -279,6 +279,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
        wl12xx_rearm_rx_streaming(wl, active_hlids);
 }
 
+#ifdef CONFIG_PM
 int wl1271_rx_filter_enable(struct wl1271 *wl,
                            int index, bool enable,
                            struct wl12xx_rx_filter *filter)
@@ -314,3 +315,4 @@ void wl1271_rx_filter_clear_all(struct wl1271 *wl)
                wl1271_rx_filter_enable(wl, i, 0, NULL);
        }
 }
+#endif /* CONFIG_PM */
index 2596401..f4a6fca 100644 (file)
@@ -325,8 +325,7 @@ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
        unsigned int count;
        int i, copy_off;
 
-       count = DIV_ROUND_UP(
-                       offset_in_page(skb->data)+skb_headlen(skb), PAGE_SIZE);
+       count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE);
 
        copy_off = skb_headlen(skb) % PAGE_SIZE;
 
index 46f4a9f..281f18c 100644 (file)
@@ -232,7 +232,7 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
 
 static int check_crc(u8 *buf, int buflen)
 {
-       u8 len;
+       int len;
        u16 crc;
 
        len = buf[0] + 1;
index 99dc29f..e3a3b49 100644 (file)
@@ -1,5 +1,5 @@
 menuconfig POWER_SUPPLY
-       tristate "Power supply class support"
+       bool "Power supply class support"
        help
          Say Y here to enable power supply class support. This allows
          power supply (batteries, AC, USB) monitoring by userspace
@@ -77,7 +77,7 @@ config BATTERY_DS2780
          Say Y here to enable support for batteries with ds2780 chip.
 
 config BATTERY_DS2781
-       tristate "2781 battery driver"
+       tristate "DS2781 battery driver"
        depends on HAS_IOMEM
        select W1
        select W1_SLAVE_DS2781
@@ -181,14 +181,15 @@ config BATTERY_MAX17040
          to operate with a single lithium cell
 
 config BATTERY_MAX17042
-       tristate "Maxim MAX17042/8997/8966 Fuel Gauge"
+       tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
        depends on I2C
        help
          MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries
          in handheld and portable equipment. The MAX17042 is configured
          to operate with a single lithium cell. MAX8997 and MAX8966 are
          multi-function devices that include fuel gauages that are compatible
-         with MAX17042.
+         with MAX17042. This driver also supports max17047/50 chips which are
+         improved version of max17042.
 
 config BATTERY_Z2
        tristate "Z2 battery driver"
@@ -291,6 +292,7 @@ config CHARGER_MAX8998
 config CHARGER_SMB347
        tristate "Summit Microelectronics SMB347 Battery Charger"
        depends on I2C
+       select REGMAP_I2C
        help
          Say Y to include support for Summit Microelectronics SMB347
          Battery Charger.
index d8bb993..bba3cca 100644 (file)
@@ -964,10 +964,15 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 {
        int irq, i, ret = 0;
        u8 val;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_btemp *di;
+
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
-       struct ab8500_btemp *di =
-               kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -977,7 +982,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
        di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
        /* get btemp specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->btemp;
        if (!di->pdata) {
                dev_err(di->dev, "no btemp platform data supplied\n");
index e2b4acc..d2303d0 100644 (file)
@@ -2534,10 +2534,15 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 {
        int irq, i, charger_status, ret = 0;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_charger *di;
 
-       struct ab8500_charger *di =
-               kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -2550,9 +2555,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
        spin_lock_init(&di->usb_state.usb_lock);
 
        /* get charger specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->charger;
-
        if (!di->pdata) {
                dev_err(di->dev, "no charger platform data supplied\n");
                ret = -EINVAL;
index c22f2f0..bf02225 100644 (file)
@@ -2446,10 +2446,15 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 {
        int i, irq;
        int ret = 0;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_fg *di;
+
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
-       struct ab8500_fg *di =
-               kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -2461,7 +2466,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
        di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
        /* get fg specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->fg;
        if (!di->pdata) {
                dev_err(di->dev, "no fg platform data supplied\n");
index 9eca9f1..86935ec 100644 (file)
 #include <linux/power/charger-manager.h>
 #include <linux/regulator/consumer.h>
 
+static const char * const default_event_names[] = {
+       [CM_EVENT_UNKNOWN] = "Unknown",
+       [CM_EVENT_BATT_FULL] = "Battery Full",
+       [CM_EVENT_BATT_IN] = "Battery Inserted",
+       [CM_EVENT_BATT_OUT] = "Battery Pulled Out",
+       [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
+       [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
+       [CM_EVENT_OTHERS] = "Other battery events"
+};
+
 /*
  * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
  * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
@@ -57,6 +67,12 @@ static bool cm_suspended;
 static bool cm_rtc_set;
 static unsigned long cm_suspend_duration_ms;
 
+/* About normal (not suspended) monitoring */
+static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
+static unsigned long next_polling; /* Next appointed polling time */
+static struct workqueue_struct *cm_wq; /* init at driver add */
+static struct delayed_work cm_monitor_work; /* init at driver add */
+
 /* Global charger-manager description */
 static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
 
@@ -71,6 +87,11 @@ static bool is_batt_present(struct charger_manager *cm)
        int i, ret;
 
        switch (cm->desc->battery_present) {
+       case CM_BATTERY_PRESENT:
+               present = true;
+               break;
+       case CM_NO_BATTERY:
+               break;
        case CM_FUEL_GAUGE:
                ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
                                POWER_SUPPLY_PROP_PRESENT, &val);
@@ -278,6 +299,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
        return err;
 }
 
+/**
+ * try_charger_restart - Restart charging.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Restart charging by turning off and on the charger.
+ */
+static int try_charger_restart(struct charger_manager *cm)
+{
+       int err;
+
+       if (cm->emergency_stop)
+               return -EAGAIN;
+
+       err = try_charger_enable(cm, false);
+       if (err)
+               return err;
+
+       return try_charger_enable(cm, true);
+}
+
 /**
  * uevent_notify - Let users know something has changed.
  * @cm: the Charger Manager representing the battery.
@@ -333,6 +374,46 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
        dev_info(cm->dev, event);
 }
 
+/**
+ * fullbatt_vchk - Check voltage drop some times after "FULL" event.
+ * @work: the work_struct appointing the function
+ *
+ * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
+ * charger_desc, Charger Manager checks voltage drop after the battery
+ * "FULL" event. It checks whether the voltage has dropped more than
+ * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
+ */
+static void fullbatt_vchk(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct charger_manager *cm = container_of(dwork,
+                       struct charger_manager, fullbatt_vchk_work);
+       struct charger_desc *desc = cm->desc;
+       int batt_uV, err, diff;
+
+       /* remove the appointment for fullbatt_vchk */
+       cm->fullbatt_vchk_jiffies_at = 0;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               return;
+
+       err = get_batt_uV(cm, &batt_uV);
+       if (err) {
+               dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
+               return;
+       }
+
+       diff = cm->fullbatt_vchk_uV;
+       diff -= batt_uV;
+
+       dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
+
+       if (diff > desc->fullbatt_vchkdrop_uV) {
+               try_charger_restart(cm);
+               uevent_notify(cm, "Recharge");
+       }
+}
+
 /**
  * _cm_monitor - Monitor the temperature and return true for exceptions.
  * @cm: the Charger Manager representing the battery.
@@ -392,6 +473,131 @@ static bool cm_monitor(void)
        return stop;
 }
 
+/**
+ * _setup_polling - Setup the next instance of polling.
+ * @work: work_struct of the function _setup_polling.
+ */
+static void _setup_polling(struct work_struct *work)
+{
+       unsigned long min = ULONG_MAX;
+       struct charger_manager *cm;
+       bool keep_polling = false;
+       unsigned long _next_polling;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
+                       keep_polling = true;
+
+                       if (min > cm->desc->polling_interval_ms)
+                               min = cm->desc->polling_interval_ms;
+               }
+       }
+
+       polling_jiffy = msecs_to_jiffies(min);
+       if (polling_jiffy <= CM_JIFFIES_SMALL)
+               polling_jiffy = CM_JIFFIES_SMALL + 1;
+
+       if (!keep_polling)
+               polling_jiffy = ULONG_MAX;
+       if (polling_jiffy == ULONG_MAX)
+               goto out;
+
+       WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
+                           ". try it later. %s\n", __func__);
+
+       _next_polling = jiffies + polling_jiffy;
+
+       if (!delayed_work_pending(&cm_monitor_work) ||
+           (delayed_work_pending(&cm_monitor_work) &&
+            time_after(next_polling, _next_polling))) {
+               cancel_delayed_work_sync(&cm_monitor_work);
+               next_polling = jiffies + polling_jiffy;
+               queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+       }
+
+out:
+       mutex_unlock(&cm_list_mtx);
+}
+static DECLARE_WORK(setup_polling, _setup_polling);
+
+/**
+ * cm_monitor_poller - The Monitor / Poller.
+ * @work: work_struct of the function cm_monitor_poller
+ *
+ * During non-suspended state, cm_monitor_poller is used to poll and monitor
+ * the batteries.
+ */
+static void cm_monitor_poller(struct work_struct *work)
+{
+       cm_monitor();
+       schedule_work(&setup_polling);
+}
+
+/**
+ * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
+ * @cm: the Charger Manager representing the battery.
+ */
+static void fullbatt_handler(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               goto out;
+
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (delayed_work_pending(&cm->fullbatt_vchk_work))
+               cancel_delayed_work(&cm->fullbatt_vchk_work);
+       queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                          msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
+       cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
+                                      desc->fullbatt_vchkdrop_ms);
+
+       if (cm->fullbatt_vchk_jiffies_at == 0)
+               cm->fullbatt_vchk_jiffies_at = 1;
+
+out:
+       dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
+       uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
+}
+
+/**
+ * battout_handler - Event handler for CM_EVENT_BATT_OUT
+ * @cm: the Charger Manager representing the battery.
+ */
+static void battout_handler(struct charger_manager *cm)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (!is_batt_present(cm)) {
+               dev_emerg(cm->dev, "Battery Pulled Out!\n");
+               uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
+       } else {
+               uevent_notify(cm, "Battery Reinserted?");
+       }
+}
+
+/**
+ * misc_event_handler - Handler for other evnets
+ * @cm: the Charger Manager representing the battery.
+ * @type: the Charger Manager representing the battery.
+ */
+static void misc_event_handler(struct charger_manager *cm,
+                       enum cm_event_types type)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (!delayed_work_pending(&cm_monitor_work) &&
+           is_polling_required(cm) && cm->desc->polling_interval_ms)
+               schedule_work(&setup_polling);
+       uevent_notify(cm, default_event_names[type]);
+}
+
 static int charger_get_property(struct power_supply *psy,
                enum power_supply_property psp,
                union power_supply_propval *val)
@@ -613,6 +819,21 @@ static bool cm_setup_timer(void)
        mutex_lock(&cm_list_mtx);
 
        list_for_each_entry(cm, &cm_list, entry) {
+               unsigned int fbchk_ms = 0;
+
+               /* fullbatt_vchk is required. setup timer for that */
+               if (cm->fullbatt_vchk_jiffies_at) {
+                       fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
+                                                   - jiffies);
+                       if (time_is_before_eq_jiffies(
+                               cm->fullbatt_vchk_jiffies_at) ||
+                               msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
+                               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+                               fbchk_ms = 0;
+                       }
+               }
+               CM_MIN_VALID(wakeup_ms, fbchk_ms);
+
                /* Skip if polling is not required for this CM */
                if (!is_polling_required(cm) && !cm->emergency_stop)
                        continue;
@@ -672,6 +893,23 @@ static bool cm_setup_timer(void)
        return false;
 }
 
+static void _cm_fbchk_in_suspend(struct charger_manager *cm)
+{
+       unsigned long jiffy_now = jiffies;
+
+       if (!cm->fullbatt_vchk_jiffies_at)
+               return;
+
+       if (g_desc && g_desc->assume_timer_stops_in_suspend)
+               jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
+
+       /* Execute now if it's going to be executed not too long after */
+       jiffy_now += CM_JIFFIES_SMALL;
+
+       if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
+               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+}
+
 /**
  * cm_suspend_again - Determine whether suspend again or not
  *
@@ -693,6 +931,8 @@ bool cm_suspend_again(void)
        ret = true;
        mutex_lock(&cm_list_mtx);
        list_for_each_entry(cm, &cm_list, entry) {
+               _cm_fbchk_in_suspend(cm);
+
                if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
                    cm->status_save_batt != is_batt_present(cm)) {
                        ret = false;
@@ -796,6 +1036,21 @@ static int charger_manager_probe(struct platform_device *pdev)
        memcpy(cm->desc, desc, sizeof(struct charger_desc));
        cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
 
+       /*
+        * The following two do not need to be errors.
+        * Users may intentionally ignore those two features.
+        */
+       if (desc->fullbatt_uV == 0) {
+               dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
+                                       " as it is not supplied.");
+       }
+       if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
+               dev_info(&pdev->dev, "Disabling full-battery voltage drop "
+                               "checking mechanism as it is not supplied.");
+               desc->fullbatt_vchkdrop_ms = 0;
+               desc->fullbatt_vchkdrop_uV = 0;
+       }
+
        if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
                ret = -EINVAL;
                dev_err(&pdev->dev, "charger_regulators undefined.\n");
@@ -903,6 +1158,8 @@ static int charger_manager_probe(struct platform_device *pdev)
                cm->charger_psy.num_properties++;
        }
 
+       INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
+
        ret = power_supply_register(NULL, &cm->charger_psy);
        if (ret) {
                dev_err(&pdev->dev, "Cannot register charger-manager with"
@@ -928,6 +1185,15 @@ static int charger_manager_probe(struct platform_device *pdev)
        list_add(&cm->entry, &cm_list);
        mutex_unlock(&cm_list_mtx);
 
+       /*
+        * Charger-manager is capable of waking up the systme from sleep
+        * when event is happend through cm_notify_event()
+        */
+       device_init_wakeup(&pdev->dev, true);
+       device_set_wakeup_capable(&pdev->dev, false);
+
+       schedule_work(&setup_polling);
+
        return 0;
 
 err_chg_enable:
@@ -958,9 +1224,17 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
        list_del(&cm->entry);
        mutex_unlock(&cm_list_mtx);
 
+       if (work_pending(&setup_polling))
+               cancel_work_sync(&setup_polling);
+       if (delayed_work_pending(&cm_monitor_work))
+               cancel_delayed_work_sync(&cm_monitor_work);
+
        regulator_bulk_free(desc->num_charger_regulators,
                            desc->charger_regulators);
        power_supply_unregister(&cm->charger_psy);
+
+       try_charger_enable(cm, false);
+
        kfree(cm->charger_psy.properties);
        kfree(cm->charger_stat);
        kfree(cm->desc);
@@ -975,6 +1249,18 @@ static const struct platform_device_id charger_manager_id[] = {
 };
 MODULE_DEVICE_TABLE(platform, charger_manager_id);
 
+static int cm_suspend_noirq(struct device *dev)
+{
+       int ret = 0;
+
+       if (device_may_wakeup(dev)) {
+               device_set_wakeup_capable(dev, false);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
 static int cm_suspend_prepare(struct device *dev)
 {
        struct charger_manager *cm = dev_get_drvdata(dev);
@@ -1000,6 +1286,8 @@ static int cm_suspend_prepare(struct device *dev)
                cm_suspended = true;
        }
 
+       if (delayed_work_pending(&cm->fullbatt_vchk_work))
+               cancel_delayed_work(&cm->fullbatt_vchk_work);
        cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
        cm->status_save_batt = is_batt_present(cm);
 
@@ -1027,11 +1315,40 @@ static void cm_suspend_complete(struct device *dev)
                cm_rtc_set = false;
        }
 
+       /* Re-enqueue delayed work (fullbatt_vchk_work) */
+       if (cm->fullbatt_vchk_jiffies_at) {
+               unsigned long delay = 0;
+               unsigned long now = jiffies + CM_JIFFIES_SMALL;
+
+               if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
+                       delay = (unsigned long)((long)now
+                               - (long)(cm->fullbatt_vchk_jiffies_at));
+                       delay = jiffies_to_msecs(delay);
+               } else {
+                       delay = 0;
+               }
+
+               /*
+                * Account for cm_suspend_duration_ms if
+                * assume_timer_stops_in_suspend is active
+                */
+               if (g_desc && g_desc->assume_timer_stops_in_suspend) {
+                       if (delay > cm_suspend_duration_ms)
+                               delay -= cm_suspend_duration_ms;
+                       else
+                               delay = 0;
+               }
+
+               queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                                  msecs_to_jiffies(delay));
+       }
+       device_set_wakeup_capable(cm->dev, false);
        uevent_notify(cm, NULL);
 }
 
 static const struct dev_pm_ops charger_manager_pm = {
        .prepare        = cm_suspend_prepare,
+       .suspend_noirq  = cm_suspend_noirq,
        .complete       = cm_suspend_complete,
 };
 
@@ -1048,16 +1365,91 @@ static struct platform_driver charger_manager_driver = {
 
 static int __init charger_manager_init(void)
 {
+       cm_wq = create_freezable_workqueue("charger_manager");
+       INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
+
        return platform_driver_register(&charger_manager_driver);
 }
 late_initcall(charger_manager_init);
 
 static void __exit charger_manager_cleanup(void)
 {
+       destroy_workqueue(cm_wq);
+       cm_wq = NULL;
+
        platform_driver_unregister(&charger_manager_driver);
 }
 module_exit(charger_manager_cleanup);
 
+/**
+ * find_power_supply - find the associated power_supply of charger
+ * @cm: the Charger Manager representing the battery
+ * @psy: pointer to instance of charger's power_supply
+ */
+static bool find_power_supply(struct charger_manager *cm,
+                       struct power_supply *psy)
+{
+       int i;
+       bool found = false;
+
+       for (i = 0; cm->charger_stat[i]; i++) {
+               if (psy == cm->charger_stat[i]) {
+                       found = true;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+/**
+ * cm_notify_event - charger driver notify Charger Manager of charger event
+ * @psy: pointer to instance of charger's power_supply
+ * @type: type of charger event
+ * @msg: optional message passed to uevent_notify fuction
+ */
+void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
+                    char *msg)
+{
+       struct charger_manager *cm;
+       bool found_power_supply = false;
+
+       if (psy == NULL)
+               return;
+
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               found_power_supply = find_power_supply(cm, psy);
+               if (found_power_supply)
+                       break;
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       if (!found_power_supply)
+               return;
+
+       switch (type) {
+       case CM_EVENT_BATT_FULL:
+               fullbatt_handler(cm);
+               break;
+       case CM_EVENT_BATT_OUT:
+               battout_handler(cm);
+               break;
+       case CM_EVENT_BATT_IN:
+       case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
+               misc_event_handler(cm, type);
+               break;
+       case CM_EVENT_UNKNOWN:
+       case CM_EVENT_OTHERS:
+               uevent_notify(cm, msg ? msg : default_event_names[type]);
+               break;
+       default:
+               dev_err(cm->dev, "%s type not specified.\n", __func__);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(cm_notify_event);
+
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 MODULE_DESCRIPTION("Charger Manager");
 MODULE_LICENSE("GPL");
index ca0d653..975684a 100644 (file)
@@ -643,9 +643,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK1_END -
-               DS2781_EEPROM_BLOCK1_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
 
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
@@ -661,9 +659,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK1_END -
-               DS2781_EEPROM_BLOCK1_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
 
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
@@ -682,7 +678,7 @@ static struct bin_attribute ds2781_param_eeprom_bin_attr = {
                .name = "param_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1,
+       .size = DS2781_PARAM_EEPROM_SIZE,
        .read = ds2781_read_param_eeprom_bin,
        .write = ds2781_write_param_eeprom_bin,
 };
@@ -696,9 +692,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK0_END -
-               DS2781_EEPROM_BLOCK0_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
 
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
@@ -715,9 +709,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK0_END -
-               DS2781_EEPROM_BLOCK0_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
 
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
@@ -736,7 +728,7 @@ static struct bin_attribute ds2781_user_eeprom_bin_attr = {
                .name = "user_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1,
+       .size = DS2781_USER_EEPROM_SIZE,
        .read = ds2781_read_user_eeprom_bin,
        .write = ds2781_write_user_eeprom_bin,
 };
index 39eb50f..e5ccd29 100644 (file)
@@ -474,13 +474,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
 fail2:
        power_supply_unregister(&isp->psy);
 fail1:
+       isp1704_charger_set_power(isp, 0);
        usb_put_transceiver(isp->phy);
 fail0:
        kfree(isp);
 
        dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
 
-       isp1704_charger_set_power(isp, 0);
        return ret;
 }
 
index 04620c2..140788b 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/pm.h>
 #include <linux/mod_devicetable.h>
 #include <linux/power_supply.h>
 #include <linux/power/max17042_battery.h>
 #define dP_ACC_100     0x1900
 #define dP_ACC_200     0x3200
 
+#define MAX17042_IC_VERSION    0x0092
+#define MAX17047_IC_VERSION    0x00AC  /* same for max17050 */
+
 struct max17042_chip {
        struct i2c_client *client;
        struct power_supply battery;
+       enum max170xx_chip_type chip_type;
        struct max17042_platform_data *pdata;
        struct work_struct work;
        int    init_complete;
@@ -105,6 +110,7 @@ static enum power_supply_property max17042_battery_props[] = {
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
        POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_TEMP,
@@ -150,7 +156,10 @@ static int max17042_get_property(struct power_supply *psy,
                val->intval *= 20000; /* Units of LSB = 20mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+               if (chip->chip_type == MAX17042)
+                       ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+               else
+                       ret = max17042_read_reg(chip->client, MAX17047_V_empty);
                if (ret < 0)
                        return ret;
 
@@ -169,6 +178,13 @@ static int max17042_get_property(struct power_supply *psy,
                if (ret < 0)
                        return ret;
 
+               val->intval = ret * 625 / 8;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+               ret = max17042_read_reg(chip->client, MAX17042_OCVInternal);
+               if (ret < 0)
+                       return ret;
+
                val->intval = ret * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
@@ -325,11 +341,10 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip,
 static int max17042_init_model(struct max17042_chip *chip)
 {
        int ret;
-       int table_size =
-               sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
        u16 *temp_data;
 
-       temp_data = kzalloc(table_size, GFP_KERNEL);
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
        if (!temp_data)
                return -ENOMEM;
 
@@ -354,12 +369,11 @@ static int max17042_init_model(struct max17042_chip *chip)
 static int max17042_verify_model_lock(struct max17042_chip *chip)
 {
        int i;
-       int table_size =
-               sizeof(chip->pdata->config_data->cell_char_tbl);
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
        u16 *temp_data;
        int ret = 0;
 
-       temp_data = kzalloc(table_size, GFP_KERNEL);
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
        if (!temp_data)
                return -ENOMEM;
 
@@ -382,6 +396,9 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
        max17042_write_reg(chip->client, MAX17042_FilterCFG,
                        config->filter_cfg);
        max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
+       if (chip->chip_type == MAX17047)
+               max17042_write_reg(chip->client, MAX17047_FullSOCThr,
+                                               config->full_soc_thresh);
 }
 
 static void  max17042_write_custom_regs(struct max17042_chip *chip)
@@ -392,12 +409,23 @@ static void  max17042_write_custom_regs(struct max17042_chip *chip)
                                config->rcomp0);
        max17042_write_verify_reg(chip->client, MAX17042_TempCo,
                                config->tcompc0);
-       max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
-                       config->empty_tempco);
-       max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
-                               config->kempty0);
        max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
                                config->ichgt_term);
+       if (chip->chip_type == MAX17042) {
+               max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
+                                       config->empty_tempco);
+               max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
+                                       config->kempty0);
+       } else {
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl00,
+                                               config->qrtbl00);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl10,
+                                               config->qrtbl10);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl20,
+                                               config->qrtbl20);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl30,
+                                               config->qrtbl30);
+       }
 }
 
 static void max17042_update_capacity_regs(struct max17042_chip *chip)
@@ -453,6 +481,8 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
                        config->design_cap);
        max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
                        config->fullcapnom);
+       /* Update SOC register with new SOC */
+       max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc);
 }
 
 /*
@@ -489,20 +519,28 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
 
        max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
        max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
-       max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
+       if (chip->chip_type == MAX17042)
+               max17042_override_por(client, MAX17042_SOC_empty,
+                                               config->socempty);
        max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
        max17042_override_por(client, MAX17042_dQacc, config->dqacc);
        max17042_override_por(client, MAX17042_dPacc, config->dpacc);
 
-       max17042_override_por(client, MAX17042_V_empty, config->vempty);
+       if (chip->chip_type == MAX17042)
+               max17042_override_por(client, MAX17042_V_empty, config->vempty);
+       else
+               max17042_override_por(client, MAX17047_V_empty, config->vempty);
        max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
        max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
        max17042_override_por(client, MAX17042_FCTC, config->fctc);
        max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
        max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
-       max17042_override_por(client, MAX17042_EmptyTempCo,
-                       config->empty_tempco);
-       max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
+       if (chip->chip_type) {
+               max17042_override_por(client, MAX17042_EmptyTempCo,
+                                       config->empty_tempco);
+               max17042_override_por(client, MAX17042_K_empty0,
+                                       config->kempty0);
+       }
 }
 
 static int max17042_init_chip(struct max17042_chip *chip)
@@ -659,7 +697,19 @@ static int __devinit max17042_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, chip);
 
-       chip->battery.name              = "max17042_battery";
+       ret = max17042_read_reg(chip->client, MAX17042_DevName);
+       if (ret == MAX17042_IC_VERSION) {
+               dev_dbg(&client->dev, "chip type max17042 detected\n");
+               chip->chip_type = MAX17042;
+       } else if (ret == MAX17047_IC_VERSION) {
+               dev_dbg(&client->dev, "chip type max17047/50 detected\n");
+               chip->chip_type = MAX17047;
+       } else {
+               dev_err(&client->dev, "device version mismatch: %x\n", ret);
+               return -EIO;
+       }
+
+       chip->battery.name              = "max170xx_battery";
        chip->battery.type              = POWER_SUPPLY_TYPE_BATTERY;
        chip->battery.get_property      = max17042_get_property;
        chip->battery.properties        = max17042_battery_props;
@@ -683,6 +733,12 @@ static int __devinit max17042_probe(struct i2c_client *client,
                max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
        }
 
+       ret = power_supply_register(&client->dev, &chip->battery);
+       if (ret) {
+               dev_err(&client->dev, "failed: power supply register\n");
+               return ret;
+       }
+
        if (client->irq) {
                ret = request_threaded_irq(client->irq, NULL,
                                                max17042_thread_handler,
@@ -693,13 +749,14 @@ static int __devinit max17042_probe(struct i2c_client *client,
                        reg |= CONFIG_ALRT_BIT_ENBL;
                        max17042_write_reg(client, MAX17042_CONFIG, reg);
                        max17042_set_soc_threshold(chip, 1);
-               } else
+               } else {
+                       client->irq = 0;
                        dev_err(&client->dev, "%s(): cannot get IRQ\n",
                                __func__);
+               }
        }
 
        reg = max17042_read_reg(chip->client, MAX17042_STATUS);
-
        if (reg & STATUS_POR_BIT) {
                INIT_WORK(&chip->work, max17042_init_worker);
                schedule_work(&chip->work);
@@ -707,23 +764,65 @@ static int __devinit max17042_probe(struct i2c_client *client,
                chip->init_complete = 1;
        }
 
-       ret = power_supply_register(&client->dev, &chip->battery);
-       if (ret)
-               dev_err(&client->dev, "failed: power supply register\n");
-       return ret;
+       return 0;
 }
 
 static int __devexit max17042_remove(struct i2c_client *client)
 {
        struct max17042_chip *chip = i2c_get_clientdata(client);
 
+       if (client->irq)
+               free_irq(client->irq, chip);
        power_supply_unregister(&chip->battery);
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int max17042_suspend(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       /*
+        * disable the irq and enable irq_wake
+        * capability to the interrupt line.
+        */
+       if (chip->client->irq) {
+               disable_irq(chip->client->irq);
+               enable_irq_wake(chip->client->irq);
+       }
+
+       return 0;
+}
+
+static int max17042_resume(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       if (chip->client->irq) {
+               disable_irq_wake(chip->client->irq);
+               enable_irq(chip->client->irq);
+               /* re-program the SOC thresholds to 1% change */
+               max17042_set_soc_threshold(chip, 1);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops max17042_pm_ops = {
+       .suspend        = max17042_suspend,
+       .resume         = max17042_resume,
+};
+
+#define MAX17042_PM_OPS (&max17042_pm_ops)
+#else
+#define MAX17042_PM_OPS NULL
+#endif
+
 #ifdef CONFIG_OF
 static const struct of_device_id max17042_dt_match[] = {
        { .compatible = "maxim,max17042" },
+       { .compatible = "maxim,max17047" },
+       { .compatible = "maxim,max17050" },
        { },
 };
 MODULE_DEVICE_TABLE(of, max17042_dt_match);
@@ -731,6 +830,8 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match);
 
 static const struct i2c_device_id max17042_id[] = {
        { "max17042", 0 },
+       { "max17047", 1 },
+       { "max17050", 2 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max17042_id);
@@ -739,6 +840,7 @@ static struct i2c_driver max17042_i2c_driver = {
        .driver = {
                .name   = "max17042",
                .of_match_table = of_match_ptr(max17042_dt_match),
+               .pm     = MAX17042_PM_OPS,
        },
        .probe          = max17042_probe,
        .remove         = __devexit_p(max17042_remove),
index 4368e7d..4150747 100644 (file)
@@ -146,6 +146,7 @@ static struct device_attribute power_supply_attrs[] = {
        POWER_SUPPLY_ATTR(voltage_min_design),
        POWER_SUPPLY_ATTR(voltage_now),
        POWER_SUPPLY_ATTR(voltage_avg),
+       POWER_SUPPLY_ATTR(voltage_ocv),
        POWER_SUPPLY_ATTR(current_max),
        POWER_SUPPLY_ATTR(current_now),
        POWER_SUPPLY_ATTR(current_avg),
index 06b659d..a5b6849 100644 (file)
@@ -89,7 +89,7 @@ static const struct chip_data {
        [REG_CURRENT] =
                SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
        [REG_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
+               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
        [REG_REMAINING_CAPACITY] =
                SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
        [REG_REMAINING_CAPACITY_CHARGE] =
index ce1694d..f8eedd8 100644 (file)
@@ -11,7 +11,7 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/debugfs.h>
+#include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -21,7 +21,7 @@
 #include <linux/mutex.h>
 #include <linux/power_supply.h>
 #include <linux/power/smb347-charger.h>
-#include <linux/seq_file.h>
+#include <linux/regmap.h>
 
 /*
  * Configuration registers. These are mirrored to volatile RAM and can be
@@ -39,6 +39,7 @@
 #define CFG_CURRENT_LIMIT_DC_SHIFT             4
 #define CFG_CURRENT_LIMIT_USB_MASK             0x0f
 #define CFG_FLOAT_VOLTAGE                      0x03
+#define CFG_FLOAT_VOLTAGE_FLOAT_MASK           0x3f
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK       0xc0
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT      6
 #define CFG_STAT                               0x05
 #define STAT_C_CHARGER_ERROR                   BIT(6)
 #define STAT_E                                 0x3f
 
+#define SMB347_MAX_REGISTER                    0x3f
+
 /**
  * struct smb347_charger - smb347 charger instance
  * @lock: protects concurrent access to online variables
- * @client: pointer to i2c client
+ * @dev: pointer to device
+ * @regmap: pointer to driver regmap
  * @mains: power_supply instance for AC/DC power
  * @usb: power_supply instance for USB power
  * @battery: power_supply instance for battery
  * @mains_online: is AC/DC input connected
  * @usb_online: is USB input connected
  * @charging_enabled: is charging enabled
- * @dentry: for debugfs
  * @pdata: pointer to platform data
  */
 struct smb347_charger {
        struct mutex            lock;
-       struct i2c_client       *client;
+       struct device           *dev;
+       struct regmap           *regmap;
        struct power_supply     mains;
        struct power_supply     usb;
        struct power_supply     battery;
        bool                    mains_online;
        bool                    usb_online;
        bool                    charging_enabled;
-       struct dentry           *dentry;
        const struct smb347_charger_platform_data *pdata;
 };
 
@@ -193,14 +196,6 @@ static const unsigned int ccc_tbl[] = {
        1200000,
 };
 
-/* Convert register value to current using lookup table */
-static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
-{
-       if (val >= size)
-               return -EINVAL;
-       return tbl[val];
-}
-
 /* Convert current to register value using lookup table */
 static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
 {
@@ -212,43 +207,22 @@ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
        return i > 0 ? i - 1 : -EINVAL;
 }
 
-static int smb347_read(struct smb347_charger *smb, u8 reg)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(smb->client, reg);
-       if (ret < 0)
-               dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n",
-                        reg, ret);
-       return ret;
-}
-
-static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val)
-{
-       int ret;
-
-       ret = i2c_smbus_write_byte_data(smb->client, reg, val);
-       if (ret < 0)
-               dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n",
-                        reg, ret);
-       return ret;
-}
-
 /**
- * smb347_update_status - updates the charging status
+ * smb347_update_ps_status - refreshes the power source status
  * @smb: pointer to smb347 charger instance
  *
- * Function checks status of the charging and updates internal state
- * accordingly. Returns %0 if there is no change in status, %1 if the
- * status has changed and negative errno in case of failure.
+ * Function checks whether any power source is connected to the charger and
+ * updates internal state accordingly. If there is a change to previous state
+ * function returns %1, otherwise %0 and negative errno in case of errror.
  */
-static int smb347_update_status(struct smb347_charger *smb)
+static int smb347_update_ps_status(struct smb347_charger *smb)
 {
        bool usb = false;
        bool dc = false;
+       unsigned int val;
        int ret;
 
-       ret = smb347_read(smb, IRQSTAT_E);
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &val);
        if (ret < 0)
                return ret;
 
@@ -257,9 +231,9 @@ static int smb347_update_status(struct smb347_charger *smb)
         * platform data _and_ whether corresponding undervoltage is set.
         */
        if (smb->pdata->use_mains)
-               dc = !(ret & IRQSTAT_E_DCIN_UV_STAT);
+               dc = !(val & IRQSTAT_E_DCIN_UV_STAT);
        if (smb->pdata->use_usb)
-               usb = !(ret & IRQSTAT_E_USBIN_UV_STAT);
+               usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
 
        mutex_lock(&smb->lock);
        ret = smb->mains_online != dc || smb->usb_online != usb;
@@ -271,15 +245,15 @@ static int smb347_update_status(struct smb347_charger *smb)
 }
 
 /*
- * smb347_is_online - returns whether input power source is connected
+ * smb347_is_ps_online - returns whether input power source is connected
  * @smb: pointer to smb347 charger instance
  *
  * Returns %true if input power source is connected. Note that this is
  * dependent on what platform has configured for usable power sources. For
- * example if USB is disabled, this will return %false even if the USB
- * cable is connected.
+ * example if USB is disabled, this will return %false even if the USB cable
+ * is connected.
  */
-static bool smb347_is_online(struct smb347_charger *smb)
+static bool smb347_is_ps_online(struct smb347_charger *smb)
 {
        bool ret;
 
@@ -299,16 +273,17 @@ static bool smb347_is_online(struct smb347_charger *smb)
  */
 static int smb347_charging_status(struct smb347_charger *smb)
 {
+       unsigned int val;
        int ret;
 
-       if (!smb347_is_online(smb))
+       if (!smb347_is_ps_online(smb))
                return 0;
 
-       ret = smb347_read(smb, STAT_C);
+       ret = regmap_read(smb->regmap, STAT_C, &val);
        if (ret < 0)
                return 0;
 
-       return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
+       return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
 }
 
 static int smb347_charging_set(struct smb347_charger *smb, bool enable)
@@ -316,27 +291,17 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable)
        int ret = 0;
 
        if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
-               dev_dbg(&smb->client->dev,
-                       "charging enable/disable in SW disabled\n");
+               dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
                return 0;
        }
 
        mutex_lock(&smb->lock);
        if (smb->charging_enabled != enable) {
-               ret = smb347_read(smb, CMD_A);
-               if (ret < 0)
-                       goto out;
-
-               smb->charging_enabled = enable;
-
-               if (enable)
-                       ret |= CMD_A_CHG_ENABLED;
-               else
-                       ret &= ~CMD_A_CHG_ENABLED;
-
-               ret = smb347_write(smb, CMD_A, ret);
+               ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
+                                        enable ? CMD_A_CHG_ENABLED : 0);
+               if (!ret)
+                       smb->charging_enabled = enable;
        }
-out:
        mutex_unlock(&smb->lock);
        return ret;
 }
@@ -351,7 +316,7 @@ static inline int smb347_charging_disable(struct smb347_charger *smb)
        return smb347_charging_set(smb, false);
 }
 
-static int smb347_update_online(struct smb347_charger *smb)
+static int smb347_start_stop_charging(struct smb347_charger *smb)
 {
        int ret;
 
@@ -360,16 +325,14 @@ static int smb347_update_online(struct smb347_charger *smb)
         * disable or enable the charging. We do it manually because it
         * depends on how the platform has configured the valid inputs.
         */
-       if (smb347_is_online(smb)) {
+       if (smb347_is_ps_online(smb)) {
                ret = smb347_charging_enable(smb);
                if (ret < 0)
-                       dev_err(&smb->client->dev,
-                               "failed to enable charging\n");
+                       dev_err(smb->dev, "failed to enable charging\n");
        } else {
                ret = smb347_charging_disable(smb);
                if (ret < 0)
-                       dev_err(&smb->client->dev,
-                               "failed to disable charging\n");
+                       dev_err(smb->dev, "failed to disable charging\n");
        }
 
        return ret;
@@ -377,112 +340,120 @@ static int smb347_update_online(struct smb347_charger *smb)
 
 static int smb347_set_charge_current(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_CHARGE_CURRENT);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->max_charge_current) {
-               val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
+               ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
                                    smb->pdata->max_charge_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_FCC_MASK;
-               ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_FCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_FCC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->pre_charge_current) {
-               val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
+               ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
                                    smb->pdata->pre_charge_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_PCC_MASK;
-               ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_PCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_PCC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->termination_current) {
-               val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
+               ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
                                    smb->pdata->termination_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_TC_MASK;
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_TC_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_CHARGE_CURRENT, ret);
+       return 0;
 }
 
 static int smb347_set_current_limits(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_CURRENT_LIMIT);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->mains_current_limit) {
-               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
                                    smb->pdata->mains_current_limit);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CURRENT_LIMIT_DC_MASK;
-               ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_DC_MASK,
+                                        ret << CFG_CURRENT_LIMIT_DC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->usb_hc_current_limit) {
-               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
                                    smb->pdata->usb_hc_current_limit);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CURRENT_LIMIT_USB_MASK;
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_USB_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_CURRENT_LIMIT, ret);
+       return 0;
 }
 
 static int smb347_set_voltage_limits(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_FLOAT_VOLTAGE);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->pre_to_fast_voltage) {
-               val = smb->pdata->pre_to_fast_voltage;
+               ret = smb->pdata->pre_to_fast_voltage;
 
                /* uV */
-               val = clamp_val(val, 2400000, 3000000) - 2400000;
-               val /= 200000;
+               ret = clamp_val(ret, 2400000, 3000000) - 2400000;
+               ret /= 200000;
 
-               ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK;
-               ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                               CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
+                               ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->max_charge_voltage) {
-               val = smb->pdata->max_charge_voltage;
+               ret = smb->pdata->max_charge_voltage;
 
                /* uV */
-               val = clamp_val(val, 3500000, 4500000) - 3500000;
-               val /= 20000;
+               ret = clamp_val(ret, 3500000, 4500000) - 3500000;
+               ret /= 20000;
 
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                                        CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret);
+       return 0;
 }
 
 static int smb347_set_temp_limits(struct smb347_charger *smb)
 {
        bool enable_therm_monitor = false;
-       int ret, val;
+       int ret = 0;
+       int val;
 
        if (smb->pdata->chip_temp_threshold) {
                val = smb->pdata->chip_temp_threshold;
@@ -491,22 +462,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 100, 130) - 100;
                val /= 10;
 
-               ret = smb347_read(smb, CFG_OTG);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK;
-               ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT;
-
-               ret = smb347_write(smb, CFG_OTG, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                                        CFG_OTG_TEMP_THRESHOLD_MASK,
+                                        val << CFG_OTG_TEMP_THRESHOLD_SHIFT);
                if (ret < 0)
                        return ret;
        }
 
-       ret = smb347_read(smb, CFG_TEMP_LIMIT);
-       if (ret < 0)
-               return ret;
-
        if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
                val = smb->pdata->soft_cold_temp_limit;
 
@@ -515,8 +477,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                /* this goes from higher to lower so invert the value */
                val = ~val & 0x3;
 
-               ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK;
-               ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -527,8 +492,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 40, 55) - 40;
                val /= 5;
 
-               ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK;
-               ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -541,8 +509,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                /* this goes from higher to lower so invert the value */
                val = ~val & 0x3;
 
-               ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK;
-               ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -553,16 +524,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 50, 65) - 50;
                val /= 5;
 
-               ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK;
-               ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
 
-       ret = smb347_write(smb, CFG_TEMP_LIMIT, ret);
-       if (ret < 0)
-               return ret;
-
        /*
         * If any of the temperature limits are set, we also enable the
         * thermistor monitoring.
@@ -574,25 +544,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
         * depending on the configuration.
         */
        if (enable_therm_monitor) {
-               ret = smb347_read(smb, CFG_THERM);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_THERM_MONITOR_DISABLED;
-
-               ret = smb347_write(smb, CFG_THERM, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                        CFG_THERM_MONITOR_DISABLED, 0);
                if (ret < 0)
                        return ret;
        }
 
        if (smb->pdata->suspend_on_hard_temp_limit) {
-               ret = smb347_read(smb, CFG_SYSOK);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED;
-
-               ret = smb347_write(smb, CFG_SYSOK, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
+                                CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0);
                if (ret < 0)
                        return ret;
        }
@@ -601,17 +561,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
            SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
                val = smb->pdata->soft_temp_limit_compensation & 0x3;
 
-               ret = smb347_read(smb, CFG_THERM);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
 
-               ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK;
-               ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT;
-
-               ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK;
-               ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT;
-
-               ret = smb347_write(smb, CFG_THERM, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
        }
@@ -622,14 +580,9 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                if (val < 0)
                        return val;
 
-               ret = smb347_read(smb, CFG_OTG);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_OTG_CC_COMPENSATION_MASK;
-               ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT;
-
-               ret = smb347_write(smb, CFG_OTG, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                               CFG_OTG_CC_COMPENSATION_MASK,
+                               (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
        }
@@ -648,22 +601,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
  */
 static int smb347_set_writable(struct smb347_charger *smb, bool writable)
 {
-       int ret;
-
-       ret = smb347_read(smb, CMD_A);
-       if (ret < 0)
-               return ret;
-
-       if (writable)
-               ret |= CMD_A_ALLOW_WRITE;
-       else
-               ret &= ~CMD_A_ALLOW_WRITE;
-
-       return smb347_write(smb, CMD_A, ret);
+       return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
+                                 writable ? CMD_A_ALLOW_WRITE : 0);
 }
 
 static int smb347_hw_init(struct smb347_charger *smb)
 {
+       unsigned int val;
        int ret;
 
        ret = smb347_set_writable(smb, true);
@@ -692,34 +636,19 @@ static int smb347_hw_init(struct smb347_charger *smb)
 
        /* If USB charging is disabled we put the USB in suspend mode */
        if (!smb->pdata->use_usb) {
-               ret = smb347_read(smb, CMD_A);
-               if (ret < 0)
-                       goto fail;
-
-               ret |= CMD_A_SUSPEND_ENABLED;
-
-               ret = smb347_write(smb, CMD_A, ret);
+               ret = regmap_update_bits(smb->regmap, CMD_A,
+                                        CMD_A_SUSPEND_ENABLED,
+                                        CMD_A_SUSPEND_ENABLED);
                if (ret < 0)
                        goto fail;
        }
 
-       ret = smb347_read(smb, CFG_OTHER);
-       if (ret < 0)
-               goto fail;
-
        /*
         * If configured by platform data, we enable hardware Auto-OTG
         * support for driving VBUS. Otherwise we disable it.
         */
-       ret &= ~CFG_OTHER_RID_MASK;
-       if (smb->pdata->use_usb_otg)
-               ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG;
-
-       ret = smb347_write(smb, CFG_OTHER, ret);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_read(smb, CFG_PIN);
+       ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
+               smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0);
        if (ret < 0)
                goto fail;
 
@@ -728,32 +657,33 @@ static int smb347_hw_init(struct smb347_charger *smb)
         * command register unless pin control is specified in the platform
         * data.
         */
-       ret &= ~CFG_PIN_EN_CTRL_MASK;
-
        switch (smb->pdata->enable_control) {
-       case SMB347_CHG_ENABLE_SW:
-               /* Do nothing, 0 means i2c control */
-               break;
        case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
-               ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW;
+               val = CFG_PIN_EN_CTRL_ACTIVE_LOW;
                break;
        case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
-               ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               val = CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               break;
+       default:
+               val = 0;
                break;
        }
 
-       /* Disable Automatic Power Source Detection (APSD) interrupt. */
-       ret &= ~CFG_PIN_EN_APSD_IRQ;
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
+                                val);
+       if (ret < 0)
+               goto fail;
 
-       ret = smb347_write(smb, CFG_PIN, ret);
+       /* Disable Automatic Power Source Detection (APSD) interrupt. */
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0);
        if (ret < 0)
                goto fail;
 
-       ret = smb347_update_status(smb);
+       ret = smb347_update_ps_status(smb);
        if (ret < 0)
                goto fail;
 
-       ret = smb347_update_online(smb);
+       ret = smb347_start_stop_charging(smb);
 
 fail:
        smb347_set_writable(smb, false);
@@ -763,24 +693,25 @@ fail:
 static irqreturn_t smb347_interrupt(int irq, void *data)
 {
        struct smb347_charger *smb = data;
-       int stat_c, irqstat_e, irqstat_c;
-       irqreturn_t ret = IRQ_NONE;
+       unsigned int stat_c, irqstat_e, irqstat_c;
+       bool handled = false;
+       int ret;
 
-       stat_c = smb347_read(smb, STAT_C);
-       if (stat_c < 0) {
-               dev_warn(&smb->client->dev, "reading STAT_C failed\n");
+       ret = regmap_read(smb->regmap, STAT_C, &stat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading STAT_C failed\n");
                return IRQ_NONE;
        }
 
-       irqstat_c = smb347_read(smb, IRQSTAT_C);
-       if (irqstat_c < 0) {
-               dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n");
+       ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_C failed\n");
                return IRQ_NONE;
        }
 
-       irqstat_e = smb347_read(smb, IRQSTAT_E);
-       if (irqstat_e < 0) {
-               dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n");
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_E failed\n");
                return IRQ_NONE;
        }
 
@@ -789,13 +720,11 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
         * disable charging.
         */
        if (stat_c & STAT_C_CHARGER_ERROR) {
-               dev_err(&smb->client->dev,
-                       "error in charger, disabling charging\n");
+               dev_err(smb->dev, "error in charger, disabling charging\n");
 
                smb347_charging_disable(smb);
                power_supply_changed(&smb->battery);
-
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
        /*
@@ -806,7 +735,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
        if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
                if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
                        power_supply_changed(&smb->battery);
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
        /*
@@ -814,15 +743,17 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
         * was connected or disconnected.
         */
        if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
-               if (smb347_update_status(smb) > 0) {
-                       smb347_update_online(smb);
-                       power_supply_changed(&smb->mains);
-                       power_supply_changed(&smb->usb);
+               if (smb347_update_ps_status(smb) > 0) {
+                       smb347_start_stop_charging(smb);
+                       if (smb->pdata->use_mains)
+                               power_supply_changed(&smb->mains);
+                       if (smb->pdata->use_usb)
+                               power_supply_changed(&smb->usb);
                }
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
-       return ret;
+       return handled ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int smb347_irq_set(struct smb347_charger *smb, bool enable)
@@ -839,41 +770,18 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
         *      - termination current reached
         *      - charger error
         */
-       if (enable) {
-               ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_write(smb, CFG_STATUS_IRQ,
-                                  CFG_STATUS_IRQ_TERMINATION_OR_TAPER);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_read(smb, CFG_PIN);
-               if (ret < 0)
-                       goto fail;
-
-               ret |= CFG_PIN_EN_CHARGER_ERROR;
-
-               ret = smb347_write(smb, CFG_PIN, ret);
-       } else {
-               ret = smb347_write(smb, CFG_FAULT_IRQ, 0);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_write(smb, CFG_STATUS_IRQ, 0);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_read(smb, CFG_PIN);
-               if (ret < 0)
-                       goto fail;
-
-               ret &= ~CFG_PIN_EN_CHARGER_ERROR;
+       ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff,
+                                enable ? CFG_FAULT_IRQ_DCIN_UV : 0);
+       if (ret < 0)
+               goto fail;
 
-               ret = smb347_write(smb, CFG_PIN, ret);
-       }
+       ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff,
+                       enable ? CFG_STATUS_IRQ_TERMINATION_OR_TAPER : 0);
+       if (ret < 0)
+               goto fail;
 
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
+                                enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
 fail:
        smb347_set_writable(smb, false);
        return ret;
@@ -889,18 +797,18 @@ static inline int smb347_irq_disable(struct smb347_charger *smb)
        return smb347_irq_set(smb, false);
 }
 
-static int smb347_irq_init(struct smb347_charger *smb)
+static int smb347_irq_init(struct smb347_charger *smb,
+                          struct i2c_client *client)
 {
        const struct smb347_charger_platform_data *pdata = smb->pdata;
        int ret, irq = gpio_to_irq(pdata->irq_gpio);
 
-       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name);
+       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
        if (ret < 0)
                goto fail;
 
        ret = request_threaded_irq(irq, NULL, smb347_interrupt,
-                                  IRQF_TRIGGER_FALLING, smb->client->name,
-                                  smb);
+                                  IRQF_TRIGGER_FALLING, client->name, smb);
        if (ret < 0)
                goto fail_gpio;
 
@@ -912,23 +820,14 @@ static int smb347_irq_init(struct smb347_charger *smb)
         * Configure the STAT output to be suitable for interrupts: disable
         * all other output (except interrupts) and make it active low.
         */
-       ret = smb347_read(smb, CFG_STAT);
-       if (ret < 0)
-               goto fail_readonly;
-
-       ret &= ~CFG_STAT_ACTIVE_HIGH;
-       ret |= CFG_STAT_DISABLED;
-
-       ret = smb347_write(smb, CFG_STAT, ret);
-       if (ret < 0)
-               goto fail_readonly;
-
-       ret = smb347_irq_enable(smb);
+       ret = regmap_update_bits(smb->regmap, CFG_STAT,
+                                CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
+                                CFG_STAT_DISABLED);
        if (ret < 0)
                goto fail_readonly;
 
        smb347_set_writable(smb, false);
-       smb->client->irq = irq;
+       client->irq = irq;
        return 0;
 
 fail_readonly:
@@ -938,7 +837,7 @@ fail_irq:
 fail_gpio:
        gpio_free(pdata->irq_gpio);
 fail:
-       smb->client->irq = 0;
+       client->irq = 0;
        return ret;
 }
 
@@ -987,13 +886,13 @@ static int smb347_battery_get_property(struct power_supply *psy,
        const struct smb347_charger_platform_data *pdata = smb->pdata;
        int ret;
 
-       ret = smb347_update_status(smb);
+       ret = smb347_update_ps_status(smb);
        if (ret < 0)
                return ret;
 
        switch (prop) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (!smb347_is_online(smb)) {
+               if (!smb347_is_ps_online(smb)) {
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
                        break;
                }
@@ -1004,7 +903,7 @@ static int smb347_battery_get_property(struct power_supply *psy,
                break;
 
        case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (!smb347_is_online(smb))
+               if (!smb347_is_ps_online(smb))
                        return -ENODATA;
 
                /*
@@ -1036,44 +935,6 @@ static int smb347_battery_get_property(struct power_supply *psy,
                val->intval = pdata->battery_info.voltage_max_design;
                break;
 
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (!smb347_is_online(smb))
-                       return -ENODATA;
-               ret = smb347_read(smb, STAT_A);
-               if (ret < 0)
-                       return ret;
-
-               ret &= STAT_A_FLOAT_VOLTAGE_MASK;
-               if (ret > 0x3d)
-                       ret = 0x3d;
-
-               val->intval = 3500000 + ret * 20000;
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               if (!smb347_is_online(smb))
-                       return -ENODATA;
-
-               ret = smb347_read(smb, STAT_B);
-               if (ret < 0)
-                       return ret;
-
-               /*
-                * The current value is composition of FCC and PCC values
-                * and we can detect which table to use from bit 5.
-                */
-               if (ret & 0x20) {
-                       val->intval = hw_to_current(fcc_tbl,
-                                                   ARRAY_SIZE(fcc_tbl),
-                                                   ret & 7);
-               } else {
-                       ret >>= 3;
-                       val->intval = hw_to_current(pcc_tbl,
-                                                   ARRAY_SIZE(pcc_tbl),
-                                                   ret & 7);
-               }
-               break;
-
        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
                val->intval = pdata->battery_info.charge_full_design;
                break;
@@ -1095,64 +956,58 @@ static enum power_supply_property smb347_battery_properties[] = {
        POWER_SUPPLY_PROP_TECHNOLOGY,
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
        POWER_SUPPLY_PROP_MODEL_NAME,
 };
 
-static int smb347_debugfs_show(struct seq_file *s, void *data)
+static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
 {
-       struct smb347_charger *smb = s->private;
-       int ret;
-       u8 reg;
-
-       seq_printf(s, "Control registers:\n");
-       seq_printf(s, "==================\n");
-       for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
-       }
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Command registers:\n");
-       seq_printf(s, "==================\n");
-       ret = smb347_read(smb, CMD_A);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret);
-       ret = smb347_read(smb, CMD_B);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret);
-       ret = smb347_read(smb, CMD_C);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret);
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Interrupt status registers:\n");
-       seq_printf(s, "===========================\n");
-       for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
-       }
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Status registers:\n");
-       seq_printf(s, "=================\n");
-       for (reg = STAT_A; reg <= STAT_E; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+       switch (reg) {
+       case IRQSTAT_A:
+       case IRQSTAT_C:
+       case IRQSTAT_E:
+       case IRQSTAT_F:
+       case STAT_A:
+       case STAT_B:
+       case STAT_C:
+       case STAT_E:
+               return true;
        }
 
-       return 0;
+       return false;
 }
 
-static int smb347_debugfs_open(struct inode *inode, struct file *file)
+static bool smb347_readable_reg(struct device *dev, unsigned int reg)
 {
-       return single_open(file, smb347_debugfs_show, inode->i_private);
+       switch (reg) {
+       case CFG_CHARGE_CURRENT:
+       case CFG_CURRENT_LIMIT:
+       case CFG_FLOAT_VOLTAGE:
+       case CFG_STAT:
+       case CFG_PIN:
+       case CFG_THERM:
+       case CFG_SYSOK:
+       case CFG_OTHER:
+       case CFG_OTG:
+       case CFG_TEMP_LIMIT:
+       case CFG_FAULT_IRQ:
+       case CFG_STATUS_IRQ:
+       case CFG_ADDRESS:
+       case CMD_A:
+       case CMD_B:
+       case CMD_C:
+               return true;
+       }
+
+       return smb347_volatile_reg(dev, reg);
 }
 
-static const struct file_operations smb347_debugfs_fops = {
-       .open           = smb347_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
+static const struct regmap_config smb347_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = SMB347_MAX_REGISTER,
+       .volatile_reg   = smb347_volatile_reg,
+       .readable_reg   = smb347_readable_reg,
 };
 
 static int smb347_probe(struct i2c_client *client,
@@ -1178,28 +1033,45 @@ static int smb347_probe(struct i2c_client *client,
        i2c_set_clientdata(client, smb);
 
        mutex_init(&smb->lock);
-       smb->client = client;
+       smb->dev = &client->dev;
        smb->pdata = pdata;
 
+       smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap);
+       if (IS_ERR(smb->regmap))
+               return PTR_ERR(smb->regmap);
+
        ret = smb347_hw_init(smb);
        if (ret < 0)
                return ret;
 
-       smb->mains.name = "smb347-mains";
-       smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
-       smb->mains.get_property = smb347_mains_get_property;
-       smb->mains.properties = smb347_mains_properties;
-       smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
-       smb->mains.supplied_to = battery;
-       smb->mains.num_supplicants = ARRAY_SIZE(battery);
-
-       smb->usb.name = "smb347-usb";
-       smb->usb.type = POWER_SUPPLY_TYPE_USB;
-       smb->usb.get_property = smb347_usb_get_property;
-       smb->usb.properties = smb347_usb_properties;
-       smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
-       smb->usb.supplied_to = battery;
-       smb->usb.num_supplicants = ARRAY_SIZE(battery);
+       if (smb->pdata->use_mains) {
+               smb->mains.name = "smb347-mains";
+               smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
+               smb->mains.get_property = smb347_mains_get_property;
+               smb->mains.properties = smb347_mains_properties;
+               smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
+               smb->mains.supplied_to = battery;
+               smb->mains.num_supplicants = ARRAY_SIZE(battery);
+               ret = power_supply_register(dev, &smb->mains);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->use_usb) {
+               smb->usb.name = "smb347-usb";
+               smb->usb.type = POWER_SUPPLY_TYPE_USB;
+               smb->usb.get_property = smb347_usb_get_property;
+               smb->usb.properties = smb347_usb_properties;
+               smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
+               smb->usb.supplied_to = battery;
+               smb->usb.num_supplicants = ARRAY_SIZE(battery);
+               ret = power_supply_register(dev, &smb->usb);
+               if (ret < 0) {
+                       if (smb->pdata->use_mains)
+                               power_supply_unregister(&smb->mains);
+                       return ret;
+               }
+       }
 
        smb->battery.name = "smb347-battery";
        smb->battery.type = POWER_SUPPLY_TYPE_BATTERY;
@@ -1207,20 +1079,13 @@ static int smb347_probe(struct i2c_client *client,
        smb->battery.properties = smb347_battery_properties;
        smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties);
 
-       ret = power_supply_register(dev, &smb->mains);
-       if (ret < 0)
-               return ret;
-
-       ret = power_supply_register(dev, &smb->usb);
-       if (ret < 0) {
-               power_supply_unregister(&smb->mains);
-               return ret;
-       }
 
        ret = power_supply_register(dev, &smb->battery);
        if (ret < 0) {
-               power_supply_unregister(&smb->usb);
-               power_supply_unregister(&smb->mains);
+               if (smb->pdata->use_usb)
+                       power_supply_unregister(&smb->usb);
+               if (smb->pdata->use_mains)
+                       power_supply_unregister(&smb->mains);
                return ret;
        }
 
@@ -1229,15 +1094,15 @@ static int smb347_probe(struct i2c_client *client,
         * interrupt support here.
         */
        if (pdata->irq_gpio >= 0) {
-               ret = smb347_irq_init(smb);
+               ret = smb347_irq_init(smb, client);
                if (ret < 0) {
                        dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
                        dev_warn(dev, "disabling IRQ support\n");
+               } else {
+                       smb347_irq_enable(smb);
                }
        }
 
-       smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb,
-                                         &smb347_debugfs_fops);
        return 0;
 }
 
@@ -1245,9 +1110,6 @@ static int smb347_remove(struct i2c_client *client)
 {
        struct smb347_charger *smb = i2c_get_clientdata(client);
 
-       if (!IS_ERR_OR_NULL(smb->dentry))
-               debugfs_remove(smb->dentry);
-
        if (client->irq) {
                smb347_irq_disable(smb);
                free_irq(client->irq, smb);
@@ -1255,8 +1117,10 @@ static int smb347_remove(struct i2c_client *client)
        }
 
        power_supply_unregister(&smb->battery);
-       power_supply_unregister(&smb->usb);
-       power_supply_unregister(&smb->mains);
+       if (smb->pdata->use_usb)
+               power_supply_unregister(&smb->usb);
+       if (smb->pdata->use_mains)
+               power_supply_unregister(&smb->mains);
        return 0;
 }
 
@@ -1275,17 +1139,7 @@ static struct i2c_driver smb347_driver = {
        .id_table     = smb347_id,
 };
 
-static int __init smb347_init(void)
-{
-       return i2c_add_driver(&smb347_driver);
-}
-module_init(smb347_init);
-
-static void __exit smb347_exit(void)
-{
-       i2c_del_driver(&smb347_driver);
-}
-module_exit(smb347_exit);
+module_i2c_driver(smb347_driver);
 
 MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
index 33a6743..c05da00 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef DASD_INT_H
 #define DASD_INT_H
 
-#ifdef __KERNEL__
-
 /* we keep old device allocation scheme; IOW, minors are still in 0..255 */
 #define DASD_PER_MAJOR (1U << (MINORBITS - DASD_PARTN_BITS))
 #define DASD_PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
@@ -791,6 +789,4 @@ static inline int dasd_eer_enabled(struct dasd_device *device)
 #define dasd_eer_enabled(d)    (0)
 #endif /* CONFIG_DASD_ERR */
 
-#endif                         /* __KERNEL__ */
-
 #endif                         /* DASD_H */
index 69e6c50..50f7115 100644 (file)
@@ -211,7 +211,7 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
        sccb.evbuf.event_qual = EQ_STORE_DATA;
        sccb.evbuf.data_id = DI_FCP_DUMP;
        sccb.evbuf.event_id = 4712;
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        sccb.evbuf.asa_size = ASA_SIZE_64;
 #else
        sccb.evbuf.asa_size = ASA_SIZE_32;
index 01bb04c..2a09679 100644 (file)
@@ -571,13 +571,12 @@ free_cmd:
 static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd,
                               int iscsi_cmd, int size)
 {
-       cmd->va = pci_alloc_consistent(phba->ctrl.pdev, sizeof(size),
-                                      &cmd->dma);
+       cmd->va = pci_alloc_consistent(phba->ctrl.pdev, size, &cmd->dma);
        if (!cmd->va) {
                SE_DEBUG(DBG_LVL_1, "Failed to allocate memory for if info\n");
                return -ENOMEM;
        }
-       memset(cmd->va, 0, sizeof(size));
+       memset(cmd->va, 0, size);
        cmd->size = size;
        be_cmd_hdr_prepare(cmd->va, CMD_SUBSYSTEM_ISCSI, iscsi_cmd, size);
        return 0;
index 8b6c6bf..b839274 100644 (file)
@@ -426,6 +426,23 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
                vshost = vport->drv_port.im_port->shost;
                fc_host_node_name(vshost) = wwn_to_u64((u8 *)&port_cfg.nwwn);
                fc_host_port_name(vshost) = wwn_to_u64((u8 *)&port_cfg.pwwn);
+               fc_host_supported_classes(vshost) = FC_COS_CLASS3;
+
+               memset(fc_host_supported_fc4s(vshost), 0,
+                       sizeof(fc_host_supported_fc4s(vshost)));
+
+               /* For FCP type 0x08 */
+               if (supported_fc4s & BFA_LPORT_ROLE_FCP_IM)
+                       fc_host_supported_fc4s(vshost)[2] = 1;
+
+               /* For fibre channel services type 0x20 */
+               fc_host_supported_fc4s(vshost)[7] = 1;
+
+               fc_host_supported_speeds(vshost) =
+                               bfad_im_supported_speeds(&bfad->bfa);
+               fc_host_maxframe_size(vshost) =
+                               bfa_fcport_get_maxfrsize(&bfad->bfa);
+
                fc_vport->dd_data = vport;
                vport->drv_port.im_port->fc_vport = fc_vport;
        } else if (rc == BFA_STATUS_INVALID_WWN)
index 3153923..1ac09af 100644 (file)
@@ -987,7 +987,7 @@ done:
        return 0;
 }
 
-static u32
+u32
 bfad_im_supported_speeds(struct bfa_s *bfa)
 {
        struct bfa_ioc_attr_s *ioc_attr;
index 0814367..f6c1023 100644 (file)
@@ -37,6 +37,7 @@ int  bfad_im_scsi_host_alloc(struct bfad_s *bfad,
                struct bfad_im_port_s *im_port, struct device *dev);
 void bfad_im_scsi_host_free(struct bfad_s *bfad,
                                struct bfad_im_port_s *im_port);
+u32 bfad_im_supported_speeds(struct bfa_s *bfa);
 
 #define MAX_FCP_TARGET 1024
 #define MAX_FCP_LUN 16384
index a4953ef..0578fa0 100644 (file)
@@ -62,7 +62,7 @@
 #include "bnx2fc_constants.h"
 
 #define BNX2FC_NAME            "bnx2fc"
-#define BNX2FC_VERSION         "1.0.10"
+#define BNX2FC_VERSION         "1.0.11"
 
 #define PFX                    "bnx2fc: "
 
@@ -228,13 +228,16 @@ struct bnx2fc_interface {
        struct packet_type fip_packet_type;
        struct workqueue_struct *timer_work_queue;
        struct kref kref;
-       struct fcoe_ctlr ctlr;
        u8 vlan_enabled;
        int vlan_id;
        bool enabled;
 };
 
-#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_interface, ctlr)
+#define bnx2fc_from_ctlr(x)                    \
+       ((struct bnx2fc_interface *)((x) + 1))
+
+#define bnx2fc_to_ctlr(x)                                      \
+       ((struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1))
 
 struct bnx2fc_lport {
        struct list_head list;
index ce0ce3e..bdbbb13 100644 (file)
@@ -854,7 +854,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
        struct fc_exch *exch = fc_seq_exch(seq);
        struct fc_lport *lport = exch->lp;
        u8 *mac;
-       struct fc_frame_header *fh;
        u8 op;
 
        if (IS_ERR(fp))
@@ -862,13 +861,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
 
        mac = fr_cb(fp)->granted_mac;
        if (is_zero_ether_addr(mac)) {
-               fh = fc_frame_header_get(fp);
-               if (fh->fh_type != FC_TYPE_ELS) {
-                       printk(KERN_ERR PFX "bnx2fc_flogi_resp:"
-                               "fh_type != FC_TYPE_ELS\n");
-                       fc_frame_free(fp);
-                       return;
-               }
                op = fc_frame_payload_op(fp);
                if (lport->vport) {
                        if (op == ELS_LS_RJT) {
@@ -878,12 +870,10 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
                                return;
                        }
                }
-               if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
-                       fc_frame_free(fp);
-                       return;
-               }
+               fcoe_ctlr_recv_flogi(fip, lport, fp);
        }
-       fip->update_mac(lport, mac);
+       if (!is_zero_ether_addr(mac))
+               fip->update_mac(lport, mac);
 done:
        fc_lport_flogi_resp(seq, fp, lport);
 }
@@ -910,7 +900,7 @@ struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct bnx2fc_interface *interface = port->priv;
-       struct fcoe_ctlr *fip = &interface->ctlr;
+       struct fcoe_ctlr *fip = bnx2fc_to_ctlr(interface);
        struct fc_frame_header *fh = fc_frame_header_get(fp);
 
        switch (op) {
index c1c6a92..f52f668 100644 (file)
@@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
 
 #define DRV_MODULE_NAME                "bnx2fc"
 #define DRV_MODULE_VERSION     BNX2FC_VERSION
-#define DRV_MODULE_RELDATE     "Jan 22, 2011"
+#define DRV_MODULE_RELDATE     "Apr 24, 2012"
 
 
 static char version[] __devinitdata =
@@ -54,6 +54,7 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb;
 static struct libfc_function_template bnx2fc_libfc_fcn_templ;
 static struct scsi_host_template bnx2fc_shost_template;
 static struct fc_function_template bnx2fc_transport_function;
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ;
 static struct fc_function_template bnx2fc_vport_xport_function;
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode);
 static void __bnx2fc_destroy(struct bnx2fc_interface *interface);
@@ -88,6 +89,7 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport);
 static void bnx2fc_stop(struct bnx2fc_interface *interface);
 static int __init bnx2fc_mod_init(void);
 static void __exit bnx2fc_mod_exit(void);
+static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev);
 
 unsigned int bnx2fc_debug_level;
 module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR);
@@ -118,6 +120,41 @@ static void bnx2fc_get_lesb(struct fc_lport *lport,
        __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 
+static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct net_device *netdev = bnx2fc_netdev(fip->lp);
+       struct fcoe_fc_els_lesb *fcoe_lesb;
+       struct fc_els_lesb fc_lesb;
+
+       __fcoe_get_lesb(fip->lp, &fc_lesb, netdev);
+       fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
+
+       ctlr_dev->lesb.lesb_link_fail =
+               ntohl(fcoe_lesb->lesb_link_fail);
+       ctlr_dev->lesb.lesb_vlink_fail =
+               ntohl(fcoe_lesb->lesb_vlink_fail);
+       ctlr_dev->lesb.lesb_miss_fka =
+               ntohl(fcoe_lesb->lesb_miss_fka);
+       ctlr_dev->lesb.lesb_symb_err =
+               ntohl(fcoe_lesb->lesb_symb_err);
+       ctlr_dev->lesb.lesb_err_block =
+               ntohl(fcoe_lesb->lesb_err_block);
+       ctlr_dev->lesb.lesb_fcs_error =
+               ntohl(fcoe_lesb->lesb_fcs_error);
+}
+EXPORT_SYMBOL(bnx2fc_ctlr_get_lesb);
+
+static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev =
+               fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       struct bnx2fc_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+       fcf_dev->vlan_id = fcoe->vlan_id;
+}
+
 static void bnx2fc_clean_rx_queue(struct fc_lport *lp)
 {
        struct fcoe_percpu_s *bg;
@@ -244,6 +281,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        struct sk_buff          *skb;
        struct fc_frame_header  *fh;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr        *ctlr;
        struct bnx2fc_hba *hba;
        struct fcoe_port        *port;
        struct fcoe_hdr         *hp;
@@ -256,6 +294,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
 
        port = (struct fcoe_port *)lport_priv(lport);
        interface = port->priv;
+       ctlr = bnx2fc_to_ctlr(interface);
        hba = interface->hba;
 
        fh = fc_frame_header_get(fp);
@@ -268,12 +307,12 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        }
 
        if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-               if (!interface->ctlr.sel_fcf) {
+               if (!ctlr->sel_fcf) {
                        BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n");
                        kfree_skb(skb);
                        return -EINVAL;
                }
-               if (fcoe_ctlr_els_send(&interface->ctlr, lport, skb))
+               if (fcoe_ctlr_els_send(ctlr, lport, skb))
                        return 0;
        }
 
@@ -346,14 +385,14 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
-       if (interface->ctlr.map_dest)
+       if (ctlr->map_dest)
                fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
        else
                /* insert GW address */
-               memcpy(eh->h_dest, interface->ctlr.dest_addr, ETH_ALEN);
+               memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
 
-       if (unlikely(interface->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-               memcpy(eh->h_source, interface->ctlr.ctl_src_addr, ETH_ALEN);
+       if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+               memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
        else
                memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
@@ -403,6 +442,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
 {
        struct fc_lport *lport;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fc_frame_header *fh;
        struct fcoe_rcv_info *fr;
        struct fcoe_percpu_s *bg;
@@ -410,7 +450,8 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
 
        interface = container_of(ptype, struct bnx2fc_interface,
                                 fcoe_packet_type);
-       lport = interface->ctlr.lp;
+       ctlr = bnx2fc_to_ctlr(interface);
+       lport = ctlr->lp;
 
        if (unlikely(lport == NULL)) {
                printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n");
@@ -758,11 +799,13 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
 {
        struct bnx2fc_hba *hba;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
        u64 wwnn, wwpn;
 
        port = lport_priv(lport);
        interface = port->priv;
+       ctlr = bnx2fc_to_ctlr(interface);
        hba = interface->hba;
 
        /* require support for get_pauseparam ethtool op. */
@@ -781,13 +824,13 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
 
        if (!lport->vport) {
                if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
-                       wwnn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr,
+                       wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 1, 0);
                BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn);
                fc_set_wwnn(lport, wwnn);
 
                if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
-                       wwpn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr,
+                       wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 2, 0);
 
                BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn);
@@ -824,6 +867,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
        struct fc_lport *lport;
        struct fc_lport *vport;
        struct bnx2fc_interface *interface, *tmp;
+       struct fcoe_ctlr *ctlr;
        int wait_for_upload = 0;
        u32 link_possible = 1;
 
@@ -874,7 +918,8 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                if (interface->hba != hba)
                        continue;
 
-               lport = interface->ctlr.lp;
+               ctlr = bnx2fc_to_ctlr(interface);
+               lport = ctlr->lp;
                BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n",
                                interface->netdev->name, event);
 
@@ -889,8 +934,8 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                         * on a stale vlan
                         */
                        if (interface->enabled)
-                               fcoe_ctlr_link_up(&interface->ctlr);
-               } else if (fcoe_ctlr_link_down(&interface->ctlr)) {
+                               fcoe_ctlr_link_up(ctlr);
+               } else if (fcoe_ctlr_link_down(ctlr)) {
                        mutex_lock(&lport->lp_mutex);
                        list_for_each_entry(vport, &lport->vports, list)
                                fc_host_port_type(vport->host) =
@@ -995,9 +1040,11 @@ static int bnx2fc_fip_recv(struct sk_buff *skb, struct net_device *dev,
                           struct net_device *orig_dev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        interface = container_of(ptype, struct bnx2fc_interface,
                                 fip_packet_type);
-       fcoe_ctlr_recv(&interface->ctlr, skb);
+       ctlr = bnx2fc_to_ctlr(interface);
+       fcoe_ctlr_recv(ctlr, skb);
        return 0;
 }
 
@@ -1155,6 +1202,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
 {
        struct net_device *netdev = interface->netdev;
        struct net_device *physdev = interface->hba->phys_dev;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct netdev_hw_addr *ha;
        int sel_san_mac = 0;
 
@@ -1169,7 +1217,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
 
                if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
                    (is_valid_ether_addr(ha->addr))) {
-                       memcpy(interface->ctlr.ctl_src_addr, ha->addr,
+                       memcpy(ctlr->ctl_src_addr, ha->addr,
                               ETH_ALEN);
                        sel_san_mac = 1;
                        BNX2FC_MISC_DBG("Found SAN MAC\n");
@@ -1224,19 +1272,23 @@ static void bnx2fc_release_transport(void)
 
 static void bnx2fc_interface_release(struct kref *kref)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct net_device *netdev;
 
        interface = container_of(kref, struct bnx2fc_interface, kref);
        BNX2FC_MISC_DBG("Interface is being released\n");
 
+       ctlr = bnx2fc_to_ctlr(interface);
+       ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
        netdev = interface->netdev;
 
        /* tear-down FIP controller */
        if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags))
-               fcoe_ctlr_destroy(&interface->ctlr);
+               fcoe_ctlr_destroy(ctlr);
 
-       kfree(interface);
+       fcoe_ctlr_device_delete(ctlr_dev);
 
        dev_put(netdev);
        module_put(THIS_MODULE);
@@ -1329,33 +1381,40 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
                                      struct net_device *netdev,
                                      enum fip_state fip_mode)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
+       int size;
        int rc = 0;
 
-       interface = kzalloc(sizeof(*interface), GFP_KERNEL);
-       if (!interface) {
+       size = (sizeof(*interface) + sizeof(struct fcoe_ctlr));
+       ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &bnx2fc_fcoe_sysfs_templ,
+                                        size);
+       if (!ctlr_dev) {
                printk(KERN_ERR PFX "Unable to allocate interface structure\n");
                return NULL;
        }
+       ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       interface = fcoe_ctlr_priv(ctlr);
        dev_hold(netdev);
        kref_init(&interface->kref);
        interface->hba = hba;
        interface->netdev = netdev;
 
        /* Initialize FIP */
-       fcoe_ctlr_init(&interface->ctlr, fip_mode);
-       interface->ctlr.send = bnx2fc_fip_send;
-       interface->ctlr.update_mac = bnx2fc_update_src_mac;
-       interface->ctlr.get_src_addr = bnx2fc_get_src_mac;
+       fcoe_ctlr_init(ctlr, fip_mode);
+       ctlr->send = bnx2fc_fip_send;
+       ctlr->update_mac = bnx2fc_update_src_mac;
+       ctlr->get_src_addr = bnx2fc_get_src_mac;
        set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags);
 
        rc = bnx2fc_interface_setup(interface);
        if (!rc)
                return interface;
 
-       fcoe_ctlr_destroy(&interface->ctlr);
+       fcoe_ctlr_destroy(ctlr);
        dev_put(netdev);
-       kfree(interface);
+       fcoe_ctlr_device_delete(ctlr_dev);
        return NULL;
 }
 
@@ -1373,6 +1432,7 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
 static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
                                  struct device *parent, int npiv)
 {
+       struct fcoe_ctlr        *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport         *lport, *n_port;
        struct fcoe_port        *port;
        struct Scsi_Host        *shost;
@@ -1383,7 +1443,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
 
        blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
        if (!blport) {
-               BNX2FC_HBA_DBG(interface->ctlr.lp, "Unable to alloc blport\n");
+               BNX2FC_HBA_DBG(ctlr->lp, "Unable to alloc blport\n");
                return NULL;
        }
 
@@ -1479,7 +1539,8 @@ static void bnx2fc_net_cleanup(struct bnx2fc_interface *interface)
 
 static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
 {
-       struct fc_lport *lport = interface->ctlr.lp;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+       struct fc_lport *lport = ctlr->lp;
        struct fcoe_port *port = lport_priv(lport);
        struct bnx2fc_hba *hba = interface->hba;
 
@@ -1519,7 +1580,8 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
 
 static void __bnx2fc_destroy(struct bnx2fc_interface *interface)
 {
-       struct fc_lport *lport = interface->ctlr.lp;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+       struct fc_lport *lport = ctlr->lp;
        struct fcoe_port *port = lport_priv(lport);
 
        bnx2fc_interface_cleanup(interface);
@@ -1543,13 +1605,15 @@ static int bnx2fc_destroy(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface = NULL;
        struct workqueue_struct *timer_work_queue;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n");
                goto netdev_err;
@@ -1646,6 +1710,7 @@ static void bnx2fc_ulp_start(void *handle)
 {
        struct bnx2fc_hba *hba = handle;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fc_lport *lport;
 
        mutex_lock(&bnx2fc_dev_lock);
@@ -1657,7 +1722,8 @@ static void bnx2fc_ulp_start(void *handle)
 
        list_for_each_entry(interface, &if_list, list) {
                if (interface->hba == hba) {
-                       lport = interface->ctlr.lp;
+                       ctlr = bnx2fc_to_ctlr(interface);
+                       lport = ctlr->lp;
                        /* Kick off Fabric discovery*/
                        printk(KERN_ERR PFX "ulp_init: start discovery\n");
                        lport->tt.frame_send = bnx2fc_xmit;
@@ -1677,13 +1743,14 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport)
 
 static void bnx2fc_stop(struct bnx2fc_interface *interface)
 {
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport *lport;
        struct fc_lport *vport;
 
        if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags))
                return;
 
-       lport = interface->ctlr.lp;
+       lport = ctlr->lp;
        bnx2fc_port_shutdown(lport);
 
        mutex_lock(&lport->lp_mutex);
@@ -1692,7 +1759,7 @@ static void bnx2fc_stop(struct bnx2fc_interface *interface)
                                        FC_PORTTYPE_UNKNOWN;
        mutex_unlock(&lport->lp_mutex);
        fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
-       fcoe_ctlr_link_down(&interface->ctlr);
+       fcoe_ctlr_link_down(ctlr);
        fcoe_clean_pending_queue(lport);
 }
 
@@ -1804,6 +1871,7 @@ exit:
 
 static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
 {
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport *lport;
        int wait_cnt = 0;
 
@@ -1814,18 +1882,18 @@ static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
                return;
        }
 
-       lport = interface->ctlr.lp;
+       lport = ctlr->lp;
        BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n");
 
        if (!bnx2fc_link_ok(lport) && interface->enabled) {
                BNX2FC_HBA_DBG(lport, "ctlr_link_up\n");
-               fcoe_ctlr_link_up(&interface->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
                set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
        }
 
        /* wait for the FCF to be selected before issuing FLOGI */
-       while (!interface->ctlr.sel_fcf) {
+       while (!ctlr->sel_fcf) {
                msleep(250);
                /* give up after 3 secs */
                if (++wait_cnt > 12)
@@ -1889,19 +1957,21 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev)
 static int bnx2fc_disable(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_disable: interface or lport not found\n");
        } else {
                interface->enabled = false;
-               fcoe_ctlr_link_down(&interface->ctlr);
-               fcoe_clean_pending_queue(interface->ctlr.lp);
+               fcoe_ctlr_link_down(ctlr);
+               fcoe_clean_pending_queue(ctlr->lp);
        }
 
        mutex_unlock(&bnx2fc_dev_lock);
@@ -1913,17 +1983,19 @@ static int bnx2fc_disable(struct net_device *netdev)
 static int bnx2fc_enable(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_enable: interface or lport not found\n");
-       } else if (!bnx2fc_link_ok(interface->ctlr.lp)) {
-               fcoe_ctlr_link_up(&interface->ctlr);
+       } else if (!bnx2fc_link_ok(ctlr->lp)) {
+               fcoe_ctlr_link_up(ctlr);
                interface->enabled = true;
        }
 
@@ -1944,6 +2016,7 @@ static int bnx2fc_enable(struct net_device *netdev)
  */
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
 {
+       struct fcoe_ctlr *ctlr;
        struct bnx2fc_interface *interface;
        struct bnx2fc_hba *hba;
        struct net_device *phys_dev;
@@ -2010,6 +2083,7 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
                goto ifput_err;
        }
 
+       ctlr = bnx2fc_to_ctlr(interface);
        interface->vlan_id = vlan_id;
        interface->vlan_enabled = 1;
 
@@ -2035,10 +2109,10 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
        lport->boot_time = jiffies;
 
        /* Make this master N_port */
-       interface->ctlr.lp = lport;
+       ctlr->lp = lport;
 
        if (!bnx2fc_link_ok(lport)) {
-               fcoe_ctlr_link_up(&interface->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
                set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
        }
@@ -2439,6 +2513,19 @@ static void __exit bnx2fc_mod_exit(void)
 module_init(bnx2fc_mod_init);
 module_exit(bnx2fc_mod_exit);
 
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = {
+       .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode,
+       .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_symb_err = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_err_block = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_fcs_error = bnx2fc_ctlr_get_lesb,
+
+       .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+       .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id,
+};
+
 static struct fc_function_template bnx2fc_transport_function = {
        .show_host_node_name = 1,
        .show_host_port_name = 1,
index afd5709..2ca6bfe 100644 (file)
@@ -167,6 +167,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
 {
        struct fc_lport *lport = port->lport;
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct kwqe *kwqe_arr[4];
        struct fcoe_kwqe_conn_offload1 ofld_req1;
@@ -314,13 +315,13 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
        ofld_req4.src_mac_addr_mid[1] =  port->data_src_addr[2];
        ofld_req4.src_mac_addr_hi[0] =  port->data_src_addr[1];
        ofld_req4.src_mac_addr_hi[1] =  port->data_src_addr[0];
-       ofld_req4.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
+       ofld_req4.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
                                                        /* fcf mac */
-       ofld_req4.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       ofld_req4.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       ofld_req4.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       ofld_req4.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       ofld_req4.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       ofld_req4.dst_mac_addr_lo[1] = ctlr->dest_addr[4];
+       ofld_req4.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       ofld_req4.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       ofld_req4.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       ofld_req4.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma;
        ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32);
@@ -351,6 +352,7 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
 {
        struct kwqe *kwqe_arr[2];
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct fcoe_kwqe_conn_enable_disable enbl_req;
        struct fc_lport *lport = port->lport;
@@ -374,12 +376,12 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
        enbl_req.src_mac_addr_hi[1] =  port->data_src_addr[0];
        memcpy(tgt->src_addr, port->data_src_addr, ETH_ALEN);
 
-       enbl_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
-       enbl_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       enbl_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       enbl_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       enbl_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       enbl_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       enbl_req.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
+       enbl_req.dst_mac_addr_lo[1] =  ctlr->dest_addr[4];
+       enbl_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       enbl_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       enbl_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       enbl_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        port_id = fc_host_port_id(lport->host);
        if (port_id != tgt->sid) {
@@ -419,6 +421,7 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
                                    struct bnx2fc_rport *tgt)
 {
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct fcoe_kwqe_conn_enable_disable disable_req;
        struct kwqe *kwqe_arr[2];
@@ -440,12 +443,12 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
        disable_req.src_mac_addr_hi[0] =  tgt->src_addr[1];
        disable_req.src_mac_addr_hi[1] =  tgt->src_addr[0];
 
-       disable_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
-       disable_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       disable_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       disable_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       disable_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       disable_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       disable_req.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
+       disable_req.dst_mac_addr_lo[1] =  ctlr->dest_addr[4];
+       disable_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       disable_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       disable_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       disable_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        port_id = tgt->sid;
        disable_req.s_id[0] = (port_id & 0x000000FF);
index e897ce9..4f7453b 100644 (file)
@@ -810,8 +810,22 @@ retry_tmf:
        spin_lock_bh(&tgt->tgt_lock);
 
        io_req->wait_for_comp = 0;
-       if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags)))
+       if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags))) {
                set_bit(BNX2FC_FLAG_TM_TIMEOUT, &io_req->req_flags);
+               if (io_req->on_tmf_queue) {
+                       list_del_init(&io_req->link);
+                       io_req->on_tmf_queue = 0;
+               }
+               io_req->wait_for_comp = 1;
+               bnx2fc_initiate_cleanup(io_req);
+               spin_unlock_bh(&tgt->tgt_lock);
+               rc = wait_for_completion_timeout(&io_req->tm_done,
+                                                BNX2FC_FW_TIMEOUT);
+               spin_lock_bh(&tgt->tgt_lock);
+               io_req->wait_for_comp = 0;
+               if (!rc)
+                       kref_put(&io_req->refcount, bnx2fc_cmd_release);
+       }
 
        spin_unlock_bh(&tgt->tgt_lock);
 
@@ -1089,6 +1103,48 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
        return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET);
 }
 
+int bnx2fc_expl_logo(struct fc_lport *lport, struct bnx2fc_cmd *io_req)
+{
+       struct bnx2fc_rport *tgt = io_req->tgt;
+       struct fc_rport_priv *rdata = tgt->rdata;
+       int logo_issued;
+       int rc = SUCCESS;
+       int wait_cnt = 0;
+
+       BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n",
+                     tgt->flags);
+       logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO,
+                                      &tgt->flags);
+       io_req->wait_for_comp = 1;
+       bnx2fc_initiate_cleanup(io_req);
+
+       spin_unlock_bh(&tgt->tgt_lock);
+
+       wait_for_completion(&io_req->tm_done);
+
+       io_req->wait_for_comp = 0;
+       /*
+        * release the reference taken in eh_abort to allow the
+        * target to re-login after flushing IOs
+        */
+        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+
+       if (!logo_issued) {
+               clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
+               mutex_lock(&lport->disc.disc_mutex);
+               lport->tt.rport_logoff(rdata);
+               mutex_unlock(&lport->disc.disc_mutex);
+               do {
+                       msleep(BNX2FC_RELOGIN_WAIT_TIME);
+                       if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) {
+                               rc = FAILED;
+                               break;
+                       }
+               } while (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags));
+       }
+       spin_lock_bh(&tgt->tgt_lock);
+       return rc;
+}
 /**
  * bnx2fc_eh_abort - eh_abort_handler api to abort an outstanding
  *                     SCSI command
@@ -1103,10 +1159,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
        struct fc_rport_libfc_priv *rp = rport->dd_data;
        struct bnx2fc_cmd *io_req;
        struct fc_lport *lport;
-       struct fc_rport_priv *rdata;
        struct bnx2fc_rport *tgt;
-       int logo_issued;
-       int wait_cnt = 0;
        int rc = FAILED;
 
 
@@ -1183,58 +1236,31 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
        list_add_tail(&io_req->link, &tgt->io_retire_queue);
 
        init_completion(&io_req->tm_done);
-       io_req->wait_for_comp = 1;
 
-       if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
-               /* Cancel the current timer running on this io_req */
-               if (cancel_delayed_work(&io_req->timeout_work))
-                       kref_put(&io_req->refcount,
-                                bnx2fc_cmd_release); /* drop timer hold */
-               set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
-               rc = bnx2fc_initiate_abts(io_req);
-       } else {
+       if (test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
                printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
                                "already in abts processing\n", io_req->xid);
                if (cancel_delayed_work(&io_req->timeout_work))
                        kref_put(&io_req->refcount,
                                 bnx2fc_cmd_release); /* drop timer hold */
-               bnx2fc_initiate_cleanup(io_req);
+               rc = bnx2fc_expl_logo(lport, io_req);
+               goto out;
+       }
 
+       /* Cancel the current timer running on this io_req */
+       if (cancel_delayed_work(&io_req->timeout_work))
+               kref_put(&io_req->refcount,
+                        bnx2fc_cmd_release); /* drop timer hold */
+       set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
+       io_req->wait_for_comp = 1;
+       rc = bnx2fc_initiate_abts(io_req);
+       if (rc == FAILED) {
+               bnx2fc_initiate_cleanup(io_req);
                spin_unlock_bh(&tgt->tgt_lock);
-
                wait_for_completion(&io_req->tm_done);
-
                spin_lock_bh(&tgt->tgt_lock);
                io_req->wait_for_comp = 0;
-               rdata = io_req->tgt->rdata;
-               logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO,
-                                              &tgt->flags);
-               kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
-
-               if (!logo_issued) {
-                       BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n",
-                                     tgt->flags);
-                       mutex_lock(&lport->disc.disc_mutex);
-                       lport->tt.rport_logoff(rdata);
-                       mutex_unlock(&lport->disc.disc_mutex);
-                       do {
-                               msleep(BNX2FC_RELOGIN_WAIT_TIME);
-                               /*
-                                * If session not recovered, let SCSI-ml
-                                * escalate error recovery.
-                                */
-                               if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT)
-                                       return FAILED;
-                       } while (!test_bit(BNX2FC_FLAG_SESSION_READY,
-                                          &tgt->flags));
-               }
-               return SUCCESS;
-       }
-       if (rc == FAILED) {
-               kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
-               return rc;
+               goto done;
        }
        spin_unlock_bh(&tgt->tgt_lock);
 
@@ -1247,7 +1273,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
                /* Let the scsi-ml try to recover this command */
                printk(KERN_ERR PFX "abort failed, xid = 0x%x\n",
                       io_req->xid);
-               rc = FAILED;
+               rc = bnx2fc_expl_logo(lport, io_req);
+               goto out;
        } else {
                /*
                 * We come here even when there was a race condition
@@ -1259,9 +1286,10 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
                bnx2fc_scsi_done(io_req, DID_ABORT);
                kref_put(&io_req->refcount, bnx2fc_cmd_release);
        }
-
+done:
        /* release the reference taken in eh_abort */
        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+out:
        spin_unlock_bh(&tgt->tgt_lock);
        return rc;
 }
index c1800b5..082a25c 100644 (file)
@@ -185,6 +185,16 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
                BUG_ON(rc);
        }
 
+       list_for_each_safe(list, tmp, &tgt->active_tm_queue) {
+               i++;
+               io_req = (struct bnx2fc_cmd *)list;
+               list_del_init(&io_req->link);
+               io_req->on_tmf_queue = 0;
+               BNX2FC_IO_DBG(io_req, "tm_queue cleanup\n");
+               if (io_req->wait_for_comp)
+                       complete(&io_req->tm_done);
+       }
+
        list_for_each_safe(list, tmp, &tgt->els_queue) {
                i++;
                io_req = (struct bnx2fc_cmd *)list;
@@ -213,8 +223,17 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
 
                BNX2FC_IO_DBG(io_req, "retire_queue flush\n");
 
-               if (cancel_delayed_work(&io_req->timeout_work))
+               if (cancel_delayed_work(&io_req->timeout_work)) {
+                       if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
+                                               &io_req->req_flags)) {
+                               /* Handle eh_abort timeout */
+                               BNX2FC_IO_DBG(io_req, "eh_abort for IO "
+                                             "in retire_q\n");
+                               if (io_req->wait_for_comp)
+                                       complete(&io_req->tm_done);
+                       }
                        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+               }
 
                clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
        }
index f6d37d0..aed0f5d 100644 (file)
@@ -1,4 +1,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 obj-$(CONFIG_LIBFCOE) += libfcoe.o
 
-libfcoe-objs := fcoe_ctlr.o fcoe_transport.o
+libfcoe-objs := fcoe_ctlr.o fcoe_transport.o fcoe_sysfs.o
index 76e3d0b..fe30b1b 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_fcoe.h>
 
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
@@ -150,6 +151,21 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled);
 static int fcoe_vport_disable(struct fc_vport *, bool disable);
 static void fcoe_set_vport_symbolic_name(struct fc_vport *);
 static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *);
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *);
+
+static struct fcoe_sysfs_function_template fcoe_sysfs_templ = {
+       .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode,
+       .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
+
+       .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+       .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id,
+};
 
 static struct libfc_function_template fcoe_libfc_fcn_templ = {
        .frame_send = fcoe_xmit,
@@ -282,7 +298,7 @@ static struct scsi_host_template fcoe_shost_template = {
 static int fcoe_interface_setup(struct fcoe_interface *fcoe,
                                struct net_device *netdev)
 {
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        struct netdev_hw_addr *ha;
        struct net_device *real_dev;
        u8 flogi_maddr[ETH_ALEN];
@@ -366,7 +382,10 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
                                                    enum fip_state fip_mode)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
+       int size;
        int err;
 
        if (!try_module_get(THIS_MODULE)) {
@@ -376,27 +395,32 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
                goto out;
        }
 
-       fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
-       if (!fcoe) {
-               FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+       size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface);
+       ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ,
+                                       size);
+       if (!ctlr_dev) {
+               FCOE_DBG("Failed to add fcoe_ctlr_device\n");
                fcoe = ERR_PTR(-ENOMEM);
                goto out_putmod;
        }
 
+       ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       fcoe = fcoe_ctlr_priv(ctlr);
+
        dev_hold(netdev);
 
        /*
         * Initialize FIP.
         */
-       fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
-       fcoe->ctlr.send = fcoe_fip_send;
-       fcoe->ctlr.update_mac = fcoe_update_src_mac;
-       fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
+       fcoe_ctlr_init(ctlr, fip_mode);
+       ctlr->send = fcoe_fip_send;
+       ctlr->update_mac = fcoe_update_src_mac;
+       ctlr->get_src_addr = fcoe_get_src_mac;
 
        err = fcoe_interface_setup(fcoe, netdev);
        if (err) {
-               fcoe_ctlr_destroy(&fcoe->ctlr);
-               kfree(fcoe);
+               fcoe_ctlr_destroy(ctlr);
+               fcoe_ctlr_device_delete(ctlr_dev);
                dev_put(netdev);
                fcoe = ERR_PTR(err);
                goto out_putmod;
@@ -419,7 +443,7 @@ out:
 static void fcoe_interface_remove(struct fcoe_interface *fcoe)
 {
        struct net_device *netdev = fcoe->netdev;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        u8 flogi_maddr[ETH_ALEN];
        const struct net_device_ops *ops;
 
@@ -462,7 +486,8 @@ static void fcoe_interface_remove(struct fcoe_interface *fcoe)
 static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
 {
        struct net_device *netdev = fcoe->netdev;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
 
        rtnl_lock();
        if (!fcoe->removed)
@@ -472,8 +497,8 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        /* Release the self-reference taken during fcoe_interface_create() */
        /* tear-down the FCoE controller */
        fcoe_ctlr_destroy(fip);
-       scsi_host_put(fcoe->ctlr.lp->host);
-       kfree(fcoe);
+       scsi_host_put(fip->lp->host);
+       fcoe_ctlr_device_delete(ctlr_dev);
        dev_put(netdev);
        module_put(THIS_MODULE);
 }
@@ -493,9 +518,11 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
                         struct net_device *orig_dev)
 {
        struct fcoe_interface *fcoe;
+       struct fcoe_ctlr *ctlr;
 
        fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
-       fcoe_ctlr_recv(&fcoe->ctlr, skb);
+       ctlr = fcoe_to_ctlr(fcoe);
+       fcoe_ctlr_recv(ctlr, skb);
        return 0;
 }
 
@@ -645,11 +672,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
        u32 mfs;
        u64 wwnn, wwpn;
        struct fcoe_interface *fcoe;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
 
        /* Setup lport private data to point to fcoe softc */
        port = lport_priv(lport);
        fcoe = port->priv;
+       ctlr = fcoe_to_ctlr(fcoe);
 
        /*
         * Determine max frame size based on underlying device and optional
@@ -676,10 +705,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 
        if (!lport->vport) {
                if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
-                       wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+                       wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0);
                fc_set_wwnn(lport, wwnn);
                if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
-                       wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
+                       wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 2, 0);
                fc_set_wwpn(lport, wwpn);
        }
@@ -1056,6 +1085,7 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
 static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                                       struct device *parent, int npiv)
 {
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
        struct net_device *netdev = fcoe->netdev;
        struct fc_lport *lport, *n_port;
        struct fcoe_port *port;
@@ -1119,7 +1149,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
 
        /* Initialize the library */
-       rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
+       rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
                                "interface\n");
@@ -1386,6 +1416,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 {
        struct fc_lport *lport;
        struct fcoe_rcv_info *fr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct fcoe_percpu_s *fps;
@@ -1393,7 +1424,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
        unsigned int cpu;
 
        fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
-       lport = fcoe->ctlr.lp;
+       ctlr = fcoe_to_ctlr(fcoe);
+       lport = ctlr->lp;
        if (unlikely(!lport)) {
                FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
                goto err2;
@@ -1409,8 +1441,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 
        eh = eth_hdr(skb);
 
-       if (is_fip_mode(&fcoe->ctlr) &&
-           compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+       if (is_fip_mode(ctlr) &&
+           compare_ether_addr(eh->h_source, ctlr->dest_addr)) {
                FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
                                eh->h_source);
                goto err;
@@ -1544,6 +1576,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        unsigned int elen;              /* eth header, may include vlan */
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
        u8 sof, eof;
        struct fcoe_hdr *hp;
 
@@ -1559,7 +1592,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        }
 
        if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
-           fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
+           fcoe_ctlr_els_send(ctlr, lport, skb))
                return 0;
 
        sof = fr_sof(fp);
@@ -1623,12 +1656,12 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
-       memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
-       if (fcoe->ctlr.map_dest)
+       memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
+       if (ctlr->map_dest)
                memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
 
-       if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-               memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
+       if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+               memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
        else
                memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
@@ -1677,6 +1710,7 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
 static inline int fcoe_filter_frames(struct fc_lport *lport,
                                     struct fc_frame *fp)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct sk_buff *skb = (struct sk_buff *)fp;
@@ -1698,7 +1732,8 @@ static inline int fcoe_filter_frames(struct fc_lport *lport,
                return 0;
 
        fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
-       if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+       ctlr = fcoe_to_ctlr(fcoe);
+       if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
            ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
                FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
                return -EINVAL;
@@ -1877,6 +1912,7 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
                                     ulong event, void *ptr)
 {
        struct dcb_app_type *entry = ptr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct net_device *netdev;
        struct fcoe_port *port;
@@ -1894,6 +1930,8 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
        if (!fcoe)
                return NOTIFY_OK;
 
+       ctlr = fcoe_to_ctlr(fcoe);
+
        if (entry->dcbx & DCB_CAP_DCBX_VER_CEE)
                prio = ffs(entry->app.priority) - 1;
        else
@@ -1904,10 +1942,10 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
 
        if (entry->app.protocol == ETH_P_FIP ||
            entry->app.protocol == ETH_P_FCOE)
-               fcoe->ctlr.priority = prio;
+               ctlr->priority = prio;
 
        if (entry->app.protocol == ETH_P_FCOE) {
-               port = lport_priv(fcoe->ctlr.lp);
+               port = lport_priv(ctlr->lp);
                port->priority = prio;
        }
 
@@ -1929,6 +1967,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 {
        struct fc_lport *lport = NULL;
        struct net_device *netdev = ptr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
        struct fcoe_dev_stats *stats;
@@ -1938,7 +1977,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 
        list_for_each_entry(fcoe, &fcoe_hostlist, list) {
                if (fcoe->netdev == netdev) {
-                       lport = fcoe->ctlr.lp;
+                       ctlr = fcoe_to_ctlr(fcoe);
+                       lport = ctlr->lp;
                        break;
                }
        }
@@ -1967,7 +2007,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
                break;
        case NETDEV_UNREGISTER:
                list_del(&fcoe->list);
-               port = lport_priv(fcoe->ctlr.lp);
+               port = lport_priv(ctlr->lp);
                queue_work(fcoe_wq, &port->destroy_work);
                goto out;
                break;
@@ -1982,8 +2022,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
        fcoe_link_speed_update(lport);
 
        if (link_possible && !fcoe_link_ok(lport))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
-       else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
+               fcoe_ctlr_link_up(ctlr);
+       else if (fcoe_ctlr_link_down(ctlr)) {
                stats = per_cpu_ptr(lport->dev_stats, get_cpu());
                stats->LinkFailureCount++;
                put_cpu();
@@ -2003,6 +2043,7 @@ out:
  */
 static int fcoe_disable(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        int rc = 0;
 
@@ -2013,8 +2054,9 @@ static int fcoe_disable(struct net_device *netdev)
        rtnl_unlock();
 
        if (fcoe) {
-               fcoe_ctlr_link_down(&fcoe->ctlr);
-               fcoe_clean_pending_queue(fcoe->ctlr.lp);
+               ctlr = fcoe_to_ctlr(fcoe);
+               fcoe_ctlr_link_down(ctlr);
+               fcoe_clean_pending_queue(ctlr->lp);
        } else
                rc = -ENODEV;
 
@@ -2032,6 +2074,7 @@ static int fcoe_disable(struct net_device *netdev)
  */
 static int fcoe_enable(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        int rc = 0;
 
@@ -2040,11 +2083,17 @@ static int fcoe_enable(struct net_device *netdev)
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
-       if (!fcoe)
+       if (!fcoe) {
                rc = -ENODEV;
-       else if (!fcoe_link_ok(fcoe->ctlr.lp))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+               goto out;
+       }
+
+       ctlr = fcoe_to_ctlr(fcoe);
+
+       if (!fcoe_link_ok(ctlr->lp))
+               fcoe_ctlr_link_up(ctlr);
 
+out:
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
@@ -2059,6 +2108,7 @@ static int fcoe_enable(struct net_device *netdev)
  */
 static int fcoe_destroy(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_lport *lport;
        struct fcoe_port *port;
@@ -2071,7 +2121,8 @@ static int fcoe_destroy(struct net_device *netdev)
                rc = -ENODEV;
                goto out_nodev;
        }
-       lport = fcoe->ctlr.lp;
+       ctlr = fcoe_to_ctlr(fcoe);
+       lport = ctlr->lp;
        port = lport_priv(lport);
        list_del(&fcoe->list);
        queue_work(fcoe_wq, &port->destroy_work);
@@ -2126,7 +2177,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
        int dcbx;
        u8 fup, up;
        struct net_device *netdev = fcoe->realdev;
-       struct fcoe_port *port = lport_priv(fcoe->ctlr.lp);
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+       struct fcoe_port *port = lport_priv(ctlr->lp);
        struct dcb_app app = {
                                .priority = 0,
                                .protocol = ETH_P_FCOE
@@ -2149,7 +2201,7 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
                }
 
                port->priority = ffs(up) ? ffs(up) - 1 : 0;
-               fcoe->ctlr.priority = ffs(fup) ? ffs(fup) - 1 : port->priority;
+               ctlr->priority = ffs(fup) ? ffs(fup) - 1 : port->priority;
        }
 #endif
 }
@@ -2166,6 +2218,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
 static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
 {
        int rc = 0;
+       struct fcoe_ctlr_device *ctlr_dev;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_lport *lport;
 
@@ -2184,7 +2238,9 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
                goto out_nodev;
        }
 
-       lport = fcoe_if_create(fcoe, &netdev->dev, 0);
+       ctlr = fcoe_to_ctlr(fcoe);
+       ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
+       lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0);
        if (IS_ERR(lport)) {
                printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
                       netdev->name);
@@ -2195,7 +2251,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        }
 
        /* Make this the "master" N_Port */
-       fcoe->ctlr.lp = lport;
+       ctlr->lp = lport;
 
        /* setup DCB priority attributes. */
        fcoe_dcb_create(fcoe);
@@ -2208,7 +2264,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        fc_fabric_login(lport);
        if (!fcoe_link_ok(lport)) {
                rtnl_unlock();
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                mutex_unlock(&fcoe_config_mutex);
                return rc;
        }
@@ -2320,11 +2376,12 @@ static int fcoe_reset(struct Scsi_Host *shost)
        struct fc_lport *lport = shost_priv(shost);
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
 
-       fcoe_ctlr_link_down(&fcoe->ctlr);
-       fcoe_clean_pending_queue(fcoe->ctlr.lp);
-       if (!fcoe_link_ok(fcoe->ctlr.lp))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+       fcoe_ctlr_link_down(ctlr);
+       fcoe_clean_pending_queue(ctlr->lp);
+       if (!fcoe_link_ok(ctlr->lp))
+               fcoe_ctlr_link_up(ctlr);
        return 0;
 }
 
@@ -2359,10 +2416,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev)
  */
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
 
        fcoe = fcoe_hostlist_lookup_port(netdev);
-       return (fcoe) ? fcoe->ctlr.lp : NULL;
+       ctlr = fcoe_to_ctlr(fcoe);
+       return (fcoe) ? ctlr->lp : NULL;
 }
 
 /**
@@ -2466,6 +2525,7 @@ module_init(fcoe_init);
 static void __exit fcoe_exit(void)
 {
        struct fcoe_interface *fcoe, *tmp;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
        unsigned int cpu;
 
@@ -2477,7 +2537,8 @@ static void __exit fcoe_exit(void)
        rtnl_lock();
        list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
                list_del(&fcoe->list);
-               port = lport_priv(fcoe->ctlr.lp);
+               ctlr = fcoe_to_ctlr(fcoe);
+               port = lport_priv(ctlr->lp);
                queue_work(fcoe_wq, &port->destroy_work);
        }
        rtnl_unlock();
@@ -2573,7 +2634,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        struct fc_frame_header *fh = fc_frame_header_get(fp);
 
        switch (op) {
@@ -2730,6 +2791,40 @@ static void fcoe_get_lesb(struct fc_lport *lport,
        __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 
+static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct net_device *netdev = fcoe_netdev(fip->lp);
+       struct fcoe_fc_els_lesb *fcoe_lesb;
+       struct fc_els_lesb fc_lesb;
+
+       __fcoe_get_lesb(fip->lp, &fc_lesb, netdev);
+       fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
+
+       ctlr_dev->lesb.lesb_link_fail =
+               ntohl(fcoe_lesb->lesb_link_fail);
+       ctlr_dev->lesb.lesb_vlink_fail =
+               ntohl(fcoe_lesb->lesb_vlink_fail);
+       ctlr_dev->lesb.lesb_miss_fka =
+               ntohl(fcoe_lesb->lesb_miss_fka);
+       ctlr_dev->lesb.lesb_symb_err =
+               ntohl(fcoe_lesb->lesb_symb_err);
+       ctlr_dev->lesb.lesb_err_block =
+               ntohl(fcoe_lesb->lesb_err_block);
+       ctlr_dev->lesb.lesb_fcs_error =
+               ntohl(fcoe_lesb->lesb_fcs_error);
+}
+
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev =
+               fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+       fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev);
+}
+
 /**
  * fcoe_set_port_id() - Callback from libfc when Port_ID is set.
  * @lport: the local port
@@ -2747,7 +2842,8 @@ static void fcoe_set_port_id(struct fc_lport *lport,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
 
        if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
-               fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
+               fcoe_ctlr_recv_flogi(ctlr, lport, fp);
 }
index 96ac938..a624add 100644 (file)
@@ -68,7 +68,6 @@ do {                                                                  \
  * @netdev:          The associated net device
  * @fcoe_packet_type: FCoE packet type
  * @fip_packet_type:  FIP packet type
- * @ctlr:            The FCoE controller (for FIP)
  * @oem:             The offload exchange manager for all local port
  *                   instances associated with this port
  * @removed:         Indicates fcoe interface removed from net device
@@ -80,12 +79,15 @@ struct fcoe_interface {
        struct net_device  *realdev;
        struct packet_type fcoe_packet_type;
        struct packet_type fip_packet_type;
-       struct fcoe_ctlr   ctlr;
        struct fc_exch_mgr *oem;
        u8      removed;
 };
 
-#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
+#define fcoe_to_ctlr(x)                                                \
+       (struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1)
+
+#define fcoe_from_ctlr(x)                      \
+       ((struct fcoe_interface *)((x) + 1))
 
 /**
  * fcoe_netdev() - Return the net device associated with a local port
index 5a4c725..d68d572 100644 (file)
@@ -160,6 +160,76 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
 }
 EXPORT_SYMBOL(fcoe_ctlr_init);
 
+static int fcoe_sysfs_fcf_add(struct fcoe_fcf *new)
+{
+       struct fcoe_ctlr *fip = new->fip;
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+       struct fcoe_fcf_device temp, *fcf_dev;
+       int rc = 0;
+
+       LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
+                       new->fabric_name, new->fcf_mac);
+
+       mutex_lock(&ctlr_dev->lock);
+
+       temp.fabric_name = new->fabric_name;
+       temp.switch_name = new->switch_name;
+       temp.fc_map = new->fc_map;
+       temp.vfid = new->vfid;
+       memcpy(temp.mac, new->fcf_mac, ETH_ALEN);
+       temp.priority = new->pri;
+       temp.fka_period = new->fka_period;
+       temp.selected = 0; /* default to unselected */
+
+       fcf_dev = fcoe_fcf_device_add(ctlr_dev, &temp);
+       if (unlikely(!fcf_dev)) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /*
+        * The fcoe_sysfs layer can return a CONNECTED fcf that
+        * has a priv (fcf was never deleted) or a CONNECTED fcf
+        * that doesn't have a priv (fcf was deleted). However,
+        * libfcoe will always delete FCFs before trying to add
+        * them. This is ensured because both recv_adv and
+        * age_fcfs are protected by the the fcoe_ctlr's mutex.
+        * This means that we should never get a FCF with a
+        * non-NULL priv pointer.
+        */
+       BUG_ON(fcf_dev->priv);
+
+       fcf_dev->priv = new;
+       new->fcf_dev = fcf_dev;
+
+       list_add(&new->list, &fip->fcfs);
+       fip->fcf_count++;
+
+out:
+       mutex_unlock(&ctlr_dev->lock);
+       return rc;
+}
+
+static void fcoe_sysfs_fcf_del(struct fcoe_fcf *new)
+{
+       struct fcoe_ctlr *fip = new->fip;
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+       struct fcoe_fcf_device *fcf_dev;
+
+       list_del(&new->list);
+       fip->fcf_count--;
+
+       mutex_lock(&ctlr_dev->lock);
+
+       fcf_dev = fcoe_fcf_to_fcf_dev(new);
+       WARN_ON(!fcf_dev);
+       new->fcf_dev = NULL;
+       fcoe_fcf_device_delete(fcf_dev);
+       kfree(new);
+
+       mutex_unlock(&ctlr_dev->lock);
+}
+
 /**
  * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
  * @fip: The FCoE controller whose FCFs are to be reset
@@ -173,10 +243,10 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
 
        fip->sel_fcf = NULL;
        list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
-               list_del(&fcf->list);
-               kfree(fcf);
+               fcoe_sysfs_fcf_del(fcf);
        }
-       fip->fcf_count = 0;
+       WARN_ON(fip->fcf_count);
+
        fip->sel_time = 0;
 }
 
@@ -717,8 +787,11 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
        unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
        unsigned long deadline;
        unsigned long sel_time = 0;
+       struct list_head del_list;
        struct fcoe_dev_stats *stats;
 
+       INIT_LIST_HEAD(&del_list);
+
        stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
 
        list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
@@ -739,10 +812,13 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
                if (time_after_eq(jiffies, deadline)) {
                        if (fip->sel_fcf == fcf)
                                fip->sel_fcf = NULL;
+                       /*
+                        * Move to delete list so we can call
+                        * fcoe_sysfs_fcf_del (which can sleep)
+                        * after the put_cpu().
+                        */
                        list_del(&fcf->list);
-                       WARN_ON(!fip->fcf_count);
-                       fip->fcf_count--;
-                       kfree(fcf);
+                       list_add(&fcf->list, &del_list);
                        stats->VLinkFailureCount++;
                } else {
                        if (time_after(next_timer, deadline))
@@ -753,6 +829,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
                }
        }
        put_cpu();
+
+       list_for_each_entry_safe(fcf, next, &del_list, list) {
+               /* Removes fcf from current list */
+               fcoe_sysfs_fcf_del(fcf);
+       }
+
        if (sel_time && !fip->sel_fcf && !fip->sel_time) {
                sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
                fip->sel_time = sel_time;
@@ -903,23 +985,23 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
        struct fcoe_fcf *fcf;
        struct fcoe_fcf new;
-       struct fcoe_fcf *found;
        unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
        int first = 0;
        int mtu_valid;
+       int found = 0;
+       int rc = 0;
 
        if (fcoe_ctlr_parse_adv(fip, skb, &new))
                return;
 
        mutex_lock(&fip->ctlr_mutex);
        first = list_empty(&fip->fcfs);
-       found = NULL;
        list_for_each_entry(fcf, &fip->fcfs, list) {
                if (fcf->switch_name == new.switch_name &&
                    fcf->fabric_name == new.fabric_name &&
                    fcf->fc_map == new.fc_map &&
                    compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) {
-                       found = fcf;
+                       found = 1;
                        break;
                }
        }
@@ -931,9 +1013,16 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                if (!fcf)
                        goto out;
 
-               fip->fcf_count++;
                memcpy(fcf, &new, sizeof(new));
-               list_add(&fcf->list, &fip->fcfs);
+               fcf->fip = fip;
+               rc = fcoe_sysfs_fcf_add(fcf);
+               if (rc) {
+                       printk(KERN_ERR "Failed to allocate sysfs instance "
+                              "for FCF, fab %16.16llx mac %pM\n",
+                              new.fabric_name, new.fcf_mac);
+                       kfree(fcf);
+                       goto out;
+               }
        } else {
                /*
                 * Update the FCF's keep-alive descriptor flags.
@@ -954,6 +1043,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                fcf->fka_period = new.fka_period;
                memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN);
        }
+
        mtu_valid = fcoe_ctlr_mtu_valid(fcf);
        fcf->time = jiffies;
        if (!found)
@@ -996,6 +1086,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                    time_before(fip->sel_time, fip->timer.expires))
                        mod_timer(&fip->timer, fip->sel_time);
        }
+
 out:
        mutex_unlock(&fip->ctlr_mutex);
 }
@@ -2718,9 +2809,9 @@ unlock:
 
 /**
  * fcoe_libfc_config() - Sets up libfc related properties for local port
- * @lp: The local port to configure libfc for
- * @fip: The FCoE controller in use by the local port
- * @tt: The libfc function template
+ * @lport:    The local port to configure libfc for
+ * @fip:      The FCoE controller in use by the local port
+ * @tt:       The libfc function template
  * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
  *
  * Returns : 0 for success
@@ -2753,3 +2844,43 @@ int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
        return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct fcoe_fcf *fcf;
+
+       mutex_lock(&fip->ctlr_mutex);
+       mutex_lock(&ctlr_dev->lock);
+
+       fcf = fcoe_fcf_device_priv(fcf_dev);
+       if (fcf)
+               fcf_dev->selected = (fcf == fip->sel_fcf) ? 1 : 0;
+       else
+               fcf_dev->selected = 0;
+
+       mutex_unlock(&ctlr_dev->lock);
+       mutex_unlock(&fip->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_fcf_get_selected);
+
+void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+       mutex_lock(&ctlr->ctlr_mutex);
+       switch (ctlr->mode) {
+       case FIP_MODE_FABRIC:
+               ctlr_dev->mode = FIP_CONN_TYPE_FABRIC;
+               break;
+       case FIP_MODE_VN2VN:
+               ctlr_dev->mode = FIP_CONN_TYPE_VN2VN;
+               break;
+       default:
+               ctlr_dev->mode = FIP_CONN_TYPE_UNKNOWN;
+               break;
+       }
+       mutex_unlock(&ctlr->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_ctlr_get_fip_mode);
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
new file mode 100644 (file)
index 0000000..2bc1631
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+
+#include <scsi/fcoe_sysfs.h>
+
+static atomic_t ctlr_num;
+static atomic_t fcf_num;
+
+/*
+ * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs
+ * should insulate the loss of a fcf.
+ */
+static unsigned int fcoe_fcf_dev_loss_tmo = 1800;  /* seconds */
+
+module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo,
+                  uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fcf_dev_loss_tmo,
+                "Maximum number of seconds that libfcoe should"
+                " insulate the loss of a fcf. Once this value is"
+                " exceeded, the fcf is removed.");
+
+/*
+ * These are used by the fcoe_*_show_function routines, they
+ * are intentionally placed in the .c file as they're not intended
+ * for use throughout the code.
+ */
+#define fcoe_ctlr_id(x)                                \
+       ((x)->id)
+#define fcoe_ctlr_work_q_name(x)               \
+       ((x)->work_q_name)
+#define fcoe_ctlr_work_q(x)                    \
+       ((x)->work_q)
+#define fcoe_ctlr_devloss_work_q_name(x)       \
+       ((x)->devloss_work_q_name)
+#define fcoe_ctlr_devloss_work_q(x)            \
+       ((x)->devloss_work_q)
+#define fcoe_ctlr_mode(x)                      \
+       ((x)->mode)
+#define fcoe_ctlr_fcf_dev_loss_tmo(x)          \
+       ((x)->fcf_dev_loss_tmo)
+#define fcoe_ctlr_link_fail(x)                 \
+       ((x)->lesb.lesb_link_fail)
+#define fcoe_ctlr_vlink_fail(x)                        \
+       ((x)->lesb.lesb_vlink_fail)
+#define fcoe_ctlr_miss_fka(x)                  \
+       ((x)->lesb.lesb_miss_fka)
+#define fcoe_ctlr_symb_err(x)                  \
+       ((x)->lesb.lesb_symb_err)
+#define fcoe_ctlr_err_block(x)                 \
+       ((x)->lesb.lesb_err_block)
+#define fcoe_ctlr_fcs_error(x)                 \
+       ((x)->lesb.lesb_fcs_error)
+#define fcoe_fcf_state(x)                      \
+       ((x)->state)
+#define fcoe_fcf_fabric_name(x)                        \
+       ((x)->fabric_name)
+#define fcoe_fcf_switch_name(x)                        \
+       ((x)->switch_name)
+#define fcoe_fcf_fc_map(x)                     \
+       ((x)->fc_map)
+#define fcoe_fcf_vfid(x)                       \
+       ((x)->vfid)
+#define fcoe_fcf_mac(x)                                \
+       ((x)->mac)
+#define fcoe_fcf_priority(x)                   \
+       ((x)->priority)
+#define fcoe_fcf_fka_period(x)                 \
+       ((x)->fka_period)
+#define fcoe_fcf_dev_loss_tmo(x)               \
+       ((x)->dev_loss_tmo)
+#define fcoe_fcf_selected(x)                   \
+       ((x)->selected)
+#define fcoe_fcf_vlan_id(x)                    \
+       ((x)->vlan_id)
+
+/*
+ * dev_loss_tmo attribute
+ */
+static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val)
+{
+       int ret;
+
+       ret = kstrtoul(buf, 0, val);
+       if (ret || *val < 0)
+               return -EINVAL;
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (*val > UINT_MAX)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf,
+                                    unsigned long val)
+{
+       if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) ||
+           (fcf->state == FCOE_FCF_STATE_DISCONNECTED) ||
+           (fcf->state == FCOE_FCF_STATE_DELETED))
+               return -EBUSY;
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (val > UINT_MAX)
+               return -EINVAL;
+
+       fcoe_fcf_dev_loss_tmo(fcf) = val;
+       return 0;
+}
+
+#define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \
+struct device_attribute device_attr_fcoe_##_prefix##_##_name = \
+       __ATTR(_name, _mode, _show, _store)
+
+#define fcoe_ctlr_show_function(field, format_string, sz, cast)        \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+                                           struct device_attribute *attr, \
+                                           char *buf)                  \
+{                                                                      \
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);               \
+       if (ctlr->f->get_fcoe_ctlr_##field)                             \
+               ctlr->f->get_fcoe_ctlr_##field(ctlr);                   \
+       return snprintf(buf, sz, format_string,                         \
+                       cast fcoe_ctlr_##field(ctlr));                  \
+}
+
+#define fcoe_fcf_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev,        \
+                                          struct device_attribute *attr, \
+                                          char *buf)                   \
+{                                                                      \
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);                  \
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);  \
+       if (ctlr->f->get_fcoe_fcf_##field)                              \
+               ctlr->f->get_fcoe_fcf_##field(fcf);                     \
+       return snprintf(buf, sz, format_string,                         \
+                       cast fcoe_fcf_##field(fcf));                    \
+}
+
+#define fcoe_ctlr_private_show_function(field, format_string, sz, cast)        \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+                                           struct device_attribute *attr, \
+                                           char *buf)                  \
+{                                                                      \
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);               \
+       return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \
+}
+
+#define fcoe_fcf_private_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev,        \
+                                          struct device_attribute *attr, \
+                                          char *buf)                   \
+{                                                              \
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);                  \
+       return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \
+}
+
+#define fcoe_ctlr_private_rd_attr(field, format_string, sz)            \
+       fcoe_ctlr_private_show_function(field, format_string, sz, )     \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_ctlr_rd_attr(field, format_string, sz)                    \
+       fcoe_ctlr_show_function(field, format_string, sz, )             \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_rd_attr(field, format_string, sz)                     \
+       fcoe_fcf_show_function(field, format_string, sz, )              \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr(field, format_string, sz)             \
+       fcoe_fcf_private_show_function(field, format_string, sz, )      \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \
+       fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast)  \
+       fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_enum_name_search(title, table_type, table)                        \
+static const char *get_fcoe_##title##_name(enum table_type table_key)  \
+{                                                                      \
+       int i;                                                          \
+       char *name = NULL;                                              \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(table); i++) {                       \
+               if (table[i].value == table_key) {                      \
+                       name = table[i].name;                           \
+                       break;                                          \
+               }                                                       \
+       }                                                               \
+       return name;                                                    \
+}
+
+static struct {
+       enum fcf_state value;
+       char           *name;
+} fcf_state_names[] = {
+       { FCOE_FCF_STATE_UNKNOWN,      "Unknown" },
+       { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" },
+       { FCOE_FCF_STATE_CONNECTED,    "Connected" },
+};
+fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names)
+#define FCOE_FCF_STATE_MAX_NAMELEN 50
+
+static ssize_t show_fcf_state(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       const char *name;
+       name = get_fcoe_fcf_state_name(fcf->state);
+       if (!name)
+               return -EINVAL;
+       return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name);
+}
+static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL);
+
+static struct {
+       enum fip_conn_type value;
+       char               *name;
+} fip_conn_type_names[] = {
+       { FIP_CONN_TYPE_UNKNOWN, "Unknown" },
+       { FIP_CONN_TYPE_FABRIC, "Fabric" },
+       { FIP_CONN_TYPE_VN2VN, "VN2VN" },
+};
+fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names)
+#define FCOE_CTLR_MODE_MAX_NAMELEN 50
+
+static ssize_t show_ctlr_mode(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       const char *name;
+
+       if (ctlr->f->get_fcoe_ctlr_mode)
+               ctlr->f->get_fcoe_ctlr_mode(ctlr);
+
+       name = get_fcoe_ctlr_mode_name(ctlr->mode);
+       if (!name)
+               return -EINVAL;
+       return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN,
+                       "%s\n", name);
+}
+static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO,
+                       show_ctlr_mode, NULL);
+
+static ssize_t
+store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       struct fcoe_fcf_device *fcf;
+       unsigned long val;
+       int rc;
+
+       rc = fcoe_str_to_dev_loss(buf, &val);
+       if (rc)
+               return rc;
+
+       fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val;
+       mutex_lock(&ctlr->lock);
+       list_for_each_entry(fcf, &ctlr->fcfs, peers)
+               fcoe_fcf_set_dev_loss_tmo(fcf, val);
+       mutex_unlock(&ctlr->lock);
+       return count;
+}
+fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, );
+static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR,
+                       show_fcoe_ctlr_device_fcf_dev_loss_tmo,
+                       store_private_fcoe_ctlr_fcf_dev_loss_tmo);
+
+/* Link Error Status Block (LESB) */
+fcoe_ctlr_rd_attr(link_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20);
+fcoe_ctlr_rd_attr(symb_err, "%u\n", 20);
+fcoe_ctlr_rd_attr(err_block, "%u\n", 20);
+fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20);
+
+fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr(priority, "%u\n", 20);
+fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20);
+fcoe_fcf_private_rd_attr(vfid, "%u\n", 20);
+fcoe_fcf_private_rd_attr(mac, "%pM\n", 20);
+fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20);
+fcoe_fcf_rd_attr(selected, "%u\n", 20);
+fcoe_fcf_rd_attr(vlan_id, "%u\n", 20);
+
+fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, )
+static ssize_t
+store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       unsigned long val;
+       int rc;
+
+       rc = fcoe_str_to_dev_loss(buf, &val);
+       if (rc)
+               return rc;
+
+       rc = fcoe_fcf_set_dev_loss_tmo(fcf, val);
+       if (rc)
+               return rc;
+       return count;
+}
+static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR,
+                       show_fcoe_fcf_device_dev_loss_tmo,
+                       store_fcoe_fcf_dev_loss_tmo);
+
+static struct attribute *fcoe_ctlr_lesb_attrs[] = {
+       &device_attr_fcoe_ctlr_link_fail.attr,
+       &device_attr_fcoe_ctlr_vlink_fail.attr,
+       &device_attr_fcoe_ctlr_miss_fka.attr,
+       &device_attr_fcoe_ctlr_symb_err.attr,
+       &device_attr_fcoe_ctlr_err_block.attr,
+       &device_attr_fcoe_ctlr_fcs_error.attr,
+       NULL,
+};
+
+static struct attribute_group fcoe_ctlr_lesb_attr_group = {
+       .name = "lesb",
+       .attrs = fcoe_ctlr_lesb_attrs,
+};
+
+static struct attribute *fcoe_ctlr_attrs[] = {
+       &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr,
+       &device_attr_fcoe_ctlr_mode.attr,
+       NULL,
+};
+
+static struct attribute_group fcoe_ctlr_attr_group = {
+       .attrs = fcoe_ctlr_attrs,
+};
+
+static const struct attribute_group *fcoe_ctlr_attr_groups[] = {
+       &fcoe_ctlr_attr_group,
+       &fcoe_ctlr_lesb_attr_group,
+       NULL,
+};
+
+static struct attribute *fcoe_fcf_attrs[] = {
+       &device_attr_fcoe_fcf_fabric_name.attr,
+       &device_attr_fcoe_fcf_switch_name.attr,
+       &device_attr_fcoe_fcf_dev_loss_tmo.attr,
+       &device_attr_fcoe_fcf_fc_map.attr,
+       &device_attr_fcoe_fcf_vfid.attr,
+       &device_attr_fcoe_fcf_mac.attr,
+       &device_attr_fcoe_fcf_priority.attr,
+       &device_attr_fcoe_fcf_fka_period.attr,
+       &device_attr_fcoe_fcf_state.attr,
+       &device_attr_fcoe_fcf_selected.attr,
+       &device_attr_fcoe_fcf_vlan_id.attr,
+       NULL
+};
+
+static struct attribute_group fcoe_fcf_attr_group = {
+       .attrs = fcoe_fcf_attrs,
+};
+
+static const struct attribute_group *fcoe_fcf_attr_groups[] = {
+       &fcoe_fcf_attr_group,
+       NULL,
+};
+
+struct bus_type fcoe_bus_type;
+
+static int fcoe_bus_match(struct device *dev,
+                         struct device_driver *drv)
+{
+       if (dev->bus == &fcoe_bus_type)
+               return 1;
+       return 0;
+}
+
+/**
+ * fcoe_ctlr_device_release() - Release the FIP ctlr memory
+ * @dev: Pointer to the FIP ctlr's embedded device
+ *
+ * Called when the last FIP ctlr reference is released.
+ */
+static void fcoe_ctlr_device_release(struct device *dev)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       kfree(ctlr);
+}
+
+/**
+ * fcoe_fcf_device_release() - Release the FIP fcf memory
+ * @dev: Pointer to the fcf's embedded device
+ *
+ * Called when the last FIP fcf reference is released.
+ */
+static void fcoe_fcf_device_release(struct device *dev)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       kfree(fcf);
+}
+
+struct device_type fcoe_ctlr_device_type = {
+       .name = "fcoe_ctlr",
+       .groups = fcoe_ctlr_attr_groups,
+       .release = fcoe_ctlr_device_release,
+};
+
+struct device_type fcoe_fcf_device_type = {
+       .name = "fcoe_fcf",
+       .groups = fcoe_fcf_attr_groups,
+       .release = fcoe_fcf_device_release,
+};
+
+struct bus_type fcoe_bus_type = {
+       .name = "fcoe",
+       .match = &fcoe_bus_match,
+};
+
+/**
+ * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed
+ */
+void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)
+{
+       if (!fcoe_ctlr_work_q(ctlr)) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to flush work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+               return;
+       }
+
+       flush_workqueue(fcoe_ctlr_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work:   Work to queue for execution
+ *
+ * Return value:
+ *     1 on success / 0 already queued / < 0 for error
+ */
+int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,
+                              struct work_struct *work)
+{
+       if (unlikely(!fcoe_ctlr_work_q(ctlr))) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to queue work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+
+               return -EINVAL;
+       }
+
+       return queue_work(fcoe_ctlr_work_q(ctlr), work);
+}
+
+/**
+ * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed
+ */
+void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)
+{
+       if (!fcoe_ctlr_devloss_work_q(ctlr)) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to flush work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+               return;
+       }
+
+       flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work:   Work to queue for execution
+ * @delay:  jiffies to delay the work queuing
+ *
+ * Return value:
+ *     1 on success / 0 already queued / < 0 for error
+ */
+int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,
+                                      struct delayed_work *work,
+                                      unsigned long delay)
+{
+       if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to queue work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+
+               return -EINVAL;
+       }
+
+       return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay);
+}
+
+static int fcoe_fcf_device_match(struct fcoe_fcf_device *new,
+                                struct fcoe_fcf_device *old)
+{
+       if (new->switch_name == old->switch_name &&
+           new->fabric_name == old->fabric_name &&
+           new->fc_map == old->fc_map &&
+           compare_ether_addr(new->mac, old->mac) == 0)
+               return 1;
+       return 0;
+}
+
+/**
+ * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs
+ * @parent:    The parent device to which the fcoe_ctlr instance
+ *             should be attached
+ * @f:         The LLD's FCoE sysfs function template pointer
+ * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD
+ *
+ * This routine allocates a FIP ctlr object with some additional memory
+ * for the LLD. The FIP ctlr is initialized, added to sysfs and then
+ * attributes are added to it.
+ */
+struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,
+                                   struct fcoe_sysfs_function_template *f,
+                                   int priv_size)
+{
+       struct fcoe_ctlr_device *ctlr;
+       int error = 0;
+
+       ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size,
+                      GFP_KERNEL);
+       if (!ctlr)
+               goto out;
+
+       ctlr->id = atomic_inc_return(&ctlr_num) - 1;
+       ctlr->f = f;
+       INIT_LIST_HEAD(&ctlr->fcfs);
+       mutex_init(&ctlr->lock);
+       ctlr->dev.parent = parent;
+       ctlr->dev.bus = &fcoe_bus_type;
+       ctlr->dev.type = &fcoe_ctlr_device_type;
+
+       ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo;
+
+       snprintf(ctlr->work_q_name, sizeof(ctlr->work_q_name),
+                "ctlr_wq_%d", ctlr->id);
+       ctlr->work_q = create_singlethread_workqueue(
+               ctlr->work_q_name);
+       if (!ctlr->work_q)
+               goto out_del;
+
+       snprintf(ctlr->devloss_work_q_name,
+                sizeof(ctlr->devloss_work_q_name),
+                "ctlr_dl_wq_%d", ctlr->id);
+       ctlr->devloss_work_q = create_singlethread_workqueue(
+               ctlr->devloss_work_q_name);
+       if (!ctlr->devloss_work_q)
+               goto out_del_q;
+
+       dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id);
+       error = device_register(&ctlr->dev);
+       if (error)
+               goto out_del_q2;
+
+       return ctlr;
+
+out_del_q2:
+       destroy_workqueue(ctlr->devloss_work_q);
+       ctlr->devloss_work_q = NULL;
+out_del_q:
+       destroy_workqueue(ctlr->work_q);
+       ctlr->work_q = NULL;
+out_del:
+       kfree(ctlr);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add);
+
+/**
+ * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs
+ * @ctlr: A pointer to the ctlr to be deleted
+ *
+ * Deletes a FIP ctlr and any fcfs attached
+ * to it. Deleting fcfs will cause their childen
+ * to be deleted as well.
+ *
+ * The ctlr is detached from sysfs and it's resources
+ * are freed (work q), but the memory is not freed
+ * until its last reference is released.
+ *
+ * This routine expects no locks to be held before
+ * calling.
+ *
+ * TODO: Currently there are no callbacks to clean up LLD data
+ * for a fcoe_fcf_device. LLDs must keep this in mind as they need
+ * to clean up each of their LLD data for all fcoe_fcf_device before
+ * calling fcoe_ctlr_device_delete.
+ */
+void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr)
+{
+       struct fcoe_fcf_device *fcf, *next;
+       /* Remove any attached fcfs */
+       mutex_lock(&ctlr->lock);
+       list_for_each_entry_safe(fcf, next,
+                                &ctlr->fcfs, peers) {
+               list_del(&fcf->peers);
+               fcf->state = FCOE_FCF_STATE_DELETED;
+               fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+       }
+       mutex_unlock(&ctlr->lock);
+
+       fcoe_ctlr_device_flush_work(ctlr);
+
+       destroy_workqueue(ctlr->devloss_work_q);
+       ctlr->devloss_work_q = NULL;
+       destroy_workqueue(ctlr->work_q);
+       ctlr->work_q = NULL;
+
+       device_unregister(&ctlr->dev);
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete);
+
+/**
+ * fcoe_fcf_device_final_delete() - Final delete routine
+ * @work: The FIP fcf's embedded work struct
+ *
+ * It is expected that the fcf has been removed from
+ * the FIP ctlr's list before calling this routine.
+ */
+static void fcoe_fcf_device_final_delete(struct work_struct *work)
+{
+       struct fcoe_fcf_device *fcf =
+               container_of(work, struct fcoe_fcf_device, delete_work);
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+       /*
+        * Cancel any outstanding timers. These should really exist
+        * only when rmmod'ing the LLDD and we're asking for
+        * immediate termination of the rports
+        */
+       if (!cancel_delayed_work(&fcf->dev_loss_work))
+               fcoe_ctlr_device_flush_devloss(ctlr);
+
+       device_unregister(&fcf->dev);
+}
+
+/**
+ * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires
+ * @work: The FIP fcf's embedded work struct
+ *
+ * Removes the fcf from the FIP ctlr's list of fcfs and
+ * queues the final deletion.
+ */
+static void fip_timeout_deleted_fcf(struct work_struct *work)
+{
+       struct fcoe_fcf_device *fcf =
+               container_of(work, struct fcoe_fcf_device, dev_loss_work.work);
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+       mutex_lock(&ctlr->lock);
+
+       /*
+        * If the fcf is deleted or reconnected before the timer
+        * fires the devloss queue will be flushed, but the state will
+        * either be CONNECTED or DELETED. If that is the case we
+        * cancel deleting the fcf.
+        */
+       if (fcf->state != FCOE_FCF_STATE_DISCONNECTED)
+               goto out;
+
+       dev_printk(KERN_ERR, &fcf->dev,
+                  "FIP fcf connection time out: removing fcf\n");
+
+       list_del(&fcf->peers);
+       fcf->state = FCOE_FCF_STATE_DELETED;
+       fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+
+out:
+       mutex_unlock(&ctlr->lock);
+}
+
+/**
+ * fcoe_fcf_device_delete() - Delete a FIP fcf
+ * @fcf: Pointer to the fcf which is to be deleted
+ *
+ * Queues the FIP fcf on the devloss workqueue
+ *
+ * Expects the ctlr_attrs mutex to be held for fcf
+ * state change.
+ */
+void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf)
+{
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+       int timeout = fcf->dev_loss_tmo;
+
+       if (fcf->state != FCOE_FCF_STATE_CONNECTED)
+               return;
+
+       fcf->state = FCOE_FCF_STATE_DISCONNECTED;
+
+       /*
+        * FCF will only be re-connected by the LLD calling
+        * fcoe_fcf_device_add, and it should be setting up
+        * priv then.
+        */
+       fcf->priv = NULL;
+
+       fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work,
+                                          timeout * HZ);
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete);
+
+/**
+ * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system
+ * @ctlr:    The fcoe_ctlr_device that will be the fcoe_fcf_device parent
+ * @new_fcf: A temporary FCF used for lookups on the current list of fcfs
+ *
+ * Expects to be called with the ctlr->lock held
+ */
+struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr,
+                                           struct fcoe_fcf_device *new_fcf)
+{
+       struct fcoe_fcf_device *fcf;
+       int error = 0;
+
+       list_for_each_entry(fcf, &ctlr->fcfs, peers) {
+               if (fcoe_fcf_device_match(new_fcf, fcf)) {
+                       if (fcf->state == FCOE_FCF_STATE_CONNECTED)
+                               return fcf;
+
+                       fcf->state = FCOE_FCF_STATE_CONNECTED;
+
+                       if (!cancel_delayed_work(&fcf->dev_loss_work))
+                               fcoe_ctlr_device_flush_devloss(ctlr);
+
+                       return fcf;
+               }
+       }
+
+       fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC);
+       if (unlikely(!fcf))
+               goto out;
+
+       INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete);
+       INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf);
+
+       fcf->dev.parent = &ctlr->dev;
+       fcf->dev.bus = &fcoe_bus_type;
+       fcf->dev.type = &fcoe_fcf_device_type;
+       fcf->id = atomic_inc_return(&fcf_num) - 1;
+       fcf->state = FCOE_FCF_STATE_UNKNOWN;
+
+       fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo;
+
+       dev_set_name(&fcf->dev, "fcf_%d", fcf->id);
+
+       fcf->fabric_name = new_fcf->fabric_name;
+       fcf->switch_name = new_fcf->switch_name;
+       fcf->fc_map = new_fcf->fc_map;
+       fcf->vfid = new_fcf->vfid;
+       memcpy(fcf->mac, new_fcf->mac, ETH_ALEN);
+       fcf->priority = new_fcf->priority;
+       fcf->fka_period = new_fcf->fka_period;
+       fcf->selected = new_fcf->selected;
+
+       error = device_register(&fcf->dev);
+       if (error)
+               goto out_del;
+
+       fcf->state = FCOE_FCF_STATE_CONNECTED;
+       list_add_tail(&fcf->peers, &ctlr->fcfs);
+
+       return fcf;
+
+out_del:
+       kfree(fcf);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_add);
+
+int __init fcoe_sysfs_setup(void)
+{
+       int error;
+
+       atomic_set(&ctlr_num, 0);
+       atomic_set(&fcf_num, 0);
+
+       error = bus_register(&fcoe_bus_type);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+void __exit fcoe_sysfs_teardown(void)
+{
+       bus_unregister(&fcoe_bus_type);
+}
index 710e149..b46f43d 100644 (file)
@@ -815,9 +815,17 @@ out_nodev:
  */
 static int __init libfcoe_init(void)
 {
-       fcoe_transport_init();
+       int rc = 0;
 
-       return 0;
+       rc = fcoe_transport_init();
+       if (rc)
+               return rc;
+
+       rc = fcoe_sysfs_setup();
+       if (rc)
+               fcoe_transport_exit();
+
+       return rc;
 }
 module_init(libfcoe_init);
 
@@ -826,6 +834,7 @@ module_init(libfcoe_init);
  */
 static void __exit libfcoe_exit(void)
 {
+       fcoe_sysfs_teardown();
        fcoe_transport_exit();
 }
 module_exit(libfcoe_exit);
index 6208d56..317a7fd 100644 (file)
@@ -25,3 +25,12 @@ config SCSI_QLA_FC
        Firmware images can be retrieved from:
 
                ftp://ftp.qlogic.com/outgoing/linux/firmware/
+
+config TCM_QLA2XXX
+       tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
+       depends on SCSI_QLA_FC && TARGET_CORE
+       select LIBFC
+       select BTREE
+       default n
+       ---help---
+       Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs
index 5df782f..dce7d78 100644 (file)
@@ -1,5 +1,6 @@
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
                qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
-        qla_nx.o
+        qla_nx.o qla_target.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
+obj-$(CONFIG_TCM_QLA2XXX) += tcm_qla2xxx.o
index 5926f5a..5ab9530 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/kthread.h>
 #include <linux/vmalloc.h>
@@ -576,6 +577,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
                scsi_block_requests(vha->host);
                set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
                if (IS_QLA82XX(ha)) {
+                       ha->flags.isp82xx_no_md_cap = 1;
                        qla82xx_idc_lock(ha);
                        qla82xx_set_reset_owner(vha);
                        qla82xx_idc_unlock(ha);
@@ -585,7 +587,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
                scsi_unblock_requests(vha->host);
                break;
        case 0x2025d:
-               if (!IS_QLA81XX(ha))
+               if (!IS_QLA81XX(ha) || !IS_QLA8031(ha))
                        return -EPERM;
 
                ql_log(ql_log_info, vha, 0x706f,
@@ -1105,9 +1107,8 @@ qla2x00_total_isp_aborts_show(struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
        scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
-       struct qla_hw_data *ha = vha->hw;
        return snprintf(buf, PAGE_SIZE, "%d\n",
-           ha->qla_stats.total_isp_aborts);
+           vha->qla_stats.total_isp_aborts);
 }
 
 static ssize_t
@@ -1154,7 +1155,7 @@ qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr,
        scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return snprintf(buf, PAGE_SIZE, "\n");
 
        return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -1537,7 +1538,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
        dma_addr_t stats_dma;
        struct fc_host_statistics *pfc_host_stat;
 
-       pfc_host_stat = &ha->fc_host_stat;
+       pfc_host_stat = &vha->fc_host_stat;
        memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics));
 
        if (test_bit(UNLOADING, &vha->dpc_flags))
@@ -1580,8 +1581,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
                pfc_host_stat->dumped_frames = stats->dumped_frames;
                pfc_host_stat->nos_count = stats->nos_rcvd;
        }
-       pfc_host_stat->fcp_input_megabytes = ha->qla_stats.input_bytes >> 20;
-       pfc_host_stat->fcp_output_megabytes = ha->qla_stats.output_bytes >> 20;
+       pfc_host_stat->fcp_input_megabytes = vha->qla_stats.input_bytes >> 20;
+       pfc_host_stat->fcp_output_megabytes = vha->qla_stats.output_bytes >> 20;
 
 done_free:
         dma_pool_free(ha->s_dma_pool, stats, stats_dma);
@@ -1737,6 +1738,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
        fc_host_supported_speeds(vha->host) =
                fc_host_supported_speeds(base_vha->host);
 
+       qlt_vport_create(vha, ha);
        qla24xx_vport_disable(fc_vport, disable);
 
        if (ha->flags.cpu_affinity_enabled) {
@@ -1951,12 +1953,16 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha)
        fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
        fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
        fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
-       fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+       fc_host_supported_classes(vha->host) = ha->tgt.enable_class_2 ?
+                       (FC_COS_CLASS2|FC_COS_CLASS3) : FC_COS_CLASS3;
        fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
        fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
 
        if (IS_CNA_CAPABLE(ha))
                speed = FC_PORTSPEED_10GBIT;
+       else if (IS_QLA2031(ha))
+               speed = FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT |
+                   FC_PORTSPEED_4GBIT;
        else if (IS_QLA25XX(ha))
                speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
                    FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
index bc3cc6d..c688838 100644 (file)
@@ -297,7 +297,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job)
 
                /* Initialize all required  fields of fcport */
                fcport->vha = vha;
-               fcport->vp_idx = vha->vp_idx;
                fcport->d_id.b.al_pa =
                        bsg_job->request->rqst_data.h_els.port_id[0];
                fcport->d_id.b.area =
@@ -483,7 +482,6 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job)
 
        /* Initialize all required  fields of fcport */
        fcport->vha = vha;
-       fcport->vp_idx = vha->vp_idx;
        fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0];
        fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1];
        fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2];
@@ -544,7 +542,7 @@ qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
        int rval = 0;
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                goto done_set_internal;
 
        new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1);
@@ -586,7 +584,7 @@ qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
        uint16_t new_config[4];
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                goto done_reset_internal;
 
        memset(new_config, 0 , sizeof(new_config));
@@ -710,8 +708,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
        elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
 
        if ((ha->current_topology == ISP_CFG_F ||
-           (atomic_read(&vha->loop_state) == LOOP_DOWN) ||
-           ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
+           ((IS_QLA81XX(ha) || IS_QLA8031(ha)) &&
            le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE
            && req_data_len == MAX_ELS_FRAME_PAYLOAD)) &&
                elreq.options == EXTERNAL_LOOPBACK) {
@@ -1402,6 +1399,9 @@ qla2x00_update_optrom(struct fc_bsg_job *bsg_job)
        if (rval)
                return rval;
 
+       /* Set the isp82xx_no_md_cap not to capture minidump */
+       ha->flags.isp82xx_no_md_cap = 1;
+
        sg_copy_to_buffer(bsg_job->request_payload.sg_list,
            bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
            ha->optrom_region_size);
index 62324a1..fdee561 100644 (file)
  * ----------------------------------------------------------------------
  * |             Level            |   Last Value Used  |     Holes     |
  * ----------------------------------------------------------------------
- * | Module Init and Probe        |       0x0120       | 0x4b,0xba,0xfa |
- * | Mailbox commands             |       0x113e       | 0x112c-0x112e  |
+ * | Module Init and Probe        |       0x0122       | 0x4b,0xba,0xfa |
+ * | Mailbox commands             |       0x1140       | 0x111a-0x111b  |
+ * |                              |                    | 0x112c-0x112e  |
  * |                              |                    | 0x113a         |
  * | Device Discovery             |       0x2086       | 0x2020-0x2022  |
  * | Queue Command and IO tracing |       0x3030       | 0x3006,0x3008  |
  * |                              |                    | 0x302d-0x302e  |
- * | DPC Thread                   |       0x401c       |               |
- * | Async Events                 |       0x505d       | 0x502b-0x502f  |
+ * | DPC Thread                   |       0x401c       | 0x4002,0x4013  |
+ * | Async Events                 |       0x505f       | 0x502b-0x502f  |
  * |                              |                    | 0x5047,0x5052  |
- * | Timer Routines               |       0x6011       | 0x600e-0x600f  |
+ * | Timer Routines               |       0x6011       |                |
  * | User Space Interactions      |       0x709f       | 0x7018,0x702e, |
  * |                              |                    | 0x7039,0x7045, |
  * |                              |                    | 0x7073-0x7075, |
  * |                              |                    | 0x708c         |
  * | Task Management              |       0x803c       | 0x8025-0x8026  |
  * |                              |                    | 0x800b,0x8039  |
- * | AER/EEH                      |       0x900f       |               |
+ * | AER/EEH                      |       0x9011       |               |
  * | Virtual Port                 |       0xa007       |               |
- * | ISP82XX Specific             |       0xb054       | 0xb053         |
+ * | ISP82XX Specific             |       0xb054       | 0xb024         |
  * | MultiQ                       |       0xc00c       |               |
  * | Misc                         |       0xd010       |               |
+ * | Target Mode                 |       0xe06f       |                |
+ * | Target Mode Management      |       0xf071       |                |
+ * | Target Mode Task Management  |      0x1000b      |                |
  * ----------------------------------------------------------------------
  */
 
@@ -378,6 +382,54 @@ qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
        return (char *)iter_reg + ntohl(fcec->size);
 }
 
+static inline void *
+qla2xxx_copy_atioqueues(struct qla_hw_data *ha, void *ptr,
+       uint32_t **last_chain)
+{
+       struct qla2xxx_mqueue_chain *q;
+       struct qla2xxx_mqueue_header *qh;
+       uint32_t num_queues;
+       int que;
+       struct {
+               int length;
+               void *ring;
+       } aq, *aqp;
+
+       if (!ha->tgt.atio_q_length)
+               return ptr;
+
+       num_queues = 1;
+       aqp = &aq;
+       aqp->length = ha->tgt.atio_q_length;
+       aqp->ring = ha->tgt.atio_ring;
+
+       for (que = 0; que < num_queues; que++) {
+               /* aqp = ha->atio_q_map[que]; */
+               q = ptr;
+               *last_chain = &q->type;
+               q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+               q->chain_size = htonl(
+                   sizeof(struct qla2xxx_mqueue_chain) +
+                   sizeof(struct qla2xxx_mqueue_header) +
+                   (aqp->length * sizeof(request_t)));
+               ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+               /* Add header. */
+               qh = ptr;
+               qh->queue = __constant_htonl(TYPE_ATIO_QUEUE);
+               qh->number = htonl(que);
+               qh->size = htonl(aqp->length * sizeof(request_t));
+               ptr += sizeof(struct qla2xxx_mqueue_header);
+
+               /* Add data. */
+               memcpy(ptr, aqp->ring, aqp->length * sizeof(request_t));
+
+               ptr += aqp->length * sizeof(request_t);
+       }
+
+       return ptr;
+}
+
 static inline void *
 qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
 {
@@ -873,6 +925,8 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        struct qla24xx_fw_dump *fw;
        uint32_t        ext_mem_cnt;
        void            *nxt;
+       void            *nxt_chain;
+       uint32_t        *last_chain = NULL;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 
        if (IS_QLA82XX(ha))
@@ -1091,6 +1145,16 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
 
        qla24xx_copy_eft(ha, nxt);
 
+       nxt_chain = (void *)ha->fw_dump + ha->chain_offset;
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
+       if (last_chain) {
+               ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+               *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+       }
+
+       /* Adjust valid length. */
+       ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
 qla24xx_fw_dump_failed_0:
        qla2xxx_dump_post_process(base_vha, rval);
 
@@ -1399,6 +1463,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
@@ -1717,6 +1782,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
@@ -2218,6 +2284,7 @@ copy_queue:
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
index 2157bdf..f278df8 100644 (file)
@@ -244,6 +244,7 @@ struct qla2xxx_mqueue_header {
        uint32_t queue;
 #define TYPE_REQUEST_QUEUE     0x1
 #define TYPE_RESPONSE_QUEUE    0x2
+#define TYPE_ATIO_QUEUE                0x3
        uint32_t number;
        uint32_t size;
 };
@@ -339,3 +340,11 @@ ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...);
 #define ql_dbg_misc    0x00010000 /* For dumping everything that is not
                                    * not covered by upper categories
                                    */
+#define ql_dbg_verbose 0x00008000 /* More verbosity for each level
+                                   * This is to be used with other levels where
+                                   * more verbosity is required. It might not
+                                   * be applicable to all the levels.
+                                   */
+#define ql_dbg_tgt     0x00004000 /* Target mode */
+#define ql_dbg_tgt_mgt 0x00002000 /* Target mode management */
+#define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */
index a244303..39007f5 100644 (file)
 #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.*/
+#define ATIO_ENTRY_CNT_24XX            4096    /* Number of ATIO entries. */
 
 struct req_que;
 
@@ -1234,11 +1235,27 @@ typedef struct {
  * ISP queue - response queue entry definition.
  */
 typedef struct {
-       uint8_t         data[60];
+       uint8_t         entry_type;             /* Entry type. */
+       uint8_t         entry_count;            /* Entry count. */
+       uint8_t         sys_define;             /* System defined. */
+       uint8_t         entry_status;           /* Entry Status. */
+       uint32_t        handle;                 /* System defined handle */
+       uint8_t         data[52];
        uint32_t        signature;
 #define RESPONSE_PROCESSED     0xDEADDEAD      /* Signature */
 } response_t;
 
+/*
+ * ISP queue - ATIO queue entry definition.
+ */
+struct atio {
+       uint8_t         entry_type;             /* Entry type. */
+       uint8_t         entry_count;            /* Entry count. */
+       uint8_t         data[58];
+       uint32_t        signature;
+#define ATIO_PROCESSED 0xDEADDEAD              /* Signature */
+};
+
 typedef union {
        uint16_t extended;
        struct {
@@ -1719,11 +1736,13 @@ typedef struct fc_port {
        struct fc_rport *rport, *drport;
        u32 supported_classes;
 
-       uint16_t vp_idx;
        uint8_t fc4_type;
        uint8_t scan_state;
 } fc_port_t;
 
+#define QLA_FCPORT_SCAN_NONE   0
+#define QLA_FCPORT_SCAN_FOUND  1
+
 /*
  * Fibre channel port/lun states.
  */
@@ -1747,6 +1766,7 @@ static const char * const port_state_str[] = {
 #define FCF_LOGIN_NEEDED       BIT_1
 #define FCF_FCP2_DEVICE                BIT_2
 #define FCF_ASYNC_SENT         BIT_3
+#define FCF_CONF_COMP_SUPPORTED BIT_4
 
 /* No loop ID flag. */
 #define FC_NO_LOOP_ID          0x1000
@@ -2419,6 +2439,40 @@ struct qlfc_fw {
        uint32_t len;
 };
 
+struct qlt_hw_data {
+       /* Protected by hw lock */
+       uint32_t enable_class_2:1;
+       uint32_t enable_explicit_conf:1;
+       uint32_t ini_mode_force_reverse:1;
+       uint32_t node_name_set:1;
+
+       dma_addr_t atio_dma;    /* Physical address. */
+       struct atio *atio_ring; /* Base virtual address */
+       struct atio *atio_ring_ptr;     /* Current address. */
+       uint16_t atio_ring_index; /* Current index. */
+       uint16_t atio_q_length;
+
+       void *target_lport_ptr;
+       struct qla_tgt_func_tmpl *tgt_ops;
+       struct qla_tgt *qla_tgt;
+       struct qla_tgt_cmd *cmds[MAX_OUTSTANDING_COMMANDS];
+       uint16_t current_handle;
+
+       struct qla_tgt_vp_map *tgt_vp_map;
+       struct mutex tgt_mutex;
+       struct mutex tgt_host_action_mutex;
+
+       int saved_set;
+       uint16_t saved_exchange_count;
+       uint32_t saved_firmware_options_1;
+       uint32_t saved_firmware_options_2;
+       uint32_t saved_firmware_options_3;
+       uint8_t saved_firmware_options[2];
+       uint8_t saved_add_firmware_options[2];
+
+       uint8_t tgt_node_name[WWN_SIZE];
+};
+
 /*
  * Qlogic host adapter specific data structure.
 */
@@ -2460,7 +2514,9 @@ struct qla_hw_data {
                uint32_t        thermal_supported:1;
                uint32_t        isp82xx_reset_hdlr_active:1;
                uint32_t        isp82xx_reset_owner:1;
-               /* 28 bits */
+               uint32_t        isp82xx_no_md_cap:1;
+               uint32_t        host_shutting_down:1;
+               /* 30 bits */
        } flags;
 
        /* This spinlock is used to protect "io transactions", you must
@@ -2804,7 +2860,6 @@ struct qla_hw_data {
                                        /* ISP2322: red, green, amber. */
        uint16_t        zio_mode;
        uint16_t        zio_timer;
-       struct fc_host_statistics fc_host_stat;
 
        struct qla_msix_entry *msix_entries;
 
@@ -2817,7 +2872,6 @@ struct qla_hw_data {
        int             cur_vport_count;
 
        struct qla_chip_state_84xx *cs84xx;
-       struct qla_statistics qla_stats;
        struct isp_operations *isp_ops;
        struct workqueue_struct *wq;
        struct qlfc_fw fw_buf;
@@ -2863,6 +2917,8 @@ struct qla_hw_data {
        dma_addr_t      md_tmplt_hdr_dma;
        void            *md_dump;
        uint32_t        md_dump_size;
+
+       struct qlt_hw_data tgt;
 };
 
 /*
@@ -2920,6 +2976,7 @@ typedef struct scsi_qla_host {
 #define FCOE_CTX_RESET_NEEDED  18      /* Initiate FCoE context reset */
 #define MPI_RESET_NEEDED       19      /* Initiate MPI FW reset */
 #define ISP_QUIESCE_NEEDED     20      /* Driver need some quiescence */
+#define SCR_PENDING            21      /* SCR in target mode */
 
        uint32_t        device_flags;
 #define SWITCH_FOUND           BIT_0
@@ -2979,10 +3036,21 @@ typedef struct scsi_qla_host {
        struct req_que *req;
        int             fw_heartbeat_counter;
        int             seconds_since_last_heartbeat;
+       struct fc_host_statistics fc_host_stat;
+       struct qla_statistics qla_stats;
 
        atomic_t        vref_count;
 } scsi_qla_host_t;
 
+#define SET_VP_IDX     1
+#define SET_AL_PA      2
+#define RESET_VP_IDX   3
+#define RESET_AL_PA    4
+struct qla_tgt_vp_map {
+       uint8_t idx;
+       scsi_qla_host_t *vha;
+};
+
 /*
  * Macros to help code, maintain, etc.
  */
index 9f06580..9eacd2d 100644 (file)
@@ -175,6 +175,7 @@ extern int  qla2x00_vp_abort_isp(scsi_qla_host_t *);
 /*
  * Global Function Prototypes in qla_iocb.c source file.
  */
+
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
@@ -188,6 +189,8 @@ extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t);
 extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
 extern int qla24xx_dif_start_scsi(srb_t *);
 
+extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *);
+extern int qla2x00_issue_marker(scsi_qla_host_t *, int);
 
 /*
  * Global Function Prototypes in qla_mbx.c source file.
@@ -238,6 +241,9 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
 extern int
 qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
 
+extern int
+qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *);
+
 extern int
 qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
 
@@ -383,6 +389,8 @@ extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
 extern void qla2x00_free_irqs(scsi_qla_host_t *);
 
 extern int qla2x00_get_data_rate(scsi_qla_host_t *);
+extern char *qla2x00_get_link_speed_str(struct qla_hw_data *);
+
 /*
  * Global Function Prototypes in qla_sup.c source file.
  */
@@ -546,6 +554,7 @@ extern void qla2x00_sp_free(void *, void *);
 extern void qla2x00_sp_timeout(unsigned long);
 extern void qla2x00_bsg_job_done(void *, void *, int);
 extern void qla2x00_bsg_sp_free(void *, void *);
+extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *);
 
 /* Interrupt related */
 extern irqreturn_t qla82xx_intr_handler(int, void *);
index 3128f80..05260d2 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *);
 static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *);
@@ -556,7 +557,8 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
        ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
        ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
 
-       ct_req->req.rff_id.fc4_feature = BIT_1;
+       qlt_rff_id(vha, ct_req);
+
        ct_req->req.rff_id.fc4_type = 0x08;             /* SCSI - FCP */
 
        /* Execute MS IOCB */
index b946564..ca50847 100644 (file)
@@ -17,6 +17,9 @@
 #include <asm/prom.h>
 #endif
 
+#include <target/target_core_base.h>
+#include "qla_target.h"
+
 /*
 *  QLogic ISP2x00 Hardware Support Function Prototypes.
 */
@@ -518,7 +521,10 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
                        return QLA_FUNCTION_FAILED;
                }
        }
-       rval = qla2x00_init_rings(vha);
+
+       if (qla_ini_mode_enabled(vha))
+               rval = qla2x00_init_rings(vha);
+
        ha->flags.chip_reset_done = 1;
 
        if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) {
@@ -1233,6 +1239,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
                        mq_size += ha->max_rsp_queues *
                            (rsp->length * sizeof(response_t));
                }
+               if (ha->tgt.atio_q_length)
+                       mq_size += ha->tgt.atio_q_length * sizeof(request_t);
                /* Allocate memory for Fibre Channel Event Buffer. */
                if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
                        goto try_eft;
@@ -1696,6 +1704,12 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
        icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
        icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
 
+       /* Setup ATIO queue dma pointers for target mode */
+       icb->atio_q_inpointer = __constant_cpu_to_le16(0);
+       icb->atio_q_length = cpu_to_le16(ha->tgt.atio_q_length);
+       icb->atio_q_address[0] = cpu_to_le32(LSD(ha->tgt.atio_dma));
+       icb->atio_q_address[1] = cpu_to_le32(MSD(ha->tgt.atio_dma));
+
        if (ha->mqenable || IS_QLA83XX(ha)) {
                icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
                icb->rid = __constant_cpu_to_le16(rid);
@@ -1739,6 +1753,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
                WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
                WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
        }
+       qlt_24xx_config_rings(vha, reg);
+
        /* PCI posting */
        RD_REG_DWORD(&ioreg->hccr);
 }
@@ -1794,6 +1810,11 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 
        spin_unlock(&ha->vport_slock);
 
+       ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
+       ha->tgt.atio_ring_index = 0;
+       /* Initialize ATIO queue entries */
+       qlt_init_atio_q_entries(vha);
+
        ha->isp_ops->config_rings(vha);
 
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -2051,6 +2072,10 @@ qla2x00_configure_hba(scsi_qla_host_t *vha)
        vha->d_id.b.area = area;
        vha->d_id.b.al_pa = al_pa;
 
+       spin_lock(&ha->vport_slock);
+       qlt_update_vp_map(vha, SET_AL_PA);
+       spin_unlock(&ha->vport_slock);
+
        if (!vha->flags.init_done)
                ql_log(ql_log_info, vha, 0x2010,
                    "Topology - %s, Host Loop address 0x%x.\n",
@@ -2185,7 +2210,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
            nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
                /* Reset NVRAM data. */
                ql_log(ql_log_warn, vha, 0x0064,
-                   "Inconisistent NVRAM "
+                   "Inconsistent NVRAM "
                    "detected: checksum=0x%x id=%c version=0x%x.\n",
                    chksum, nv->id[0], nv->nvram_version);
                ql_log(ql_log_warn, vha, 0x0065,
@@ -2270,7 +2295,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
        if (IS_QLA23XX(ha)) {
                nv->firmware_options[0] |= BIT_2;
                nv->firmware_options[0] &= ~BIT_3;
-               nv->firmware_options[0] &= ~BIT_6;
+               nv->special_options[0] &= ~BIT_6;
                nv->add_firmware_options[1] |= BIT_5 | BIT_4;
 
                if (IS_QLA2300(ha)) {
@@ -2467,14 +2492,21 @@ qla2x00_rport_del(void *data)
 {
        fc_port_t *fcport = data;
        struct fc_rport *rport;
+       scsi_qla_host_t *vha = fcport->vha;
        unsigned long flags;
 
        spin_lock_irqsave(fcport->vha->host->host_lock, flags);
        rport = fcport->drport ? fcport->drport: fcport->rport;
        fcport->drport = NULL;
        spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
-       if (rport)
+       if (rport) {
                fc_remote_port_delete(rport);
+               /*
+                * Release the target mode FC NEXUS in qla_target.c code
+                * if target mod is enabled.
+                */
+               qlt_fc_port_deleted(vha, fcport);
+       }
 }
 
 /**
@@ -2495,11 +2527,11 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
 
        /* Setup fcport template structure. */
        fcport->vha = vha;
-       fcport->vp_idx = vha->vp_idx;
        fcport->port_type = FCT_UNKNOWN;
        fcport->loop_id = FC_NO_LOOP_ID;
        qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
        fcport->supported_classes = FC_COS_UNSPECIFIED;
+       fcport->scan_state = QLA_FCPORT_SCAN_NONE;
 
        return fcport;
 }
@@ -2726,7 +2758,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
                new_fcport->d_id.b.area = area;
                new_fcport->d_id.b.al_pa = al_pa;
                new_fcport->loop_id = loop_id;
-               new_fcport->vp_idx = vha->vp_idx;
                rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
                if (rval2 != QLA_SUCCESS) {
                        ql_dbg(ql_dbg_disc, vha, 0x201a,
@@ -2760,10 +2791,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
 
                if (!found) {
                        /* New device, add to fcports list. */
-                       if (vha->vp_idx) {
-                               new_fcport->vha = vha;
-                               new_fcport->vp_idx = vha->vp_idx;
-                       }
                        list_add_tail(&new_fcport->list, &vha->vp_fcports);
 
                        /* Allocate a new replacement fcport. */
@@ -2800,8 +2827,6 @@ cleanup_allocation:
 static void
 qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
 {
-#define LS_UNKNOWN      2
-       static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
        char *link_speed;
        int rval;
        uint16_t mb[4];
@@ -2829,11 +2854,7 @@ 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];
+               link_speed = qla2x00_get_link_speed_str(ha);
                ql_dbg(ql_dbg_disc, vha, 0x2005,
                    "iIDMA adjusted to %s GB/s "
                    "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed,
@@ -2864,6 +2885,12 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
                    "Unable to allocate fc remote port.\n");
                return;
        }
+       /*
+        * Create target mode FC NEXUS in qla_target.c if target mode is
+        * enabled..
+        */
+       qlt_fc_port_added(vha, fcport);
+
        spin_lock_irqsave(fcport->vha->host->host_lock, flags);
        *((fc_port_t **)rport->dd_data) = fcport;
        spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
@@ -2921,7 +2948,7 @@ static int
 qla2x00_configure_fabric(scsi_qla_host_t *vha)
 {
        int     rval;
-       fc_port_t       *fcport, *fcptemp;
+       fc_port_t       *fcport;
        uint16_t        next_loopid;
        uint16_t        mb[MAILBOX_REGISTER_COUNT];
        uint16_t        loop_id;
@@ -2959,7 +2986,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                    0xfc, mb, BIT_1|BIT_0);
                if (rval != QLA_SUCCESS) {
                        set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
-                       return rval;
+                       break;
                }
                if (mb[0] != MBS_COMMAND_COMPLETE) {
                        ql_dbg(ql_dbg_disc, vha, 0x2042,
@@ -2991,21 +3018,16 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                        }
                }
 
-#define QLA_FCPORT_SCAN                1
-#define QLA_FCPORT_FOUND       2
-
-               list_for_each_entry(fcport, &vha->vp_fcports, list) {
-                       fcport->scan_state = QLA_FCPORT_SCAN;
-               }
-
                rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
                if (rval != QLA_SUCCESS)
                        break;
 
-               /*
-                * Logout all previous fabric devices marked lost, except
-                * FCP2 devices.
-                */
+               /* Add new ports to existing port list */
+               list_splice_tail_init(&new_fcports, &vha->vp_fcports);
+
+               /* Starting free loop ID. */
+               next_loopid = ha->min_external_loopid;
+
                list_for_each_entry(fcport, &vha->vp_fcports, list) {
                        if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
                                break;
@@ -3013,7 +3035,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                        if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
                                continue;
 
-                       if (fcport->scan_state == QLA_FCPORT_SCAN &&
+                       /* Logout lost/gone fabric devices (non-FCP2) */
+                       if (fcport->scan_state != QLA_FCPORT_SCAN_FOUND &&
                            atomic_read(&fcport->state) == FCS_ONLINE) {
                                qla2x00_mark_device_lost(vha, fcport,
                                    ql2xplogiabsentdevice, 0);
@@ -3026,78 +3049,30 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                                            fcport->d_id.b.domain,
                                            fcport->d_id.b.area,
                                            fcport->d_id.b.al_pa);
-                                       fcport->loop_id = FC_NO_LOOP_ID;
                                }
-                       }
-               }
-
-               /* Starting free loop ID. */
-               next_loopid = ha->min_external_loopid;
-
-               /*
-                * Scan through our port list and login entries that need to be
-                * logged in.
-                */
-               list_for_each_entry(fcport, &vha->vp_fcports, list) {
-                       if (atomic_read(&vha->loop_down_timer) ||
-                           test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
-                               break;
-
-                       if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
-                           (fcport->flags & FCF_LOGIN_NEEDED) == 0)
                                continue;
-
-                       if (fcport->loop_id == FC_NO_LOOP_ID) {
-                               fcport->loop_id = next_loopid;
-                               rval = qla2x00_find_new_loop_id(
-                                   base_vha, fcport);
-                               if (rval != QLA_SUCCESS) {
-                                       /* Ran out of IDs to use */
-                                       break;
-                               }
                        }
-                       /* Login and update database */
-                       qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
-               }
-
-               /* Exit if out of loop IDs. */
-               if (rval != QLA_SUCCESS) {
-                       break;
-               }
-
-               /*
-                * Login and add the new devices to our port list.
-                */
-               list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
-                       if (atomic_read(&vha->loop_down_timer) ||
-                           test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
-                               break;
-
-                       /* Find a new loop ID to use. */
-                       fcport->loop_id = next_loopid;
-                       rval = qla2x00_find_new_loop_id(base_vha, fcport);
-                       if (rval != QLA_SUCCESS) {
-                               /* Ran out of IDs to use */
-                               break;
+                       fcport->scan_state = QLA_FCPORT_SCAN_NONE;
+
+                       /* Login fabric devices that need a login */
+                       if ((fcport->flags & FCF_LOGIN_NEEDED) != 0 &&
+                           atomic_read(&vha->loop_down_timer) == 0) {
+                               if (fcport->loop_id == FC_NO_LOOP_ID) {
+                                       fcport->loop_id = next_loopid;
+                                       rval = qla2x00_find_new_loop_id(
+                                           base_vha, fcport);
+                                       if (rval != QLA_SUCCESS) {
+                                               /* Ran out of IDs to use */
+                                               continue;
+                                       }
+                               }
                        }
 
                        /* Login and update database */
                        qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
-
-                       if (vha->vp_idx) {
-                               fcport->vha = vha;
-                               fcport->vp_idx = vha->vp_idx;
-                       }
-                       list_move_tail(&fcport->list, &vha->vp_fcports);
                }
        } while (0);
 
-       /* Free all new device structures not processed. */
-       list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
-               list_del(&fcport->list);
-               kfree(fcport);
-       }
-
        if (rval) {
                ql_dbg(ql_dbg_disc, vha, 0x2068,
                    "Configure fabric error exit rval=%d.\n", rval);
@@ -3287,7 +3262,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
                            WWN_SIZE))
                                continue;
 
-                       fcport->scan_state = QLA_FCPORT_FOUND;
+                       fcport->scan_state = QLA_FCPORT_SCAN_FOUND;
 
                        found++;
 
@@ -3595,6 +3570,12 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
                        if (mb[10] & BIT_1)
                                fcport->supported_classes |= FC_COS_CLASS3;
 
+                       if (IS_FWI2_CAPABLE(ha)) {
+                               if (mb[10] & BIT_7)
+                                       fcport->flags |=
+                                           FCF_CONF_COMP_SUPPORTED;
+                       }
+
                        rval = QLA_SUCCESS;
                        break;
                } else if (mb[0] == MBS_LOOP_ID_USED) {
@@ -3841,7 +3822,7 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
                vha->flags.online = 0;
        ha->flags.chip_reset_done = 0;
        clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
-       ha->qla_stats.total_isp_aborts++;
+       vha->qla_stats.total_isp_aborts++;
 
        ql_log(ql_log_info, vha, 0x00af,
            "Performing ISP error recovery - ha=%p.\n", ha);
@@ -4066,6 +4047,7 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = ha->req_q_map[0];
        struct rsp_que *rsp = ha->rsp_q_map[0];
+       unsigned long flags;
 
        /* If firmware needs to be loaded */
        if (qla2x00_isp_firmware(vha)) {
@@ -4090,6 +4072,16 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
                        qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
 
                        vha->flags.online = 1;
+
+                       /*
+                        * Process any ATIO queue entries that came in
+                        * while we weren't online.
+                        */
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       if (qla_tgt_mode_enabled(vha))
+                               qlt_24xx_process_atio_queue(vha);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
                        /* Wait at most MAX_TARGET RSCNs for a stable link. */
                        wait_time = 256;
                        do {
@@ -4279,7 +4271,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
            nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
                /* Reset NVRAM data. */
                ql_log(ql_log_warn, vha, 0x006b,
-                   "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+                   "Inconsistent NVRAM detected: checksum=0x%x id=%c "
                    "version=0x%x.\n", chksum, nv->id[0], nv->nvram_version);
                ql_log(ql_log_warn, vha, 0x006c,
                    "Falling back to functioning (yet invalid -- WWPN) "
@@ -4330,6 +4322,15 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
                rval = 1;
        }
 
+       if (!qla_ini_mode_enabled(vha)) {
+               /* Don't enable full login after initial LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13);
+               /* Don't enable LIP full login for initiator */
+               nv->host_p &= __constant_cpu_to_le32(~BIT_10);
+       }
+
+       qlt_24xx_config_nvram_stage1(vha, nv);
+
        /* Reset Initialization control block */
        memset(icb, 0, ha->init_cb_size);
 
@@ -4357,8 +4358,10 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
        qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
            "QLA2462");
 
-       /* Use alternate WWN? */
+       qlt_24xx_config_nvram_stage2(vha, icb);
+
        if (nv->host_p & __constant_cpu_to_le32(BIT_15)) {
+               /* Use alternate WWN? */
                memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
                memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
        }
@@ -5029,7 +5032,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
            nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
                /* Reset NVRAM data. */
                ql_log(ql_log_info, vha, 0x0073,
-                   "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+                   "Inconsistent NVRAM detected: checksum=0x%x id=%c "
                    "version=0x%x.\n", chksum, nv->id[0],
                    le16_to_cpu(nv->nvram_version));
                ql_log(ql_log_info, vha, 0x0074,
index eac9509..70dbf53 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/blkdev.h>
 #include <linux/delay.h>
@@ -23,18 +24,17 @@ qla2x00_get_cmd_direction(srb_t *sp)
 {
        uint16_t cflags;
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+       struct scsi_qla_host *vha = sp->fcport->vha;
 
        cflags = 0;
 
        /* Set transfer direction */
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cflags = CF_WRITE;
-               sp->fcport->vha->hw->qla_stats.output_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cflags = CF_READ;
-               sp->fcport->vha->hw->qla_stats.input_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
        return (cflags);
 }
@@ -385,9 +385,10 @@ qla2x00_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                            (req->ring_index - cnt);
+               /* If still no head room then bail out */
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
 
        /* Build command packet */
        req->current_outstanding_cmd = handle;
@@ -470,7 +471,7 @@ queuing_error:
 /**
  * qla2x00_start_iocbs() - Execute the IOCB command
  */
-static void
+void
 qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
 {
        struct qla_hw_data *ha = vha->hw;
@@ -571,6 +572,29 @@ qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
        return (ret);
 }
 
+/*
+ * qla2x00_issue_marker
+ *
+ * Issue marker
+ * Caller CAN have hardware lock held as specified by ha_locked parameter.
+ * Might release it, then reaquire.
+ */
+int qla2x00_issue_marker(scsi_qla_host_t *vha, int ha_locked)
+{
+       if (ha_locked) {
+               if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+                                       MK_SYNC_ALL) != QLA_SUCCESS)
+                       return QLA_FUNCTION_FAILED;
+       } else {
+               if (qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+                                       MK_SYNC_ALL) != QLA_SUCCESS)
+                       return QLA_FUNCTION_FAILED;
+       }
+       vha->marker_needed = 0;
+
+       return QLA_SUCCESS;
+}
+
 /**
  * qla24xx_calc_iocbs() - Determine number of Command Type 3 and
  * Continuation Type 1 IOCBs to allocate.
@@ -629,11 +653,11 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cmd_pkt->control_flags =
                    __constant_cpu_to_le16(CF_WRITE_DATA);
-               ha->qla_stats.output_bytes += scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cmd_pkt->control_flags =
                    __constant_cpu_to_le16(CF_READ_DATA);
-               ha->qla_stats.input_bytes += scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
 
        cur_seg = scsi_sglist(cmd);
@@ -745,13 +769,11 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cmd_pkt->task_mgmt_flags =
                    __constant_cpu_to_le16(TMF_WRITE_DATA);
-               sp->fcport->vha->hw->qla_stats.output_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cmd_pkt->task_mgmt_flags =
                    __constant_cpu_to_le16(TMF_READ_DATA);
-               sp->fcport->vha->hw->qla_stats.input_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
 
        /* One DSD is available in the Command Type 3 IOCB */
@@ -1245,7 +1267,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
                return QLA_SUCCESS;
        }
 
-       cmd_pkt->vp_index = sp->fcport->vp_idx;
+       cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
        /* Set transfer direction */
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
@@ -1502,9 +1524,9 @@ qla24xx_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                                (req->ring_index - cnt);
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
 
        /* Build command packet. */
        req->current_outstanding_cmd = handle;
@@ -1527,7 +1549,7 @@ qla24xx_start_scsi(srb_t *sp)
        cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
        cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
        cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-       cmd_pkt->vp_index = sp->fcport->vp_idx;
+       cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
        int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
        host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -1717,11 +1739,10 @@ qla24xx_dif_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                                (req->ring_index - cnt);
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
 
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
-
        status |= QDSS_GOT_Q_SPACE;
 
        /* Build header part of command packet (excluding the OPCODE). */
@@ -1898,7 +1919,7 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->port_id[0] = sp->fcport->d_id.b.al_pa;
        logio->port_id[1] = sp->fcport->d_id.b.area;
        logio->port_id[2] = sp->fcport->d_id.b.domain;
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1922,7 +1943,7 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
        mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
            sp->fcport->d_id.b.al_pa);
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
 }
 
 static void
@@ -1935,7 +1956,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->port_id[0] = sp->fcport->d_id.b.al_pa;
        logio->port_id[1] = sp->fcport->d_id.b.area;
        logio->port_id[2] = sp->fcport->d_id.b.domain;
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1952,7 +1973,7 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
        mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
            sp->fcport->d_id.b.al_pa);
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
        /* Implicit: mbx->mbx10 = 0. */
 }
 
@@ -1962,7 +1983,7 @@ qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
        logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC);
        logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1983,7 +2004,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma));
        mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma)));
        mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma)));
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
 }
 
 static void
@@ -2009,7 +2030,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
        tsk->port_id[0] = fcport->d_id.b.al_pa;
        tsk->port_id[1] = fcport->d_id.b.area;
        tsk->port_id[2] = fcport->d_id.b.domain;
-       tsk->vp_index = fcport->vp_idx;
+       tsk->vp_index = fcport->vha->vp_idx;
 
        if (flags == TCF_LUN_RESET) {
                int_to_scsilun(lun, &tsk->lun);
@@ -2030,7 +2051,7 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
         els_iocb->handle = sp->handle;
         els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
         els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
-        els_iocb->vp_index = sp->fcport->vp_idx;
+       els_iocb->vp_index = sp->fcport->vha->vp_idx;
         els_iocb->sof_type = EST_SOFI3;
         els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
 
@@ -2160,7 +2181,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
         ct_iocb->handle = sp->handle;
 
        ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-       ct_iocb->vp_index = sp->fcport->vp_idx;
+       ct_iocb->vp_index = sp->fcport->vha->vp_idx;
         ct_iocb->comp_status = __constant_cpu_to_le16(0);
 
        ct_iocb->cmd_dsd_count =
@@ -2343,11 +2364,10 @@ sufficient_dsds:
                        else
                                req->cnt = req->length -
                                        (req->ring_index - cnt);
+                       if (req->cnt < (req_cnt + 2))
+                               goto queuing_error;
                }
 
-               if (req->cnt < (req_cnt + 2))
-                       goto queuing_error;
-
                ctx = sp->u.scmd.ctx =
                    mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
                if (!ctx) {
@@ -2362,7 +2382,7 @@ sufficient_dsds:
                if (!ctx->fcp_cmnd) {
                        ql_log(ql_log_fatal, vha, 0x3011,
                            "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
-                       goto queuing_error_fcp_cmnd;
+                       goto queuing_error;
                }
 
                /* Initialize the DSD list and dma handle */
@@ -2400,7 +2420,7 @@ sufficient_dsds:
                cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
                cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
                cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-               cmd_pkt->vp_index = sp->fcport->vp_idx;
+               cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
                /* Build IOCB segments */
                if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds))
@@ -2489,7 +2509,7 @@ sufficient_dsds:
                cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
                cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
                cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-               cmd_pkt->vp_index = sp->fcport->vp_idx;
+               cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
                int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
                host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
index ce42288..6f67a9d 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -309,6 +310,28 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
                    "IDC failed to post ACK.\n");
 }
 
+#define LS_UNKNOWN     2
+char *
+qla2x00_get_link_speed_str(struct qla_hw_data *ha)
+{
+       static char *link_speeds[] = {"1", "2", "?", "4", "8", "16", "10"};
+       char *link_speed;
+       int fw_speed = ha->link_data_rate;
+
+       if (IS_QLA2100(ha) || IS_QLA2200(ha))
+               link_speed = link_speeds[0];
+       else if (fw_speed == 0x13)
+               link_speed = link_speeds[6];
+       else {
+               link_speed = link_speeds[LS_UNKNOWN];
+               if (fw_speed < 6)
+                       link_speed =
+                           link_speeds[fw_speed];
+       }
+
+       return link_speed;
+}
+
 /**
  * qla2x00_async_event() - Process aynchronous events.
  * @ha: SCSI driver HA context
@@ -317,9 +340,6 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
 void
 qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
 {
-#define LS_UNKNOWN     2
-       static char *link_speeds[] = { "1", "2", "?", "4", "8", "16", "10" };
-       char            *link_speed;
        uint16_t        handle_cnt;
        uint16_t        cnt, mbx;
        uint32_t        handles[5];
@@ -454,8 +474,8 @@ skip_rio:
        case MBA_WAKEUP_THRES:          /* Request Queue Wake-up */
                ql_dbg(ql_dbg_async, vha, 0x5008,
                    "Asynchronous WAKEUP_THRES.\n");
-               break;
 
+               break;
        case MBA_LIP_OCCURRED:          /* Loop Initialization Procedure */
                ql_dbg(ql_dbg_async, vha, 0x5009,
                    "LIP occurred (%x).\n", mb[1]);
@@ -479,20 +499,14 @@ skip_rio:
                break;
 
        case MBA_LOOP_UP:               /* Loop Up Event */
-               if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-                       link_speed = link_speeds[0];
+               if (IS_QLA2100(ha) || IS_QLA2200(ha))
                        ha->link_data_rate = PORT_SPEED_1GB;
-               } else {
-                       link_speed = link_speeds[LS_UNKNOWN];
-                       if (mb[1] < 6)
-                               link_speed = link_speeds[mb[1]];
-                       else if (mb[1] == 0x13)
-                               link_speed = link_speeds[6];
+               else
                        ha->link_data_rate = mb[1];
-               }
 
                ql_dbg(ql_dbg_async, vha, 0x500a,
-                   "LOOP UP detected (%s Gbps).\n", link_speed);
+                   "LOOP UP detected (%s Gbps).\n",
+                   qla2x00_get_link_speed_str(ha));
 
                vha->flags.management_server_logged_in = 0;
                qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
@@ -638,6 +652,8 @@ skip_rio:
                        ql_dbg(ql_dbg_async, vha, 0x5010,
                            "Port unavailable %04x %04x %04x.\n",
                            mb[1], mb[2], mb[3]);
+                       ql_log(ql_log_warn, vha, 0x505e,
+                           "Link is offline.\n");
 
                        if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
                                atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -670,12 +686,17 @@ skip_rio:
                        ql_dbg(ql_dbg_async, vha, 0x5011,
                            "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
                            mb[1], mb[2], mb[3]);
+
+                       qlt_async_event(mb[0], vha, mb);
                        break;
                }
 
                ql_dbg(ql_dbg_async, vha, 0x5012,
                    "Port database changed %04x %04x %04x.\n",
                    mb[1], mb[2], mb[3]);
+               ql_log(ql_log_warn, vha, 0x505f,
+                   "Link is operational (%s Gbps).\n",
+                   qla2x00_get_link_speed_str(ha));
 
                /*
                 * Mark all devices as missing so we will login again.
@@ -684,8 +705,13 @@ skip_rio:
 
                qla2x00_mark_all_devices_lost(vha, 1);
 
+               if (vha->vp_idx == 0 && !qla_ini_mode_enabled(vha))
+                       set_bit(SCR_PENDING, &vha->dpc_flags);
+
                set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
                set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+
+               qlt_async_event(mb[0], vha, mb);
                break;
 
        case MBA_RSCN_UPDATE:           /* State Change Registration */
@@ -807,6 +833,8 @@ skip_rio:
                    mb[0], mb[1], mb[2], mb[3]);
        }
 
+       qlt_async_event(mb[0], vha, mb);
+
        if (!vha->vp_idx && ha->num_vhosts)
                qla2x00_alert_all_vps(rsp, mb);
 }
@@ -1172,6 +1200,9 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
                } else if (iop[0] & BIT_5)
                        fcport->port_type = FCT_INITIATOR;
 
+               if (iop[0] & BIT_7)
+                       fcport->flags |= FCF_CONF_COMP_SUPPORTED;
+
                if (logio->io_parameter[7] || logio->io_parameter[8])
                        fcport->supported_classes |= FC_COS_CLASS2;
                if (logio->io_parameter[9] || logio->io_parameter[10])
@@ -1986,6 +2017,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 
                if (pkt->entry_status != 0) {
                        qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
+
+                       (void)qlt_24xx_process_response_error(vha, pkt);
+
                        ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
                        wmb();
                        continue;
@@ -2016,6 +2050,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
                 case ELS_IOCB_TYPE:
                        qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE);
                        break;
+               case ABTS_RECV_24XX:
+                       /* ensure that the ATIO queue is empty */
+                       qlt_24xx_process_atio_queue(vha);
+               case ABTS_RESP_24XX:
+               case CTIO_TYPE7:
+               case NOTIFY_ACK_TYPE:
+                       qlt_response_pkt_all_vps(vha, (response_t *)pkt);
+                       break;
                case MARKER_TYPE:
                        /* Do nothing in this case, this check is to prevent it
                         * from falling into default case
@@ -2168,6 +2210,13 @@ qla24xx_intr_handler(int irq, void *dev_id)
                case 0x14:
                        qla24xx_process_response_queue(vha, rsp);
                        break;
+               case 0x1C: /* ATIO queue updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       break;
+               case 0x1D: /* ATIO and response queues updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       qla24xx_process_response_queue(vha, rsp);
+                       break;
                default:
                        ql_dbg(ql_dbg_async, vha, 0x504f,
                            "Unrecognized interrupt type (%d).\n", stat * 0xff);
@@ -2312,6 +2361,13 @@ qla24xx_msix_default(int irq, void *dev_id)
                case 0x14:
                        qla24xx_process_response_queue(vha, rsp);
                        break;
+               case 0x1C: /* ATIO queue updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       break;
+               case 0x1D: /* ATIO and response queues updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       qla24xx_process_response_queue(vha, rsp);
+                       break;
                default:
                        ql_dbg(ql_dbg_async, vha, 0x5051,
                            "Unrecognized interrupt type (%d).\n", stat & 0xff);
@@ -2564,7 +2620,15 @@ void
 qla2x00_free_irqs(scsi_qla_host_t *vha)
 {
        struct qla_hw_data *ha = vha->hw;
-       struct rsp_que *rsp = ha->rsp_q_map[0];
+       struct rsp_que *rsp;
+
+       /*
+        * We need to check that ha->rsp_q_map is valid in case we are called
+        * from a probe failure context.
+        */
+       if (!ha->rsp_q_map || !ha->rsp_q_map[0])
+               return;
+       rsp = ha->rsp_q_map[0];
 
        if (ha->flags.msix_enabled)
                qla24xx_disable_msix(ha);
index b4a2339..d5ce92c 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/delay.h>
 #include <linux/gfp.h>
@@ -270,11 +271,8 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                        ictrl = RD_REG_WORD(&reg->isp.ictrl);
                }
                ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
-                   "MBX Command timeout for cmd %x.\n", command);
-               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111a,
-                   "iocontrol=%x jiffies=%lx.\n", ictrl, jiffies);
-               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111b,
-                   "mb[0] = 0x%x.\n", mb0);
+                   "MBX Command timeout for cmd %x, iocontrol=%x jiffies=%lx "
+                   "mb[0]=0x%x\n", command, ictrl, jiffies, mb0);
                ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
 
                /*
@@ -320,7 +318,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                                            CRB_NIU_XG_PAUSE_CTL_P1);
                                }
                                ql_log(ql_log_info, base_vha, 0x101c,
-                                   "Mailbox cmd timeout occured, cmd=0x%x, "
+                                   "Mailbox cmd timeout occurred, cmd=0x%x, "
                                    "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP "
                                    "abort.\n", command, mcp->mb[0],
                                    ha->flags.eeh_busy);
@@ -345,7 +343,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                                            CRB_NIU_XG_PAUSE_CTL_P1);
                                }
                                ql_log(ql_log_info, base_vha, 0x101e,
-                                   "Mailbox cmd timeout occured, cmd=0x%x, "
+                                   "Mailbox cmd timeout occurred, cmd=0x%x, "
                                    "mb[0]=0x%x. Scheduling ISP abort ",
                                    command, mcp->mb[0]);
                                set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
@@ -390,7 +388,8 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1022, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1022,
+           "Entered %s.\n", __func__);
 
        if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
                mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
@@ -424,7 +423,8 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
                ql_dbg(ql_dbg_mbx, vha, 0x1023,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1024, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -454,7 +454,8 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1025, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1025,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
        mcp->out_mb = MBX_0;
@@ -489,10 +490,11 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
                if (IS_FWI2_CAPABLE(ha)) {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1027,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1027,
                            "Done exchanges=%x.\n", mcp->mb[1]);
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1028, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1028,
+                           "Done %s.\n", __func__);
                }
        }
 
@@ -523,7 +525,8 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
        mbx_cmd_t       *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1029, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1029,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
        mcp->out_mb = MBX_0;
@@ -561,11 +564,11 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
                        ha->fw_attributes_h = mcp->mb[15];
                        ha->fw_attributes_ext[0] = mcp->mb[16];
                        ha->fw_attributes_ext[1] = mcp->mb[17];
-                       ql_dbg(ql_dbg_mbx, vha, 0x1139,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1139,
                            "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n",
                            __func__, mcp->mb[15], mcp->mb[6]);
                } else
-                       ql_dbg(ql_dbg_mbx, vha, 0x112f,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f,
                            "%s: FwAttributes [Upper]  invalid, MB6:%04x\n",
                            __func__, mcp->mb[6]);
        }
@@ -576,7 +579,8 @@ failed:
                ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x102b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102b,
+                   "Done %s.\n", __func__);
        }
        return rval;
 }
@@ -602,7 +606,8 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x102c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
        mcp->out_mb = MBX_0;
@@ -620,7 +625,8 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
                fwopts[2] = mcp->mb[2];
                fwopts[3] = mcp->mb[3];
 
-               ql_dbg(ql_dbg_mbx, vha, 0x102e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102e,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -648,7 +654,8 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x102f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102f,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
        mcp->mb[1] = fwopts[1];
@@ -676,7 +683,8 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
                    "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1031, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1031,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -704,7 +712,8 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1032, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1032,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
        mcp->mb[1] = 0xAAAA;
@@ -734,7 +743,8 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1034, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -762,7 +772,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1035, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1035,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_VERIFY_CHECKSUM;
        mcp->out_mb = MBX_0;
@@ -787,7 +798,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
                    "Failed=%x chm sum=%x.\n", rval, IS_FWI2_CAPABLE(vha->hw) ?
                    (mcp->mb[2] << 16) | mcp->mb[1] : mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1037, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1037,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -819,7 +831,8 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
        mbx_cmd_t       mc;
        mbx_cmd_t       *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1038, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1038,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_IOCB_COMMAND_A64;
        mcp->mb[1] = 0;
@@ -842,7 +855,8 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
                /* Mask reserved bits. */
                sts_entry->entry_status &=
                    IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
-               ql_dbg(ql_dbg_mbx, vha, 0x103a, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103a,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -884,7 +898,8 @@ qla2x00_abort_command(srb_t *sp)
        struct req_que *req = vha->req;
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x103b, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103b,
+           "Entered %s.\n", __func__);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -915,7 +930,8 @@ qla2x00_abort_command(srb_t *sp)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x103d, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103d,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -934,7 +950,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
        l = l;
        vha = fcport->vha;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x103e, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103e,
+           "Entered %s.\n", __func__);
 
        req = vha->hw->req_q_map[0];
        rsp = req->rsp;
@@ -955,7 +972,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(vha, mcp);
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x103f, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103f,
+                   "Failed=%x.\n", rval);
        }
 
        /* Issue marker IOCB. */
@@ -965,7 +983,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
                ql_dbg(ql_dbg_mbx, vha, 0x1040,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1041, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1041,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -983,7 +1002,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
 
        vha = fcport->vha;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1042, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1042,
+           "Entered %s.\n", __func__);
 
        req = vha->hw->req_q_map[0];
        rsp = req->rsp;
@@ -1012,7 +1032,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
                ql_dbg(ql_dbg_mbx, vha, 0x1044,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1045, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1045,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1046,7 +1067,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1046, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1046,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
        mcp->mb[9] = vha->vp_idx;
@@ -1074,7 +1096,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
                /*EMPTY*/
                ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1048, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1048,
+                   "Done %s.\n", __func__);
 
                if (IS_CNA_CAPABLE(vha->hw)) {
                        vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
@@ -1115,7 +1138,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1049, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1049,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_RETRY_COUNT;
        mcp->out_mb = MBX_0;
@@ -1138,7 +1162,7 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
                        *tov = ratov;
                }
 
-               ql_dbg(ql_dbg_mbx, vha, 0x104b,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104b,
                    "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov);
        }
 
@@ -1170,7 +1194,8 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x104c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104c,
+           "Entered %s.\n", __func__);
 
        if (IS_QLA82XX(ha) && ql2xdbwr)
                qla82xx_wr_32(ha, ha->nxdb_wr_ptr,
@@ -1213,9 +1238,100 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x104e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104e,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
+
+/*
+ * qla2x00_get_node_name_list
+ *      Issue get node name list mailbox command, kmalloc()
+ *      and return the resulting list. Caller must kfree() it!
+ *
+ * Input:
+ *      ha = adapter state pointer.
+ *      out_data = resulting list
+ *      out_len = length of the resulting list
+ *
+ * Returns:
+ *      qla2x00 local function return status code.
+ *
+ * Context:
+ *      Kernel context.
+ */
+int
+qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_port_24xx_data *list = NULL;
+       void *pmap;
+       mbx_cmd_t mc;
+       dma_addr_t pmap_dma;
+       ulong dma_size;
+       int rval, left;
+
+       left = 1;
+       while (left > 0) {
+               dma_size = left * sizeof(*list);
+               pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size,
+                                        &pmap_dma, GFP_KERNEL);
+               if (!pmap) {
+                       ql_log(ql_log_warn, vha, 0x113f,
+                           "%s(%ld): DMA Alloc failed of %ld\n",
+                           __func__, vha->host_no, dma_size);
+                       rval = QLA_MEMORY_ALLOC_FAILED;
+                       goto out;
+               }
+
+               mc.mb[0] = MBC_PORT_NODE_NAME_LIST;
+               mc.mb[1] = BIT_1 | BIT_3;
+               mc.mb[2] = MSW(pmap_dma);
+               mc.mb[3] = LSW(pmap_dma);
+               mc.mb[6] = MSW(MSD(pmap_dma));
+               mc.mb[7] = LSW(MSD(pmap_dma));
+               mc.mb[8] = dma_size;
+               mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8;
+               mc.in_mb = MBX_0|MBX_1;
+               mc.tov = 30;
+               mc.flags = MBX_DMA_IN;
+
+               rval = qla2x00_mailbox_command(vha, &mc);
+               if (rval != QLA_SUCCESS) {
+                       if ((mc.mb[0] == MBS_COMMAND_ERROR) &&
+                           (mc.mb[1] == 0xA)) {
+                               left += le16_to_cpu(mc.mb[2]) /
+                                   sizeof(struct qla_port_24xx_data);
+                               goto restart;
+                       }
+                       goto out_free;
+               }
+
+               left = 0;
+
+               list = kzalloc(dma_size, GFP_KERNEL);
+               if (!list) {
+                       ql_log(ql_log_warn, vha, 0x1140,
+                           "%s(%ld): failed to allocate node names list "
+                           "structure.\n", __func__, vha->host_no);
+                       rval = QLA_MEMORY_ALLOC_FAILED;
+                       goto out_free;
+               }
+
+               memcpy(list, pmap, dma_size);
+restart:
+               dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
        }
 
+       *out_data = list;
+       *out_len = dma_size;
+
+out:
+       return rval;
+
+out_free:
+       dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
        return rval;
 }
 
@@ -1246,7 +1362,8 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
        dma_addr_t pd_dma;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x104f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104f,
+           "Entered %s.\n", __func__);
 
        pd24 = NULL;
        pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
@@ -1326,6 +1443,13 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
                        fcport->port_type = FCT_INITIATOR;
                else
                        fcport->port_type = FCT_TARGET;
+
+               /* Passback COS information. */
+               fcport->supported_classes = (pd24->flags & PDF_CLASS_2) ?
+                               FC_COS_CLASS2 : FC_COS_CLASS3;
+
+               if (pd24->prli_svc_param_word_3[0] & BIT_7)
+                       fcport->flags |= FCF_CONF_COMP_SUPPORTED;
        } else {
                uint64_t zero = 0;
 
@@ -1378,7 +1502,8 @@ gpd_error_out:
                    "Failed=%x mb[0]=%x mb[1]=%x.\n", rval,
                    mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1053, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1053,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1407,7 +1532,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1054, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1054,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
        mcp->out_mb = MBX_0;
@@ -1433,7 +1559,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
                ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1056, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1056,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1465,7 +1592,8 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1057, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1057,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_PORT_NAME;
        mcp->mb[9] = vha->vp_idx;
@@ -1499,7 +1627,8 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
                        name[7] = LSB(mcp->mb[7]);
                }
 
-               ql_dbg(ql_dbg_mbx, vha, 0x1059, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1059,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1527,7 +1656,8 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105a, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105a,
+           "Entered %s.\n", __func__);
 
        if (IS_CNA_CAPABLE(vha->hw)) {
                /* Logout across all FCFs. */
@@ -1564,7 +1694,8 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x105c, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105c,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1596,9 +1727,10 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105d,
+           "Entered %s.\n", __func__);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105e,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105e,
            "Retry cnt=%d ratov=%d total tov=%d.\n",
            vha->hw->retry_count, vha->hw->login_timeout, mcp->tov);
 
@@ -1622,7 +1754,8 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1060, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1060,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1641,7 +1774,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        struct req_que *req;
        struct rsp_que *rsp;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1061, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1061,
+           "Entered %s.\n", __func__);
 
        if (ha->flags.cpu_affinity_enabled)
                req = ha->req_q_map[0];
@@ -1715,7 +1849,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                        break;
                }
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1066, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1066,
+                   "Done %s.\n", __func__);
 
                iop[0] = le32_to_cpu(lg->io_parameter[0]);
 
@@ -1733,6 +1868,10 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                        mb[10] |= BIT_0;        /* Class 2. */
                if (lg->io_parameter[9] || lg->io_parameter[10])
                        mb[10] |= BIT_1;        /* Class 3. */
+               if (lg->io_parameter[0] & __constant_cpu_to_le32(BIT_7))
+                       mb[10] |= BIT_7;        /* Confirmed Completion
+                                                * Allowed
+                                                */
        }
 
        dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1770,7 +1909,8 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1067, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
        mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -1818,7 +1958,8 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1069, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1849,7 +1990,8 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x106a, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106a,
+           "Entered %s.\n", __func__);
 
        if (IS_FWI2_CAPABLE(ha))
                return qla24xx_login_fabric(vha, fcport->loop_id,
@@ -1891,7 +2033,8 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x106c, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106c,
+                   "Done %s.\n", __func__);
        }
 
        return (rval);
@@ -1908,7 +2051,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        struct req_que *req;
        struct rsp_que *rsp;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x106d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d,
+           "Entered %s.\n", __func__);
 
        lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
        if (lg == NULL) {
@@ -1952,7 +2096,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    le32_to_cpu(lg->io_parameter[1]));
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1072, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1072,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1984,7 +2129,8 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1073, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1073,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
        mcp->out_mb = MBX_1|MBX_0;
@@ -2007,7 +2153,8 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1075, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1075,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2035,7 +2182,8 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1076, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1076,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LIP_FULL_LOGIN;
        mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
@@ -2052,7 +2200,8 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1078, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1078,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2078,7 +2227,8 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1079, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1079,
+           "Entered %s.\n", __func__);
 
        if (id_list == NULL)
                return QLA_FUNCTION_FAILED;
@@ -2110,7 +2260,8 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
                ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval);
        } else {
                *entries = mcp->mb[1];
-               ql_dbg(ql_dbg_mbx, vha, 0x107b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107b,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2138,7 +2289,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x107c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
        mcp->out_mb = MBX_0;
@@ -2154,7 +2306,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
                ql_dbg(ql_dbg_mbx, vha, 0x107d,
                    "Failed mb[0]=%x.\n", mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x107e,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107e,
                    "Done %s mb1=%x mb2=%x mb3=%x mb6=%x mb7=%x mb10=%x "
                    "mb11=%x mb12=%x.\n", __func__, mcp->mb[1], mcp->mb[2],
                    mcp->mb[3], mcp->mb[6], mcp->mb[7], mcp->mb[10],
@@ -2201,7 +2353,8 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        dma_addr_t pmap_dma;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x107f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107f,
+           "Entered %s.\n", __func__);
 
        pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
        if (pmap  == NULL) {
@@ -2224,7 +2377,7 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        rval = qla2x00_mailbox_command(vha, mcp);
 
        if (rval == QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x1081,
+               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1081,
                    "mb0/mb1=%x/%X FC/AL position map size (%x).\n",
                    mcp->mb[0], mcp->mb[1], (unsigned)pmap[0]);
                ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111d,
@@ -2238,7 +2391,8 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1083, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1083,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2267,7 +2421,8 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
        uint32_t *siter, *diter, dwords;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1084, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1084,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_LINK_STATUS;
        mcp->mb[2] = MSW(stats_dma);
@@ -2301,7 +2456,8 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
                        rval = QLA_FUNCTION_FAILED;
                } else {
                        /* Copy over data -- firmware data is LE. */
-                       ql_dbg(ql_dbg_mbx, vha, 0x1086, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1086,
+                           "Done %s.\n", __func__);
                        dwords = offsetof(struct link_statistics, unused1) / 4;
                        siter = diter = &stats->link_fail_cnt;
                        while (dwords--)
@@ -2324,7 +2480,8 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
        mbx_cmd_t *mcp = &mc;
        uint32_t *siter, *diter, dwords;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1088, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1088,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
        mcp->mb[2] = MSW(stats_dma);
@@ -2346,7 +2503,8 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
                            "Failed mb[0]=%x.\n", mcp->mb[0]);
                        rval = QLA_FUNCTION_FAILED;
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x108a, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108a,
+                           "Done %s.\n", __func__);
                        /* Copy over data -- firmware data is LE. */
                        dwords = sizeof(struct link_statistics) / 4;
                        siter = diter = &stats->link_fail_cnt;
@@ -2375,7 +2533,8 @@ qla24xx_abort_command(srb_t *sp)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = vha->req;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x108c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108c,
+           "Entered %s.\n", __func__);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -2404,7 +2563,7 @@ qla24xx_abort_command(srb_t *sp)
        abt->port_id[0] = fcport->d_id.b.al_pa;
        abt->port_id[1] = fcport->d_id.b.area;
        abt->port_id[2] = fcport->d_id.b.domain;
-       abt->vp_index = fcport->vp_idx;
+       abt->vp_index = fcport->vha->vp_idx;
 
        abt->req_que_no = cpu_to_le16(req->id);
 
@@ -2423,7 +2582,8 @@ qla24xx_abort_command(srb_t *sp)
                    le16_to_cpu(abt->nport_handle));
                rval = QLA_FUNCTION_FAILED;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1091, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, abt, abt_dma);
@@ -2455,7 +2615,8 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        ha = vha->hw;
        req = vha->req;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1092, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1092,
+           "Entered %s.\n", __func__);
 
        if (ha->flags.cpu_affinity_enabled)
                rsp = ha->rsp_q_map[tag + 1];
@@ -2478,7 +2639,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
        tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
        tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
-       tsk->p.tsk.vp_index = fcport->vp_idx;
+       tsk->p.tsk.vp_index = fcport->vha->vp_idx;
        if (type == TCF_LUN_RESET) {
                int_to_scsilun(l, &tsk->p.tsk.lun);
                host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun,
@@ -2504,7 +2665,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        } else if (le16_to_cpu(sts->scsi_status) &
            SS_RESPONSE_INFO_LEN_VALID) {
                if (le32_to_cpu(sts->rsp_data_len) < 4) {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1097,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097,
                            "Ignoring inconsistent data length -- not enough "
                            "response info (%d).\n",
                            le32_to_cpu(sts->rsp_data_len));
@@ -2523,7 +2684,8 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
                ql_dbg(ql_dbg_mbx, vha, 0x1099,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x109a, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109a,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
@@ -2564,7 +2726,8 @@ qla2x00_system_error(scsi_qla_host_t *vha)
        if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x109b, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109b,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
        mcp->out_mb = MBX_0;
@@ -2576,7 +2739,8 @@ qla2x00_system_error(scsi_qla_host_t *vha)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x109d, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109d,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2596,7 +2760,8 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x109e, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109e,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SERDES_PARAMS;
        mcp->mb[1] = BIT_0;
@@ -2615,7 +2780,8 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x10a0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a0,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2631,7 +2797,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a1, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a1,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_STOP_FIRMWARE;
        mcp->mb[1] = 0;
@@ -2646,7 +2813,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
                if (mcp->mb[0] == MBS_INVALID_COMMAND)
                        rval = QLA_INVALID_COMMAND;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a3, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a3,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2660,7 +2828,8 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a4, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a4,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2686,7 +2855,8 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a6,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2699,7 +2869,8 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a7, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a7,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2719,7 +2890,8 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a9, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a9,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2733,7 +2905,8 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10aa, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10aa,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
            !IS_QLA83XX(vha->hw))
@@ -2764,7 +2937,8 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ac, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ac,
+                   "Done %s.\n", __func__);
 
                if (mb)
                        memcpy(mb, mcp->mb, 8 * sizeof(*mb));
@@ -2782,7 +2956,8 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ad, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ad,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2804,7 +2979,8 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10af, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10af,
+                   "Done %s.\n", __func__);
 
                if (wr)
                        *wr = (uint64_t) mcp->mb[5] << 48 |
@@ -2829,7 +3005,8 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b0, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b0,
+           "Entered %s.\n", __func__);
 
        if (!IS_IIDMA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2854,7 +3031,8 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b2, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b2,
+                   "Done %s.\n", __func__);
                if (port_speed)
                        *port_speed = mcp->mb[3];
        }
@@ -2870,7 +3048,8 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b3, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b3,
+           "Entered %s.\n", __func__);
 
        if (!IS_IIDMA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2897,9 +3076,11 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        }
 
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b4, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx, vha, 0x10b4,
+                   "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b5, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b5,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2915,24 +3096,25 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
        scsi_qla_host_t *vp;
        unsigned long   flags;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b6, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6,
+           "Entered %s.\n", __func__);
 
        if (rptid_entry->entry_status != 0)
                return;
 
        if (rptid_entry->format == 0) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b7,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b7,
                    "Format 0 : Number of VPs setup %d, number of "
                    "VPs acquired %d.\n",
                    MSB(le16_to_cpu(rptid_entry->vp_count)),
                    LSB(le16_to_cpu(rptid_entry->vp_count)));
-               ql_dbg(ql_dbg_mbx, vha, 0x10b8,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b8,
                    "Primary port id %02x%02x%02x.\n",
                    rptid_entry->port_id[2], rptid_entry->port_id[1],
                    rptid_entry->port_id[0]);
        } else if (rptid_entry->format == 1) {
                vp_idx = LSB(stat);
-               ql_dbg(ql_dbg_mbx, vha, 0x10b9,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b9,
                    "Format 1: VP[%d] enabled - status %d - with "
                    "port id %02x%02x%02x.\n", vp_idx, MSB(stat),
                    rptid_entry->port_id[2], rptid_entry->port_id[1],
@@ -2999,7 +3181,8 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 
        /* This can be called by the parent */
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10bb, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10bb,
+           "Entered %s.\n", __func__);
 
        vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
        if (!vpmod) {
@@ -3015,6 +3198,9 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
        vpmod->vp_count = 1;
        vpmod->vp_index1 = vha->vp_idx;
        vpmod->options_idx1 = BIT_3|BIT_4|BIT_5;
+
+       qlt_modify_vp_config(vha, vpmod);
+
        memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE);
        memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
        vpmod->entry_count = 1;
@@ -3035,7 +3221,8 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
                rval = QLA_FUNCTION_FAILED;
        } else {
                /* EMPTY */
-               ql_dbg(ql_dbg_mbx, vha, 0x10c0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
+                   "Done %s.\n", __func__);
                fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
        }
        dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
@@ -3069,7 +3256,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
        int     vp_index = vha->vp_idx;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c1,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c1,
            "Entered %s enabling index %d.\n", __func__, vp_index);
 
        if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
@@ -3112,7 +3299,8 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
                    le16_to_cpu(vce->comp_status));
                rval = QLA_FUNCTION_FAILED;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10c6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c6,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, vce, vce_dma);
@@ -3149,14 +3337,8 @@ qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c7, "Entered %s.\n", __func__);
-
-       /*
-        * This command is implicitly executed by firmware during login for the
-        * physical hosts
-        */
-       if (vp_idx == 0)
-               return QLA_FUNCTION_FAILED;
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c7,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
        mcp->mb[1] = format;
@@ -3185,7 +3367,8 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1009, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1009,
+           "Entered %s.\n", __func__);
 
        if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
                mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
@@ -3219,7 +3402,8 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
                ql_dbg(ql_dbg_mbx, vha, 0x1008,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1007, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1007,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3244,7 +3428,8 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
        unsigned long flags;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c8, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c8,
+           "Entered %s.\n", __func__);
 
        mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
        if (mn == NULL) {
@@ -3285,7 +3470,7 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
                status[0] = le16_to_cpu(mn->p.rsp.comp_status);
                status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
                    le16_to_cpu(mn->p.rsp.failure_code) : 0;
-               ql_dbg(ql_dbg_mbx, vha, 0x10ce,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ce,
                    "cs=%x fc=%x.\n", status[0], status[1]);
 
                if (status[0] != CS_COMPLETE) {
@@ -3299,7 +3484,7 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
                                retry = 1;
                        }
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x10d0,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d0,
                            "Firmware updated to %x.\n",
                            le32_to_cpu(mn->p.rsp.fw_ver));
 
@@ -3316,9 +3501,11 @@ verify_done:
        dma_pool_free(ha->s_dma_pool, mn, mn_dma);
 
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d1, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx, vha, 0x10d1,
+                   "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d2, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d2,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3334,7 +3521,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
        struct device_reg_25xxmq __iomem *reg;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d3, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d3,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
        mcp->mb[1] = req->options;
@@ -3388,7 +3576,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
                ql_dbg(ql_dbg_mbx, vha, 0x10d4,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d5, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d5,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3404,7 +3593,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
        struct device_reg_25xxmq __iomem *reg;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d6, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d6,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
        mcp->mb[1] = rsp->options;
@@ -3456,7 +3646,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
                ql_dbg(ql_dbg_mbx, vha, 0x10d7,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d8, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d8,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3469,7 +3660,8 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d9, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d9,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_IDC_ACK;
        memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
@@ -3483,7 +3675,8 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x10da,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10db, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10db,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3496,7 +3689,8 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10dc, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10dc,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3514,7 +3708,8 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10de, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10de,
+                   "Done %s.\n", __func__);
                *sector_size = mcp->mb[1];
        }
 
@@ -3531,7 +3726,8 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10df, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10df,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
        mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE :
@@ -3547,7 +3743,8 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e1, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e1,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3563,7 +3760,8 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e2, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
        mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR;
@@ -3582,7 +3780,8 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e4, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e4,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3595,7 +3794,8 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e5, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e5,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_RESTART_MPI_FW;
        mcp->out_mb = MBX_0;
@@ -3609,7 +3809,8 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e7, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e7,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3624,7 +3825,8 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e8, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -3654,7 +3856,8 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
                ql_dbg(ql_dbg_mbx, vha, 0x10e9,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ea, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3669,7 +3872,8 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10eb, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10eb,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -3699,7 +3903,8 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
                ql_dbg(ql_dbg_mbx, vha, 0x10ec,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ed, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ed,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3713,7 +3918,8 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ee, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ee,
+           "Entered %s.\n", __func__);
 
        if (!IS_CNA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3735,7 +3941,8 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f0,
+                   "Done %s.\n", __func__);
 
 
                *actual_size = mcp->mb[2] << 2;
@@ -3752,7 +3959,8 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f1, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f1,
+           "Entered %s.\n", __func__);
 
        if (!IS_CNA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3775,7 +3983,8 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f3, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f3,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3788,7 +3997,8 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f4, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f4,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3805,7 +4015,8 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
                ql_dbg(ql_dbg_mbx, vha, 0x10f5,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f6,
+                   "Done %s.\n", __func__);
                *data = mcp->mb[3] << 16 | mcp->mb[2];
        }
 
@@ -3821,7 +4032,8 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
        mbx_cmd_t *mcp = &mc;
        uint32_t iter_cnt = 0x1;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f7, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f7,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK;
@@ -3865,7 +4077,8 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
                    "mb[19]=%x.\n", rval, mcp->mb[0], mcp->mb[1], mcp->mb[2],
                    mcp->mb[3], mcp->mb[18], mcp->mb[19]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f9, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f9,
+                   "Done %s.\n", __func__);
        }
 
        /* Copy mailbox information */
@@ -3882,7 +4095,8 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10fa, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fa,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
@@ -3926,7 +4140,8 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10fc, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fc,
+                   "Done %s.\n", __func__);
        }
 
        /* Copy mailbox information */
@@ -3941,7 +4156,7 @@ qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10fd,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fd,
            "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic);
 
        mcp->mb[0] = MBC_ISP84XX_RESET;
@@ -3955,7 +4170,8 @@ qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
        if (rval != QLA_SUCCESS)
                ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval);
        else
-               ql_dbg(ql_dbg_mbx, vha, 0x10ff, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ff,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
@@ -3967,7 +4183,8 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1100, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1100,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3986,7 +4203,8 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
                ql_dbg(ql_dbg_mbx, vha, 0x1101,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1102, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1102,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4003,7 +4221,8 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
 
        rval = QLA_SUCCESS;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1103, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1103,
+           "Entered %s.\n", __func__);
 
        clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
@@ -4046,7 +4265,8 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x1104,
                    "Failed=%x mb[0]=%x.\n", rval, mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1105, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1105,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4060,7 +4280,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1106, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -4078,7 +4299,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1107,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1108, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
+                   "Done %s.\n", __func__);
                if (mcp->mb[1] != 0x7)
                        ha->link_data_rate = mcp->mb[1];
        }
@@ -4094,7 +4316,8 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1109, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1109,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4113,7 +4336,8 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
                /* Copy all bits to preserve original value */
                memcpy(mb, &mcp->mb[1], sizeof(uint16_t) * 4);
 
-               ql_dbg(ql_dbg_mbx, vha, 0x110b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+                   "Done %s.\n", __func__);
        }
        return rval;
 }
@@ -4125,7 +4349,8 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x110c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SET_PORT_CONFIG;
        /* Copy all bits to preserve original setting */
@@ -4140,7 +4365,8 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x110d,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else
-               ql_dbg(ql_dbg_mbx, vha, 0x110e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110e,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
@@ -4155,7 +4381,8 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x110f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110f,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4183,7 +4410,8 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority,
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10cc, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10cc,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4196,7 +4424,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
        uint8_t byte;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ca, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ca,
+           "Entered %s.\n", __func__);
 
        /* Integer part */
        rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0);
@@ -4216,7 +4445,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
        }
        *frac = (byte >> 6) * 25;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1018, "Done %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1018,
+           "Done %s.\n", __func__);
 fail:
        return rval;
 }
@@ -4229,7 +4459,8 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1017, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1017,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -4248,7 +4479,8 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1016,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x100e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100e,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4262,7 +4494,8 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x100d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100d,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA82XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4281,7 +4514,8 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x100c,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x100b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100b,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4295,7 +4529,8 @@ qla82xx_md_get_template_size(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        int rval = QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x111f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x111f,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
@@ -4318,7 +4553,8 @@ qla82xx_md_get_template_size(scsi_qla_host_t *vha)
                    (mcp->mb[1] << 16) | mcp->mb[0],
                    (mcp->mb[3] << 16) | mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1121, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1121,
+                   "Done %s.\n", __func__);
                ha->md_template_size = ((mcp->mb[3] << 16) | mcp->mb[2]);
                if (!ha->md_template_size) {
                        ql_dbg(ql_dbg_mbx, vha, 0x1122,
@@ -4337,7 +4573,8 @@ qla82xx_md_get_template(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        int rval = QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1123, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1123,
+           "Entered %s.\n", __func__);
 
        ha->md_tmplt_hdr = dma_alloc_coherent(&ha->pdev->dev,
           ha->md_template_size, &ha->md_tmplt_hdr_dma, GFP_KERNEL);
@@ -4372,7 +4609,8 @@ qla82xx_md_get_template(scsi_qla_host_t *vha)
                    ((mcp->mb[1] << 16) | mcp->mb[0]),
                    ((mcp->mb[3] << 16) | mcp->mb[2]));
        } else
-               ql_dbg(ql_dbg_mbx, vha, 0x1126, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1126,
+                   "Done %s.\n", __func__);
        return rval;
 }
 
@@ -4387,7 +4625,8 @@ qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
        if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1133, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1133,
+           "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
        mcp->mb[0] = MBC_SET_LED_CONFIG;
@@ -4412,7 +4651,8 @@ qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
                ql_dbg(ql_dbg_mbx, vha, 0x1134,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1135, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1135,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4429,7 +4669,8 @@ qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
        if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1136, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1136,
+           "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
        mcp->mb[0] = MBC_GET_LED_CONFIG;
@@ -4454,7 +4695,8 @@ qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
                        led_cfg[4] = mcp->mb[5];
                        led_cfg[5] = mcp->mb[6];
                }
-               ql_dbg(ql_dbg_mbx, vha, 0x1138, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1138,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4471,7 +4713,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
        if (!IS_QLA82XX(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1127,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1127,
                "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
@@ -4491,7 +4733,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
                ql_dbg(ql_dbg_mbx, vha, 0x1128,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1129,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1129,
                    "Done %s.\n", __func__);
        }
 
@@ -4509,7 +4751,8 @@ qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
        if (!IS_QLA83XX(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1130, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1130,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_WRITE_REMOTE_REG;
        mcp->mb[1] = LSW(reg);
@@ -4527,7 +4770,7 @@ qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
                ql_dbg(ql_dbg_mbx, vha, 0x1131,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1132,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1132,
                    "Done %s.\n", __func__);
        }
 
@@ -4543,13 +4786,14 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
        mbx_cmd_t *mcp = &mc;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-               ql_dbg(ql_dbg_mbx, vha, 0x113b,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113b,
                    "Implicit LOGO Unsupported.\n");
                return QLA_FUNCTION_FAILED;
        }
 
 
-       ql_dbg(ql_dbg_mbx, vha, 0x113c, "Done %s.\n",  __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113c,
+           "Entering %s.\n",  __func__);
 
        /* Perform Implicit LOGO. */
        mcp->mb[0] = MBC_PORT_LOGOUT;
@@ -4564,7 +4808,8 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
                ql_dbg(ql_dbg_mbx, vha, 0x113d,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        else
-               ql_dbg(ql_dbg_mbx, vha, 0x113e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113e,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
index aa062a1..3e8b324 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include "qla_def.h"
 #include "qla_gbl.h"
+#include "qla_target.h"
 
 #include <linux/moduleparam.h>
 #include <linux/vmalloc.h>
@@ -49,6 +50,9 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
 
        spin_lock_irqsave(&ha->vport_slock, flags);
        list_add_tail(&vha->list, &ha->vp_list);
+
+       qlt_update_vp_map(vha, SET_VP_IDX);
+
        spin_unlock_irqrestore(&ha->vport_slock, flags);
 
        mutex_unlock(&ha->vport_lock);
@@ -79,6 +83,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
                spin_lock_irqsave(&ha->vport_slock, flags);
        }
        list_del(&vha->list);
+       qlt_update_vp_map(vha, RESET_VP_IDX);
        spin_unlock_irqrestore(&ha->vport_slock, flags);
 
        vp_id = vha->vp_idx;
@@ -134,7 +139,7 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
        list_for_each_entry(fcport, &vha->vp_fcports, list) {
                ql_dbg(ql_dbg_vport, vha, 0xa001,
                    "Marking port dead, loop_id=0x%04x : %x.\n",
-                   fcport->loop_id, fcport->vp_idx);
+                   fcport->loop_id, fcport->vha->vp_idx);
 
                qla2x00_mark_device_lost(vha, fcport, 0, 0);
                qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
@@ -150,6 +155,9 @@ qla24xx_disable_vp(scsi_qla_host_t *vha)
        atomic_set(&vha->loop_state, LOOP_DOWN);
        atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
 
+       /* Remove port id from vp target map */
+       qlt_update_vp_map(vha, RESET_AL_PA);
+
        qla2x00_mark_vp_devices_dead(vha);
        atomic_set(&vha->vp_state, VP_FAILED);
        vha->flags.management_server_logged_in = 0;
@@ -295,10 +303,8 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
 static int
 qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
 {
-       ql_dbg(ql_dbg_dpc, vha, 0x4012,
-           "Entering %s.\n", __func__);
-       ql_dbg(ql_dbg_dpc, vha, 0x4013,
-           "vp_flags: 0x%lx.\n", vha->vp_flags);
+       ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x4012,
+           "Entering %s vp_flags: 0x%lx.\n", __func__, vha->vp_flags);
 
        qla2x00_do_work(vha);
 
@@ -348,7 +354,7 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
                }
        }
 
-       ql_dbg(ql_dbg_dpc, vha, 0x401c,
+       ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x401c,
            "Exiting %s.\n", __func__);
        return 0;
 }
index de722a9..caf627b 100644 (file)
@@ -1190,12 +1190,12 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
        }
 
        /* Offset in flash = lower 16 bits
-        * Number of enteries = upper 16 bits
+        * Number of entries = upper 16 bits
         */
        offset = n & 0xffffU;
        n = (n >> 16) & 0xffffU;
 
-       /* number of addr/value pair should not exceed 1024 enteries */
+       /* number of addr/value pair should not exceed 1024 entries */
        if (n  >= 1024) {
                ql_log(ql_log_fatal, vha, 0x0071,
                    "Card flash not initialized:n=0x%x.\n", n);
@@ -2050,7 +2050,7 @@ qla82xx_intr_handler(int irq, void *dev_id)
 
        rsp = (struct rsp_que *) dev_id;
        if (!rsp) {
-               ql_log(ql_log_info, NULL, 0xb054,
+               ql_log(ql_log_info, NULL, 0xb053,
                    "%s: NULL response queue pointer.\n", __func__);
                return IRQ_NONE;
        }
@@ -2446,7 +2446,7 @@ qla82xx_load_fw(scsi_qla_host_t *vha)
 
        if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) {
                ql_log(ql_log_info, vha, 0x00a1,
-                   "Firmware loaded successully from flash.\n");
+                   "Firmware loaded successfully from flash.\n");
                return QLA_SUCCESS;
        } else {
                ql_log(ql_log_warn, vha, 0x0108,
@@ -2461,7 +2461,7 @@ try_blob_fw:
        blob = ha->hablob = qla2x00_request_firmware(vha);
        if (!blob) {
                ql_log(ql_log_fatal, vha, 0x00a3,
-                   "Firmware image not preset.\n");
+                   "Firmware image not present.\n");
                goto fw_load_failed;
        }
 
@@ -2689,7 +2689,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr,
                if (!optrom) {
                        ql_log(ql_log_warn, vha, 0xb01b,
                            "Unable to allocate memory "
-                           "for optron burst write (%x KB).\n",
+                           "for optrom burst write (%x KB).\n",
                            OPTROM_BURST_SIZE / 1024);
                }
        }
@@ -2960,9 +2960,8 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha)
                         * changing the state to DEV_READY
                         */
                        ql_log(ql_log_info, vha, 0xb023,
-                           "%s : QUIESCENT TIMEOUT.\n", QLA2XXX_DRIVER_NAME);
-                       ql_log(ql_log_info, vha, 0xb024,
-                           "DRV_ACTIVE:%d DRV_STATE:%d.\n",
+                           "%s : QUIESCENT TIMEOUT DRV_ACTIVE:%d "
+                           "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME,
                            drv_active, drv_state);
                        qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                            QLA82XX_DEV_READY);
@@ -3129,7 +3128,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha)
                if (ql2xmdenable) {
                        if (qla82xx_md_collect(vha))
                                ql_log(ql_log_warn, vha, 0xb02c,
-                                   "Not able to collect minidump.\n");
+                                   "Minidump not collected.\n");
                } else
                        ql_log(ql_log_warn, vha, 0xb04f,
                            "Minidump disabled.\n");
@@ -3160,11 +3159,11 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha)
                                    "Firmware version differs "
                                    "Previous version: %d:%d:%d - "
                                    "New version: %d:%d:%d\n",
+                                   fw_major_version, fw_minor_version,
+                                   fw_subminor_version,
                                    ha->fw_major_version,
                                    ha->fw_minor_version,
-                                   ha->fw_subminor_version,
-                                   fw_major_version, fw_minor_version,
-                                   fw_subminor_version);
+                                   ha->fw_subminor_version);
                                /* Release MiniDump resources */
                                qla82xx_md_free(vha);
                                /* ALlocate MiniDump resources */
@@ -3325,6 +3324,30 @@ exit:
        return rval;
 }
 
+static int qla82xx_check_temp(scsi_qla_host_t *vha)
+{
+       uint32_t temp, temp_state, temp_val;
+       struct qla_hw_data *ha = vha->hw;
+
+       temp = qla82xx_rd_32(ha, CRB_TEMP_STATE);
+       temp_state = qla82xx_get_temp_state(temp);
+       temp_val = qla82xx_get_temp_val(temp);
+
+       if (temp_state == QLA82XX_TEMP_PANIC) {
+               ql_log(ql_log_warn, vha, 0x600e,
+                   "Device temperature %d degrees C exceeds "
+                   " maximum allowed. Hardware has been shut down.\n",
+                   temp_val);
+               return 1;
+       } else if (temp_state == QLA82XX_TEMP_WARN) {
+               ql_log(ql_log_warn, vha, 0x600f,
+                   "Device temperature %d degrees C exceeds "
+                   "operating range. Immediate action needed.\n",
+                   temp_val);
+       }
+       return 0;
+}
+
 void qla82xx_clear_pending_mbx(scsi_qla_host_t *vha)
 {
        struct qla_hw_data *ha = vha->hw;
@@ -3347,18 +3370,20 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
        /* don't poll if reset is going on */
        if (!ha->flags.isp82xx_reset_hdlr_active) {
                dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               if (dev_state == QLA82XX_DEV_NEED_RESET &&
+               if (qla82xx_check_temp(vha)) {
+                       set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags);
+                       ha->flags.isp82xx_fw_hung = 1;
+                       qla82xx_clear_pending_mbx(vha);
+               } else if (dev_state == QLA82XX_DEV_NEED_RESET &&
                    !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
                        ql_log(ql_log_warn, vha, 0x6001,
                            "Adapter reset needed.\n");
                        set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
-                       qla2xxx_wake_dpc(vha);
                } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
                        !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
                        ql_log(ql_log_warn, vha, 0x6002,
                            "Quiescent needed.\n");
                        set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
-                       qla2xxx_wake_dpc(vha);
                } else {
                        if (qla82xx_check_fw_alive(vha)) {
                                ql_dbg(ql_dbg_timer, vha, 0x6011,
@@ -3398,7 +3423,6 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
                                        set_bit(ISP_ABORT_NEEDED,
                                            &vha->dpc_flags);
                                }
-                               qla2xxx_wake_dpc(vha);
                                ha->flags.isp82xx_fw_hung = 1;
                                ql_log(ql_log_warn, vha, 0x6007, "Firmware hung.\n");
                                qla82xx_clear_pending_mbx(vha);
@@ -4113,6 +4137,14 @@ qla82xx_md_collect(scsi_qla_host_t *vha)
                goto md_failed;
        }
 
+       if (ha->flags.isp82xx_no_md_cap) {
+               ql_log(ql_log_warn, vha, 0xb054,
+                   "Forced reset from application, "
+                   "ignore minidump capture\n");
+               ha->flags.isp82xx_no_md_cap = 0;
+               goto md_failed;
+       }
+
        if (qla82xx_validate_template_chksum(vha)) {
                ql_log(ql_log_info, vha, 0xb039,
                    "Template checksum validation error\n");
index 4ac50e2..6eb210e 100644 (file)
@@ -26,6 +26,7 @@
 #define CRB_RCVPEG_STATE               QLA82XX_REG(0x13c)
 #define BOOT_LOADER_DIMM_STATUS                QLA82XX_REG(0x54)
 #define CRB_DMA_SHIFT                  QLA82XX_REG(0xcc)
+#define CRB_TEMP_STATE                 QLA82XX_REG(0x1b4)
 #define QLA82XX_DMA_SHIFT_VALUE                0x55555555
 
 #define QLA82XX_HW_H0_CH_HUB_ADR    0x05
 #define QLA82XX_FW_VERSION_SUB         (QLA82XX_CAM_RAM(0x158))
 #define QLA82XX_PCIE_REG(reg)          (QLA82XX_CRB_PCIE + (reg))
 
-#define PCIE_CHICKEN3                  (0x120c8)
 #define PCIE_SETUP_FUNCTION            (0x12040)
 #define PCIE_SETUP_FUNCTION2           (0x12048)
 
@@ -1178,4 +1178,16 @@ static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC,
 #define CRB_NIU_XG_PAUSE_CTL_P0        0x1
 #define CRB_NIU_XG_PAUSE_CTL_P1        0x8
 
+#define qla82xx_get_temp_val(x)          ((x) >> 16)
+#define qla82xx_get_temp_state(x)        ((x) & 0xffff)
+#define qla82xx_encode_temp(val, state)  (((val) << 16) | (state))
+
+/*
+ * Temperature control.
+ */
+enum {
+       QLA82XX_TEMP_NORMAL = 0x1, /* Normal operating range */
+       QLA82XX_TEMP_WARN,         /* Sound alert, temperature getting high */
+       QLA82XX_TEMP_PANIC         /* Fatal error, hardware has shut down. */
+};
 #endif
index c9c56a8..6d1d873 100644 (file)
 #include <linux/mutex.h>
 #include <linux/kobject.h>
 #include <linux/slab.h>
-
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "qla_target.h"
+
 /*
  * Driver version
  */
@@ -40,6 +41,12 @@ static struct kmem_cache *ctx_cachep;
  */
 int ql_errlev = ql_log_all;
 
+int ql2xenableclass2;
+module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xenableclass2,
+               "Specify if Class 2 operations are supported from the very "
+               "beginning. Default is 0 - class 2 not supported.");
+
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xlogintimeout,
@@ -255,6 +262,8 @@ struct scsi_host_template qla2xxx_driver_template = {
 
        .max_sectors            = 0xFFFF,
        .shost_attrs            = qla2x00_host_attrs,
+
+       .supported_mode         = MODE_INITIATOR,
 };
 
 static struct scsi_transport_template *qla2xxx_transport_template = NULL;
@@ -306,7 +315,8 @@ static void qla2x00_free_fw_dump(struct qla_hw_data *);
 static void qla2x00_mem_free(struct qla_hw_data *);
 
 /* -------------------------------------------------------------------------- */
-static int qla2x00_alloc_queues(struct qla_hw_data *ha)
+static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
+                               struct rsp_que *rsp)
 {
        scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
        ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
@@ -324,6 +334,12 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha)
                    "Unable to allocate memory for response queue ptrs.\n");
                goto fail_rsp_map;
        }
+       /*
+        * Make sure we record at least the request and response queue zero in
+        * case we need to free them if part of the probe fails.
+        */
+       ha->rsp_q_map[0] = rsp;
+       ha->req_q_map[0] = req;
        set_bit(0, ha->rsp_qid_map);
        set_bit(0, ha->req_qid_map);
        return 1;
@@ -642,12 +658,12 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 
        if (ha->flags.eeh_busy) {
                if (ha->flags.pci_channel_io_perm_failure) {
-                       ql_dbg(ql_dbg_io, vha, 0x3001,
+                       ql_dbg(ql_dbg_aer, vha, 0x9010,
                            "PCI Channel IO permanent failure, exiting "
                            "cmd=%p.\n", cmd);
                        cmd->result = DID_NO_CONNECT << 16;
                } else {
-                       ql_dbg(ql_dbg_io, vha, 0x3002,
+                       ql_dbg(ql_dbg_aer, vha, 0x9011,
                            "EEH_Busy, Requeuing the cmd=%p.\n", cmd);
                        cmd->result = DID_REQUEUE << 16;
                }
@@ -657,7 +673,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
        rval = fc_remote_port_chkready(rport);
        if (rval) {
                cmd->result = rval;
-               ql_dbg(ql_dbg_io, vha, 0x3003,
+               ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3003,
                    "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",
                    cmd, rval);
                goto qc24_fail_command;
@@ -1136,7 +1152,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
        ret = FAILED;
 
        ql_log(ql_log_info, vha, 0x8012,
-           "BUS RESET ISSUED nexus=%ld:%d%d.\n", vha->host_no, id, lun);
+           "BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun);
 
        if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
                ql_log(ql_log_fatal, vha, 0x8013,
@@ -2180,6 +2196,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
            "Memory allocated for ha=%p.\n", ha);
        ha->pdev = pdev;
+       ha->tgt.enable_class_2 = ql2xenableclass2;
 
        /* Clear our data area */
        ha->bars = bars;
@@ -2243,6 +2260,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                req_length = REQUEST_ENTRY_CNT_24XX;
                rsp_length = RESPONSE_ENTRY_CNT_2300;
+               ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
                ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
                ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
                ha->gid_list_info_size = 8;
@@ -2258,6 +2276,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                req_length = REQUEST_ENTRY_CNT_24XX;
                rsp_length = RESPONSE_ENTRY_CNT_2300;
+               ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
                ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
                ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
                ha->gid_list_info_size = 8;
@@ -2417,6 +2436,17 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
            host->max_cmd_len, host->max_channel, host->max_lun,
            host->transportt, sht->vendor_id);
 
+que_init:
+       /* Alloc arrays of request and response ring ptrs */
+       if (!qla2x00_alloc_queues(ha, req, rsp)) {
+               ql_log(ql_log_fatal, base_vha, 0x003d,
+                   "Failed to allocate memory for queue pointers..."
+                   "aborting.\n");
+               goto probe_init_failed;
+       }
+
+       qlt_probe_one_stage1(base_vha, ha);
+
        /* Set up the irqs */
        ret = qla2x00_request_irqs(ha, rsp);
        if (ret)
@@ -2424,20 +2454,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        pci_save_state(pdev);
 
-       /* Alloc arrays of request and response ring ptrs */
-que_init:
-       if (!qla2x00_alloc_queues(ha)) {
-               ql_log(ql_log_fatal, base_vha, 0x003d,
-                   "Failed to allocate memory for queue pointers.. aborting.\n");
-               goto probe_init_failed;
-       }
-
-       ha->rsp_q_map[0] = rsp;
-       ha->req_q_map[0] = req;
+       /* Assign back pointers */
        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;
@@ -2514,6 +2534,14 @@ que_init:
        ql_dbg(ql_dbg_init, base_vha, 0x00ee,
            "DPC thread started successfully.\n");
 
+       /*
+        * If we're not coming up in initiator mode, we might sit for
+        * a while without waking up the dpc thread, which leads to a
+        * stuck process warning.  So just kick the dpc once here and
+        * let the kthread start (and go back to sleep in qla2x00_do_dpc).
+        */
+       qla2xxx_wake_dpc(base_vha);
+
 skip_dpc:
        list_add_tail(&base_vha->list, &ha->vp_list);
        base_vha->host->irq = ha->pdev->irq;
@@ -2559,7 +2587,11 @@ skip_dpc:
        ql_dbg(ql_dbg_init, base_vha, 0x00f2,
            "Init done and hba is online.\n");
 
-       scsi_scan_host(host);
+       if (qla_ini_mode_enabled(base_vha))
+               scsi_scan_host(host);
+       else
+               ql_dbg(ql_dbg_init, base_vha, 0x0122,
+                       "skipping scsi_scan_host() for non-initiator port\n");
 
        qla2x00_alloc_sysfs_attr(base_vha);
 
@@ -2577,11 +2609,17 @@ skip_dpc:
            base_vha->host_no,
            ha->isp_ops->fw_version_str(base_vha, fw_str));
 
+       qlt_add_target(ha, base_vha);
+
        return 0;
 
 probe_init_failed:
        qla2x00_free_req_que(ha, req);
+       ha->req_q_map[0] = NULL;
+       clear_bit(0, ha->req_qid_map);
        qla2x00_free_rsp_que(ha, rsp);
+       ha->rsp_q_map[0] = NULL;
+       clear_bit(0, ha->rsp_qid_map);
        ha->max_req_queues = ha->max_rsp_queues = 0;
 
 probe_failed:
@@ -2620,6 +2658,22 @@ probe_out:
        return ret;
 }
 
+static void
+qla2x00_stop_dpc_thread(scsi_qla_host_t *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct task_struct *t = ha->dpc_thread;
+
+       if (ha->dpc_thread == NULL)
+               return;
+       /*
+        * qla2xxx_wake_dpc checks for ->dpc_thread
+        * so we need to zero it out.
+        */
+       ha->dpc_thread = NULL;
+       kthread_stop(t);
+}
+
 static void
 qla2x00_shutdown(struct pci_dev *pdev)
 {
@@ -2663,9 +2717,18 @@ qla2x00_remove_one(struct pci_dev *pdev)
        struct qla_hw_data  *ha;
        unsigned long flags;
 
+       /*
+        * If the PCI device is disabled that means that probe failed and any
+        * resources should be have cleaned up on probe exit.
+        */
+       if (!atomic_read(&pdev->enable_cnt))
+               return;
+
        base_vha = pci_get_drvdata(pdev);
        ha = base_vha->hw;
 
+       ha->flags.host_shutting_down = 1;
+
        mutex_lock(&ha->vport_lock);
        while (ha->cur_vport_count) {
                struct Scsi_Host *scsi_host;
@@ -2719,6 +2782,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
                ha->dpc_thread = NULL;
                kthread_stop(t);
        }
+       qlt_remove_target(ha, base_vha);
 
        qla2x00_free_sysfs_attr(base_vha);
 
@@ -2770,17 +2834,7 @@ qla2x00_free_device(scsi_qla_host_t *vha)
        if (vha->timer_active)
                qla2x00_stop_timer(vha);
 
-       /* Kill the kernel thread for this host */
-       if (ha->dpc_thread) {
-               struct task_struct *t = ha->dpc_thread;
-
-               /*
-                * qla2xxx_wake_dpc checks for ->dpc_thread
-                * so we need to zero it out.
-                */
-               ha->dpc_thread = NULL;
-               kthread_stop(t);
-       }
+       qla2x00_stop_dpc_thread(vha);
 
        qla25xx_delete_queues(vha);
 
@@ -2842,8 +2896,10 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
                spin_unlock_irqrestore(vha->host->host_lock, flags);
                set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
                qla2xxx_wake_dpc(base_vha);
-       } else
+       } else {
                fc_remote_port_delete(rport);
+               qlt_fc_port_deleted(vha, fcport);
+       }
 }
 
 /*
@@ -2859,7 +2915,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
     int do_login, int defer)
 {
        if (atomic_read(&fcport->state) == FCS_ONLINE &&
-           vha->vp_idx == fcport->vp_idx) {
+           vha->vp_idx == fcport->vha->vp_idx) {
                qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
                qla2x00_schedule_rport_del(vha, fcport, defer);
        }
@@ -2908,7 +2964,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
        fc_port_t *fcport;
 
        list_for_each_entry(fcport, &vha->vp_fcports, list) {
-               if (vha->vp_idx != 0 && vha->vp_idx != fcport->vp_idx)
+               if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx)
                        continue;
 
                /*
@@ -2921,7 +2977,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
                        qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
                        if (defer)
                                qla2x00_schedule_rport_del(vha, fcport, defer);
-                       else if (vha->vp_idx == fcport->vp_idx)
+                       else if (vha->vp_idx == fcport->vha->vp_idx)
                                qla2x00_schedule_rport_del(vha, fcport, defer);
                }
        }
@@ -2946,10 +3002,13 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
        if (!ha->init_cb)
                goto fail;
 
+       if (qlt_mem_alloc(ha) < 0)
+               goto fail_free_init_cb;
+
        ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
                qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
        if (!ha->gid_list)
-               goto fail_free_init_cb;
+               goto fail_free_tgt_mem;
 
        ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
        if (!ha->srb_mempool)
@@ -3167,6 +3226,8 @@ fail_free_gid_list:
        ha->gid_list_dma);
        ha->gid_list = NULL;
        ha->gid_list_dma = 0;
+fail_free_tgt_mem:
+       qlt_mem_free(ha);
 fail_free_init_cb:
        dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
        ha->init_cb_dma);
@@ -3282,6 +3343,8 @@ qla2x00_mem_free(struct qla_hw_data *ha)
        if (ha->ctx_mempool)
                mempool_destroy(ha->ctx_mempool);
 
+       qlt_mem_free(ha);
+
        if (ha->init_cb)
                dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
                        ha->init_cb, ha->init_cb_dma);
@@ -3311,6 +3374,10 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 
        ha->gid_list = NULL;
        ha->gid_list_dma = 0;
+
+       ha->tgt.atio_ring = NULL;
+       ha->tgt.atio_dma = 0;
+       ha->tgt.tgt_vp_map = NULL;
 }
 
 struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
@@ -3671,10 +3738,9 @@ qla2x00_do_dpc(void *data)
 
                ha->dpc_active = 1;
 
-               ql_dbg(ql_dbg_dpc, base_vha, 0x4001,
-                   "DPC handler waking up.\n");
-               ql_dbg(ql_dbg_dpc, base_vha, 0x4002,
-                   "dpc_flags=0x%lx.\n", base_vha->dpc_flags);
+               ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001,
+                   "DPC handler waking up, dpc_flags=0x%lx.\n",
+                   base_vha->dpc_flags);
 
                qla2x00_do_work(base_vha);
 
@@ -3740,6 +3806,16 @@ qla2x00_do_dpc(void *data)
                        clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
                }
 
+               if (test_bit(SCR_PENDING, &base_vha->dpc_flags)) {
+                       int ret;
+                       ret = qla2x00_send_change_request(base_vha, 0x3, 0);
+                       if (ret != QLA_SUCCESS)
+                               ql_log(ql_log_warn, base_vha, 0x121,
+                                   "Failed to enable receiving of RSCN "
+                                   "requests: 0x%x.\n", ret);
+                       clear_bit(SCR_PENDING, &base_vha->dpc_flags);
+               }
+
                if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) {
                        ql_dbg(ql_dbg_dpc, base_vha, 0x4009,
                            "Quiescence mode scheduled.\n");
@@ -4457,6 +4533,21 @@ qla2x00_module_init(void)
                return -ENOMEM;
        }
 
+       /* Initialize target kmem_cache and mem_pools */
+       ret = qlt_init();
+       if (ret < 0) {
+               kmem_cache_destroy(srb_cachep);
+               return ret;
+       } else if (ret > 0) {
+               /*
+                * If initiator mode is explictly disabled by qlt_init(),
+                * prevent scsi_transport_fc.c:fc_scsi_scan_rport() from
+                * performing scsi_scan_target() during LOOP UP event.
+                */
+               qla2xxx_transport_functions.disable_target_scan = 1;
+               qla2xxx_transport_vport_functions.disable_target_scan = 1;
+       }
+
        /* Derive version string. */
        strcpy(qla2x00_version_str, QLA2XXX_VERSION);
        if (ql2xextended_error_logging)
@@ -4468,6 +4559,7 @@ qla2x00_module_init(void)
                kmem_cache_destroy(srb_cachep);
                ql_log(ql_log_fatal, NULL, 0x0002,
                    "fc_attach_transport failed...Failing load!.\n");
+               qlt_exit();
                return -ENODEV;
        }
 
@@ -4481,6 +4573,7 @@ qla2x00_module_init(void)
            fc_attach_transport(&qla2xxx_transport_vport_functions);
        if (!qla2xxx_transport_vport_template) {
                kmem_cache_destroy(srb_cachep);
+               qlt_exit();
                fc_release_transport(qla2xxx_transport_template);
                ql_log(ql_log_fatal, NULL, 0x0004,
                    "fc_attach_transport vport failed...Failing load!.\n");
@@ -4492,6 +4585,7 @@ qla2x00_module_init(void)
        ret = pci_register_driver(&qla2xxx_pci_driver);
        if (ret) {
                kmem_cache_destroy(srb_cachep);
+               qlt_exit();
                fc_release_transport(qla2xxx_transport_template);
                fc_release_transport(qla2xxx_transport_vport_template);
                ql_log(ql_log_fatal, NULL, 0x0006,
@@ -4511,6 +4605,7 @@ qla2x00_module_exit(void)
        pci_unregister_driver(&qla2xxx_pci_driver);
        qla2x00_release_firmware();
        kmem_cache_destroy(srb_cachep);
+       qlt_exit();
        if (ctx_cachep)
                kmem_cache_destroy(ctx_cachep);
        fc_release_transport(qla2xxx_transport_template);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
new file mode 100644 (file)
index 0000000..04f80eb
--- /dev/null
@@ -0,0 +1,4973 @@
+/*
+ *  qla_target.c SCSI LLD infrastructure for QLogic 22xx/23xx/24xx/25xx
+ *
+ *  based on qla2x00t.c code:
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  Copyright (C) 2006 - 2010 ID7 Ltd.
+ *
+ *  Forward port and refactoring to modern qla2xxx and target/configfs
+ *
+ *  Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation, 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "qla_def.h"
+#include "qla_target.h"
+
+static char *qlini_mode = QLA2XXX_INI_MODE_STR_ENABLED;
+module_param(qlini_mode, charp, S_IRUGO);
+MODULE_PARM_DESC(qlini_mode,
+       "Determines when initiator mode will be enabled. Possible values: "
+       "\"exclusive\" - initiator mode will be enabled on load, "
+       "disabled on enabling target mode and then on disabling target mode "
+       "enabled back; "
+       "\"disabled\" - initiator mode will never be enabled; "
+       "\"enabled\" (default) - initiator mode will always stay enabled.");
+
+static int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
+
+/*
+ * From scsi/fc/fc_fcp.h
+ */
+enum fcp_resp_rsp_codes {
+       FCP_TMF_CMPL = 0,
+       FCP_DATA_LEN_INVALID = 1,
+       FCP_CMND_FIELDS_INVALID = 2,
+       FCP_DATA_PARAM_MISMATCH = 3,
+       FCP_TMF_REJECTED = 4,
+       FCP_TMF_FAILED = 5,
+       FCP_TMF_INVALID_LUN = 9,
+};
+
+/*
+ * fc_pri_ta from scsi/fc/fc_fcp.h
+ */
+#define FCP_PTA_SIMPLE      0   /* simple task attribute */
+#define FCP_PTA_HEADQ       1   /* head of queue task attribute */
+#define FCP_PTA_ORDERED     2   /* ordered task attribute */
+#define FCP_PTA_ACA         4   /* auto. contigent allegiance */
+#define FCP_PTA_MASK        7   /* mask for task attribute field */
+#define FCP_PRI_SHIFT       3   /* priority field starts in bit 3 */
+#define FCP_PRI_RESVD_MASK  0x80        /* reserved bits in priority field */
+
+/*
+ * This driver calls qla2x00_alloc_iocbs() and qla2x00_issue_marker(), which
+ * must be called under HW lock and could unlock/lock it inside.
+ * It isn't an issue, since in the current implementation on the time when
+ * those functions are called:
+ *
+ *   - Either context is IRQ and only IRQ handler can modify HW data,
+ *     including rings related fields,
+ *
+ *   - Or access to target mode variables from struct qla_tgt doesn't
+ *     cross those functions boundaries, except tgt_stop, which
+ *     additionally protected by irq_cmd_count.
+ */
+/* Predefs for callbacks handed to qla2xxx LLD */
+static void qlt_24xx_atio_pkt(struct scsi_qla_host *ha,
+       struct atio_from_isp *pkt);
+static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt);
+static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
+       int fn, void *iocb, int flags);
+static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd
+       *cmd, struct atio_from_isp *atio, int ha_locked);
+static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha,
+       struct qla_tgt_srr_imm *imm, int ha_lock);
+/*
+ * Global Variables
+ */
+static struct kmem_cache *qla_tgt_cmd_cachep;
+static struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
+static mempool_t *qla_tgt_mgmt_cmd_mempool;
+static struct workqueue_struct *qla_tgt_wq;
+static DEFINE_MUTEX(qla_tgt_mutex);
+static LIST_HEAD(qla_tgt_glist);
+
+/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
+static struct qla_tgt_sess *qlt_find_sess_by_port_name(
+       struct qla_tgt *tgt,
+       const uint8_t *port_name)
+{
+       struct qla_tgt_sess *sess;
+
+       list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
+               if (!memcmp(sess->port_name, port_name, WWN_SIZE))
+                       return sess;
+       }
+
+       return NULL;
+}
+
+/* Might release hw lock, then reaquire!! */
+static inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked)
+{
+       /* Send marker if required */
+       if (unlikely(vha->marker_needed != 0)) {
+               int rc = qla2x00_issue_marker(vha, vha_locked);
+               if (rc != QLA_SUCCESS) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe03d,
+                           "qla_target(%d): issue_marker() failed\n",
+                           vha->vp_idx);
+               }
+               return rc;
+       }
+       return QLA_SUCCESS;
+}
+
+static inline
+struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha,
+       uint8_t *d_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint8_t vp_idx;
+
+       if ((vha->d_id.b.area != d_id[1]) || (vha->d_id.b.domain != d_id[0]))
+               return NULL;
+
+       if (vha->d_id.b.al_pa == d_id[2])
+               return vha;
+
+       BUG_ON(ha->tgt.tgt_vp_map == NULL);
+       vp_idx = ha->tgt.tgt_vp_map[d_id[2]].idx;
+       if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+               return ha->tgt.tgt_vp_map[vp_idx].vha;
+
+       return NULL;
+}
+
+static inline
+struct scsi_qla_host *qlt_find_host_by_vp_idx(struct scsi_qla_host *vha,
+       uint16_t vp_idx)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (vha->vp_idx == vp_idx)
+               return vha;
+
+       BUG_ON(ha->tgt.tgt_vp_map == NULL);
+       if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+               return ha->tgt.tgt_vp_map[vp_idx].vha;
+
+       return NULL;
+}
+
+void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       switch (atio->u.raw.entry_type) {
+       case ATIO_TYPE7:
+       {
+               struct scsi_qla_host *host = qlt_find_host_by_d_id(vha,
+                   atio->u.isp24.fcp_hdr.d_id);
+               if (unlikely(NULL == host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe03e,
+                           "qla_target(%d): Received ATIO_TYPE7 "
+                           "with unknown d_id %x:%x:%x\n", vha->vp_idx,
+                           atio->u.isp24.fcp_hdr.d_id[0],
+                           atio->u.isp24.fcp_hdr.d_id[1],
+                           atio->u.isp24.fcp_hdr.d_id[2]);
+                       break;
+               }
+               qlt_24xx_atio_pkt(host, atio);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct imm_ntfy_from_isp *entry =
+                   (struct imm_ntfy_from_isp *)atio;
+
+               if ((entry->u.isp24.vp_index != 0xFF) &&
+                   (entry->u.isp24.nport_handle != 0xFFFF)) {
+                       host = qlt_find_host_by_vp_idx(vha,
+                           entry->u.isp24.vp_index);
+                       if (unlikely(!host)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe03f,
+                                   "qla_target(%d): Received "
+                                   "ATIO (IMMED_NOTIFY_TYPE) "
+                                   "with unknown vp_index %d\n",
+                                   vha->vp_idx, entry->u.isp24.vp_index);
+                               break;
+                       }
+               }
+               qlt_24xx_atio_pkt(host, atio);
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe040,
+                   "qla_target(%d): Received unknown ATIO atio "
+                   "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
+               break;
+       }
+
+       return;
+}
+
+void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt)
+{
+       switch (pkt->entry_type) {
+       case CTIO_TYPE7:
+       {
+               struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe041,
+                           "qla_target(%d): Response pkt (CTIO_TYPE7) "
+                           "received, with unknown vp_index %d\n",
+                           vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct imm_ntfy_from_isp *entry =
+                   (struct imm_ntfy_from_isp *)pkt;
+
+               host = qlt_find_host_by_vp_idx(vha, entry->u.isp24.vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe042,
+                           "qla_target(%d): Response pkt (IMMED_NOTIFY_TYPE) "
+                           "received, with unknown vp_index %d\n",
+                           vha->vp_idx, entry->u.isp24.vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case NOTIFY_ACK_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
+
+               if (0xFF != entry->u.isp24.vp_index) {
+                       host = qlt_find_host_by_vp_idx(vha,
+                           entry->u.isp24.vp_index);
+                       if (unlikely(!host)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe043,
+                                   "qla_target(%d): Response "
+                                   "pkt (NOTIFY_ACK_TYPE) "
+                                   "received, with unknown "
+                                   "vp_index %d\n", vha->vp_idx,
+                                   entry->u.isp24.vp_index);
+                               break;
+                       }
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case ABTS_RECV_24XX:
+       {
+               struct abts_recv_from_24xx *entry =
+                   (struct abts_recv_from_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe044,
+                           "qla_target(%d): Response pkt "
+                           "(ABTS_RECV_24XX) received, with unknown "
+                           "vp_index %d\n", vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case ABTS_RESP_24XX:
+       {
+               struct abts_resp_to_24xx *entry =
+                   (struct abts_resp_to_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe045,
+                           "qla_target(%d): Response pkt "
+                           "(ABTS_RECV_24XX) received, with unknown "
+                           "vp_index %d\n", vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       default:
+               qlt_response_pkt(vha, pkt);
+               break;
+       }
+
+}
+
+static void qlt_free_session_done(struct work_struct *work)
+{
+       struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess,
+           free_work);
+       struct qla_tgt *tgt = sess->tgt;
+       struct scsi_qla_host *vha = sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+
+       BUG_ON(!tgt);
+       /*
+        * Release the target session for FC Nexus from fabric module code.
+        */
+       if (sess->se_sess != NULL)
+               ha->tgt.tgt_ops->free_session(sess);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001,
+           "Unregistration of sess %p finished\n", sess);
+
+       kfree(sess);
+       /*
+        * We need to protect against race, when tgt is freed before or
+        * inside wake_up()
+        */
+       tgt->sess_count--;
+       if (tgt->sess_count == 0)
+               wake_up_all(&tgt->waitQ);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+void qlt_unreg_sess(struct qla_tgt_sess *sess)
+{
+       struct scsi_qla_host *vha = sess->vha;
+
+       vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess);
+
+       list_del(&sess->sess_list_entry);
+       if (sess->deleted)
+               list_del(&sess->del_list_entry);
+
+       INIT_WORK(&sess->free_work, qlt_free_session_done);
+       schedule_work(&sess->free_work);
+}
+EXPORT_SYMBOL(qlt_unreg_sess);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       uint32_t unpacked_lun, lun = 0;
+       uint16_t loop_id;
+       int res = 0;
+       struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb;
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+
+       loop_id = le16_to_cpu(n->u.isp24.nport_handle);
+       if (loop_id == 0xFFFF) {
+#if 0 /* FIXME: Re-enable Global event handling.. */
+               /* Global event */
+               atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count);
+               qlt_clear_tgt_db(ha->tgt.qla_tgt, 1);
+               if (!list_empty(&ha->tgt.qla_tgt->sess_list)) {
+                       sess = list_entry(ha->tgt.qla_tgt->sess_list.next,
+                           typeof(*sess), sess_list_entry);
+                       switch (mcmd) {
+                       case QLA_TGT_NEXUS_LOSS_SESS:
+                               mcmd = QLA_TGT_NEXUS_LOSS;
+                               break;
+                       case QLA_TGT_ABORT_ALL_SESS:
+                               mcmd = QLA_TGT_ABORT_ALL;
+                               break;
+                       case QLA_TGT_NEXUS_LOSS:
+                       case QLA_TGT_ABORT_ALL:
+                               break;
+                       default:
+                               ql_dbg(ql_dbg_tgt, vha, 0xe046,
+                                   "qla_target(%d): Not allowed "
+                                   "command %x in %s", vha->vp_idx,
+                                   mcmd, __func__);
+                               sess = NULL;
+                               break;
+                       }
+               } else
+                       sess = NULL;
+#endif
+       } else {
+               sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe000,
+           "Using sess for qla_tgt_reset: %p\n", sess);
+       if (!sess) {
+               res = -ESRCH;
+               return res;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe047,
+           "scsi(%ld): resetting (session %p from port "
+           "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, "
+           "mcmd %x, loop_id %d)\n", vha->host_no, sess,
+           sess->port_name[0], sess->port_name[1],
+           sess->port_name[2], sess->port_name[3],
+           sess->port_name[4], sess->port_name[5],
+           sess->port_name[6], sess->port_name[7],
+           mcmd, loop_id);
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       return qlt_issue_task_mgmt(sess, unpacked_lun, mcmd,
+           iocb, QLA24XX_MGMT_SEND_NACK);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess,
+       bool immediate)
+{
+       struct qla_tgt *tgt = sess->tgt;
+       uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
+
+       if (sess->deleted)
+               return;
+
+       ql_dbg(ql_dbg_tgt, sess->vha, 0xe001,
+           "Scheduling sess %p for deletion\n", sess);
+       list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
+       sess->deleted = 1;
+
+       if (immediate)
+               dev_loss_tmo = 0;
+
+       sess->expires = jiffies + dev_loss_tmo * HZ;
+
+       ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
+           "qla_target(%d): session for port %02x:%02x:%02x:"
+           "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
+           "deletion in %u secs (expires: %lu) immed: %d\n",
+           sess->vha->vp_idx,
+           sess->port_name[0], sess->port_name[1],
+           sess->port_name[2], sess->port_name[3],
+           sess->port_name[4], sess->port_name[5],
+           sess->port_name[6], sess->port_name[7],
+           sess->loop_id, dev_loss_tmo, sess->expires, immediate);
+
+       if (immediate)
+               schedule_delayed_work(&tgt->sess_del_work, 0);
+       else
+               schedule_delayed_work(&tgt->sess_del_work,
+                   jiffies - sess->expires);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_clear_tgt_db(struct qla_tgt *tgt, bool local_only)
+{
+       struct qla_tgt_sess *sess;
+
+       list_for_each_entry(sess, &tgt->sess_list, sess_list_entry)
+               qlt_schedule_sess_for_deletion(sess, true);
+
+       /* At this point tgt could be already dead */
+}
+
+static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id,
+       uint16_t *loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       dma_addr_t gid_list_dma;
+       struct gid_list_info *gid_list;
+       char *id_iter;
+       int res, rc, i;
+       uint16_t entries;
+
+       gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+           &gid_list_dma, GFP_KERNEL);
+       if (!gid_list) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf044,
+                   "qla_target(%d): DMA Alloc failed of %u\n",
+                   vha->vp_idx, qla2x00_gid_list_size(ha));
+               return -ENOMEM;
+       }
+
+       /* Get list of logged in devices */
+       rc = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf045,
+                   "qla_target(%d): get_id_list() failed: %x\n",
+                   vha->vp_idx, rc);
+               res = -1;
+               goto out_free_id_list;
+       }
+
+       id_iter = (char *)gid_list;
+       res = -1;
+       for (i = 0; i < entries; i++) {
+               struct gid_list_info *gid = (struct gid_list_info *)id_iter;
+               if ((gid->al_pa == s_id[2]) &&
+                   (gid->area == s_id[1]) &&
+                   (gid->domain == s_id[0])) {
+                       *loop_id = le16_to_cpu(gid->loop_id);
+                       res = 0;
+                       break;
+               }
+               id_iter += ha->gid_list_info_size;
+       }
+
+out_free_id_list:
+       dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+           gid_list, gid_list_dma);
+       return res;
+}
+
+static bool qlt_check_fcport_exist(struct scsi_qla_host *vha,
+       struct qla_tgt_sess *sess)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_port_24xx_data *pmap24;
+       bool res, found = false;
+       int rc, i;
+       uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */
+       uint16_t entries;
+       void *pmap;
+       int pmap_len;
+       fc_port_t *fcport;
+       int global_resets;
+
+retry:
+       global_resets = atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count);
+
+       rc = qla2x00_get_node_name_list(vha, &pmap, &pmap_len);
+       if (rc != QLA_SUCCESS) {
+               res = false;
+               goto out;
+       }
+
+       pmap24 = pmap;
+       entries = pmap_len/sizeof(*pmap24);
+
+       for (i = 0; i < entries; ++i) {
+               if (!memcmp(sess->port_name, pmap24[i].port_name, WWN_SIZE)) {
+                       loop_id = le16_to_cpu(pmap24[i].loop_id);
+                       found = true;
+                       break;
+               }
+       }
+
+       kfree(pmap);
+
+       if (!found) {
+               res = false;
+               goto out;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf046,
+           "qlt_check_fcport_exist(): loop_id %d", loop_id);
+
+       fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+       if (fcport == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf047,
+                   "qla_target(%d): Allocation of tmp FC port failed",
+                   vha->vp_idx);
+               res = false;
+               goto out;
+       }
+
+       fcport->loop_id = loop_id;
+
+       rc = qla2x00_get_port_database(vha, fcport, 0);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf048,
+                   "qla_target(%d): Failed to retrieve fcport "
+                   "information -- get_port_database() returned %x "
+                   "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
+               res = false;
+               goto out_free_fcport;
+       }
+
+       if (global_resets !=
+           atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf002,
+                   "qla_target(%d): global reset during session discovery"
+                   " (counter was %d, new %d), retrying",
+                   vha->vp_idx, global_resets,
+                   atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count));
+               goto retry;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf003,
+           "Updating sess %p s_id %x:%x:%x, loop_id %d) to d_id %x:%x:%x, "
+           "loop_id %d", sess, sess->s_id.b.domain, sess->s_id.b.al_pa,
+           sess->s_id.b.area, sess->loop_id, fcport->d_id.b.domain,
+           fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id);
+
+       sess->s_id = fcport->d_id;
+       sess->loop_id = fcport->loop_id;
+       sess->conf_compl_supported = !!(fcport->flags &
+           FCF_CONF_COMP_SUPPORTED);
+
+       res = true;
+
+out_free_fcport:
+       kfree(fcport);
+
+out:
+       return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_undelete_sess(struct qla_tgt_sess *sess)
+{
+       BUG_ON(!sess->deleted);
+
+       list_del(&sess->del_list_entry);
+       sess->deleted = 0;
+}
+
+static void qlt_del_sess_work_fn(struct delayed_work *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt,
+           sess_del_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       while (!list_empty(&tgt->del_sess_list)) {
+               sess = list_entry(tgt->del_sess_list.next, typeof(*sess),
+                   del_list_entry);
+               if (time_after_eq(jiffies, sess->expires)) {
+                       bool cancel;
+
+                       qlt_undelete_sess(sess);
+
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       cancel = qlt_check_fcport_exist(vha, sess);
+
+                       if (cancel) {
+                               if (sess->deleted) {
+                                       /*
+                                        * sess was again deleted while we were
+                                        * discovering it
+                                        */
+                                       spin_lock_irqsave(&ha->hardware_lock,
+                                           flags);
+                                       continue;
+                               }
+
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf049,
+                                   "qla_target(%d): cancel deletion of "
+                                   "session for port %02x:%02x:%02x:%02x:%02x:"
+                                   "%02x:%02x:%02x (loop ID %d), because "
+                                   " it isn't deleted by firmware",
+                                   vha->vp_idx, sess->port_name[0],
+                                   sess->port_name[1], sess->port_name[2],
+                                   sess->port_name[3], sess->port_name[4],
+                                   sess->port_name[5], sess->port_name[6],
+                                   sess->port_name[7], sess->loop_id);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
+                                   "Timeout: sess %p about to be deleted\n",
+                                   sess);
+                               ha->tgt.tgt_ops->shutdown_sess(sess);
+                               ha->tgt.tgt_ops->put_sess(sess);
+                       }
+
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+               } else {
+                       schedule_delayed_work(&tgt->sess_del_work,
+                           jiffies - sess->expires);
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * Adds an extra ref to allow to drop hw lock after adding sess to the list.
+ * Caller must put it.
+ */
+static struct qla_tgt_sess *qlt_create_sess(
+       struct scsi_qla_host *vha,
+       fc_port_t *fcport,
+       bool local)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+       unsigned char be_sid[3];
+
+       /* Check to avoid double sessions */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       list_for_each_entry(sess, &ha->tgt.qla_tgt->sess_list,
+                               sess_list_entry) {
+               if (!memcmp(sess->port_name, fcport->port_name, WWN_SIZE)) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005,
+                           "Double sess %p found (s_id %x:%x:%x, "
+                           "loop_id %d), updating to d_id %x:%x:%x, "
+                           "loop_id %d", sess, sess->s_id.b.domain,
+                           sess->s_id.b.al_pa, sess->s_id.b.area,
+                           sess->loop_id, fcport->d_id.b.domain,
+                           fcport->d_id.b.al_pa, fcport->d_id.b.area,
+                           fcport->loop_id);
+
+                       if (sess->deleted)
+                               qlt_undelete_sess(sess);
+
+                       kref_get(&sess->se_sess->sess_kref);
+                       sess->s_id = fcport->d_id;
+                       sess->loop_id = fcport->loop_id;
+                       sess->conf_compl_supported = !!(fcport->flags &
+                           FCF_CONF_COMP_SUPPORTED);
+                       if (sess->local && !local)
+                               sess->local = 0;
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+                       return sess;
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a,
+                   "qla_target(%u): session allocation failed, "
+                   "all commands from port %02x:%02x:%02x:%02x:"
+                   "%02x:%02x:%02x:%02x will be refused", vha->vp_idx,
+                   fcport->port_name[0], fcport->port_name[1],
+                   fcport->port_name[2], fcport->port_name[3],
+                   fcport->port_name[4], fcport->port_name[5],
+                   fcport->port_name[6], fcport->port_name[7]);
+
+               return NULL;
+       }
+       sess->tgt = ha->tgt.qla_tgt;
+       sess->vha = vha;
+       sess->s_id = fcport->d_id;
+       sess->loop_id = fcport->loop_id;
+       sess->local = local;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
+           "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n",
+           sess, ha->tgt.qla_tgt);
+
+       be_sid[0] = sess->s_id.b.domain;
+       be_sid[1] = sess->s_id.b.area;
+       be_sid[2] = sess->s_id.b.al_pa;
+       /*
+        * Determine if this fc_port->port_name is allowed to access
+        * target mode using explict NodeACLs+MappedLUNs, or using
+        * TPG demo mode.  If this is successful a target mode FC nexus
+        * is created.
+        */
+       if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
+           &fcport->port_name[0], sess, &be_sid[0], fcport->loop_id) < 0) {
+               kfree(sess);
+               return NULL;
+       }
+       /*
+        * Take an extra reference to ->sess_kref here to handle qla_tgt_sess
+        * access across ->hardware_lock reaquire.
+        */
+       kref_get(&sess->se_sess->sess_kref);
+
+       sess->conf_compl_supported = !!(fcport->flags &
+           FCF_CONF_COMP_SUPPORTED);
+       BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name));
+       memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name));
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       list_add_tail(&sess->sess_list_entry, &ha->tgt.qla_tgt->sess_list);
+       ha->tgt.qla_tgt->sess_count++;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
+           "qla_target(%d): %ssession for wwn %02x:%02x:%02x:%02x:"
+           "%02x:%02x:%02x:%02x (loop_id %d, s_id %x:%x:%x, confirmed"
+           " completion %ssupported) added\n",
+           vha->vp_idx, local ?  "local " : "", fcport->port_name[0],
+           fcport->port_name[1], fcport->port_name[2], fcport->port_name[3],
+           fcport->port_name[4], fcport->port_name[5], fcport->port_name[6],
+           fcport->port_name[7], fcport->loop_id, sess->s_id.b.domain,
+           sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ?
+           "" : "not ");
+
+       return sess;
+}
+
+/*
+ * Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
+ */
+void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       if (!vha->hw->tgt.tgt_ops)
+               return;
+
+       if (!tgt || (fcport->port_type != FCT_INITIATOR))
+               return;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (tgt->tgt_stop) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+       sess = qlt_find_sess_by_port_name(tgt, fcport->port_name);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_create_sess(vha, fcport, false);
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+
+               if (sess->deleted) {
+                       qlt_undelete_sess(sess);
+
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c,
+                           "qla_target(%u): %ssession for port %02x:"
+                           "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
+                           "reappeared\n", vha->vp_idx, sess->local ? "local "
+                           : "", sess->port_name[0], sess->port_name[1],
+                           sess->port_name[2], sess->port_name[3],
+                           sess->port_name[4], sess->port_name[5],
+                           sess->port_name[6], sess->port_name[7],
+                           sess->loop_id);
+
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007,
+                           "Reappeared sess %p\n", sess);
+               }
+               sess->s_id = fcport->d_id;
+               sess->loop_id = fcport->loop_id;
+               sess->conf_compl_supported = !!(fcport->flags &
+                   FCF_CONF_COMP_SUPPORTED);
+       }
+
+       if (sess && sess->local) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d,
+                   "qla_target(%u): local session for "
+                   "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
+                   "(loop ID %d) became global\n", vha->vp_idx,
+                   fcport->port_name[0], fcport->port_name[1],
+                   fcport->port_name[2], fcport->port_name[3],
+                   fcport->port_name[4], fcport->port_name[5],
+                   fcport->port_name[6], fcport->port_name[7],
+                   sess->loop_id);
+               sess->local = 0;
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+}
+
+void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       if (!vha->hw->tgt.tgt_ops)
+               return;
+
+       if (!tgt || (fcport->port_type != FCT_INITIATOR))
+               return;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (tgt->tgt_stop) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+       sess = qlt_find_sess_by_port_name(tgt, fcport->port_name);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess);
+
+       sess->local = 1;
+       qlt_schedule_sess_for_deletion(sess, false);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static inline int test_tgt_sess_count(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+       int res;
+       /*
+        * We need to protect against race, when tgt is freed before or
+        * inside wake_up()
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002,
+           "tgt %p, empty(sess_list)=%d sess_count=%d\n",
+           tgt, list_empty(&tgt->sess_list), tgt->sess_count);
+       res = (tgt->sess_count == 0);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+
+/* Called by tcm_qla2xxx configfs code */
+void qlt_stop_phase1(struct qla_tgt *tgt)
+{
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+
+       if (tgt->tgt_stop || tgt->tgt_stopped) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e,
+                   "Already in tgt->tgt_stop or tgt_stopped state\n");
+               dump_stack();
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n",
+           vha->host_no, vha);
+       /*
+        * Mutex needed to sync with qla_tgt_fc_port_[added,deleted].
+        * Lock is needed, because we still can get an incoming packet.
+        */
+       mutex_lock(&ha->tgt.tgt_mutex);
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tgt->tgt_stop = 1;
+       qlt_clear_tgt_db(tgt, true);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       mutex_unlock(&ha->tgt.tgt_mutex);
+
+       flush_delayed_work_sync(&tgt->sess_del_work);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009,
+           "Waiting for sess works (tgt %p)", tgt);
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       while (!list_empty(&tgt->sess_works_list)) {
+               spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+               flush_scheduled_work();
+               spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       }
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a,
+           "Waiting for tgt %p: list_empty(sess_list)=%d "
+           "sess_count=%d\n", tgt, list_empty(&tgt->sess_list),
+           tgt->sess_count);
+
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+
+       /* Big hammer */
+       if (!ha->flags.host_shutting_down && qla_tgt_mode_enabled(vha))
+               qlt_disable_vha(vha);
+
+       /* Wait for sessions to clear out (just in case) */
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+}
+EXPORT_SYMBOL(qlt_stop_phase1);
+
+/* Called by tcm_qla2xxx configfs code */
+void qlt_stop_phase2(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+
+       if (tgt->tgt_stopped) {
+               ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf04f,
+                   "Already in tgt->tgt_stopped state\n");
+               dump_stack();
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00b,
+           "Waiting for %d IRQ commands to complete (tgt %p)",
+           tgt->irq_cmd_count, tgt);
+
+       mutex_lock(&ha->tgt.tgt_mutex);
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       while (tgt->irq_cmd_count != 0) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               udelay(2);
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+       }
+       tgt->tgt_stop = 0;
+       tgt->tgt_stopped = 1;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       mutex_unlock(&ha->tgt.tgt_mutex);
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00c, "Stop of tgt %p finished",
+           tgt);
+}
+EXPORT_SYMBOL(qlt_stop_phase2);
+
+/* Called from qlt_remove_target() -> qla2x00_remove_one() */
+void qlt_release(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+
+       if ((ha->tgt.qla_tgt != NULL) && !tgt->tgt_stopped)
+               qlt_stop_phase2(tgt);
+
+       ha->tgt.qla_tgt = NULL;
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00d,
+           "Release of tgt %p finished\n", tgt);
+
+       kfree(tgt);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_sched_sess_work(struct qla_tgt *tgt, int type,
+       const void *param, unsigned int param_size)
+{
+       struct qla_tgt_sess_work_param *prm;
+       unsigned long flags;
+
+       prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
+       if (!prm) {
+               ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf050,
+                   "qla_target(%d): Unable to create session "
+                   "work, command will be refused", 0);
+               return -ENOMEM;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00e,
+           "Scheduling work (type %d, prm %p)"
+           " to find session for param %p (size %d, tgt %p)\n",
+           type, prm, param, param_size, tgt);
+
+       prm->type = type;
+       memcpy(&prm->tm_iocb, param, param_size);
+
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+       schedule_work(&tgt->sess_work);
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_send_notify_ack(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *ntfy,
+       uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
+       uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan)
+{
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       struct nack_to_isp *nack;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe004, "Sending NOTIFY_ACK (ha=%p)\n", ha);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!pkt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe049,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return;
+       }
+
+       if (ha->tgt.qla_tgt != NULL)
+               ha->tgt.qla_tgt->notify_ack_expected++;
+
+       pkt->entry_type = NOTIFY_ACK_TYPE;
+       pkt->entry_count = 1;
+
+       nack = (struct nack_to_isp *)pkt;
+       nack->ox_id = ntfy->ox_id;
+
+       nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
+       if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
+               nack->u.isp24.flags = ntfy->u.isp24.flags &
+                       __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB);
+       }
+       nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
+       nack->u.isp24.status = ntfy->u.isp24.status;
+       nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
+       nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
+       nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
+       nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
+       nack->u.isp24.srr_flags = cpu_to_le16(srr_flags);
+       nack->u.isp24.srr_reject_code = srr_reject_code;
+       nack->u.isp24.srr_reject_code_expl = srr_explan;
+       nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe005,
+           "qla_target(%d): Sending 24xx Notify Ack %d\n",
+           vha->vp_idx, nack->u.isp24.status);
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_send_abts_resp(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts, uint32_t status,
+       bool ids_reversed)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct abts_resp_to_24xx *resp;
+       uint32_t f_ctl;
+       uint8_t *p;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe006,
+           "Sending task mgmt ABTS response (ha=%p, atio=%p, status=%x\n",
+           ha, abts, status);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       resp = (struct abts_resp_to_24xx *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!resp) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe04a,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet", vha->vp_idx, __func__);
+               return;
+       }
+
+       resp->entry_type = ABTS_RESP_24XX;
+       resp->entry_count = 1;
+       resp->nport_handle = abts->nport_handle;
+       resp->vp_index = vha->vp_idx;
+       resp->sof_type = abts->sof_type;
+       resp->exchange_address = abts->exchange_address;
+       resp->fcp_hdr_le = abts->fcp_hdr_le;
+       f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP |
+           F_CTL_LAST_SEQ | F_CTL_END_SEQ |
+           F_CTL_SEQ_INITIATIVE);
+       p = (uint8_t *)&f_ctl;
+       resp->fcp_hdr_le.f_ctl[0] = *p++;
+       resp->fcp_hdr_le.f_ctl[1] = *p++;
+       resp->fcp_hdr_le.f_ctl[2] = *p;
+       if (ids_reversed) {
+               resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.d_id[0];
+               resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.d_id[1];
+               resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.d_id[2];
+               resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.s_id[0];
+               resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.s_id[1];
+               resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.s_id[2];
+       } else {
+               resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.s_id[0];
+               resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.s_id[1];
+               resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.s_id[2];
+               resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.d_id[0];
+               resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.d_id[1];
+               resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.d_id[2];
+       }
+       resp->exchange_addr_to_abort = abts->exchange_addr_to_abort;
+       if (status == FCP_TMF_CMPL) {
+               resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC;
+               resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID;
+               resp->payload.ba_acct.low_seq_cnt = 0x0000;
+               resp->payload.ba_acct.high_seq_cnt = 0xFFFF;
+               resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id;
+               resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id;
+       } else {
+               resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT;
+               resp->payload.ba_rjt.reason_code =
+                       BA_RJT_REASON_CODE_UNABLE_TO_PERFORM;
+               /* Other bytes are zero */
+       }
+
+       ha->tgt.qla_tgt->abts_resp_expected++;
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha,
+       struct abts_resp_from_24xx_fw *entry)
+{
+       struct ctio7_to_24xx *ctio;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe007,
+           "Sending retry TERM EXCH CTIO7 (ha=%p)\n", vha->hw);
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(vha, NULL);
+       if (ctio == NULL) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe04b,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return;
+       }
+
+       /*
+        * We've got on entrance firmware's response on by us generated
+        * ABTS response. So, in it ID fields are reversed.
+        */
+
+       ctio->entry_type = CTIO_TYPE7;
+       ctio->entry_count = 1;
+       ctio->nport_handle = entry->nport_handle;
+       ctio->handle = QLA_TGT_SKIP_HANDLE |    CTIO_COMPLETION_HANDLE_MARK;
+       ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio->vp_index = vha->vp_idx;
+       ctio->initiator_id[0] = entry->fcp_hdr_le.d_id[0];
+       ctio->initiator_id[1] = entry->fcp_hdr_le.d_id[1];
+       ctio->initiator_id[2] = entry->fcp_hdr_le.d_id[2];
+       ctio->exchange_addr = entry->exchange_addr_to_abort;
+       ctio->u.status1.flags =
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_TERMINATE);
+       ctio->u.status1.ox_id = entry->fcp_hdr_le.ox_id;
+
+       qla2x00_start_iocbs(vha, vha->req);
+
+       qlt_24xx_send_abts_resp(vha, (struct abts_recv_from_24xx *)entry,
+           FCP_TMF_CMPL, true);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       int rc;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
+           "qla_target(%d): task abort (tag=%d)\n",
+           vha->vp_idx, abts->exchange_addr_to_abort);
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf051,
+                   "qla_target(%d): %s: Allocation of ABORT cmd failed",
+                   vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+
+       mcmd->sess = sess;
+       memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
+
+       rc = ha->tgt.tgt_ops->handle_tmr(mcmd, 0, TMR_ABORT_TASK,
+           abts->exchange_addr_to_abort);
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf052,
+                   "qla_target(%d):  tgt_ops->handle_tmr()"
+                   " failed: %d", vha->vp_idx, rc);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       uint32_t tag = abts->exchange_addr_to_abort;
+       uint8_t s_id[3];
+       int rc;
+
+       if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf053,
+                   "qla_target(%d): ABTS: Abort Sequence not "
+                   "supported\n", vha->vp_idx);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+
+       if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf010,
+                   "qla_target(%d): ABTS: Unknown Exchange "
+                   "Address received\n", vha->vp_idx);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf011,
+           "qla_target(%d): task abort (s_id=%x:%x:%x, "
+           "tag=%d, param=%x)\n", vha->vp_idx, abts->fcp_hdr_le.s_id[2],
+           abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[0], tag,
+           le32_to_cpu(abts->fcp_hdr_le.parameter));
+
+       s_id[0] = abts->fcp_hdr_le.s_id[2];
+       s_id[1] = abts->fcp_hdr_le.s_id[1];
+       s_id[2] = abts->fcp_hdr_le.s_id[0];
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012,
+                   "qla_target(%d): task abort for non-existant session\n",
+                   vha->vp_idx);
+               rc = qlt_sched_sess_work(ha->tgt.qla_tgt,
+                   QLA_TGT_SESS_WORK_ABORT, abts, sizeof(*abts));
+               if (rc != 0) {
+                       qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED,
+                           false);
+               }
+               return;
+       }
+
+       rc = __qlt_24xx_handle_abts(vha, abts, sess);
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054,
+                   "qla_target(%d): __qlt_24xx_handle_abts() failed: %d\n",
+                   vha->vp_idx, rc);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha,
+       struct qla_tgt_mgmt_cmd *mcmd, uint32_t resp_code)
+{
+       struct atio_from_isp *atio = &mcmd->orig_iocb.atio;
+       struct ctio7_to_24xx *ctio;
+
+       ql_dbg(ql_dbg_tgt, ha, 0xe008,
+           "Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x\n",
+           ha, atio, resp_code);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(ha, 1) != QLA_SUCCESS)
+               return;
+
+       ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(ha, NULL);
+       if (ctio == NULL) {
+               ql_dbg(ql_dbg_tgt, ha, 0xe04c,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", ha->vp_idx, __func__);
+               return;
+       }
+
+       ctio->entry_type = CTIO_TYPE7;
+       ctio->entry_count = 1;
+       ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+       ctio->nport_handle = mcmd->sess->loop_id;
+       ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio->vp_index = ha->vp_idx;
+       ctio->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_SEND_STATUS);
+       ctio->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       ctio->u.status1.scsi_status =
+           __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID);
+       ctio->u.status1.response_len = __constant_cpu_to_le16(8);
+       ((uint32_t *)ctio->u.status1.sense_data)[0] = cpu_to_be32(resp_code);
+
+       qla2x00_start_iocbs(ha, ha->req);
+}
+
+void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+}
+EXPORT_SYMBOL(qlt_free_mcmd);
+
+/* callback from target fabric module code */
+void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       struct scsi_qla_host *vha = mcmd->sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf013,
+           "TM response mcmd (%p) status %#x state %#x",
+           mcmd, mcmd->fc_tm_rsp, mcmd->flags);
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (mcmd->flags == QLA24XX_MGMT_SEND_NACK)
+               qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy,
+                   0, 0, 0, 0, 0, 0);
+       else {
+               if (mcmd->se_cmd.se_tmr_req->function == TMR_ABORT_TASK)
+                       qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts,
+                           mcmd->fc_tm_rsp, false);
+               else
+                       qlt_24xx_send_task_mgmt_ctio(vha, mcmd,
+                           mcmd->fc_tm_rsp);
+       }
+       /*
+        * Make the callback for ->free_mcmd() to queue_work() and invoke
+        * target_put_sess_cmd() to drop cmd_kref to 1.  The final
+        * target_put_sess_cmd() call will be made from TFO->check_stop_free()
+        * -> tcm_qla2xxx_check_stop_free() to release the TMR associated se_cmd
+        * descriptor after TFO->queue_tm_rsp() -> tcm_qla2xxx_queue_tm_rsp() ->
+        * qlt_xmit_tm_rsp() returns here..
+        */
+       ha->tgt.tgt_ops->free_mcmd(mcmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+EXPORT_SYMBOL(qlt_xmit_tm_rsp);
+
+/* No locks */
+static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm)
+{
+       struct qla_tgt_cmd *cmd = prm->cmd;
+
+       BUG_ON(cmd->sg_cnt == 0);
+
+       prm->sg = (struct scatterlist *)cmd->sg;
+       prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, cmd->sg,
+           cmd->sg_cnt, cmd->dma_data_direction);
+       if (unlikely(prm->seg_cnt == 0))
+               goto out_err;
+
+       prm->cmd->sg_mapped = 1;
+
+       /*
+        * If greater than four sg entries then we need to allocate
+        * the continuation entries
+        */
+       if (prm->seg_cnt > prm->tgt->datasegs_per_cmd)
+               prm->req_cnt += DIV_ROUND_UP(prm->seg_cnt -
+                   prm->tgt->datasegs_per_cmd, prm->tgt->datasegs_per_cont);
+
+       ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe009, "seg_cnt=%d, req_cnt=%d\n",
+           prm->seg_cnt, prm->req_cnt);
+       return 0;
+
+out_err:
+       ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe04d,
+           "qla_target(%d): PCI mapping failed: sg_cnt=%d",
+           0, prm->cmd->sg_cnt);
+       return -1;
+}
+
+static inline void qlt_unmap_sg(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       BUG_ON(!cmd->sg_mapped);
+       pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+       cmd->sg_mapped = 0;
+}
+
+static int qlt_check_reserve_free_req(struct scsi_qla_host *vha,
+       uint32_t req_cnt)
+{
+       struct qla_hw_data *ha = vha->hw;
+       device_reg_t __iomem *reg = ha->iobase;
+       uint32_t cnt;
+
+       if (vha->req->cnt < (req_cnt + 2)) {
+               cnt = (uint16_t)RD_REG_DWORD(&reg->isp24.req_q_out);
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe00a,
+                   "Request ring circled: cnt=%d, vha->->ring_index=%d, "
+                   "vha->req->cnt=%d, req_cnt=%d\n", cnt,
+                   vha->req->ring_index, vha->req->cnt, req_cnt);
+               if  (vha->req->ring_index < cnt)
+                       vha->req->cnt = cnt - vha->req->ring_index;
+               else
+                       vha->req->cnt = vha->req->length -
+                           (vha->req->ring_index - cnt);
+       }
+
+       if (unlikely(vha->req->cnt < (req_cnt + 2))) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe00b,
+                   "qla_target(%d): There is no room in the "
+                   "request ring: vha->req->ring_index=%d, vha->req->cnt=%d, "
+                   "req_cnt=%d\n", vha->vp_idx, vha->req->ring_index,
+                   vha->req->cnt, req_cnt);
+               return -EAGAIN;
+       }
+       vha->req->cnt -= req_cnt;
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static inline void *qlt_get_req_pkt(struct scsi_qla_host *vha)
+{
+       /* Adjust ring index. */
+       vha->req->ring_index++;
+       if (vha->req->ring_index == vha->req->length) {
+               vha->req->ring_index = 0;
+               vha->req->ring_ptr = vha->req->ring;
+       } else {
+               vha->req->ring_ptr++;
+       }
+       return (cont_entry_t *)vha->req->ring_ptr;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline uint32_t qlt_make_handle(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint32_t h;
+
+       h = ha->tgt.current_handle;
+       /* always increment cmd handle */
+       do {
+               ++h;
+               if (h > MAX_OUTSTANDING_COMMANDS)
+                       h = 1; /* 0 is QLA_TGT_NULL_HANDLE */
+               if (h == ha->tgt.current_handle) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe04e,
+                           "qla_target(%d): Ran out of "
+                           "empty cmd slots in ha %p\n", vha->vp_idx, ha);
+                       h = QLA_TGT_NULL_HANDLE;
+                       break;
+               }
+       } while ((h == QLA_TGT_NULL_HANDLE) ||
+           (h == QLA_TGT_SKIP_HANDLE) ||
+           (ha->tgt.cmds[h-1] != NULL));
+
+       if (h != QLA_TGT_NULL_HANDLE)
+               ha->tgt.current_handle = h;
+
+       return h;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       uint32_t h;
+       struct ctio7_to_24xx *pkt;
+       struct qla_hw_data *ha = vha->hw;
+       struct atio_from_isp *atio = &prm->cmd->atio;
+
+       pkt = (struct ctio7_to_24xx *)vha->req->ring_ptr;
+       prm->pkt = pkt;
+       memset(pkt, 0, sizeof(*pkt));
+
+       pkt->entry_type = CTIO_TYPE7;
+       pkt->entry_count = (uint8_t)prm->req_cnt;
+       pkt->vp_index = vha->vp_idx;
+
+       h = qlt_make_handle(vha);
+       if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
+               /*
+                * CTIO type 7 from the firmware doesn't provide a way to
+                * know the initiator's LOOP ID, hence we can't find
+                * the session and, so, the command.
+                */
+               return -EAGAIN;
+       } else
+               ha->tgt.cmds[h-1] = prm->cmd;
+
+       pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK;
+       pkt->nport_handle = prm->cmd->loop_id;
+       pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       pkt->exchange_addr = atio->u.isp24.exchange_addr;
+       pkt->u.status0.flags |= (atio->u.isp24.attr << 9);
+       pkt->u.status0.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       pkt->u.status0.relative_offset = cpu_to_le32(prm->cmd->offset);
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe00c,
+           "qla_target(%d): handle(cmd) -> %08x, timeout %d, ox_id %#x\n",
+           vha->vp_idx, pkt->handle, QLA_TGT_TIMEOUT,
+           le16_to_cpu(pkt->u.status0.ox_id));
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. We have already made sure
+ * that there is sufficient amount of request entries to not drop it.
+ */
+static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       int cnt;
+       uint32_t *dword_ptr;
+       int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+
+       /* Build continuation packets */
+       while (prm->seg_cnt > 0) {
+               cont_a64_entry_t *cont_pkt64 =
+                       (cont_a64_entry_t *)qlt_get_req_pkt(vha);
+
+               /*
+                * Make sure that from cont_pkt64 none of
+                * 64-bit specific fields used for 32-bit
+                * addressing. Cast to (cont_entry_t *) for
+                * that.
+                */
+
+               memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+
+               cont_pkt64->entry_count = 1;
+               cont_pkt64->sys_define = 0;
+
+               if (enable_64bit_addressing) {
+                       cont_pkt64->entry_type = CONTINUE_A64_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&cont_pkt64->dseg_0_address;
+               } else {
+                       cont_pkt64->entry_type = CONTINUE_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&((cont_entry_t *)
+                               cont_pkt64)->dseg_0_address;
+               }
+
+               /* Load continuation entry data segments */
+               for (cnt = 0;
+                   cnt < prm->tgt->datasegs_per_cont && prm->seg_cnt;
+                   cnt++, prm->seg_cnt--) {
+                       *dword_ptr++ =
+                           cpu_to_le32(pci_dma_lo32
+                               (sg_dma_address(prm->sg)));
+                       if (enable_64bit_addressing) {
+                               *dword_ptr++ =
+                                   cpu_to_le32(pci_dma_hi32
+                                       (sg_dma_address
+                                       (prm->sg)));
+                       }
+                       *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
+
+                       ql_dbg(ql_dbg_tgt, vha, 0xe00d,
+                           "S/G Segment Cont. phys_addr=%llx:%llx, len=%d\n",
+                           (long long unsigned int)
+                           pci_dma_hi32(sg_dma_address(prm->sg)),
+                           (long long unsigned int)
+                           pci_dma_lo32(sg_dma_address(prm->sg)),
+                           (int)sg_dma_len(prm->sg));
+
+                       prm->sg = sg_next(prm->sg);
+               }
+       }
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. We have already made sure
+ * that there is sufficient amount of request entries to not drop it.
+ */
+static void qlt_load_data_segments(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       int cnt;
+       uint32_t *dword_ptr;
+       int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+       struct ctio7_to_24xx *pkt24 = (struct ctio7_to_24xx *)prm->pkt;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe00e,
+           "iocb->scsi_status=%x, iocb->flags=%x\n",
+           le16_to_cpu(pkt24->u.status0.scsi_status),
+           le16_to_cpu(pkt24->u.status0.flags));
+
+       pkt24->u.status0.transfer_length = cpu_to_le32(prm->cmd->bufflen);
+
+       /* Setup packet address segment pointer */
+       dword_ptr = pkt24->u.status0.dseg_0_address;
+
+       /* Set total data segment count */
+       if (prm->seg_cnt)
+               pkt24->dseg_count = cpu_to_le16(prm->seg_cnt);
+
+       if (prm->seg_cnt == 0) {
+               /* No data transfer */
+               *dword_ptr++ = 0;
+               *dword_ptr = 0;
+               return;
+       }
+
+       /* If scatter gather */
+       ql_dbg(ql_dbg_tgt, vha, 0xe00f, "%s", "Building S/G data segments...");
+
+       /* Load command entry data segments */
+       for (cnt = 0;
+           (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt;
+           cnt++, prm->seg_cnt--) {
+               *dword_ptr++ =
+                   cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg)));
+               if (enable_64bit_addressing) {
+                       *dword_ptr++ =
+                           cpu_to_le32(pci_dma_hi32(
+                               sg_dma_address(prm->sg)));
+               }
+               *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe010,
+                   "S/G Segment phys_addr=%llx:%llx, len=%d\n",
+                   (long long unsigned int)pci_dma_hi32(sg_dma_address(
+                   prm->sg)),
+                   (long long unsigned int)pci_dma_lo32(sg_dma_address(
+                   prm->sg)),
+                   (int)sg_dma_len(prm->sg));
+
+               prm->sg = sg_next(prm->sg);
+       }
+
+       qlt_load_cont_data_segments(prm, vha);
+}
+
+static inline int qlt_has_data(struct qla_tgt_cmd *cmd)
+{
+       return cmd->bufflen > 0;
+}
+
+/*
+ * Called without ha->hardware_lock held
+ */
+static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd,
+       struct qla_tgt_prm *prm, int xmit_type, uint8_t scsi_status,
+       uint32_t *full_req_cnt)
+{
+       struct qla_tgt *tgt = cmd->tgt;
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+
+       if (unlikely(cmd->aborted)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
+                   "qla_target(%d): terminating exchange "
+                   "for aborted cmd=%p (se_cmd=%p, tag=%d)", vha->vp_idx, cmd,
+                   se_cmd, cmd->tag);
+
+               cmd->state = QLA_TGT_STATE_ABORTED;
+
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 0);
+
+               /* !! At this point cmd could be already freed !! */
+               return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe011, "qla_target(%d): tag=%u\n",
+           vha->vp_idx, cmd->tag);
+
+       prm->cmd = cmd;
+       prm->tgt = tgt;
+       prm->rq_result = scsi_status;
+       prm->sense_buffer = &cmd->sense_buffer[0];
+       prm->sense_buffer_len = TRANSPORT_SENSE_BUFFER;
+       prm->sg = NULL;
+       prm->seg_cnt = -1;
+       prm->req_cnt = 1;
+       prm->add_status_pkt = 0;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe012, "rq_result=%x, xmit_type=%x\n",
+           prm->rq_result, xmit_type);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 0) != QLA_SUCCESS)
+               return -EFAULT;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe013, "CTIO start: vha(%d)\n", vha->vp_idx);
+
+       if ((xmit_type & QLA_TGT_XMIT_DATA) && qlt_has_data(cmd)) {
+               if  (qlt_pci_map_calc_cnt(prm) != 0)
+                       return -EAGAIN;
+       }
+
+       *full_req_cnt = prm->req_cnt;
+
+       if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+               prm->residual = se_cmd->residual_count;
+               ql_dbg(ql_dbg_tgt, vha, 0xe014,
+                   "Residual underflow: %d (tag %d, "
+                   "op %x, bufflen %d, rq_result %x)\n", prm->residual,
+                   cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0,
+                   cmd->bufflen, prm->rq_result);
+               prm->rq_result |= SS_RESIDUAL_UNDER;
+       } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+               prm->residual = se_cmd->residual_count;
+               ql_dbg(ql_dbg_tgt, vha, 0xe015,
+                   "Residual overflow: %d (tag %d, "
+                   "op %x, bufflen %d, rq_result %x)\n", prm->residual,
+                   cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0,
+                   cmd->bufflen, prm->rq_result);
+               prm->rq_result |= SS_RESIDUAL_OVER;
+       }
+
+       if (xmit_type & QLA_TGT_XMIT_STATUS) {
+               /*
+                * If QLA_TGT_XMIT_DATA is not set, add_status_pkt will be
+                * ignored in *xmit_response() below
+                */
+               if (qlt_has_data(cmd)) {
+                       if (QLA_TGT_SENSE_VALID(prm->sense_buffer) ||
+                           (IS_FWI2_CAPABLE(ha) &&
+                           (prm->rq_result != 0))) {
+                               prm->add_status_pkt = 1;
+                               (*full_req_cnt)++;
+                       }
+               }
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe016,
+           "req_cnt=%d, full_req_cnt=%d, add_status_pkt=%d\n",
+           prm->req_cnt, *full_req_cnt, prm->add_status_pkt);
+
+       return 0;
+}
+
+static inline int qlt_need_explicit_conf(struct qla_hw_data *ha,
+       struct qla_tgt_cmd *cmd, int sending_sense)
+{
+       if (ha->tgt.enable_class_2)
+               return 0;
+
+       if (sending_sense)
+               return cmd->conf_compl_supported;
+       else
+               return ha->tgt.enable_explicit_conf &&
+                   cmd->conf_compl_supported;
+}
+
+#ifdef CONFIG_QLA_TGT_DEBUG_SRR
+/*
+ *  Original taken from the XFS code
+ */
+static unsigned long qlt_srr_random(void)
+{
+       static int Inited;
+       static unsigned long RandomValue;
+       static DEFINE_SPINLOCK(lock);
+       /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */
+       register long rv;
+       register long lo;
+       register long hi;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+       if (!Inited) {
+               RandomValue = jiffies;
+               Inited = 1;
+       }
+       rv = RandomValue;
+       hi = rv / 127773;
+       lo = rv % 127773;
+       rv = 16807 * lo - 2836 * hi;
+       if (rv <= 0)
+               rv += 2147483647;
+       RandomValue = rv;
+       spin_unlock_irqrestore(&lock, flags);
+       return rv;
+}
+
+static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type)
+{
+#if 0 /* This is not a real status packets lost, so it won't lead to SRR */
+       if ((*xmit_type & QLA_TGT_XMIT_STATUS) && (qlt_srr_random() % 200)
+           == 50) {
+               *xmit_type &= ~QLA_TGT_XMIT_STATUS;
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf015,
+                   "Dropping cmd %p (tag %d) status", cmd, cmd->tag);
+       }
+#endif
+       /*
+        * It's currently not possible to simulate SRRs for FCP_WRITE without
+        * a physical link layer failure, so don't even try here..
+        */
+       if (cmd->dma_data_direction != DMA_FROM_DEVICE)
+               return;
+
+       if (qlt_has_data(cmd) && (cmd->sg_cnt > 1) &&
+           ((qlt_srr_random() % 100) == 20)) {
+               int i, leave = 0;
+               unsigned int tot_len = 0;
+
+               while (leave == 0)
+                       leave = qlt_srr_random() % cmd->sg_cnt;
+
+               for (i = 0; i < leave; i++)
+                       tot_len += cmd->sg[i].length;
+
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf016,
+                   "Cutting cmd %p (tag %d) buffer"
+                   " tail to len %d, sg_cnt %d (cmd->bufflen %d,"
+                   " cmd->sg_cnt %d)", cmd, cmd->tag, tot_len, leave,
+                   cmd->bufflen, cmd->sg_cnt);
+
+               cmd->bufflen = tot_len;
+               cmd->sg_cnt = leave;
+       }
+
+       if (qlt_has_data(cmd) && ((qlt_srr_random() % 100) == 70)) {
+               unsigned int offset = qlt_srr_random() % cmd->bufflen;
+
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf017,
+                   "Cutting cmd %p (tag %d) buffer head "
+                   "to offset %d (cmd->bufflen %d)", cmd, cmd->tag, offset,
+                   cmd->bufflen);
+               if (offset == 0)
+                       *xmit_type &= ~QLA_TGT_XMIT_DATA;
+               else if (qlt_set_data_offset(cmd, offset)) {
+                       ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf018,
+                           "qlt_set_data_offset() failed (tag %d)", cmd->tag);
+               }
+       }
+}
+#else
+static inline void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type)
+{}
+#endif
+
+static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio,
+       struct qla_tgt_prm *prm)
+{
+       prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len,
+           (uint32_t)sizeof(ctio->u.status1.sense_data));
+       ctio->u.status0.flags |=
+           __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS);
+       if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) {
+               ctio->u.status0.flags |= __constant_cpu_to_le16(
+                   CTIO7_FLAGS_EXPLICIT_CONFORM |
+                   CTIO7_FLAGS_CONFORM_REQ);
+       }
+       ctio->u.status0.residual = cpu_to_le32(prm->residual);
+       ctio->u.status0.scsi_status = cpu_to_le16(prm->rq_result);
+       if (QLA_TGT_SENSE_VALID(prm->sense_buffer)) {
+               int i;
+
+               if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) {
+                       if (prm->cmd->se_cmd.scsi_status != 0) {
+                               ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe017,
+                                   "Skipping EXPLICIT_CONFORM and "
+                                   "CTIO7_FLAGS_CONFORM_REQ for FCP READ w/ "
+                                   "non GOOD status\n");
+                               goto skip_explict_conf;
+                       }
+                       ctio->u.status1.flags |= __constant_cpu_to_le16(
+                           CTIO7_FLAGS_EXPLICIT_CONFORM |
+                           CTIO7_FLAGS_CONFORM_REQ);
+               }
+skip_explict_conf:
+               ctio->u.status1.flags &=
+                   ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
+               ctio->u.status1.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
+               ctio->u.status1.scsi_status |=
+                   __constant_cpu_to_le16(SS_SENSE_LEN_VALID);
+               ctio->u.status1.sense_length =
+                   cpu_to_le16(prm->sense_buffer_len);
+               for (i = 0; i < prm->sense_buffer_len/4; i++)
+                       ((uint32_t *)ctio->u.status1.sense_data)[i] =
+                               cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]);
+#if 0
+               if (unlikely((prm->sense_buffer_len % 4) != 0)) {
+                       static int q;
+                       if (q < 10) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe04f,
+                                   "qla_target(%d): %d bytes of sense "
+                                   "lost", prm->tgt->ha->vp_idx,
+                                   prm->sense_buffer_len % 4);
+                               q++;
+                       }
+               }
+#endif
+       } else {
+               ctio->u.status1.flags &=
+                   ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
+               ctio->u.status1.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
+               ctio->u.status1.sense_length = 0;
+               memset(ctio->u.status1.sense_data, 0,
+                   sizeof(ctio->u.status1.sense_data));
+       }
+
+       /* Sense with len > 24, is it possible ??? */
+}
+
+/*
+ * Callback to setup response of xmit_type of QLA_TGT_XMIT_DATA and *
+ * QLA_TGT_XMIT_STATUS for >= 24xx silicon
+ */
+int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
+       uint8_t scsi_status)
+{
+       struct scsi_qla_host *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct ctio7_to_24xx *pkt;
+       struct qla_tgt_prm prm;
+       uint32_t full_req_cnt = 0;
+       unsigned long flags = 0;
+       int res;
+
+       memset(&prm, 0, sizeof(prm));
+       qlt_check_srr_debug(cmd, &xmit_type);
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe018,
+           "is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, "
+           "cmd->dma_data_direction=%d\n", (xmit_type & QLA_TGT_XMIT_STATUS) ?
+           1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction);
+
+       res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status,
+           &full_req_cnt);
+       if (unlikely(res != 0)) {
+               if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED)
+                       return 0;
+
+               return res;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       /* Does F/W have an IOCBs for this request */
+       res = qlt_check_reserve_free_req(vha, full_req_cnt);
+       if (unlikely(res))
+               goto out_unmap_unlock;
+
+       res = qlt_24xx_build_ctio_pkt(&prm, vha);
+       if (unlikely(res != 0))
+               goto out_unmap_unlock;
+
+
+       pkt = (struct ctio7_to_24xx *)prm.pkt;
+
+       if (qlt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) {
+               pkt->u.status0.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN |
+                       CTIO7_FLAGS_STATUS_MODE_0);
+
+               qlt_load_data_segments(&prm, vha);
+
+               if (prm.add_status_pkt == 0) {
+                       if (xmit_type & QLA_TGT_XMIT_STATUS) {
+                               pkt->u.status0.scsi_status =
+                                   cpu_to_le16(prm.rq_result);
+                               pkt->u.status0.residual =
+                                   cpu_to_le32(prm.residual);
+                               pkt->u.status0.flags |= __constant_cpu_to_le16(
+                                   CTIO7_FLAGS_SEND_STATUS);
+                               if (qlt_need_explicit_conf(ha, cmd, 0)) {
+                                       pkt->u.status0.flags |=
+                                           __constant_cpu_to_le16(
+                                               CTIO7_FLAGS_EXPLICIT_CONFORM |
+                                               CTIO7_FLAGS_CONFORM_REQ);
+                               }
+                       }
+
+               } else {
+                       /*
+                        * We have already made sure that there is sufficient
+                        * amount of request entries to not drop HW lock in
+                        * req_pkt().
+                        */
+                       struct ctio7_to_24xx *ctio =
+                               (struct ctio7_to_24xx *)qlt_get_req_pkt(vha);
+
+                       ql_dbg(ql_dbg_tgt, vha, 0xe019,
+                           "Building additional status packet\n");
+
+                       memcpy(ctio, pkt, sizeof(*ctio));
+                       ctio->entry_count = 1;
+                       ctio->dseg_count = 0;
+                       ctio->u.status1.flags &= ~__constant_cpu_to_le16(
+                           CTIO7_FLAGS_DATA_IN);
+
+                       /* Real finish is ctio_m1's finish */
+                       pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK;
+                       pkt->u.status0.flags |= __constant_cpu_to_le16(
+                           CTIO7_FLAGS_DONT_RET_CTIO);
+                       qlt_24xx_init_ctio_to_isp((struct ctio7_to_24xx *)ctio,
+                           &prm);
+                       pr_debug("Status CTIO7: %p\n", ctio);
+               }
+       } else
+               qlt_24xx_init_ctio_to_isp(pkt, &prm);
+
+
+       cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01a,
+           "Xmitting CTIO7 response pkt for 24xx: %p scsi_status: 0x%02x\n",
+           pkt, scsi_status);
+
+       qla2x00_start_iocbs(vha, vha->req);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return 0;
+
+out_unmap_unlock:
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+EXPORT_SYMBOL(qlt_xmit_response);
+
+int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
+{
+       struct ctio7_to_24xx *pkt;
+       struct scsi_qla_host *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = cmd->tgt;
+       struct qla_tgt_prm prm;
+       unsigned long flags;
+       int res = 0;
+
+       memset(&prm, 0, sizeof(prm));
+       prm.cmd = cmd;
+       prm.tgt = tgt;
+       prm.sg = NULL;
+       prm.req_cnt = 1;
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 0) != QLA_SUCCESS)
+               return -EIO;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01b, "CTIO_start: vha(%d)",
+           (int)vha->vp_idx);
+
+       /* Calculate number of entries and segments required */
+       if (qlt_pci_map_calc_cnt(&prm) != 0)
+               return -EAGAIN;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       /* Does F/W have an IOCBs for this request */
+       res = qlt_check_reserve_free_req(vha, prm.req_cnt);
+       if (res != 0)
+               goto out_unlock_free_unmap;
+
+       res = qlt_24xx_build_ctio_pkt(&prm, vha);
+       if (unlikely(res != 0))
+               goto out_unlock_free_unmap;
+       pkt = (struct ctio7_to_24xx *)prm.pkt;
+       pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT |
+           CTIO7_FLAGS_STATUS_MODE_0);
+       qlt_load_data_segments(&prm, vha);
+
+       cmd->state = QLA_TGT_STATE_NEED_DATA;
+
+       qla2x00_start_iocbs(vha, vha->req);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+
+out_unlock_free_unmap:
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+EXPORT_SYMBOL(qlt_rdy_to_xfer);
+
+/* If hardware_lock held on entry, might drop it, then reaquire */
+/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
+static int __qlt_send_term_exchange(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd,
+       struct atio_from_isp *atio)
+{
+       struct ctio7_to_24xx *ctio24;
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       int ret = 0;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01c, "Sending TERM EXCH CTIO (ha=%p)\n", ha);
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (pkt == NULL) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe050,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+
+       if (cmd != NULL) {
+               if (cmd->state < QLA_TGT_STATE_PROCESSED) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe051,
+                           "qla_target(%d): Terminating cmd %p with "
+                           "incorrect state %d\n", vha->vp_idx, cmd,
+                           cmd->state);
+               } else
+                       ret = 1;
+       }
+
+       pkt->entry_count = 1;
+       pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+
+       ctio24 = (struct ctio7_to_24xx *)pkt;
+       ctio24->entry_type = CTIO_TYPE7;
+       ctio24->nport_handle = cmd ? cmd->loop_id : CTIO7_NHANDLE_UNRECOGNIZED;
+       ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio24->vp_index = vha->vp_idx;
+       ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio24->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio24->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_TERMINATE);
+       ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+
+       /* Most likely, it isn't needed */
+       ctio24->u.status1.residual = get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]);
+       if (ctio24->u.status1.residual != 0)
+               ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER;
+
+       qla2x00_start_iocbs(vha, vha->req);
+       return ret;
+}
+
+static void qlt_send_term_exchange(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked)
+{
+       unsigned long flags;
+       int rc;
+
+       if (qlt_issue_marker(vha, ha_locked) < 0)
+               return;
+
+       if (ha_locked) {
+               rc = __qlt_send_term_exchange(vha, cmd, atio);
+               goto done;
+       }
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       rc = __qlt_send_term_exchange(vha, cmd, atio);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+done:
+       if (rc == 1) {
+               if (!ha_locked && !in_interrupt())
+                       msleep(250); /* just in case */
+
+               vha->hw->tgt.tgt_ops->free_cmd(cmd);
+       }
+}
+
+void qlt_free_cmd(struct qla_tgt_cmd *cmd)
+{
+       BUG_ON(cmd->sg_mapped);
+
+       if (unlikely(cmd->free_sg))
+               kfree(cmd->sg);
+       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
+}
+EXPORT_SYMBOL(qlt_free_cmd);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_prepare_srr_ctio(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd, void *ctio)
+{
+       struct qla_tgt_srr_ctio *sc;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_srr_imm *imm;
+
+       tgt->ctio_srr_id++;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf019,
+           "qla_target(%d): CTIO with SRR status received\n", vha->vp_idx);
+
+       if (!ctio) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf055,
+                   "qla_target(%d): SRR CTIO, but ctio is NULL\n",
+                   vha->vp_idx);
+               return -EINVAL;
+       }
+
+       sc = kzalloc(sizeof(*sc), GFP_ATOMIC);
+       if (sc != NULL) {
+               sc->cmd = cmd;
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               sc->srr_id = tgt->ctio_srr_id;
+               list_add_tail(&sc->srr_list_entry,
+                   &tgt->srr_ctio_list);
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01a,
+                   "CTIO SRR %p added (id %d)\n", sc, sc->srr_id);
+               if (tgt->imm_srr_id == tgt->ctio_srr_id) {
+                       int found = 0;
+                       list_for_each_entry(imm, &tgt->srr_imm_list,
+                           srr_list_entry) {
+                               if (imm->srr_id == sc->srr_id) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01b,
+                                   "Scheduling srr work\n");
+                               schedule_work(&tgt->srr_work);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf056,
+                                   "qla_target(%d): imm_srr_id "
+                                   "== ctio_srr_id (%d), but there is no "
+                                   "corresponding SRR IMM, deleting CTIO "
+                                   "SRR %p\n", vha->vp_idx,
+                                   tgt->ctio_srr_id, sc);
+                               list_del(&sc->srr_list_entry);
+                               spin_unlock(&tgt->srr_lock);
+
+                               kfree(sc);
+                               return -EINVAL;
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+       } else {
+               struct qla_tgt_srr_imm *ti;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf057,
+                   "qla_target(%d): Unable to allocate SRR CTIO entry\n",
+                   vha->vp_idx);
+               spin_lock(&tgt->srr_lock);
+               list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list,
+                   srr_list_entry) {
+                       if (imm->srr_id == tgt->ctio_srr_id) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01c,
+                                   "IMM SRR %p deleted (id %d)\n",
+                                   imm, imm->srr_id);
+                               list_del(&imm->srr_list_entry);
+                               qlt_reject_free_srr_imm(vha, imm, 1);
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio,
+       struct qla_tgt_cmd *cmd, uint32_t status)
+{
+       int term = 0;
+
+       if (ctio != NULL) {
+               struct ctio7_from_24xx *c = (struct ctio7_from_24xx *)ctio;
+               term = !(c->flags &
+                   __constant_cpu_to_le16(OF_TERM_EXCH));
+       } else
+               term = 1;
+
+       if (term)
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
+
+       return term;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline struct qla_tgt_cmd *qlt_get_cmd(struct scsi_qla_host *vha,
+       uint32_t handle)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       handle--;
+       if (ha->tgt.cmds[handle] != NULL) {
+               struct qla_tgt_cmd *cmd = ha->tgt.cmds[handle];
+               ha->tgt.cmds[handle] = NULL;
+               return cmd;
+       } else
+               return NULL;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static struct qla_tgt_cmd *qlt_ctio_to_cmd(struct scsi_qla_host *vha,
+       uint32_t handle, void *ctio)
+{
+       struct qla_tgt_cmd *cmd = NULL;
+
+       /* Clear out internal marks */
+       handle &= ~(CTIO_COMPLETION_HANDLE_MARK |
+           CTIO_INTERMEDIATE_HANDLE_MARK);
+
+       if (handle != QLA_TGT_NULL_HANDLE) {
+               if (unlikely(handle == QLA_TGT_SKIP_HANDLE)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe01d, "%s",
+                           "SKIP_HANDLE CTIO\n");
+                       return NULL;
+               }
+               /* handle-1 is actually used */
+               if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe052,
+                           "qla_target(%d): Wrong handle %x received\n",
+                           vha->vp_idx, handle);
+                       return NULL;
+               }
+               cmd = qlt_get_cmd(vha, handle);
+               if (unlikely(cmd == NULL)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe053,
+                           "qla_target(%d): Suspicious: unable to "
+                           "find the command with handle %x\n", vha->vp_idx,
+                           handle);
+                       return NULL;
+               }
+       } else if (ctio != NULL) {
+               /* We can't get loop ID from CTIO7 */
+               ql_dbg(ql_dbg_tgt, vha, 0xe054,
+                   "qla_target(%d): Wrong CTIO received: QLA24xx doesn't "
+                   "support NULL handles\n", vha->vp_idx);
+               return NULL;
+       }
+
+       return cmd;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
+       uint32_t status, void *ctio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct se_cmd *se_cmd;
+       struct target_core_fabric_ops *tfo;
+       struct qla_tgt_cmd *cmd;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01e,
+           "qla_target(%d): handle(ctio %p status %#x) <- %08x\n",
+           vha->vp_idx, ctio, status, handle);
+
+       if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) {
+               /* That could happen only in case of an error/reset/abort */
+               if (status != CTIO_SUCCESS) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01d,
+                           "Intermediate CTIO received"
+                           " (status %x)\n", status);
+               }
+               return;
+       }
+
+       cmd = qlt_ctio_to_cmd(vha, handle, ctio);
+       if (cmd == NULL) {
+               if (status != CTIO_SUCCESS)
+                       qlt_term_ctio_exchange(vha, ctio, NULL, status);
+               return;
+       }
+       se_cmd = &cmd->se_cmd;
+       tfo = se_cmd->se_tfo;
+
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+
+       if (unlikely(status != CTIO_SUCCESS)) {
+               switch (status & 0xFFFF) {
+               case CTIO_LIP_RESET:
+               case CTIO_TARGET_RESET:
+               case CTIO_ABORTED:
+               case CTIO_TIMEOUT:
+               case CTIO_INVALID_RX_ID:
+                       /* They are OK */
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf058,
+                           "qla_target(%d): CTIO with "
+                           "status %#x received, state %x, se_cmd %p, "
+                           "(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, "
+                           "TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx,
+                           status, cmd->state, se_cmd);
+                       break;
+
+               case CTIO_PORT_LOGGED_OUT:
+               case CTIO_PORT_UNAVAILABLE:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059,
+                           "qla_target(%d): CTIO with PORT LOGGED "
+                           "OUT (29) or PORT UNAVAILABLE (28) status %x "
+                           "received (state %x, se_cmd %p)\n", vha->vp_idx,
+                           status, cmd->state, se_cmd);
+                       break;
+
+               case CTIO_SRR_RECEIVED:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05a,
+                           "qla_target(%d): CTIO with SRR_RECEIVED"
+                           " status %x received (state %x, se_cmd %p)\n",
+                           vha->vp_idx, status, cmd->state, se_cmd);
+                       if (qlt_prepare_srr_ctio(vha, cmd, ctio) != 0)
+                               break;
+                       else
+                               return;
+
+               default:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
+                           "qla_target(%d): CTIO with error status "
+                           "0x%x received (state %x, se_cmd %p\n",
+                           vha->vp_idx, status, cmd->state, se_cmd);
+                       break;
+               }
+
+               if (cmd->state != QLA_TGT_STATE_NEED_DATA)
+                       if (qlt_term_ctio_exchange(vha, ctio, cmd, status))
+                               return;
+       }
+
+       if (cmd->state == QLA_TGT_STATE_PROCESSED) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe01f, "Command %p finished\n", cmd);
+       } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
+               int rx_status = 0;
+
+               cmd->state = QLA_TGT_STATE_DATA_IN;
+
+               if (unlikely(status != CTIO_SUCCESS))
+                       rx_status = -EIO;
+               else
+                       cmd->write_data_transferred = 1;
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe020,
+                   "Data received, context %x, rx_status %d\n",
+                   0x0, rx_status);
+
+               ha->tgt.tgt_ops->handle_data(cmd);
+               return;
+       } else if (cmd->state == QLA_TGT_STATE_ABORTED) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e,
+                   "Aborted command %p (tag %d) finished\n", cmd, cmd->tag);
+       } else {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c,
+                   "qla_target(%d): A command in state (%d) should "
+                   "not return a CTIO complete\n", vha->vp_idx, cmd->state);
+       }
+
+       if (unlikely(status != CTIO_SUCCESS)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01f, "Finishing failed CTIO\n");
+               dump_stack();
+       }
+
+       ha->tgt.tgt_ops->free_cmd(cmd);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+void qlt_ctio_completion(struct scsi_qla_host *vha, uint32_t handle)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+
+       if (likely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe021,
+                   "CTIO, but target mode not enabled"
+                   " (ha %d %p handle %#x)", vha->vp_idx, ha, handle);
+               return;
+       }
+
+       tgt->irq_cmd_count++;
+       qlt_do_ctio_completion(vha, handle, CTIO_SUCCESS, NULL);
+       tgt->irq_cmd_count--;
+}
+
+static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha,
+       uint8_t task_codes)
+{
+       int fcp_task_attr;
+
+       switch (task_codes) {
+       case ATIO_SIMPLE_QUEUE:
+               fcp_task_attr = MSG_SIMPLE_TAG;
+               break;
+       case ATIO_HEAD_OF_QUEUE:
+               fcp_task_attr = MSG_HEAD_TAG;
+               break;
+       case ATIO_ORDERED_QUEUE:
+               fcp_task_attr = MSG_ORDERED_TAG;
+               break;
+       case ATIO_ACA_QUEUE:
+               fcp_task_attr = MSG_ACA_TAG;
+               break;
+       case ATIO_UNTAGGED:
+               fcp_task_attr = MSG_SIMPLE_TAG;
+               break;
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05d,
+                   "qla_target: unknown task code %x, use ORDERED instead\n",
+                   task_codes);
+               fcp_task_attr = MSG_ORDERED_TAG;
+               break;
+       }
+
+       return fcp_task_attr;
+}
+
+static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *,
+                                       uint8_t *);
+/*
+ * Process context for I/O path into tcm_qla2xxx code
+ */
+static void qlt_do_work(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       scsi_qla_host_t *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess = NULL;
+       struct atio_from_isp *atio = &cmd->atio;
+       unsigned char *cdb;
+       unsigned long flags;
+       uint32_t data_length;
+       int ret, fcp_task_attr, data_dir, bidi = 0;
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           atio->u.isp24.fcp_hdr.s_id);
+       if (sess) {
+               if (unlikely(sess->tearing_down)) {
+                       sess = NULL;
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       goto out_term;
+               } else {
+                       /*
+                        * Do the extra kref_get() before dropping
+                        * qla_hw_data->hardware_lock.
+                        */
+                       kref_get(&sess->se_sess->sess_kref);
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       if (unlikely(!sess)) {
+               uint8_t *s_id = atio->u.isp24.fcp_hdr.s_id;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
+                       "qla_target(%d): Unable to find wwn login"
+                       " (s_id %x:%x:%x), trying to create it manually\n",
+                       vha->vp_idx, s_id[0], s_id[1], s_id[2]);
+
+               if (atio->u.raw.entry_count > 1) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
+                               "Dropping multy entry cmd %p\n", cmd);
+                       goto out_term;
+               }
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has an extra creation ref. */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               if (!sess)
+                       goto out_term;
+       }
+
+       cmd->sess = sess;
+       cmd->loop_id = sess->loop_id;
+       cmd->conf_compl_supported = sess->conf_compl_supported;
+
+       cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
+       cmd->tag = atio->u.isp24.exchange_addr;
+       cmd->unpacked_lun = scsilun_to_int(
+           (struct scsi_lun *)&atio->u.isp24.fcp_cmnd.lun);
+
+       if (atio->u.isp24.fcp_cmnd.rddata &&
+           atio->u.isp24.fcp_cmnd.wrdata) {
+               bidi = 1;
+               data_dir = DMA_TO_DEVICE;
+       } else if (atio->u.isp24.fcp_cmnd.rddata)
+               data_dir = DMA_FROM_DEVICE;
+       else if (atio->u.isp24.fcp_cmnd.wrdata)
+               data_dir = DMA_TO_DEVICE;
+       else
+               data_dir = DMA_NONE;
+
+       fcp_task_attr = qlt_get_fcp_task_attr(vha,
+           atio->u.isp24.fcp_cmnd.task_attr);
+       data_length = be32_to_cpu(get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]));
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe022,
+           "qla_target: START qla command: %p lun: 0x%04x (tag %d)\n",
+           cmd, cmd->unpacked_lun, cmd->tag);
+
+       ret = vha->hw->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
+           fcp_task_attr, data_dir, bidi);
+       if (ret != 0)
+               goto out_term;
+       /*
+        * Drop extra session reference from qla_tgt_handle_cmd_for_atio*(
+        */
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf020, "Terminating work cmd %p", cmd);
+       /*
+        * cmd has not sent to target yet, so pass NULL as the second argument
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_term_exchange(vha, NULL, &cmd->atio, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_cmd *cmd;
+
+       if (unlikely(tgt->tgt_stop)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf021,
+                   "New command while device %p is shutting down\n", tgt);
+               return -EFAULT;
+       }
+
+       cmd = kmem_cache_zalloc(qla_tgt_cmd_cachep, GFP_ATOMIC);
+       if (!cmd) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05e,
+                   "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&cmd->cmd_list);
+
+       memcpy(&cmd->atio, atio, sizeof(*atio));
+       cmd->state = QLA_TGT_STATE_NEW;
+       cmd->tgt = ha->tgt.qla_tgt;
+       cmd->vha = vha;
+
+       INIT_WORK(&cmd->work, qlt_do_work);
+       queue_work(qla_tgt_wq, &cmd->work);
+       return 0;
+
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
+       int fn, void *iocb, int flags)
+{
+       struct scsi_qla_host *vha = sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       int res;
+       uint8_t tmr_func;
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (!mcmd) {
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10009,
+                   "qla_target(%d): Allocation of management "
+                   "command failed, some commands and their data could "
+                   "leak\n", vha->vp_idx);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+       mcmd->sess = sess;
+
+       if (iocb) {
+               memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
+                   sizeof(mcmd->orig_iocb.imm_ntfy));
+       }
+       mcmd->tmr_func = fn;
+       mcmd->flags = flags;
+
+       switch (fn) {
+       case QLA_TGT_CLEAR_ACA:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10000,
+                   "qla_target(%d): CLEAR_ACA received\n", sess->vha->vp_idx);
+               tmr_func = TMR_CLEAR_ACA;
+               break;
+
+       case QLA_TGT_TARGET_RESET:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10001,
+                   "qla_target(%d): TARGET_RESET received\n",
+                   sess->vha->vp_idx);
+               tmr_func = TMR_TARGET_WARM_RESET;
+               break;
+
+       case QLA_TGT_LUN_RESET:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002,
+                   "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx);
+               tmr_func = TMR_LUN_RESET;
+               break;
+
+       case QLA_TGT_CLEAR_TS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10003,
+                   "qla_target(%d): CLEAR_TS received\n", sess->vha->vp_idx);
+               tmr_func = TMR_CLEAR_TASK_SET;
+               break;
+
+       case QLA_TGT_ABORT_TS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10004,
+                   "qla_target(%d): ABORT_TS received\n", sess->vha->vp_idx);
+               tmr_func = TMR_ABORT_TASK_SET;
+               break;
+#if 0
+       case QLA_TGT_ABORT_ALL:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10005,
+                   "qla_target(%d): Doing ABORT_ALL_TASKS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_ABORT_ALL_SESS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10006,
+                   "qla_target(%d): Doing ABORT_ALL_TASKS_SESS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_NEXUS_LOSS_SESS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10007,
+                   "qla_target(%d): Doing NEXUS_LOSS_SESS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_NEXUS_LOSS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10008,
+                   "qla_target(%d): Doing NEXUS_LOSS\n", sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+#endif
+       default:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000a,
+                   "qla_target(%d): Unknown task mgmt fn 0x%x\n",
+                   sess->vha->vp_idx, fn);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -ENOSYS;
+       }
+
+       res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, tmr_func, 0);
+       if (res != 0) {
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000b,
+                   "qla_target(%d): tgt.tgt_ops->handle_tmr() failed: %d\n",
+                   sess->vha->vp_idx, res);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb)
+{
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt;
+       struct qla_tgt_sess *sess;
+       uint32_t lun, unpacked_lun;
+       int lun_size, fn;
+
+       tgt = ha->tgt.qla_tgt;
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       lun_size = sizeof(a->u.isp24.fcp_cmnd.lun);
+       fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           a->u.isp24.fcp_hdr.s_id);
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf024,
+                   "qla_target(%d): task mgmt fn 0x%x for "
+                   "non-existant session\n", vha->vp_idx, fn);
+               return qlt_sched_sess_work(tgt, QLA_TGT_SESS_WORK_TM, iocb,
+                   sizeof(struct atio_from_isp));
+       }
+
+       return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __qlt_abort_task(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb, struct qla_tgt_sess *sess)
+{
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       uint32_t lun, unpacked_lun;
+       int rc;
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05f,
+                   "qla_target(%d): %s: Allocation of ABORT cmd failed\n",
+                   vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+
+       mcmd->sess = sess;
+       memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
+           sizeof(mcmd->orig_iocb.imm_ntfy));
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, TMR_ABORT_TASK,
+           le16_to_cpu(iocb->u.isp2x.seq_id));
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf060,
+                   "qla_target(%d): tgt_ops->handle_tmr() failed: %d\n",
+                   vha->vp_idx, rc);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_abort_task(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       int loop_id;
+
+       loop_id = GET_TARGET_ID(ha, (struct atio_from_isp *)iocb);
+
+       sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
+       if (sess == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf025,
+                   "qla_target(%d): task abort for unexisting "
+                   "session\n", vha->vp_idx);
+               return qlt_sched_sess_work(ha->tgt.qla_tgt,
+                   QLA_TGT_SESS_WORK_ABORT, iocb, sizeof(*iocb));
+       }
+
+       return __qlt_abort_task(vha, iocb, sess);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       int res = 0;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026,
+           "qla_target(%d): Port ID: 0x%02x:%02x:%02x"
+           " ELS opcode: 0x%02x\n", vha->vp_idx, iocb->u.isp24.port_id[0],
+           iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[2],
+           iocb->u.isp24.status_subcode);
+
+       switch (iocb->u.isp24.status_subcode) {
+       case ELS_PLOGI:
+       case ELS_FLOGI:
+       case ELS_PRLI:
+       case ELS_LOGO:
+       case ELS_PRLO:
+               res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
+               break;
+       case ELS_PDISC:
+       case ELS_ADISC:
+       {
+               struct qla_tgt *tgt = ha->tgt.qla_tgt;
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, &tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+                       tgt->link_reinit_iocb_pending = 0;
+               }
+               res = 1; /* send notify ack */
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
+                   "qla_target(%d): Unsupported ELS command %x "
+                   "received\n", vha->vp_idx, iocb->u.isp24.status_subcode);
+               res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
+               break;
+       }
+
+       return res;
+}
+
+static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
+{
+       struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL;
+       size_t first_offset = 0, rem_offset = offset, tmp = 0;
+       int i, sg_srr_cnt, bufflen = 0;
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe023,
+           "Entering qla_tgt_set_data_offset: cmd: %p, cmd->sg: %p, "
+           "cmd->sg_cnt: %u, direction: %d\n",
+           cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+
+       /*
+        * FIXME: Reject non zero SRR relative offset until we can test
+        * this code properly.
+        */
+       pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
+       return -1;
+
+       if (!cmd->sg || !cmd->sg_cnt) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055,
+                   "Missing cmd->sg or zero cmd->sg_cnt in"
+                   " qla_tgt_set_data_offset\n");
+               return -EINVAL;
+       }
+       /*
+        * Walk the current cmd->sg list until we locate the new sg_srr_start
+        */
+       for_each_sg(cmd->sg, sg, cmd->sg_cnt, i) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe024,
+                   "sg[%d]: %p page: %p, length: %d, offset: %d\n",
+                   i, sg, sg_page(sg), sg->length, sg->offset);
+
+               if ((sg->length + tmp) > offset) {
+                       first_offset = rem_offset;
+                       sg_srr_start = sg;
+                       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe025,
+                           "Found matching sg[%d], using %p as sg_srr_start, "
+                           "and using first_offset: %zu\n", i, sg,
+                           first_offset);
+                       break;
+               }
+               tmp += sg->length;
+               rem_offset -= sg->length;
+       }
+
+       if (!sg_srr_start) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe056,
+                   "Unable to locate sg_srr_start for offset: %u\n", offset);
+               return -EINVAL;
+       }
+       sg_srr_cnt = (cmd->sg_cnt - i);
+
+       sg_srr = kzalloc(sizeof(struct scatterlist) * sg_srr_cnt, GFP_KERNEL);
+       if (!sg_srr) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe057,
+                   "Unable to allocate sgp\n");
+               return -ENOMEM;
+       }
+       sg_init_table(sg_srr, sg_srr_cnt);
+       sgp = &sg_srr[0];
+       /*
+        * Walk the remaining list for sg_srr_start, mapping to the newly
+        * allocated sg_srr taking first_offset into account.
+        */
+       for_each_sg(sg_srr_start, sg, sg_srr_cnt, i) {
+               if (first_offset) {
+                       sg_set_page(sgp, sg_page(sg),
+                           (sg->length - first_offset), first_offset);
+                       first_offset = 0;
+               } else {
+                       sg_set_page(sgp, sg_page(sg), sg->length, 0);
+               }
+               bufflen += sgp->length;
+
+               sgp = sg_next(sgp);
+               if (!sgp)
+                       break;
+       }
+
+       cmd->sg = sg_srr;
+       cmd->sg_cnt = sg_srr_cnt;
+       cmd->bufflen = bufflen;
+       cmd->offset += offset;
+       cmd->free_sg = 1;
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe026, "New cmd->sg: %p\n", cmd->sg);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe027, "New cmd->sg_cnt: %u\n",
+           cmd->sg_cnt);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe028, "New cmd->bufflen: %u\n",
+           cmd->bufflen);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe029, "New cmd->offset: %u\n",
+           cmd->offset);
+
+       if (cmd->sg_cnt < 0)
+               BUG();
+
+       if (cmd->bufflen < 0)
+               BUG();
+
+       return 0;
+}
+
+static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd,
+       uint32_t srr_rel_offs, int *xmit_type)
+{
+       int res = 0, rel_offs;
+
+       rel_offs = srr_rel_offs - cmd->offset;
+       ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf027, "srr_rel_offs=%d, rel_offs=%d",
+           srr_rel_offs, rel_offs);
+
+       *xmit_type = QLA_TGT_XMIT_ALL;
+
+       if (rel_offs < 0) {
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf062,
+                   "qla_target(%d): SRR rel_offs (%d) < 0",
+                   cmd->vha->vp_idx, rel_offs);
+               res = -1;
+       } else if (rel_offs == cmd->bufflen)
+               *xmit_type = QLA_TGT_XMIT_STATUS;
+       else if (rel_offs > 0)
+               res = qlt_set_data_offset(cmd, rel_offs);
+
+       return res;
+}
+
+/* No locks, thread context */
+static void qlt_handle_srr(struct scsi_qla_host *vha,
+       struct qla_tgt_srr_ctio *sctio, struct qla_tgt_srr_imm *imm)
+{
+       struct imm_ntfy_from_isp *ntfy =
+           (struct imm_ntfy_from_isp *)&imm->imm_ntfy;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_cmd *cmd = sctio->cmd;
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       unsigned long flags;
+       int xmit_type = 0, resp = 0;
+       uint32_t offset;
+       uint16_t srr_ui;
+
+       offset = le32_to_cpu(ntfy->u.isp24.srr_rel_offs);
+       srr_ui = ntfy->u.isp24.srr_ui;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf028, "SRR cmd %p, srr_ui %x\n",
+           cmd, srr_ui);
+
+       switch (srr_ui) {
+       case SRR_IU_STATUS:
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               qlt_send_notify_ack(vha, ntfy,
+                   0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               xmit_type = QLA_TGT_XMIT_STATUS;
+               resp = 1;
+               break;
+       case SRR_IU_DATA_IN:
+               if (!cmd->sg || !cmd->sg_cnt) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf063,
+                           "Unable to process SRR_IU_DATA_IN due to"
+                           " missing cmd->sg, state: %d\n", cmd->state);
+                       dump_stack();
+                       goto out_reject;
+               }
+               if (se_cmd->scsi_status != 0) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe02a,
+                           "Rejecting SRR_IU_DATA_IN with non GOOD "
+                           "scsi_status\n");
+                       goto out_reject;
+               }
+               cmd->bufflen = se_cmd->data_length;
+
+               if (qlt_has_data(cmd)) {
+                       if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0)
+                               goto out_reject;
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       qlt_send_notify_ack(vha, ntfy,
+                           0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       resp = 1;
+               } else {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf064,
+                           "qla_target(%d): SRR for in data for cmd "
+                           "without them (tag %d, SCSI status %d), "
+                           "reject", vha->vp_idx, cmd->tag,
+                           cmd->se_cmd.scsi_status);
+                       goto out_reject;
+               }
+               break;
+       case SRR_IU_DATA_OUT:
+               if (!cmd->sg || !cmd->sg_cnt) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf065,
+                           "Unable to process SRR_IU_DATA_OUT due to"
+                           " missing cmd->sg\n");
+                       dump_stack();
+                       goto out_reject;
+               }
+               if (se_cmd->scsi_status != 0) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe02b,
+                           "Rejecting SRR_IU_DATA_OUT"
+                           " with non GOOD scsi_status\n");
+                       goto out_reject;
+               }
+               cmd->bufflen = se_cmd->data_length;
+
+               if (qlt_has_data(cmd)) {
+                       if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0)
+                               goto out_reject;
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       qlt_send_notify_ack(vha, ntfy,
+                           0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       if (xmit_type & QLA_TGT_XMIT_DATA)
+                               qlt_rdy_to_xfer(cmd);
+               } else {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf066,
+                           "qla_target(%d): SRR for out data for cmd "
+                           "without them (tag %d, SCSI status %d), "
+                           "reject", vha->vp_idx, cmd->tag,
+                           cmd->se_cmd.scsi_status);
+                       goto out_reject;
+               }
+               break;
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf067,
+                   "qla_target(%d): Unknown srr_ui value %x",
+                   vha->vp_idx, srr_ui);
+               goto out_reject;
+       }
+
+       /* Transmit response in case of status and data-in cases */
+       if (resp)
+               qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status);
+
+       return;
+
+out_reject:
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_notify_ack(vha, ntfy, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+       if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
+               cmd->state = QLA_TGT_STATE_DATA_IN;
+               dump_stack();
+       } else
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void qlt_reject_free_srr_imm(struct scsi_qla_host *vha,
+       struct qla_tgt_srr_imm *imm, int ha_locked)
+{
+       struct qla_hw_data *ha = vha->hw;
+       unsigned long flags = 0;
+
+       if (!ha_locked)
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       qlt_send_notify_ack(vha, (void *)&imm->imm_ntfy, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+
+       if (!ha_locked)
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       kfree(imm);
+}
+
+static void qlt_handle_srr_work(struct work_struct *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt, srr_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_tgt_srr_ctio *sctio;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf029, "Entering SRR work (tgt %p)\n",
+           tgt);
+
+restart:
+       spin_lock_irqsave(&tgt->srr_lock, flags);
+       list_for_each_entry(sctio, &tgt->srr_ctio_list, srr_list_entry) {
+               struct qla_tgt_srr_imm *imm, *i, *ti;
+               struct qla_tgt_cmd *cmd;
+               struct se_cmd *se_cmd;
+
+               imm = NULL;
+               list_for_each_entry_safe(i, ti, &tgt->srr_imm_list,
+                                               srr_list_entry) {
+                       if (i->srr_id == sctio->srr_id) {
+                               list_del(&i->srr_list_entry);
+                               if (imm) {
+                                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf068,
+                                         "qla_target(%d): There must be "
+                                         "only one IMM SRR per CTIO SRR "
+                                         "(IMM SRR %p, id %d, CTIO %p\n",
+                                         vha->vp_idx, i, i->srr_id, sctio);
+                                       qlt_reject_free_srr_imm(tgt->vha, i, 0);
+                               } else
+                                       imm = i;
+                       }
+               }
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02a,
+                   "IMM SRR %p, CTIO SRR %p (id %d)\n", imm, sctio,
+                   sctio->srr_id);
+
+               if (imm == NULL) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02b,
+                           "Not found matching IMM for SRR CTIO (id %d)\n",
+                           sctio->srr_id);
+                       continue;
+               } else
+                       list_del(&sctio->srr_list_entry);
+
+               spin_unlock_irqrestore(&tgt->srr_lock, flags);
+
+               cmd = sctio->cmd;
+               /*
+                * Reset qla_tgt_cmd SRR values and SGL pointer+count to follow
+                * tcm_qla2xxx_write_pending() and tcm_qla2xxx_queue_data_in()
+                * logic..
+                */
+               cmd->offset = 0;
+               if (cmd->free_sg) {
+                       kfree(cmd->sg);
+                       cmd->sg = NULL;
+                       cmd->free_sg = 0;
+               }
+               se_cmd = &cmd->se_cmd;
+
+               cmd->sg_cnt = se_cmd->t_data_nents;
+               cmd->sg = se_cmd->t_data_sg;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02c,
+                   "SRR cmd %p (se_cmd %p, tag %d, op %x), "
+                   "sg_cnt=%d, offset=%d", cmd, &cmd->se_cmd, cmd->tag,
+                   se_cmd->t_task_cdb[0], cmd->sg_cnt, cmd->offset);
+
+               qlt_handle_srr(vha, sctio, imm);
+
+               kfree(imm);
+               kfree(sctio);
+               goto restart;
+       }
+       spin_unlock_irqrestore(&tgt->srr_lock, flags);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_prepare_srr_imm(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_tgt_srr_imm *imm;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_srr_ctio *sctio;
+
+       tgt->imm_srr_id++;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02d, "qla_target(%d): SRR received\n",
+           vha->vp_idx);
+
+       imm = kzalloc(sizeof(*imm), GFP_ATOMIC);
+       if (imm != NULL) {
+               memcpy(&imm->imm_ntfy, iocb, sizeof(imm->imm_ntfy));
+
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               imm->srr_id = tgt->imm_srr_id;
+               list_add_tail(&imm->srr_list_entry,
+                   &tgt->srr_imm_list);
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02e,
+                   "IMM NTFY SRR %p added (id %d, ui %x)\n",
+                   imm, imm->srr_id, iocb->u.isp24.srr_ui);
+               if (tgt->imm_srr_id == tgt->ctio_srr_id) {
+                       int found = 0;
+                       list_for_each_entry(sctio, &tgt->srr_ctio_list,
+                           srr_list_entry) {
+                               if (sctio->srr_id == imm->srr_id) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02f, "%s",
+                                   "Scheduling srr work\n");
+                               schedule_work(&tgt->srr_work);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf030,
+                                   "qla_target(%d): imm_srr_id "
+                                   "== ctio_srr_id (%d), but there is no "
+                                   "corresponding SRR CTIO, deleting IMM "
+                                   "SRR %p\n", vha->vp_idx, tgt->ctio_srr_id,
+                                   imm);
+                               list_del(&imm->srr_list_entry);
+
+                               kfree(imm);
+
+                               spin_unlock(&tgt->srr_lock);
+                               goto out_reject;
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+       } else {
+               struct qla_tgt_srr_ctio *ts;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf069,
+                   "qla_target(%d): Unable to allocate SRR IMM "
+                   "entry, SRR request will be rejected\n", vha->vp_idx);
+
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               list_for_each_entry_safe(sctio, ts, &tgt->srr_ctio_list,
+                   srr_list_entry) {
+                       if (sctio->srr_id == tgt->imm_srr_id) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf031,
+                                   "CTIO SRR %p deleted (id %d)\n",
+                                   sctio, sctio->srr_id);
+                               list_del(&sctio->srr_list_entry);
+                               qlt_send_term_exchange(vha, sctio->cmd,
+                                   &sctio->cmd->atio, 1);
+                               kfree(sctio);
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+               goto out_reject;
+       }
+
+       return;
+
+out_reject:
+       qlt_send_notify_ack(vha, iocb, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_handle_imm_notify(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint32_t add_flags = 0;
+       int send_notify_ack = 1;
+       uint16_t status;
+
+       status = le16_to_cpu(iocb->u.isp2x.status);
+       switch (status) {
+       case IMM_NTFY_LIP_RESET:
+       {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf032,
+                   "qla_target(%d): LIP reset (loop %#x), subcode %x\n",
+                   vha->vp_idx, le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+
+               if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
+                       send_notify_ack = 0;
+               break;
+       }
+
+       case IMM_NTFY_LIP_LINK_REINIT:
+       {
+               struct qla_tgt *tgt = ha->tgt.qla_tgt;
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf033,
+                   "qla_target(%d): LINK REINIT (loop %#x, "
+                   "subcode %x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, &tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+               }
+               memcpy(&tgt->link_reinit_iocb, iocb, sizeof(*iocb));
+               tgt->link_reinit_iocb_pending = 1;
+               /*
+                * QLogic requires to wait after LINK REINIT for possible
+                * PDISC or ADISC ELS commands
+                */
+               send_notify_ack = 0;
+               break;
+       }
+
+       case IMM_NTFY_PORT_LOGOUT:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf034,
+                   "qla_target(%d): Port logout (loop "
+                   "%#x, subcode %x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_GLBL_TPRLO:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf035,
+                   "qla_target(%d): Global TPRLO (%x)\n", vha->vp_idx, status);
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_PORT_CONFIG:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf036,
+                   "qla_target(%d): Port config changed (%x)\n", vha->vp_idx,
+                   status);
+               if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_GLBL_LOGO:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06a,
+                   "qla_target(%d): Link failure detected\n",
+                   vha->vp_idx);
+               /* I_T nexus loss */
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_IOCB_OVERFLOW:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06b,
+                   "qla_target(%d): Cannot provide requested "
+                   "capability (IOCB overflowed the immediate notify "
+                   "resource count)\n", vha->vp_idx);
+               break;
+
+       case IMM_NTFY_ABORT_TASK:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf037,
+                   "qla_target(%d): Abort Task (S %08x I %#x -> "
+                   "L %#x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp2x.seq_id),
+                   GET_TARGET_ID(ha, (struct atio_from_isp *)iocb),
+                   le16_to_cpu(iocb->u.isp2x.lun));
+               if (qlt_abort_task(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_RESOURCE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06c,
+                   "qla_target(%d): Out of resources, host %ld\n",
+                   vha->vp_idx, vha->host_no);
+               break;
+
+       case IMM_NTFY_MSG_RX:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf038,
+                   "qla_target(%d): Immediate notify task %x\n",
+                   vha->vp_idx, iocb->u.isp2x.task_flags);
+               if (qlt_handle_task_mgmt(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_ELS:
+               if (qlt_24xx_handle_els(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_SRR:
+               qlt_prepare_srr_imm(vha, iocb);
+               send_notify_ack = 0;
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06d,
+                   "qla_target(%d): Received unknown immediate "
+                   "notify status %x\n", vha->vp_idx, status);
+               break;
+       }
+
+       if (send_notify_ack)
+               qlt_send_notify_ack(vha, iocb, add_flags, 0, 0, 0, 0, 0);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ * This function sends busy to ISP 2xxx or 24xx.
+ */
+static void qlt_send_busy(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio, uint16_t status)
+{
+       struct ctio7_to_24xx *ctio24;
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       struct qla_tgt_sess *sess = NULL;
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           atio->u.isp24.fcp_hdr.s_id);
+       if (!sess) {
+               qlt_send_term_exchange(vha, NULL, atio, 1);
+               return;
+       }
+       /* Sending marker isn't necessary, since we called from ISR */
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!pkt) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06e,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet", vha->vp_idx, __func__);
+               return;
+       }
+
+       pkt->entry_count = 1;
+       pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+
+       ctio24 = (struct ctio7_to_24xx *)pkt;
+       ctio24->entry_type = CTIO_TYPE7;
+       ctio24->nport_handle = sess->loop_id;
+       ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio24->vp_index = vha->vp_idx;
+       ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio24->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio24->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(
+               CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS |
+               CTIO7_FLAGS_DONT_RET_CTIO);
+       /*
+        * CTIO from fw w/o se_cmd doesn't provide enough info to retry it,
+        * if the explicit conformation is used.
+        */
+       ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       ctio24->u.status1.scsi_status = cpu_to_le16(status);
+       ctio24->u.status1.residual = get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]);
+       if (ctio24->u.status1.residual != 0)
+               ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER;
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       int rc;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf039,
+                   "ATIO pkt, but no tgt (ha %p)", ha);
+               return;
+       }
+       ql_dbg(ql_dbg_tgt, vha, 0xe02c,
+           "qla_target(%d): ATIO pkt %p: type %02x count %02x",
+           vha->vp_idx, atio, atio->u.raw.entry_type,
+           atio->u.raw.entry_count);
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (atio->u.raw.entry_type) {
+       case ATIO_TYPE7:
+               ql_dbg(ql_dbg_tgt, vha, 0xe02d,
+                   "ATIO_TYPE7 instance %d, lun %Lx, read/write %d/%d, "
+                   "add_cdb_len %d, data_length %04x, s_id %x:%x:%x\n",
+                   vha->vp_idx, atio->u.isp24.fcp_cmnd.lun,
+                   atio->u.isp24.fcp_cmnd.rddata,
+                   atio->u.isp24.fcp_cmnd.wrdata,
+                   atio->u.isp24.fcp_cmnd.add_cdb_len,
+                   be32_to_cpu(get_unaligned((uint32_t *)
+                       &atio->u.isp24.fcp_cmnd.add_cdb[
+                       atio->u.isp24.fcp_cmnd.add_cdb_len])),
+                   atio->u.isp24.fcp_hdr.s_id[0],
+                   atio->u.isp24.fcp_hdr.s_id[1],
+                   atio->u.isp24.fcp_hdr.s_id[2]);
+
+               if (unlikely(atio->u.isp24.exchange_addr ==
+                   ATIO_EXCHANGE_ADDRESS_UNKNOWN)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe058,
+                           "qla_target(%d): ATIO_TYPE7 "
+                           "received with UNKNOWN exchange address, "
+                           "sending QUEUE_FULL\n", vha->vp_idx);
+                       qlt_send_busy(vha, atio, SAM_STAT_TASK_SET_FULL);
+                       break;
+               }
+               if (likely(atio->u.isp24.fcp_cmnd.task_mgmt_flags == 0))
+                       rc = qlt_handle_cmd_for_atio(vha, atio);
+               else
+                       rc = qlt_handle_task_mgmt(vha, atio);
+               if (unlikely(rc != 0)) {
+                       if (rc == -ESRCH) {
+#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */
+                               qlt_send_busy(vha, atio, SAM_STAT_BUSY);
+#else
+                               qlt_send_term_exchange(vha, NULL, atio, 1);
+#endif
+                       } else {
+                               if (tgt->tgt_stop) {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe059,
+                                           "qla_target: Unable to send "
+                                           "command to target for req, "
+                                           "ignoring.\n");
+                               } else {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe05a,
+                                           "qla_target(%d): Unable to send "
+                                           "command to target, sending BUSY "
+                                           "status.\n", vha->vp_idx);
+                                       qlt_send_busy(vha, atio, SAM_STAT_BUSY);
+                               }
+                       }
+               }
+               break;
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               if (unlikely(atio->u.isp2x.entry_status != 0)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe05b,
+                           "qla_target(%d): Received ATIO packet %x "
+                           "with error status %x\n", vha->vp_idx,
+                           atio->u.raw.entry_type,
+                           atio->u.isp2x.entry_status);
+                       break;
+               }
+               ql_dbg(ql_dbg_tgt, vha, 0xe02e, "%s", "IMMED_NOTIFY ATIO");
+               qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)atio);
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe05c,
+                   "qla_target(%d): Received unknown ATIO atio "
+                   "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe05d,
+                   "qla_target(%d): Response pkt %x received, but no "
+                   "tgt (ha %p)\n", vha->vp_idx, pkt->entry_type, ha);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe02f,
+           "qla_target(%d): response pkt %p: T %02x C %02x S %02x "
+           "handle %#x\n", vha->vp_idx, pkt, pkt->entry_type,
+           pkt->entry_count, pkt->entry_status, pkt->handle);
+
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (pkt->entry_type) {
+       case CTIO_TYPE7:
+       {
+               struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe030, "CTIO_TYPE7: instance %d\n",
+                   vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case ACCEPT_TGT_IO_TYPE:
+       {
+               struct atio_from_isp *atio = (struct atio_from_isp *)pkt;
+               int rc;
+               ql_dbg(ql_dbg_tgt, vha, 0xe031,
+                   "ACCEPT_TGT_IO instance %d status %04x "
+                   "lun %04x read/write %d data_length %04x "
+                   "target_id %02x rx_id %04x\n ", vha->vp_idx,
+                   le16_to_cpu(atio->u.isp2x.status),
+                   le16_to_cpu(atio->u.isp2x.lun),
+                   atio->u.isp2x.execution_codes,
+                   le32_to_cpu(atio->u.isp2x.data_length), GET_TARGET_ID(ha,
+                   atio), atio->u.isp2x.rx_id);
+               if (atio->u.isp2x.status !=
+                   __constant_cpu_to_le16(ATIO_CDB_VALID)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe05e,
+                           "qla_target(%d): ATIO with error "
+                           "status %x received\n", vha->vp_idx,
+                           le16_to_cpu(atio->u.isp2x.status));
+                       break;
+               }
+               ql_dbg(ql_dbg_tgt, vha, 0xe032,
+                   "FCP CDB: 0x%02x, sizeof(cdb): %lu",
+                   atio->u.isp2x.cdb[0], (unsigned long
+                   int)sizeof(atio->u.isp2x.cdb));
+
+               rc = qlt_handle_cmd_for_atio(vha, atio);
+               if (unlikely(rc != 0)) {
+                       if (rc == -ESRCH) {
+#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */
+                               qlt_send_busy(vha, atio, 0);
+#else
+                               qlt_send_term_exchange(vha, NULL, atio, 1);
+#endif
+                       } else {
+                               if (tgt->tgt_stop) {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe05f,
+                                           "qla_target: Unable to send "
+                                           "command to target, sending TERM "
+                                           "EXCHANGE for rsp\n");
+                                       qlt_send_term_exchange(vha, NULL,
+                                           atio, 1);
+                               } else {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe060,
+                                           "qla_target(%d): Unable to send "
+                                           "command to target, sending BUSY "
+                                           "status\n", vha->vp_idx);
+                                       qlt_send_busy(vha, atio, 0);
+                               }
+                       }
+               }
+       }
+       break;
+
+       case CONTINUE_TGT_IO_TYPE:
+       {
+               struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe033,
+                   "CONTINUE_TGT_IO: instance %d\n", vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case CTIO_A64_TYPE:
+       {
+               struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe034, "CTIO_A64: instance %d\n",
+                   vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+               ql_dbg(ql_dbg_tgt, vha, 0xe035, "%s", "IMMED_NOTIFY\n");
+               qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)pkt);
+               break;
+
+       case NOTIFY_ACK_TYPE:
+               if (tgt->notify_ack_expected > 0) {
+                       struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
+                       ql_dbg(ql_dbg_tgt, vha, 0xe036,
+                           "NOTIFY_ACK seq %08x status %x\n",
+                           le16_to_cpu(entry->u.isp2x.seq_id),
+                           le16_to_cpu(entry->u.isp2x.status));
+                       tgt->notify_ack_expected--;
+                       if (entry->u.isp2x.status !=
+                           __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe061,
+                                   "qla_target(%d): NOTIFY_ACK "
+                                   "failed %x\n", vha->vp_idx,
+                                   le16_to_cpu(entry->u.isp2x.status));
+                       }
+               } else {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe062,
+                           "qla_target(%d): Unexpected NOTIFY_ACK received\n",
+                           vha->vp_idx);
+               }
+               break;
+
+       case ABTS_RECV_24XX:
+               ql_dbg(ql_dbg_tgt, vha, 0xe037,
+                   "ABTS_RECV_24XX: instance %d\n", vha->vp_idx);
+               qlt_24xx_handle_abts(vha, (struct abts_recv_from_24xx *)pkt);
+               break;
+
+       case ABTS_RESP_24XX:
+               if (tgt->abts_resp_expected > 0) {
+                       struct abts_resp_from_24xx_fw *entry =
+                               (struct abts_resp_from_24xx_fw *)pkt;
+                       ql_dbg(ql_dbg_tgt, vha, 0xe038,
+                           "ABTS_RESP_24XX: compl_status %x\n",
+                           entry->compl_status);
+                       tgt->abts_resp_expected--;
+                       if (le16_to_cpu(entry->compl_status) !=
+                           ABTS_RESP_COMPL_SUCCESS) {
+                               if ((entry->error_subcode1 == 0x1E) &&
+                                   (entry->error_subcode2 == 0)) {
+                                       /*
+                                        * We've got a race here: aborted
+                                        * exchange not terminated, i.e.
+                                        * response for the aborted command was
+                                        * sent between the abort request was
+                                        * received and processed.
+                                        * Unfortunately, the firmware has a
+                                        * silly requirement that all aborted
+                                        * exchanges must be explicitely
+                                        * terminated, otherwise it refuses to
+                                        * send responses for the abort
+                                        * requests. So, we have to
+                                        * (re)terminate the exchange and retry
+                                        * the abort response.
+                                        */
+                                       qlt_24xx_retry_term_exchange(vha,
+                                           entry);
+                               } else
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe063,
+                                           "qla_target(%d): ABTS_RESP_24XX "
+                                           "failed %x (subcode %x:%x)",
+                                           vha->vp_idx, entry->compl_status,
+                                           entry->error_subcode1,
+                                           entry->error_subcode2);
+                       }
+               } else {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe064,
+                           "qla_target(%d): Unexpected ABTS_RESP_24XX "
+                           "received\n", vha->vp_idx);
+               }
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe065,
+                   "qla_target(%d): Received unknown response pkt "
+                   "type %x\n", vha->vp_idx, pkt->entry_type);
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
+       uint16_t *mailbox)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       int reason_code;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe039,
+           "scsi(%ld): ha state %d init_done %d oper_mode %d topo %d\n",
+           vha->host_no, atomic_read(&vha->loop_state), vha->flags.init_done,
+           ha->operating_mode, ha->current_topology);
+
+       if (!ha->tgt.tgt_ops)
+               return;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe03a,
+                   "ASYNC EVENT %#x, but no tgt (ha %p)\n", code, ha);
+               return;
+       }
+
+       if (((code == MBA_POINT_TO_POINT) || (code == MBA_CHG_IN_CONNECTION)) &&
+           IS_QLA2100(ha))
+               return;
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (code) {
+       case MBA_RESET:                 /* Reset */
+       case MBA_SYSTEM_ERR:            /* System Error */
+       case MBA_REQ_TRANSFER_ERR:      /* Request Transfer Error */
+       case MBA_RSP_TRANSFER_ERR:      /* Response Transfer Error */
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03a,
+                   "qla_target(%d): System error async event %#x "
+                   "occured", vha->vp_idx, code);
+               break;
+       case MBA_WAKEUP_THRES:          /* Request Queue Wake-up. */
+               set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+               break;
+
+       case MBA_LOOP_UP:
+       {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b,
+                   "qla_target(%d): Async LOOP_UP occured "
+                   "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, (void *)&tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+                       tgt->link_reinit_iocb_pending = 0;
+               }
+               break;
+       }
+
+       case MBA_LIP_OCCURRED:
+       case MBA_LOOP_DOWN:
+       case MBA_LIP_RESET:
+       case MBA_RSCN_UPDATE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c,
+                   "qla_target(%d): Async event %#x occured "
+                   "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx, code,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               break;
+
+       case MBA_PORT_UPDATE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d,
+                   "qla_target(%d): Port update async event %#x "
+                   "occured: updating the ports database (m[1]=%x, m[2]=%x, "
+                   "m[3]=%x, m[4]=%x)", vha->vp_idx, code,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               reason_code = le16_to_cpu(mailbox[2]);
+               if (reason_code == 0x4)
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e,
+                           "Async MB 2: Got PLOGI Complete\n");
+               else if (reason_code == 0x7)
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f,
+                           "Async MB 2: Port Logged Out\n");
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf040,
+                   "qla_target(%d): Async event %#x occured: "
+                   "ignore (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx,
+                   code, le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha,
+       uint16_t loop_id)
+{
+       fc_port_t *fcport;
+       int rc;
+
+       fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+       if (!fcport) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f,
+                   "qla_target(%d): Allocation of tmp FC port failed",
+                   vha->vp_idx);
+               return NULL;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf041, "loop_id %d", loop_id);
+
+       fcport->loop_id = loop_id;
+
+       rc = qla2x00_get_port_database(vha, fcport, 0);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf070,
+                   "qla_target(%d): Failed to retrieve fcport "
+                   "information -- get_port_database() returned %x "
+                   "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
+               kfree(fcport);
+               return NULL;
+       }
+
+       return fcport;
+}
+
+/* Must be called under tgt_mutex */
+static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha,
+       uint8_t *s_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       fc_port_t *fcport = NULL;
+       int rc, global_resets;
+       uint16_t loop_id = 0;
+
+retry:
+       global_resets = atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count);
+
+       rc = qla24xx_get_loop_id(vha, s_id, &loop_id);
+       if (rc != 0) {
+               if ((s_id[0] == 0xFF) &&
+                   (s_id[1] == 0xFC)) {
+                       /*
+                        * This is Domain Controller, so it should be
+                        * OK to drop SCSI commands from it.
+                        */
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042,
+                           "Unable to find initiator with S_ID %x:%x:%x",
+                           s_id[0], s_id[1], s_id[2]);
+               } else
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf071,
+                           "qla_target(%d): Unable to find "
+                           "initiator with S_ID %x:%x:%x",
+                           vha->vp_idx, s_id[0], s_id[1],
+                           s_id[2]);
+               return NULL;
+       }
+
+       fcport = qlt_get_port_database(vha, loop_id);
+       if (!fcport)
+               return NULL;
+
+       if (global_resets !=
+           atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf043,
+                   "qla_target(%d): global reset during session discovery "
+                   "(counter was %d, new %d), retrying", vha->vp_idx,
+                   global_resets,
+                   atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count));
+               goto retry;
+       }
+
+       sess = qlt_create_sess(vha, fcport, true);
+
+       kfree(fcport);
+       return sess;
+}
+
+static void qlt_abort_work(struct qla_tgt *tgt,
+       struct qla_tgt_sess_work_param *prm)
+{
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       unsigned long flags;
+       uint32_t be_s_id;
+       uint8_t s_id[3];
+       int rc;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       s_id[0] = prm->abts.fcp_hdr_le.s_id[2];
+       s_id[1] = prm->abts.fcp_hdr_le.s_id[1];
+       s_id[2] = prm->abts.fcp_hdr_le.s_id[0];
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           (unsigned char *)&be_s_id);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has got an extra creation ref */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (!sess)
+                       goto out_term;
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+       }
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       rc = __qlt_24xx_handle_abts(vha, &prm->abts, sess);
+       if (rc != 0)
+               goto out_term;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+static void qlt_tmr_work(struct qla_tgt *tgt,
+       struct qla_tgt_sess_work_param *prm)
+{
+       struct atio_from_isp *a = &prm->tm_iocb2;
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       unsigned long flags;
+       uint8_t *s_id = NULL; /* to hide compiler warnings */
+       int rc;
+       uint32_t lun, unpacked_lun;
+       int lun_size, fn;
+       void *iocb;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       s_id = prm->tm_iocb2.u.isp24.fcp_hdr.s_id;
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has got an extra creation ref */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (!sess)
+                       goto out_term;
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+       }
+
+       iocb = a;
+       lun = a->u.isp24.fcp_cmnd.lun;
+       lun_size = sizeof(lun);
+       fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       rc = qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
+       if (rc != 0)
+               goto out_term;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+static void qlt_sess_work_fn(struct work_struct *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt, sess_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf000, "Sess work (tgt %p)", tgt);
+
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       while (!list_empty(&tgt->sess_works_list)) {
+               struct qla_tgt_sess_work_param *prm = list_entry(
+                   tgt->sess_works_list.next, typeof(*prm),
+                   sess_works_list_entry);
+
+               /*
+                * This work can be scheduled on several CPUs at time, so we
+                * must delete the entry to eliminate double processing
+                */
+               list_del(&prm->sess_works_list_entry);
+
+               spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+               switch (prm->type) {
+               case QLA_TGT_SESS_WORK_ABORT:
+                       qlt_abort_work(tgt, prm);
+                       break;
+               case QLA_TGT_SESS_WORK_TM:
+                       qlt_tmr_work(tgt, prm);
+                       break;
+               default:
+                       BUG_ON(1);
+                       break;
+               }
+
+               spin_lock_irqsave(&tgt->sess_work_lock, flags);
+
+               kfree(prm);
+       }
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+}
+
+/* Must be called under tgt_host_action_mutex */
+int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha)
+{
+       struct qla_tgt *tgt;
+
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       ql_dbg(ql_dbg_tgt, base_vha, 0xe03b,
+           "Registering target for host %ld(%p)", base_vha->host_no, ha);
+
+       BUG_ON((ha->tgt.qla_tgt != NULL) || (ha->tgt.tgt_ops != NULL));
+
+       tgt = kzalloc(sizeof(struct qla_tgt), GFP_KERNEL);
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, base_vha, 0xe066,
+                   "Unable to allocate struct qla_tgt\n");
+               return -ENOMEM;
+       }
+
+       if (!(base_vha->host->hostt->supported_mode & MODE_TARGET))
+               base_vha->host->hostt->supported_mode |= MODE_TARGET;
+
+       tgt->ha = ha;
+       tgt->vha = base_vha;
+       init_waitqueue_head(&tgt->waitQ);
+       INIT_LIST_HEAD(&tgt->sess_list);
+       INIT_LIST_HEAD(&tgt->del_sess_list);
+       INIT_DELAYED_WORK(&tgt->sess_del_work,
+               (void (*)(struct work_struct *))qlt_del_sess_work_fn);
+       spin_lock_init(&tgt->sess_work_lock);
+       INIT_WORK(&tgt->sess_work, qlt_sess_work_fn);
+       INIT_LIST_HEAD(&tgt->sess_works_list);
+       spin_lock_init(&tgt->srr_lock);
+       INIT_LIST_HEAD(&tgt->srr_ctio_list);
+       INIT_LIST_HEAD(&tgt->srr_imm_list);
+       INIT_WORK(&tgt->srr_work, qlt_handle_srr_work);
+       atomic_set(&tgt->tgt_global_resets_count, 0);
+
+       ha->tgt.qla_tgt = tgt;
+
+       ql_dbg(ql_dbg_tgt, base_vha, 0xe067,
+               "qla_target(%d): using 64 Bit PCI addressing",
+               base_vha->vp_idx);
+       tgt->tgt_enable_64bit_addr = 1;
+       /* 3 is reserved */
+       tgt->sg_tablesize = QLA_TGT_MAX_SG_24XX(base_vha->req->length - 3);
+       tgt->datasegs_per_cmd = QLA_TGT_DATASEGS_PER_CMD_24XX;
+       tgt->datasegs_per_cont = QLA_TGT_DATASEGS_PER_CONT_24XX;
+
+       mutex_lock(&qla_tgt_mutex);
+       list_add_tail(&tgt->tgt_list_entry, &qla_tgt_glist);
+       mutex_unlock(&qla_tgt_mutex);
+
+       return 0;
+}
+
+/* Must be called under tgt_host_action_mutex */
+int qlt_remove_target(struct qla_hw_data *ha, struct scsi_qla_host *vha)
+{
+       if (!ha->tgt.qla_tgt)
+               return 0;
+
+       mutex_lock(&qla_tgt_mutex);
+       list_del(&ha->tgt.qla_tgt->tgt_list_entry);
+       mutex_unlock(&qla_tgt_mutex);
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe03c, "Unregistering target for host %ld(%p)",
+           vha->host_no, ha);
+       qlt_release(ha->tgt.qla_tgt);
+
+       return 0;
+}
+
+static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn,
+       unsigned char *b)
+{
+       int i;
+
+       pr_debug("qla2xxx HW vha->node_name: ");
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", vha->node_name[i]);
+       pr_debug("\n");
+       pr_debug("qla2xxx HW vha->port_name: ");
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", vha->port_name[i]);
+       pr_debug("\n");
+
+       pr_debug("qla2xxx passed configfs WWPN: ");
+       put_unaligned_be64(wwpn, b);
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", b[i]);
+       pr_debug("\n");
+}
+
+/**
+ * qla_tgt_lport_register - register lport with external module
+ *
+ * @qla_tgt_ops: Pointer for tcm_qla2xxx qla_tgt_ops
+ * @wwpn: Passwd FC target WWPN
+ * @callback:  lport initialization callback for tcm_qla2xxx code
+ * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data
+ */
+int qlt_lport_register(struct qla_tgt_func_tmpl *qla_tgt_ops, u64 wwpn,
+       int (*callback)(struct scsi_qla_host *), void *target_lport_ptr)
+{
+       struct qla_tgt *tgt;
+       struct scsi_qla_host *vha;
+       struct qla_hw_data *ha;
+       struct Scsi_Host *host;
+       unsigned long flags;
+       int rc;
+       u8 b[WWN_SIZE];
+
+       mutex_lock(&qla_tgt_mutex);
+       list_for_each_entry(tgt, &qla_tgt_glist, tgt_list_entry) {
+               vha = tgt->vha;
+               ha = vha->hw;
+
+               host = vha->host;
+               if (!host)
+                       continue;
+
+               if (ha->tgt.tgt_ops != NULL)
+                       continue;
+
+               if (!(host->hostt->supported_mode & MODE_TARGET))
+                       continue;
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (host->active_mode & MODE_TARGET) {
+                       pr_debug("MODE_TARGET already active on qla2xxx(%d)\n",
+                           host->host_no);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       continue;
+               }
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               if (!scsi_host_get(host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe068,
+                           "Unable to scsi_host_get() for"
+                           " qla2xxx scsi_host\n");
+                       continue;
+               }
+               qlt_lport_dump(vha, wwpn, b);
+
+               if (memcmp(vha->port_name, b, WWN_SIZE)) {
+                       scsi_host_put(host);
+                       continue;
+               }
+               /*
+                * Setup passed parameters ahead of invoking callback
+                */
+               ha->tgt.tgt_ops = qla_tgt_ops;
+               ha->tgt.target_lport_ptr = target_lport_ptr;
+               rc = (*callback)(vha);
+               if (rc != 0) {
+                       ha->tgt.tgt_ops = NULL;
+                       ha->tgt.target_lport_ptr = NULL;
+               }
+               mutex_unlock(&qla_tgt_mutex);
+               return rc;
+       }
+       mutex_unlock(&qla_tgt_mutex);
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(qlt_lport_register);
+
+/**
+ * qla_tgt_lport_deregister - Degister lport
+ *
+ * @vha:  Registered scsi_qla_host pointer
+ */
+void qlt_lport_deregister(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct Scsi_Host *sh = vha->host;
+       /*
+        * Clear the target_lport_ptr qla_target_template pointer in qla_hw_data
+        */
+       ha->tgt.target_lport_ptr = NULL;
+       ha->tgt.tgt_ops = NULL;
+       /*
+        * Release the Scsi_Host reference for the underlying qla2xxx host
+        */
+       scsi_host_put(sh);
+}
+EXPORT_SYMBOL(qlt_lport_deregister);
+
+/* Must be called under HW lock */
+void qlt_set_mode(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       switch (ql2x_ini_mode) {
+       case QLA2XXX_INI_MODE_DISABLED:
+       case QLA2XXX_INI_MODE_EXCLUSIVE:
+               vha->host->active_mode = MODE_TARGET;
+               break;
+       case QLA2XXX_INI_MODE_ENABLED:
+               vha->host->active_mode |= MODE_TARGET;
+               break;
+       default:
+               break;
+       }
+
+       if (ha->tgt.ini_mode_force_reverse)
+               qla_reverse_ini_mode(vha);
+}
+
+/* Must be called under HW lock */
+void qlt_clear_mode(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       switch (ql2x_ini_mode) {
+       case QLA2XXX_INI_MODE_DISABLED:
+               vha->host->active_mode = MODE_UNKNOWN;
+               break;
+       case QLA2XXX_INI_MODE_EXCLUSIVE:
+               vha->host->active_mode = MODE_INITIATOR;
+               break;
+       case QLA2XXX_INI_MODE_ENABLED:
+               vha->host->active_mode &= ~MODE_TARGET;
+               break;
+       default:
+               break;
+       }
+
+       if (ha->tgt.ini_mode_force_reverse)
+               qla_reverse_ini_mode(vha);
+}
+
+/*
+ * qla_tgt_enable_vha - NO LOCK HELD
+ *
+ * host_reset, bring up w/ Target Mode Enabled
+ */
+void
+qlt_enable_vha(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       unsigned long flags;
+
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe069,
+                   "Unable to locate qla_tgt pointer from"
+                   " struct qla_hw_data\n");
+               dump_stack();
+               return;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tgt->tgt_stopped = 0;
+       qlt_set_mode(vha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+       qla2xxx_wake_dpc(vha);
+       qla2x00_wait_for_hba_online(vha);
+}
+EXPORT_SYMBOL(qlt_enable_vha);
+
+/*
+ * qla_tgt_disable_vha - NO LOCK HELD
+ *
+ * Disable Target Mode and reset the adapter
+ */
+void
+qlt_disable_vha(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       unsigned long flags;
+
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe06a,
+                   "Unable to locate qla_tgt pointer from"
+                   " struct qla_hw_data\n");
+               dump_stack();
+               return;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_clear_mode(vha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+       qla2xxx_wake_dpc(vha);
+       qla2x00_wait_for_hba_online(vha);
+}
+
+/*
+ * Called from qla_init.c:qla24xx_vport_create() contex to setup
+ * the target mode specific struct scsi_qla_host and struct qla_hw_data
+ * members.
+ */
+void
+qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha)
+{
+       if (!qla_tgt_mode_enabled(vha))
+               return;
+
+       mutex_init(&ha->tgt.tgt_mutex);
+       mutex_init(&ha->tgt.tgt_host_action_mutex);
+
+       qlt_clear_mode(vha);
+
+       /*
+        * NOTE: Currently the value is kept the same for <24xx and
+        * >=24xx ISPs. If it is necessary to change it,
+        * the check should be added for specific ISPs,
+        * assigning the value appropriately.
+        */
+       ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
+}
+
+void
+qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req)
+{
+       /*
+        * FC-4 Feature bit 0 indicates target functionality to the name server.
+        */
+       if (qla_tgt_mode_enabled(vha)) {
+               if (qla_ini_mode_enabled(vha))
+                       ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1;
+               else
+                       ct_req->req.rff_id.fc4_feature = BIT_0;
+       } else if (qla_ini_mode_enabled(vha)) {
+               ct_req->req.rff_id.fc4_feature = BIT_1;
+       }
+}
+
+/*
+ * qlt_init_atio_q_entries() - Initializes ATIO queue entries.
+ * @ha: HA context
+ *
+ * Beginning of ATIO ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+void
+qlt_init_atio_q_entries(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint16_t cnt;
+       struct atio_from_isp *pkt = (struct atio_from_isp *)ha->tgt.atio_ring;
+
+       if (!qla_tgt_mode_enabled(vha))
+               return;
+
+       for (cnt = 0; cnt < ha->tgt.atio_q_length; cnt++) {
+               pkt->u.raw.signature = ATIO_PROCESSED;
+               pkt++;
+       }
+
+}
+
+/*
+ * qlt_24xx_process_atio_queue() - Process ATIO queue entries.
+ * @ha: SCSI driver HA context
+ */
+void
+qlt_24xx_process_atio_queue(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+       struct atio_from_isp *pkt;
+       int cnt, i;
+
+       if (!vha->flags.online)
+               return;
+
+       while (ha->tgt.atio_ring_ptr->signature != ATIO_PROCESSED) {
+               pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
+               cnt = pkt->u.raw.entry_count;
+
+               qlt_24xx_atio_pkt_all_vps(vha, (struct atio_from_isp *)pkt);
+
+               for (i = 0; i < cnt; i++) {
+                       ha->tgt.atio_ring_index++;
+                       if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) {
+                               ha->tgt.atio_ring_index = 0;
+                               ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
+                       } else
+                               ha->tgt.atio_ring_ptr++;
+
+                       pkt->u.raw.signature = ATIO_PROCESSED;
+                       pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
+               }
+               wmb();
+       }
+
+       /* Adjust ring index */
+       WRT_REG_DWORD(&reg->atio_q_out, ha->tgt.atio_ring_index);
+}
+
+void
+qlt_24xx_config_rings(struct scsi_qla_host *vha, device_reg_t __iomem *reg)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+/* FIXME: atio_q in/out for ha->mqenable=1..? */
+       if (ha->mqenable) {
+#if 0
+               WRT_REG_DWORD(&reg->isp25mq.atio_q_in, 0);
+               WRT_REG_DWORD(&reg->isp25mq.atio_q_out, 0);
+               RD_REG_DWORD(&reg->isp25mq.atio_q_out);
+#endif
+       } else {
+               /* Setup APTIO registers for target mode */
+               WRT_REG_DWORD(&reg->isp24.atio_q_in, 0);
+               WRT_REG_DWORD(&reg->isp24.atio_q_out, 0);
+               RD_REG_DWORD(&reg->isp24.atio_q_out);
+       }
+}
+
+void
+qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (qla_tgt_mode_enabled(vha)) {
+               if (!ha->tgt.saved_set) {
+                       /* We save only once */
+                       ha->tgt.saved_exchange_count = nv->exchange_count;
+                       ha->tgt.saved_firmware_options_1 =
+                           nv->firmware_options_1;
+                       ha->tgt.saved_firmware_options_2 =
+                           nv->firmware_options_2;
+                       ha->tgt.saved_firmware_options_3 =
+                           nv->firmware_options_3;
+                       ha->tgt.saved_set = 1;
+               }
+
+               nv->exchange_count = __constant_cpu_to_le16(0xFFFF);
+
+               /* Enable target mode */
+               nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4);
+
+               /* Disable ini mode, if requested */
+               if (!qla_ini_mode_enabled(vha))
+                       nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5);
+
+               /* Disable Full Login after LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13);
+               /* Enable initial LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9);
+               /* Enable FC tapes support */
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12);
+               /* Disable Full Login after LIP */
+               nv->host_p &= __constant_cpu_to_le32(~BIT_10);
+               /* Enable target PRLI control */
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14);
+       } else {
+               if (ha->tgt.saved_set) {
+                       nv->exchange_count = ha->tgt.saved_exchange_count;
+                       nv->firmware_options_1 =
+                           ha->tgt.saved_firmware_options_1;
+                       nv->firmware_options_2 =
+                           ha->tgt.saved_firmware_options_2;
+                       nv->firmware_options_3 =
+                           ha->tgt.saved_firmware_options_3;
+               }
+               return;
+       }
+
+       /* out-of-order frames reassembly */
+       nv->firmware_options_3 |= BIT_6|BIT_9;
+
+       if (ha->tgt.enable_class_2) {
+               if (vha->flags.init_done)
+                       fc_host_supported_classes(vha->host) =
+                               FC_COS_CLASS2 | FC_COS_CLASS3;
+
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8);
+       } else {
+               if (vha->flags.init_done)
+                       fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+
+               nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8);
+       }
+}
+
+void
+qlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha,
+       struct init_cb_24xx *icb)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (ha->tgt.node_name_set) {
+               memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE);
+               icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14);
+       }
+}
+
+int
+qlt_24xx_process_response_error(struct scsi_qla_host *vha,
+       struct sts_entry_24xx *pkt)
+{
+       switch (pkt->entry_type) {
+       case ABTS_RECV_24XX:
+       case ABTS_RESP_24XX:
+       case CTIO_TYPE7:
+       case NOTIFY_ACK_TYPE:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+void
+qlt_modify_vp_config(struct scsi_qla_host *vha,
+       struct vp_config_entry_24xx *vpmod)
+{
+       if (qla_tgt_mode_enabled(vha))
+               vpmod->options_idx1 &= ~BIT_5;
+       /* Disable ini mode, if requested */
+       if (!qla_ini_mode_enabled(vha))
+               vpmod->options_idx1 &= ~BIT_4;
+}
+
+void
+qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       mutex_init(&ha->tgt.tgt_mutex);
+       mutex_init(&ha->tgt.tgt_host_action_mutex);
+       qlt_clear_mode(base_vha);
+}
+
+int
+qlt_mem_alloc(struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       ha->tgt.tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) *
+           MAX_MULTI_ID_FABRIC, GFP_KERNEL);
+       if (!ha->tgt.tgt_vp_map)
+               return -ENOMEM;
+
+       ha->tgt.atio_ring = dma_alloc_coherent(&ha->pdev->dev,
+           (ha->tgt.atio_q_length + 1) * sizeof(struct atio_from_isp),
+           &ha->tgt.atio_dma, GFP_KERNEL);
+       if (!ha->tgt.atio_ring) {
+               kfree(ha->tgt.tgt_vp_map);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void
+qlt_mem_free(struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       if (ha->tgt.atio_ring) {
+               dma_free_coherent(&ha->pdev->dev, (ha->tgt.atio_q_length + 1) *
+                   sizeof(struct atio_from_isp), ha->tgt.atio_ring,
+                   ha->tgt.atio_dma);
+       }
+       kfree(ha->tgt.tgt_vp_map);
+}
+
+/* vport_slock to be held by the caller */
+void
+qlt_update_vp_map(struct scsi_qla_host *vha, int cmd)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       switch (cmd) {
+       case SET_VP_IDX:
+               vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = vha;
+               break;
+       case SET_AL_PA:
+               vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = vha->vp_idx;
+               break;
+       case RESET_VP_IDX:
+               vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = NULL;
+               break;
+       case RESET_AL_PA:
+               vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = 0;
+               break;
+       }
+}
+
+static int __init qlt_parse_ini_mode(void)
+{
+       if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_EXCLUSIVE) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
+       else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DISABLED) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_DISABLED;
+       else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_ENABLED) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_ENABLED;
+       else
+               return false;
+
+       return true;
+}
+
+int __init qlt_init(void)
+{
+       int ret;
+
+       if (!qlt_parse_ini_mode()) {
+               ql_log(ql_log_fatal, NULL, 0xe06b,
+                   "qlt_parse_ini_mode() failed\n");
+               return -EINVAL;
+       }
+
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       qla_tgt_cmd_cachep = kmem_cache_create("qla_tgt_cmd_cachep",
+           sizeof(struct qla_tgt_cmd), __alignof__(struct qla_tgt_cmd), 0,
+           NULL);
+       if (!qla_tgt_cmd_cachep) {
+               ql_log(ql_log_fatal, NULL, 0xe06c,
+                   "kmem_cache_create for qla_tgt_cmd_cachep failed\n");
+               return -ENOMEM;
+       }
+
+       qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep",
+           sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct
+           qla_tgt_mgmt_cmd), 0, NULL);
+       if (!qla_tgt_mgmt_cmd_cachep) {
+               ql_log(ql_log_fatal, NULL, 0xe06d,
+                   "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab,
+           mempool_free_slab, qla_tgt_mgmt_cmd_cachep);
+       if (!qla_tgt_mgmt_cmd_mempool) {
+               ql_log(ql_log_fatal, NULL, 0xe06e,
+                   "mempool_create for qla_tgt_mgmt_cmd_mempool failed\n");
+               ret = -ENOMEM;
+               goto out_mgmt_cmd_cachep;
+       }
+
+       qla_tgt_wq = alloc_workqueue("qla_tgt_wq", 0, 0);
+       if (!qla_tgt_wq) {
+               ql_log(ql_log_fatal, NULL, 0xe06f,
+                   "alloc_workqueue for qla_tgt_wq failed\n");
+               ret = -ENOMEM;
+               goto out_cmd_mempool;
+       }
+       /*
+        * Return 1 to signal that initiator-mode is being disabled
+        */
+       return (ql2x_ini_mode == QLA2XXX_INI_MODE_DISABLED) ? 1 : 0;
+
+out_cmd_mempool:
+       mempool_destroy(qla_tgt_mgmt_cmd_mempool);
+out_mgmt_cmd_cachep:
+       kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
+out:
+       kmem_cache_destroy(qla_tgt_cmd_cachep);
+       return ret;
+}
+
+void qlt_exit(void)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       destroy_workqueue(qla_tgt_wq);
+       mempool_destroy(qla_tgt_mgmt_cmd_mempool);
+       kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
+       kmem_cache_destroy(qla_tgt_cmd_cachep);
+}
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
new file mode 100644 (file)
index 0000000..9ec19bc
--- /dev/null
@@ -0,0 +1,1005 @@
+/*
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  Forward port and refactoring to modern qla2xxx and target/configfs
+ *
+ *  Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ *  Additional file for the target driver support.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+/*
+ * This is the global def file that is useful for including from the
+ * target portion.
+ */
+
+#ifndef __QLA_TARGET_H
+#define __QLA_TARGET_H
+
+#include "qla_def.h"
+
+/*
+ * Must be changed on any change in any initiator visible interfaces or
+ * data in the target add-on
+ */
+#define QLA2XXX_TARGET_MAGIC   269
+
+/*
+ * Must be changed on any change in any target visible interfaces or
+ * data in the initiator
+ */
+#define QLA2XXX_INITIATOR_MAGIC   57222
+
+#define QLA2XXX_INI_MODE_STR_EXCLUSIVE "exclusive"
+#define QLA2XXX_INI_MODE_STR_DISABLED  "disabled"
+#define QLA2XXX_INI_MODE_STR_ENABLED   "enabled"
+
+#define QLA2XXX_INI_MODE_EXCLUSIVE     0
+#define QLA2XXX_INI_MODE_DISABLED      1
+#define QLA2XXX_INI_MODE_ENABLED       2
+
+#define QLA2XXX_COMMAND_COUNT_INIT     250
+#define QLA2XXX_IMMED_NOTIFY_COUNT_INIT 250
+
+/*
+ * Used to mark which completion handles (for RIO Status's) are for CTIO's
+ * vs. regular (non-target) info. This is checked for in
+ * qla2x00_process_response_queue() to see if a handle coming back in a
+ * multi-complete should come to the tgt driver or be handled there by qla2xxx
+ */
+#define CTIO_COMPLETION_HANDLE_MARK    BIT_29
+#if (CTIO_COMPLETION_HANDLE_MARK <= MAX_OUTSTANDING_COMMANDS)
+#error "CTIO_COMPLETION_HANDLE_MARK not larger than MAX_OUTSTANDING_COMMANDS"
+#endif
+#define HANDLE_IS_CTIO_COMP(h) (h & CTIO_COMPLETION_HANDLE_MARK)
+
+/* Used to mark CTIO as intermediate */
+#define CTIO_INTERMEDIATE_HANDLE_MARK  BIT_30
+
+#ifndef OF_SS_MODE_0
+/*
+ * ISP target entries - Flags bit definitions.
+ */
+#define OF_SS_MODE_0        0
+#define OF_SS_MODE_1        1
+#define OF_SS_MODE_2        2
+#define OF_SS_MODE_3        3
+
+#define OF_EXPL_CONF        BIT_5       /* Explicit Confirmation Requested */
+#define OF_DATA_IN          BIT_6       /* Data in to initiator */
+                                       /*  (data from target to initiator) */
+#define OF_DATA_OUT         BIT_7       /* Data out from initiator */
+                                       /*  (data from initiator to target) */
+#define OF_NO_DATA          (BIT_7 | BIT_6)
+#define OF_INC_RC           BIT_8       /* Increment command resource count */
+#define OF_FAST_POST        BIT_9       /* Enable mailbox fast posting. */
+#define OF_CONF_REQ         BIT_13      /* Confirmation Requested */
+#define OF_TERM_EXCH        BIT_14      /* Terminate exchange */
+#define OF_SSTS             BIT_15      /* Send SCSI status */
+#endif
+
+#ifndef QLA_TGT_DATASEGS_PER_CMD32
+#define QLA_TGT_DATASEGS_PER_CMD32     3
+#define QLA_TGT_DATASEGS_PER_CONT32    7
+#define QLA_TGT_MAX_SG32(ql) \
+       (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD32 + \
+               QLA_TGT_DATASEGS_PER_CONT32*((ql) - 1)) : 0)
+
+#define QLA_TGT_DATASEGS_PER_CMD64     2
+#define QLA_TGT_DATASEGS_PER_CONT64    5
+#define QLA_TGT_MAX_SG64(ql) \
+       (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD64 + \
+               QLA_TGT_DATASEGS_PER_CONT64*((ql) - 1)) : 0)
+#endif
+
+#ifndef QLA_TGT_DATASEGS_PER_CMD_24XX
+#define QLA_TGT_DATASEGS_PER_CMD_24XX  1
+#define QLA_TGT_DATASEGS_PER_CONT_24XX 5
+#define QLA_TGT_MAX_SG_24XX(ql) \
+       (min(1270, ((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD_24XX + \
+               QLA_TGT_DATASEGS_PER_CONT_24XX*((ql) - 1)) : 0))
+#endif
+#endif
+
+#define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha))                        \
+                        ? le16_to_cpu((iocb)->u.isp2x.target.extended) \
+                        : (uint16_t)(iocb)->u.isp2x.target.id.standard)
+
+#ifndef IMMED_NOTIFY_TYPE
+#define IMMED_NOTIFY_TYPE 0x0D         /* Immediate notify entry. */
+/*
+ * ISP queue - immediate notify entry structure definition.
+ *             This is sent by the ISP to the Target driver.
+ *             This IOCB would have report of events sent by the
+ *             initiator, that needs to be handled by the target
+ *             driver immediately.
+ */
+struct imm_ntfy_from_isp {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       union {
+               struct {
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint16_t lun;
+                       uint8_t  target_id;
+                       uint8_t  reserved_1;
+                       uint16_t status_modifier;
+                       uint16_t status;
+                       uint16_t task_flags;
+                       uint16_t seq_id;
+                       uint16_t srr_rx_id;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+#define SRR_IU_DATA_IN 0x1
+#define SRR_IU_DATA_OUT        0x5
+#define SRR_IU_STATUS  0x7
+                       uint16_t srr_ox_id;
+                       uint8_t reserved_2[28];
+               } isp2x;
+               struct {
+                       uint32_t reserved;
+                       uint16_t nport_handle;
+                       uint16_t reserved_2;
+                       uint16_t flags;
+#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO   BIT_1
+#define NOTIFY24XX_FLAGS_PUREX_IOCB     BIT_0
+                       uint16_t srr_rx_id;
+                       uint16_t status;
+                       uint8_t  status_subcode;
+                       uint8_t  reserved_3;
+                       uint32_t exchange_address;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_ox_id;
+                       uint8_t  reserved_4[19];
+                       uint8_t  vp_index;
+                       uint32_t reserved_5;
+                       uint8_t  port_id[3];
+                       uint8_t  reserved_6;
+               } isp24;
+       } u;
+       uint16_t reserved_7;
+       uint16_t ox_id;
+} __packed;
+#endif
+
+#ifndef NOTIFY_ACK_TYPE
+#define NOTIFY_ACK_TYPE 0x0E     /* Notify acknowledge entry. */
+/*
+ * ISP queue - notify acknowledge entry structure definition.
+ *             This is sent to the ISP from the target driver.
+ */
+struct nack_to_isp {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       union {
+               struct {
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint8_t  target_id;
+                       uint8_t  reserved_1;
+                       uint16_t flags;
+                       uint16_t resp_code;
+                       uint16_t status;
+                       uint16_t task_flags;
+                       uint16_t seq_id;
+                       uint16_t srr_rx_id;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_flags;
+                       uint16_t srr_reject_code;
+                       uint8_t  srr_reject_vendor_uniq;
+                       uint8_t  srr_reject_code_expl;
+                       uint8_t  reserved_2[24];
+               } isp2x;
+               struct {
+                       uint32_t handle;
+                       uint16_t nport_handle;
+                       uint16_t reserved_1;
+                       uint16_t flags;
+                       uint16_t srr_rx_id;
+                       uint16_t status;
+                       uint8_t  status_subcode;
+                       uint8_t  reserved_3;
+                       uint32_t exchange_address;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_flags;
+                       uint8_t  reserved_4[19];
+                       uint8_t  vp_index;
+                       uint8_t  srr_reject_vendor_uniq;
+                       uint8_t  srr_reject_code_expl;
+                       uint8_t  srr_reject_code;
+                       uint8_t  reserved_5[5];
+               } isp24;
+       } u;
+       uint8_t  reserved[2];
+       uint16_t ox_id;
+} __packed;
+#define NOTIFY_ACK_SRR_FLAGS_ACCEPT    0
+#define NOTIFY_ACK_SRR_FLAGS_REJECT    1
+
+#define NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM 0x9
+
+#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL               0
+#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_UNABLE_TO_SUPPLY_DATA 0x2a
+
+#define NOTIFY_ACK_SUCCESS      0x01
+#endif
+
+#ifndef ACCEPT_TGT_IO_TYPE
+#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */
+#endif
+
+#ifndef CONTINUE_TGT_IO_TYPE
+#define CONTINUE_TGT_IO_TYPE 0x17
+/*
+ * ISP queue - Continue Target I/O (CTIO) entry for status mode 0 structure.
+ *             This structure is sent to the ISP 2xxx from target driver.
+ */
+struct ctio_to_2xxx {
+       uint8_t  entry_type;            /* Entry type. */
+       uint8_t  entry_count;           /* Entry count. */
+       uint8_t  sys_define;            /* System defined. */
+       uint8_t  entry_status;          /* Entry Status. */
+       uint32_t handle;                /* System defined handle */
+       target_id_t target;
+       uint16_t rx_id;
+       uint16_t flags;
+       uint16_t status;
+       uint16_t timeout;               /* 0 = 30 seconds, 0xFFFF = disable */
+       uint16_t dseg_count;            /* Data segment count. */
+       uint32_t relative_offset;
+       uint32_t residual;
+       uint16_t reserved_1[3];
+       uint16_t scsi_status;
+       uint32_t transfer_length;
+       uint32_t dseg_0_address;        /* Data segment 0 address. */
+       uint32_t dseg_0_length;         /* Data segment 0 length. */
+       uint32_t dseg_1_address;        /* Data segment 1 address. */
+       uint32_t dseg_1_length;         /* Data segment 1 length. */
+       uint32_t dseg_2_address;        /* Data segment 2 address. */
+       uint32_t dseg_2_length;         /* Data segment 2 length. */
+} __packed;
+#define ATIO_PATH_INVALID       0x07
+#define ATIO_CANT_PROV_CAP      0x16
+#define ATIO_CDB_VALID          0x3D
+
+#define ATIO_EXEC_READ          BIT_1
+#define ATIO_EXEC_WRITE         BIT_0
+#endif
+
+#ifndef CTIO_A64_TYPE
+#define CTIO_A64_TYPE 0x1F
+#define CTIO_SUCCESS                   0x01
+#define CTIO_ABORTED                   0x02
+#define CTIO_INVALID_RX_ID             0x08
+#define CTIO_TIMEOUT                   0x0B
+#define CTIO_LIP_RESET                 0x0E
+#define CTIO_TARGET_RESET              0x17
+#define CTIO_PORT_UNAVAILABLE          0x28
+#define CTIO_PORT_LOGGED_OUT           0x29
+#define CTIO_PORT_CONF_CHANGED         0x2A
+#define CTIO_SRR_RECEIVED              0x45
+#endif
+
+#ifndef CTIO_RET_TYPE
+#define CTIO_RET_TYPE  0x17            /* CTIO return entry */
+#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */
+
+struct fcp_hdr {
+       uint8_t  r_ctl;
+       uint8_t  d_id[3];
+       uint8_t  cs_ctl;
+       uint8_t  s_id[3];
+       uint8_t  type;
+       uint8_t  f_ctl[3];
+       uint8_t  seq_id;
+       uint8_t  df_ctl;
+       uint16_t seq_cnt;
+       uint16_t ox_id;
+       uint16_t rx_id;
+       uint32_t parameter;
+} __packed;
+
+struct fcp_hdr_le {
+       uint8_t  d_id[3];
+       uint8_t  r_ctl;
+       uint8_t  s_id[3];
+       uint8_t  cs_ctl;
+       uint8_t  f_ctl[3];
+       uint8_t  type;
+       uint16_t seq_cnt;
+       uint8_t  df_ctl;
+       uint8_t  seq_id;
+       uint16_t rx_id;
+       uint16_t ox_id;
+       uint32_t parameter;
+} __packed;
+
+#define F_CTL_EXCH_CONTEXT_RESP        BIT_23
+#define F_CTL_SEQ_CONTEXT_RESIP        BIT_22
+#define F_CTL_LAST_SEQ         BIT_20
+#define F_CTL_END_SEQ          BIT_19
+#define F_CTL_SEQ_INITIATIVE   BIT_16
+
+#define R_CTL_BASIC_LINK_SERV  0x80
+#define R_CTL_B_ACC            0x4
+#define R_CTL_B_RJT            0x5
+
+struct atio7_fcp_cmnd {
+       uint64_t lun;
+       uint8_t  cmnd_ref;
+       uint8_t  task_attr:3;
+       uint8_t  reserved:5;
+       uint8_t  task_mgmt_flags;
+#define FCP_CMND_TASK_MGMT_CLEAR_ACA           6
+#define FCP_CMND_TASK_MGMT_TARGET_RESET                5
+#define FCP_CMND_TASK_MGMT_LU_RESET            4
+#define FCP_CMND_TASK_MGMT_CLEAR_TASK_SET      2
+#define FCP_CMND_TASK_MGMT_ABORT_TASK_SET      1
+       uint8_t  wrdata:1;
+       uint8_t  rddata:1;
+       uint8_t  add_cdb_len:6;
+       uint8_t  cdb[16];
+       /*
+        * add_cdb is optional and can absent from struct atio7_fcp_cmnd. Size 4
+        * only to make sizeof(struct atio7_fcp_cmnd) be as expected by
+        * BUILD_BUG_ON in qlt_init().
+        */
+       uint8_t  add_cdb[4];
+       /* uint32_t data_length; */
+} __packed;
+
+/*
+ * ISP queue - Accept Target I/O (ATIO) type entry IOCB structure.
+ *             This is sent from the ISP to the target driver.
+ */
+struct atio_from_isp {
+       union {
+               struct {
+                       uint16_t entry_hdr;
+                       uint8_t  sys_define;   /* System defined. */
+                       uint8_t  entry_status; /* Entry Status.   */
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint16_t rx_id;
+                       uint16_t flags;
+                       uint16_t status;
+                       uint8_t  command_ref;
+                       uint8_t  task_codes;
+                       uint8_t  task_flags;
+                       uint8_t  execution_codes;
+                       uint8_t  cdb[MAX_CMDSZ];
+                       uint32_t data_length;
+                       uint16_t lun;
+                       uint8_t  initiator_port_name[WWN_SIZE]; /* on qla23xx */
+                       uint16_t reserved_32[6];
+                       uint16_t ox_id;
+               } isp2x;
+               struct {
+                       uint16_t entry_hdr;
+                       uint8_t  fcp_cmnd_len_low;
+                       uint8_t  fcp_cmnd_len_high:4;
+                       uint8_t  attr:4;
+                       uint32_t exchange_addr;
+#define ATIO_EXCHANGE_ADDRESS_UNKNOWN  0xFFFFFFFF
+                       struct fcp_hdr fcp_hdr;
+                       struct atio7_fcp_cmnd fcp_cmnd;
+               } isp24;
+               struct {
+                       uint8_t  entry_type;    /* Entry type. */
+                       uint8_t  entry_count;   /* Entry count. */
+                       uint8_t  data[58];
+                       uint32_t signature;
+#define ATIO_PROCESSED 0xDEADDEAD              /* Signature */
+               } raw;
+       } u;
+} __packed;
+
+#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */
+
+/*
+ * ISP queue - Continue Target I/O (ATIO) type 7 entry (for 24xx) structure.
+ *             This structure is sent to the ISP 24xx from the target driver.
+ */
+
+struct ctio7_to_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;                    /* System defined handle */
+       uint16_t nport_handle;
+#define CTIO7_NHANDLE_UNRECOGNIZED     0xFFFF
+       uint16_t timeout;
+       uint16_t dseg_count;                /* Data segment count. */
+       uint8_t  vp_index;
+       uint8_t  add_flags;
+       uint8_t  initiator_id[3];
+       uint8_t  reserved;
+       uint32_t exchange_addr;
+       union {
+               struct {
+                       uint16_t reserved1;
+                       uint16_t flags;
+                       uint32_t residual;
+                       uint16_t ox_id;
+                       uint16_t scsi_status;
+                       uint32_t relative_offset;
+                       uint32_t reserved2;
+                       uint32_t transfer_length;
+                       uint32_t reserved3;
+                       /* Data segment 0 address. */
+                       uint32_t dseg_0_address[2];
+                       /* Data segment 0 length. */
+                       uint32_t dseg_0_length;
+               } status0;
+               struct {
+                       uint16_t sense_length;
+                       uint16_t flags;
+                       uint32_t residual;
+                       uint16_t ox_id;
+                       uint16_t scsi_status;
+                       uint16_t response_len;
+                       uint16_t reserved;
+                       uint8_t sense_data[24];
+               } status1;
+       } u;
+} __packed;
+
+/*
+ * ISP queue - CTIO type 7 from ISP 24xx to target driver
+ * returned entry structure.
+ */
+struct ctio7_from_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;                    /* System defined handle */
+       uint16_t status;
+       uint16_t timeout;
+       uint16_t dseg_count;                /* Data segment count. */
+       uint8_t  vp_index;
+       uint8_t  reserved1[5];
+       uint32_t exchange_address;
+       uint16_t reserved2;
+       uint16_t flags;
+       uint32_t residual;
+       uint16_t ox_id;
+       uint16_t reserved3;
+       uint32_t relative_offset;
+       uint8_t  reserved4[24];
+} __packed;
+
+/* CTIO7 flags values */
+#define CTIO7_FLAGS_SEND_STATUS                BIT_15
+#define CTIO7_FLAGS_TERMINATE          BIT_14
+#define CTIO7_FLAGS_CONFORM_REQ                BIT_13
+#define CTIO7_FLAGS_DONT_RET_CTIO      BIT_8
+#define CTIO7_FLAGS_STATUS_MODE_0      0
+#define CTIO7_FLAGS_STATUS_MODE_1      BIT_6
+#define CTIO7_FLAGS_EXPLICIT_CONFORM   BIT_5
+#define CTIO7_FLAGS_CONFIRM_SATISF     BIT_4
+#define CTIO7_FLAGS_DSD_PTR            BIT_2
+#define CTIO7_FLAGS_DATA_IN            BIT_1
+#define CTIO7_FLAGS_DATA_OUT           BIT_0
+
+#define ELS_PLOGI                      0x3
+#define ELS_FLOGI                      0x4
+#define ELS_LOGO                       0x5
+#define ELS_PRLI                       0x20
+#define ELS_PRLO                       0x21
+#define ELS_TPRLO                      0x24
+#define ELS_PDISC                      0x50
+#define ELS_ADISC                      0x52
+
+/*
+ * ISP queue - ABTS received/response entries structure definition for 24xx.
+ */
+#define ABTS_RECV_24XX         0x54 /* ABTS received (for 24xx) */
+#define ABTS_RESP_24XX         0x55 /* ABTS responce (for 24xx) */
+
+/*
+ * ISP queue - ABTS received IOCB entry structure definition for 24xx.
+ *             The ABTS BLS received from the wire is sent to the
+ *             target driver by the ISP 24xx.
+ *             The IOCB is placed on the response queue.
+ */
+struct abts_recv_from_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint8_t  reserved_1[6];
+       uint16_t nport_handle;
+       uint8_t  reserved_2[2];
+       uint8_t  vp_index;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       uint8_t  reserved_4[16];
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+#define ABTS_PARAM_ABORT_SEQ           BIT_0
+
+struct ba_acc_le {
+       uint16_t reserved;
+       uint8_t  seq_id_last;
+       uint8_t  seq_id_valid;
+#define SEQ_ID_VALID   0x80
+#define SEQ_ID_INVALID 0x00
+       uint16_t rx_id;
+       uint16_t ox_id;
+       uint16_t high_seq_cnt;
+       uint16_t low_seq_cnt;
+} __packed;
+
+struct ba_rjt_le {
+       uint8_t vendor_uniq;
+       uint8_t reason_expl;
+       uint8_t reason_code;
+#define BA_RJT_REASON_CODE_INVALID_COMMAND     0x1
+#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM   0x9
+       uint8_t reserved;
+} __packed;
+
+/*
+ * ISP queue - ABTS Response IOCB entry structure definition for 24xx.
+ *             The ABTS response to the ABTS received is sent by the
+ *             target driver to the ISP 24xx.
+ *             The IOCB is placed on the request queue.
+ */
+struct abts_resp_to_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;
+       uint16_t reserved_1;
+       uint16_t nport_handle;
+       uint16_t control_flags;
+#define ABTS_CONTR_FLG_TERM_EXCHG      BIT_0
+       uint8_t  vp_index;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       union {
+               struct ba_acc_le ba_acct;
+               struct ba_rjt_le ba_rjt;
+       } __packed payload;
+       uint32_t reserved_4;
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+/*
+ * ISP queue - ABTS Response IOCB from ISP24xx Firmware entry structure.
+ *             The ABTS response with completion status to the ABTS response
+ *             (sent by the target driver to the ISP 24xx) is sent by the
+ *             ISP24xx firmware to the target driver.
+ *             The IOCB is placed on the response queue.
+ */
+struct abts_resp_from_24xx_fw {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;
+       uint16_t compl_status;
+#define ABTS_RESP_COMPL_SUCCESS                0
+#define ABTS_RESP_COMPL_SUBCODE_ERROR  0x31
+       uint16_t nport_handle;
+       uint16_t reserved_1;
+       uint8_t  reserved_2;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       uint8_t reserved_4[8];
+       uint32_t error_subcode1;
+#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM    0x1E
+       uint32_t error_subcode2;
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+/********************************************************************\
+ * Type Definitions used by initiator & target halves
+\********************************************************************/
+
+struct qla_tgt_mgmt_cmd;
+struct qla_tgt_sess;
+
+/*
+ * This structure provides a template of function calls that the
+ * target driver (from within qla_target.c) can issue to the
+ * target module (tcm_qla2xxx).
+ */
+struct qla_tgt_func_tmpl {
+
+       int (*handle_cmd)(struct scsi_qla_host *, struct qla_tgt_cmd *,
+                       unsigned char *, uint32_t, int, int, int);
+       int (*handle_data)(struct qla_tgt_cmd *);
+       int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint8_t,
+                       uint32_t);
+       void (*free_cmd)(struct qla_tgt_cmd *);
+       void (*free_mcmd)(struct qla_tgt_mgmt_cmd *);
+       void (*free_session)(struct qla_tgt_sess *);
+
+       int (*check_initiator_node_acl)(struct scsi_qla_host *, unsigned char *,
+                                       void *, uint8_t *, uint16_t);
+       struct qla_tgt_sess *(*find_sess_by_loop_id)(struct scsi_qla_host *,
+                                               const uint16_t);
+       struct qla_tgt_sess *(*find_sess_by_s_id)(struct scsi_qla_host *,
+                                               const uint8_t *);
+       void (*clear_nacl_from_fcport_map)(struct qla_tgt_sess *);
+       void (*put_sess)(struct qla_tgt_sess *);
+       void (*shutdown_sess)(struct qla_tgt_sess *);
+};
+
+int qla2x00_wait_for_hba_online(struct scsi_qla_host *);
+
+#include <target/target_core_base.h>
+
+#define QLA_TGT_TIMEOUT                        10      /* in seconds */
+
+#define QLA_TGT_MAX_HW_PENDING_TIME    60 /* in seconds */
+
+/* Immediate notify status constants */
+#define IMM_NTFY_LIP_RESET          0x000E
+#define IMM_NTFY_LIP_LINK_REINIT    0x000F
+#define IMM_NTFY_IOCB_OVERFLOW      0x0016
+#define IMM_NTFY_ABORT_TASK         0x0020
+#define IMM_NTFY_PORT_LOGOUT        0x0029
+#define IMM_NTFY_PORT_CONFIG        0x002A
+#define IMM_NTFY_GLBL_TPRLO         0x002D
+#define IMM_NTFY_GLBL_LOGO          0x002E
+#define IMM_NTFY_RESOURCE           0x0034
+#define IMM_NTFY_MSG_RX             0x0036
+#define IMM_NTFY_SRR                0x0045
+#define IMM_NTFY_ELS                0x0046
+
+/* Immediate notify task flags */
+#define IMM_NTFY_TASK_MGMT_SHIFT    8
+
+#define QLA_TGT_CLEAR_ACA               0x40
+#define QLA_TGT_TARGET_RESET            0x20
+#define QLA_TGT_LUN_RESET               0x10
+#define QLA_TGT_CLEAR_TS                0x04
+#define QLA_TGT_ABORT_TS                0x02
+#define QLA_TGT_ABORT_ALL_SESS          0xFFFF
+#define QLA_TGT_ABORT_ALL               0xFFFE
+#define QLA_TGT_NEXUS_LOSS_SESS         0xFFFD
+#define QLA_TGT_NEXUS_LOSS              0xFFFC
+
+/* Notify Acknowledge flags */
+#define NOTIFY_ACK_RES_COUNT        BIT_8
+#define NOTIFY_ACK_CLEAR_LIP_RESET  BIT_5
+#define NOTIFY_ACK_TM_RESP_CODE_VALID BIT_4
+
+/* Command's states */
+#define QLA_TGT_STATE_NEW              0 /* New command + target processing */
+#define QLA_TGT_STATE_NEED_DATA                1 /* target needs data to continue */
+#define QLA_TGT_STATE_DATA_IN          2 /* Data arrived + target processing */
+#define QLA_TGT_STATE_PROCESSED                3 /* target done processing */
+#define QLA_TGT_STATE_ABORTED          4 /* Command aborted */
+
+/* Special handles */
+#define QLA_TGT_NULL_HANDLE    0
+#define QLA_TGT_SKIP_HANDLE    (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK)
+
+/* ATIO task_codes field */
+#define ATIO_SIMPLE_QUEUE           0
+#define ATIO_HEAD_OF_QUEUE          1
+#define ATIO_ORDERED_QUEUE          2
+#define ATIO_ACA_QUEUE              4
+#define ATIO_UNTAGGED               5
+
+/* TM failed response codes, see FCP (9.4.11 FCP_RSP_INFO) */
+#define        FC_TM_SUCCESS               0
+#define        FC_TM_BAD_FCP_DATA          1
+#define        FC_TM_BAD_CMD               2
+#define        FC_TM_FCP_DATA_MISMATCH     3
+#define        FC_TM_REJECT                4
+#define FC_TM_FAILED                5
+
+/*
+ * Error code of qlt_pre_xmit_response() meaning that cmd's exchange was
+ * terminated, so no more actions is needed and success should be returned
+ * to target.
+ */
+#define QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED      0x1717
+
+#if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G)
+#define pci_dma_lo32(a) (a & 0xffffffff)
+#define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff)
+#else
+#define pci_dma_lo32(a) (a & 0xffffffff)
+#define pci_dma_hi32(a) 0
+#endif
+
+#define QLA_TGT_SENSE_VALID(sense)  ((sense != NULL) && \
+                               (((const uint8_t *)(sense))[0] & 0x70) == 0x70)
+
+struct qla_port_24xx_data {
+       uint8_t port_name[WWN_SIZE];
+       uint16_t loop_id;
+       uint16_t reserved;
+};
+
+struct qla_tgt {
+       struct scsi_qla_host *vha;
+       struct qla_hw_data *ha;
+
+       /*
+        * To sync between IRQ handlers and qlt_target_release(). Needed,
+        * because req_pkt() can drop/reaquire HW lock inside. Protected by
+        * HW lock.
+        */
+       int irq_cmd_count;
+
+       int datasegs_per_cmd, datasegs_per_cont, sg_tablesize;
+
+       /* Target's flags, serialized by pha->hardware_lock */
+       unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addr enabled */
+       unsigned int link_reinit_iocb_pending:1;
+
+       /*
+        * Protected by tgt_mutex AND hardware_lock for writing and tgt_mutex
+        * OR hardware_lock for reading.
+        */
+       int tgt_stop; /* the target mode driver is being stopped */
+       int tgt_stopped; /* the target mode driver has been stopped */
+
+       /* Count of sessions refering qla_tgt. Protected by hardware_lock. */
+       int sess_count;
+
+       /* Protected by hardware_lock. Addition also protected by tgt_mutex. */
+       struct list_head sess_list;
+
+       /* Protected by hardware_lock */
+       struct list_head del_sess_list;
+       struct delayed_work sess_del_work;
+
+       spinlock_t sess_work_lock;
+       struct list_head sess_works_list;
+       struct work_struct sess_work;
+
+       struct imm_ntfy_from_isp link_reinit_iocb;
+       wait_queue_head_t waitQ;
+       int notify_ack_expected;
+       int abts_resp_expected;
+       int modify_lun_expected;
+
+       int ctio_srr_id;
+       int imm_srr_id;
+       spinlock_t srr_lock;
+       struct list_head srr_ctio_list;
+       struct list_head srr_imm_list;
+       struct work_struct srr_work;
+
+       atomic_t tgt_global_resets_count;
+
+       struct list_head tgt_list_entry;
+};
+
+/*
+ * Equivilant to IT Nexus (Initiator-Target)
+ */
+struct qla_tgt_sess {
+       uint16_t loop_id;
+       port_id_t s_id;
+
+       unsigned int conf_compl_supported:1;
+       unsigned int deleted:1;
+       unsigned int local:1;
+       unsigned int tearing_down:1;
+
+       struct se_session *se_sess;
+       struct scsi_qla_host *vha;
+       struct qla_tgt *tgt;
+
+       struct list_head sess_list_entry;
+       unsigned long expires;
+       struct list_head del_list_entry;
+
+       uint8_t port_name[WWN_SIZE];
+       struct work_struct free_work;
+};
+
+struct qla_tgt_cmd {
+       struct qla_tgt_sess *sess;
+       int state;
+       struct se_cmd se_cmd;
+       struct work_struct free_work;
+       struct work_struct work;
+       /* Sense buffer that will be mapped into outgoing status */
+       unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
+
+       /* to save extra sess dereferences */
+       unsigned int conf_compl_supported:1;
+       unsigned int sg_mapped:1;
+       unsigned int free_sg:1;
+       unsigned int aborted:1; /* Needed in case of SRR */
+       unsigned int write_data_transferred:1;
+
+       struct scatterlist *sg; /* cmd data buffer SG vector */
+       int sg_cnt;             /* SG segments count */
+       int bufflen;            /* cmd buffer length */
+       int offset;
+       uint32_t tag;
+       uint32_t unpacked_lun;
+       enum dma_data_direction dma_data_direction;
+
+       uint16_t loop_id;       /* to save extra sess dereferences */
+       struct qla_tgt *tgt;    /* to save extra sess dereferences */
+       struct scsi_qla_host *vha;
+       struct list_head cmd_list;
+
+       struct atio_from_isp atio;
+};
+
+struct qla_tgt_sess_work_param {
+       struct list_head sess_works_list_entry;
+
+#define QLA_TGT_SESS_WORK_ABORT        1
+#define QLA_TGT_SESS_WORK_TM   2
+       int type;
+
+       union {
+               struct abts_recv_from_24xx abts;
+               struct imm_ntfy_from_isp tm_iocb;
+               struct atio_from_isp tm_iocb2;
+       };
+};
+
+struct qla_tgt_mgmt_cmd {
+       uint8_t tmr_func;
+       uint8_t fc_tm_rsp;
+       struct qla_tgt_sess *sess;
+       struct se_cmd se_cmd;
+       struct work_struct free_work;
+       unsigned int flags;
+#define QLA24XX_MGMT_SEND_NACK 1
+       union {
+               struct atio_from_isp atio;
+               struct imm_ntfy_from_isp imm_ntfy;
+               struct abts_recv_from_24xx abts;
+       } __packed orig_iocb;
+};
+
+struct qla_tgt_prm {
+       struct qla_tgt_cmd *cmd;
+       struct qla_tgt *tgt;
+       void *pkt;
+       struct scatterlist *sg; /* cmd data buffer SG vector */
+       int seg_cnt;
+       int req_cnt;
+       uint16_t rq_result;
+       uint16_t scsi_status;
+       unsigned char *sense_buffer;
+       int sense_buffer_len;
+       int residual;
+       int add_status_pkt;
+};
+
+struct qla_tgt_srr_imm {
+       struct list_head srr_list_entry;
+       int srr_id;
+       struct imm_ntfy_from_isp imm_ntfy;
+};
+
+struct qla_tgt_srr_ctio {
+       struct list_head srr_list_entry;
+       int srr_id;
+       struct qla_tgt_cmd *cmd;
+};
+
+#define QLA_TGT_XMIT_DATA              1
+#define QLA_TGT_XMIT_STATUS            2
+#define QLA_TGT_XMIT_ALL               (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA)
+
+#include <linux/version.h>
+
+extern struct qla_tgt_data qla_target;
+/*
+ * Internal function prototypes
+ */
+void qlt_disable_vha(struct scsi_qla_host *);
+
+/*
+ * Function prototypes for qla_target.c logic used by qla2xxx LLD code.
+ */
+extern int qlt_add_target(struct qla_hw_data *, struct scsi_qla_host *);
+extern int qlt_remove_target(struct qla_hw_data *, struct scsi_qla_host *);
+extern int qlt_lport_register(struct qla_tgt_func_tmpl *, u64,
+                       int (*callback)(struct scsi_qla_host *), void *);
+extern void qlt_lport_deregister(struct scsi_qla_host *);
+extern void qlt_unreg_sess(struct qla_tgt_sess *);
+extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *);
+extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *);
+extern void qlt_set_mode(struct scsi_qla_host *ha);
+extern void qlt_clear_mode(struct scsi_qla_host *ha);
+extern int __init qlt_init(void);
+extern void qlt_exit(void);
+extern void qlt_update_vp_map(struct scsi_qla_host *, int);
+
+/*
+ * This macro is used during early initializations when host->active_mode
+ * is not set. Right now, ha value is ignored.
+ */
+#define QLA_TGT_MODE_ENABLED() (ql2x_ini_mode != QLA2XXX_INI_MODE_ENABLED)
+
+static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha)
+{
+       return ha->host->active_mode & MODE_TARGET;
+}
+
+static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha)
+{
+       return ha->host->active_mode & MODE_INITIATOR;
+}
+
+static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha)
+{
+       if (ha->host->active_mode & MODE_INITIATOR)
+               ha->host->active_mode &= ~MODE_INITIATOR;
+       else
+               ha->host->active_mode |= MODE_INITIATOR;
+}
+
+/*
+ * Exported symbols from qla_target.c LLD logic used by qla2xxx code..
+ */
+extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *,
+       struct atio_from_isp *);
+extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *);
+extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *);
+extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t);
+extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *);
+extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *);
+extern void qlt_free_cmd(struct qla_tgt_cmd *cmd);
+extern void qlt_ctio_completion(struct scsi_qla_host *, uint32_t);
+extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *);
+extern void qlt_enable_vha(struct scsi_qla_host *);
+extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *);
+extern void qlt_rff_id(struct scsi_qla_host *, struct ct_sns_req *);
+extern void qlt_init_atio_q_entries(struct scsi_qla_host *);
+extern void qlt_24xx_process_atio_queue(struct scsi_qla_host *);
+extern void qlt_24xx_config_rings(struct scsi_qla_host *,
+       device_reg_t __iomem *);
+extern void qlt_24xx_config_nvram_stage1(struct scsi_qla_host *,
+       struct nvram_24xx *);
+extern void qlt_24xx_config_nvram_stage2(struct scsi_qla_host *,
+       struct init_cb_24xx *);
+extern int qlt_24xx_process_response_error(struct scsi_qla_host *,
+       struct sts_entry_24xx *);
+extern void qlt_modify_vp_config(struct scsi_qla_host *,
+       struct vp_config_entry_24xx *);
+extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *);
+extern int qlt_mem_alloc(struct qla_hw_data *);
+extern void qlt_mem_free(struct qla_hw_data *);
+extern void qlt_stop_phase1(struct qla_tgt *);
+extern void qlt_stop_phase2(struct qla_tgt *);
+
+#endif /* __QLA_TARGET_H */
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
new file mode 100644 (file)
index 0000000..436598f
--- /dev/null
@@ -0,0 +1,1955 @@
+/*******************************************************************************
+ * This file contains tcm implementation using v4 configfs fabric infrastructure
+ * for QLogic target mode HBAs
+ *
+ * ?? Copyright 2010-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL)
+ * version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ *
+ * tcm_qla2xxx_parse_wwn() and tcm_qla2xxx_format_wwn() contains code from
+ * the TCM_FC / Open-FCoE.org fabric module.
+ *
+ * Copyright (c) 2010 Cisco Systems, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "qla_def.h"
+#include "qla_target.h"
+#include "tcm_qla2xxx.h"
+
+struct workqueue_struct *tcm_qla2xxx_free_wq;
+struct workqueue_struct *tcm_qla2xxx_cmd_wq;
+
+static int tcm_qla2xxx_check_true(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static int tcm_qla2xxx_check_false(struct se_portal_group *se_tpg)
+{
+       return 0;
+}
+
+/*
+ * Parse WWN.
+ * If strict, we require lower-case hex and colon separators to be sure
+ * the name is the same as what would be generated by ft_format_wwn()
+ * so the name and wwn are mapped one-to-one.
+ */
+static ssize_t tcm_qla2xxx_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+       const char *cp;
+       char c;
+       u32 nibble;
+       u32 byte = 0;
+       u32 pos = 0;
+       u32 err;
+
+       *wwn = 0;
+       for (cp = name; cp < &name[TCM_QLA2XXX_NAMELEN - 1]; cp++) {
+               c = *cp;
+               if (c == '\n' && cp[1] == '\0')
+                       continue;
+               if (strict && pos++ == 2 && byte++ < 7) {
+                       pos = 0;
+                       if (c == ':')
+                               continue;
+                       err = 1;
+                       goto fail;
+               }
+               if (c == '\0') {
+                       err = 2;
+                       if (strict && byte != 8)
+                               goto fail;
+                       return cp - name;
+               }
+               err = 3;
+               if (isdigit(c))
+                       nibble = c - '0';
+               else if (isxdigit(c) && (islower(c) || !strict))
+                       nibble = tolower(c) - 'a' + 10;
+               else
+                       goto fail;
+               *wwn = (*wwn << 4) | nibble;
+       }
+       err = 4;
+fail:
+       pr_debug("err %u len %zu pos %u byte %u\n",
+                       err, cp - name, pos, byte);
+       return -1;
+}
+
+static ssize_t tcm_qla2xxx_format_wwn(char *buf, size_t len, u64 wwn)
+{
+       u8 b[8];
+
+       put_unaligned_be64(wwn, b);
+       return snprintf(buf, len,
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+               b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+static char *tcm_qla2xxx_get_fabric_name(void)
+{
+       return "qla2xxx";
+}
+
+/*
+ * From drivers/scsi/scsi_transport_fc.c:fc_parse_wwn
+ */
+static int tcm_qla2xxx_npiv_extract_wwn(const char *ns, u64 *nm)
+{
+       unsigned int i, j, value;
+       u8 wwn[8];
+
+       memset(wwn, 0, sizeof(wwn));
+
+       /* Validate and store the new name */
+       for (i = 0, j = 0; i < 16; i++) {
+               value = hex_to_bin(*ns++);
+               if (value >= 0)
+                       j = (j << 4) | value;
+               else
+                       return -EINVAL;
+
+               if (i % 2) {
+                       wwn[i/2] = j & 0xff;
+                       j = 0;
+               }
+       }
+
+       *nm = wwn_to_u64(wwn);
+       return 0;
+}
+
+/*
+ * This parsing logic follows drivers/scsi/scsi_transport_fc.c:
+ * store_fc_host_vport_create()
+ */
+static int tcm_qla2xxx_npiv_parse_wwn(
+       const char *name,
+       size_t count,
+       u64 *wwpn,
+       u64 *wwnn)
+{
+       unsigned int cnt = count;
+       int rc;
+
+       *wwpn = 0;
+       *wwnn = 0;
+
+       /* count may include a LF at end of string */
+       if (name[cnt-1] == '\n')
+               cnt--;
+
+       /* validate we have enough characters for WWPN */
+       if ((cnt != (16+1+16)) || (name[16] != ':'))
+               return -EINVAL;
+
+       rc = tcm_qla2xxx_npiv_extract_wwn(&name[0], wwpn);
+       if (rc != 0)
+               return rc;
+
+       rc = tcm_qla2xxx_npiv_extract_wwn(&name[17], wwnn);
+       if (rc != 0)
+               return rc;
+
+       return 0;
+}
+
+static ssize_t tcm_qla2xxx_npiv_format_wwn(char *buf, size_t len,
+                                       u64 wwpn, u64 wwnn)
+{
+       u8 b[8], b2[8];
+
+       put_unaligned_be64(wwpn, b);
+       put_unaligned_be64(wwnn, b2);
+       return snprintf(buf, len,
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x,"
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+               b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
+               b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6], b2[7]);
+}
+
+static char *tcm_qla2xxx_npiv_get_fabric_name(void)
+{
+       return "qla2xxx_npiv";
+}
+
+static u8 tcm_qla2xxx_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       u8 proto_id;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               proto_id = fc_get_fabric_proto_ident(se_tpg);
+               break;
+       }
+
+       return proto_id;
+}
+
+static char *tcm_qla2xxx_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+
+       return &lport->lport_name[0];
+}
+
+static char *tcm_qla2xxx_npiv_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+
+       return &lport->lport_npiv_name[0];
+}
+
+static u16 tcm_qla2xxx_get_tag(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       return tpg->lport_tpgt;
+}
+
+static u32 tcm_qla2xxx_get_default_depth(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static u32 tcm_qla2xxx_get_pr_transport_id(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct t10_pr_registration *pr_reg,
+       int *format_code,
+       unsigned char *buf)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       int ret = 0;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               ret = fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                                       format_code, buf);
+               break;
+       }
+
+       return ret;
+}
+
+static u32 tcm_qla2xxx_get_pr_transport_id_len(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct t10_pr_registration *pr_reg,
+       int *format_code)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       int ret = 0;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               ret = fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                                       format_code);
+               break;
+       }
+
+       return ret;
+}
+
+static char *tcm_qla2xxx_parse_pr_out_transport_id(
+       struct se_portal_group *se_tpg,
+       const char *buf,
+       u32 *out_tid_len,
+       char **port_nexus_ptr)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       char *tid = NULL;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               tid = fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                                       port_nexus_ptr);
+               break;
+       }
+
+       return tid;
+}
+
+static int tcm_qla2xxx_check_demo_mode(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->generate_node_acls;
+}
+
+static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls;
+}
+
+static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect;
+}
+
+static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->prod_mode_write_protect;
+}
+
+static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl(
+       struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_nacl *nacl;
+
+       nacl = kzalloc(sizeof(struct tcm_qla2xxx_nacl), GFP_KERNEL);
+       if (!nacl) {
+               pr_err("Unable to alocate struct tcm_qla2xxx_nacl\n");
+               return NULL;
+       }
+
+       return &nacl->se_node_acl;
+}
+
+static void tcm_qla2xxx_release_fabric_acl(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl)
+{
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl,
+                       struct tcm_qla2xxx_nacl, se_node_acl);
+       kfree(nacl);
+}
+
+static u32 tcm_qla2xxx_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return tpg->lport_tpgt;
+}
+
+static void tcm_qla2xxx_complete_mcmd(struct work_struct *work)
+{
+       struct qla_tgt_mgmt_cmd *mcmd = container_of(work,
+                       struct qla_tgt_mgmt_cmd, free_work);
+
+       transport_generic_free_cmd(&mcmd->se_cmd, 0);
+}
+
+/*
+ * Called from qla_target_template->free_mcmd(), and will call
+ * tcm_qla2xxx_release_cmd() via normal struct target_core_fabric_ops
+ * release callback.  qla_hw_data->hardware_lock is expected to be held
+ */
+static void tcm_qla2xxx_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       INIT_WORK(&mcmd->free_work, tcm_qla2xxx_complete_mcmd);
+       queue_work(tcm_qla2xxx_free_wq, &mcmd->free_work);
+}
+
+static void tcm_qla2xxx_complete_free(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+
+       transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+/*
+ * Called from qla_target_template->free_cmd(), and will call
+ * tcm_qla2xxx_release_cmd via normal struct target_core_fabric_ops
+ * release callback.  qla_hw_data->hardware_lock is expected to be held
+ */
+static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd)
+{
+       INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free);
+       queue_work(tcm_qla2xxx_free_wq, &cmd->work);
+}
+
+/*
+ * Called from struct target_core_fabric_ops->check_stop_free() context
+ */
+static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd)
+{
+       return target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+}
+
+/* tcm_qla2xxx_release_cmd - Callback from TCM Core to release underlying
+ * fabric descriptor @se_cmd command to release
+ */
+static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd;
+
+       if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) {
+               struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
+                               struct qla_tgt_mgmt_cmd, se_cmd);
+               qlt_free_mcmd(mcmd);
+               return;
+       }
+
+       cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd);
+       qlt_free_cmd(cmd);
+}
+
+static int tcm_qla2xxx_shutdown_session(struct se_session *se_sess)
+{
+       struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr;
+       struct scsi_qla_host *vha;
+       unsigned long flags;
+
+       BUG_ON(!sess);
+       vha = sess->vha;
+
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       sess->tearing_down = 1;
+       target_splice_sess_cmd_list(se_sess);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+
+       return 1;
+}
+
+static void tcm_qla2xxx_close_session(struct se_session *se_sess)
+{
+       struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr;
+       struct scsi_qla_host *vha;
+       unsigned long flags;
+
+       BUG_ON(!sess);
+       vha = sess->vha;
+
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       qlt_unreg_sess(sess);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+}
+
+static u32 tcm_qla2xxx_sess_get_index(struct se_session *se_sess)
+{
+       return 0;
+}
+
+/*
+ * The LIO target core uses DMA_TO_DEVICE to mean that data is going
+ * to the target (eg handling a WRITE) and DMA_FROM_DEVICE to mean
+ * that data is coming from the target (eg handling a READ).  However,
+ * this is just the opposite of what we have to tell the DMA mapping
+ * layer -- eg when handling a READ, the HBA will have to DMA the data
+ * out of memory so it can send it to the initiator, which means we
+ * need to use DMA_TO_DEVICE when we map the data.
+ */
+static enum dma_data_direction tcm_qla2xxx_mapping_dir(struct se_cmd *se_cmd)
+{
+       if (se_cmd->se_cmd_flags & SCF_BIDI)
+               return DMA_BIDIRECTIONAL;
+
+       switch (se_cmd->data_direction) {
+       case DMA_TO_DEVICE:
+               return DMA_FROM_DEVICE;
+       case DMA_FROM_DEVICE:
+               return DMA_TO_DEVICE;
+       case DMA_NONE:
+       default:
+               return DMA_NONE;
+       }
+}
+
+static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+
+       cmd->sg_cnt = se_cmd->t_data_nents;
+       cmd->sg = se_cmd->t_data_sg;
+
+       /*
+        * qla_target.c:qlt_rdy_to_xfer() will call pci_map_sg() to setup
+        * the SGL mappings into PCIe memory for incoming FCP WRITE data.
+        */
+       return qlt_rdy_to_xfer(cmd);
+}
+
+static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd)
+{
+       unsigned long flags;
+       /*
+        * Check for WRITE_PENDING status to determine if we need to wait for
+        * CTIO aborts to be posted via hardware in tcm_qla2xxx_handle_data().
+        */
+       spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+       if (se_cmd->t_state == TRANSPORT_WRITE_PENDING ||
+           se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) {
+               spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+               wait_for_completion_timeout(&se_cmd->t_transport_stop_comp,
+                                               3000);
+               return 0;
+       }
+       spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+       return 0;
+}
+
+static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl)
+{
+       return;
+}
+
+static u32 tcm_qla2xxx_get_task_tag(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       return cmd->tag;
+}
+
+static int tcm_qla2xxx_get_cmd_state(struct se_cmd *se_cmd)
+{
+       return 0;
+}
+
+/*
+ * Called from process context in qla_target.c:qlt_do_work() code
+ */
+static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd,
+       unsigned char *cdb, uint32_t data_length, int fcp_task_attr,
+       int data_dir, int bidi)
+{
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       struct se_session *se_sess;
+       struct qla_tgt_sess *sess;
+       int flags = TARGET_SCF_ACK_KREF;
+
+       if (bidi)
+               flags |= TARGET_SCF_BIDI_OP;
+
+       sess = cmd->sess;
+       if (!sess) {
+               pr_err("Unable to locate struct qla_tgt_sess from qla_tgt_cmd\n");
+               return -EINVAL;
+       }
+
+       se_sess = sess->se_sess;
+       if (!se_sess) {
+               pr_err("Unable to locate active struct se_session\n");
+               return -EINVAL;
+       }
+
+       target_submit_cmd(se_cmd, se_sess, cdb, &cmd->sense_buffer[0],
+                               cmd->unpacked_lun, data_length, fcp_task_attr,
+                               data_dir, flags);
+       return 0;
+}
+
+static void tcm_qla2xxx_do_rsp(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       /*
+        * Dispatch ->queue_status from workqueue process context
+        */
+       transport_generic_request_failure(&cmd->se_cmd);
+}
+
+/*
+ * Called from qla_target.c:qlt_do_ctio_completion()
+ */
+static int tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd)
+{
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       unsigned long flags;
+       /*
+        * Ensure that the complete FCP WRITE payload has been received.
+        * Otherwise return an exception via CHECK_CONDITION status.
+        */
+       if (!cmd->write_data_transferred) {
+               /*
+                * Check if se_cmd has already been aborted via LUN_RESET, and
+                * waiting upon completion in tcm_qla2xxx_write_pending_status()
+                */
+               spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+               if (se_cmd->transport_state & CMD_T_ABORTED) {
+                       spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+                       complete(&se_cmd->t_transport_stop_comp);
+                       return 0;
+               }
+               spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+               se_cmd->scsi_sense_reason = TCM_CHECK_CONDITION_ABORT_CMD;
+               INIT_WORK(&cmd->work, tcm_qla2xxx_do_rsp);
+               queue_work(tcm_qla2xxx_free_wq, &cmd->work);
+               return 0;
+       }
+       /*
+        * We now tell TCM to queue this WRITE CDB with TRANSPORT_PROCESS_WRITE
+        * status to the backstore processing thread.
+        */
+       return transport_generic_handle_data(&cmd->se_cmd);
+}
+
+/*
+ * Called from qla_target.c:qlt_issue_task_mgmt()
+ */
+int tcm_qla2xxx_handle_tmr(struct qla_tgt_mgmt_cmd *mcmd, uint32_t lun,
+                       uint8_t tmr_func, uint32_t tag)
+{
+       struct qla_tgt_sess *sess = mcmd->sess;
+       struct se_cmd *se_cmd = &mcmd->se_cmd;
+
+       return target_submit_tmr(se_cmd, sess->se_sess, NULL, lun, mcmd,
+                       tmr_func, GFP_ATOMIC, tag, TARGET_SCF_ACK_KREF);
+}
+
+static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
+
+       cmd->sg_cnt = se_cmd->t_data_nents;
+       cmd->sg = se_cmd->t_data_sg;
+       cmd->offset = 0;
+
+       /*
+        * Now queue completed DATA_IN the qla2xxx LLD and response ring
+        */
+       return qlt_xmit_response(cmd, QLA_TGT_XMIT_DATA|QLA_TGT_XMIT_STATUS,
+                               se_cmd->scsi_status);
+}
+
+static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+       int xmit_type = QLA_TGT_XMIT_STATUS;
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->sg = NULL;
+       cmd->sg_cnt = 0;
+       cmd->offset = 0;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
+
+       if (se_cmd->data_direction == DMA_FROM_DEVICE) {
+               /*
+                * For FCP_READ with CHECK_CONDITION status, clear cmd->bufflen
+                * for qla_tgt_xmit_response LLD code
+                */
+               se_cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+               se_cmd->residual_count = se_cmd->data_length;
+
+               cmd->bufflen = 0;
+       }
+       /*
+        * Now queue status response to qla2xxx LLD code and response ring
+        */
+       return qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status);
+}
+
+static int tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+       struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+       struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
+                               struct qla_tgt_mgmt_cmd, se_cmd);
+
+       pr_debug("queue_tm_rsp: mcmd: %p func: 0x%02x response: 0x%02x\n",
+                       mcmd, se_tmr->function, se_tmr->response);
+       /*
+        * Do translation between TCM TM response codes and
+        * QLA2xxx FC TM response codes.
+        */
+       switch (se_tmr->response) {
+       case TMR_FUNCTION_COMPLETE:
+               mcmd->fc_tm_rsp = FC_TM_SUCCESS;
+               break;
+       case TMR_TASK_DOES_NOT_EXIST:
+               mcmd->fc_tm_rsp = FC_TM_BAD_CMD;
+               break;
+       case TMR_FUNCTION_REJECTED:
+               mcmd->fc_tm_rsp = FC_TM_REJECT;
+               break;
+       case TMR_LUN_DOES_NOT_EXIST:
+       default:
+               mcmd->fc_tm_rsp = FC_TM_FAILED;
+               break;
+       }
+       /*
+        * Queue the TM response to QLA2xxx LLD to build a
+        * CTIO response packet.
+        */
+       qlt_xmit_tm_rsp(mcmd);
+
+       return 0;
+}
+
+static u16 tcm_qla2xxx_get_fabric_sense_len(void)
+{
+       return 0;
+}
+
+static u16 tcm_qla2xxx_set_fabric_sense_len(struct se_cmd *se_cmd,
+                                       u32 sense_length)
+{
+       return 0;
+}
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *tcm_qla2xxx_fabric_configfs;
+struct target_fabric_configfs *tcm_qla2xxx_npiv_fabric_configfs;
+
+static int tcm_qla2xxx_setup_nacl_from_rport(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct tcm_qla2xxx_lport *lport,
+       struct tcm_qla2xxx_nacl *nacl,
+       u64 rport_wwnn)
+{
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct Scsi_Host *sh = vha->host;
+       struct fc_host_attrs *fc_host = shost_to_fc_host(sh);
+       struct fc_rport *rport;
+       unsigned long flags;
+       void *node;
+       int rc;
+
+       /*
+        * Scan the existing rports, and create a session for the
+        * explict NodeACL is an matching rport->node_name already
+        * exists.
+        */
+       spin_lock_irqsave(sh->host_lock, flags);
+       list_for_each_entry(rport, &fc_host->rports, peers) {
+               if (rport_wwnn != rport->node_name)
+                       continue;
+
+               pr_debug("Located existing rport_wwpn and rport->node_name: 0x%016LX, port_id: 0x%04x\n",
+                   rport->node_name, rport->port_id);
+               nacl->nport_id = rport->port_id;
+
+               spin_unlock_irqrestore(sh->host_lock, flags);
+
+               spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+               node = btree_lookup32(&lport->lport_fcport_map, rport->port_id);
+               if (node) {
+                       rc = btree_update32(&lport->lport_fcport_map,
+                                           rport->port_id, se_nacl);
+               } else {
+                       rc = btree_insert32(&lport->lport_fcport_map,
+                                           rport->port_id, se_nacl,
+                                           GFP_ATOMIC);
+               }
+               spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+
+               if (rc) {
+                       pr_err("Unable to insert se_nacl into fcport_map");
+                       WARN_ON(rc > 0);
+                       return rc;
+               }
+
+               pr_debug("Inserted into fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%08x\n",
+                   se_nacl, rport_wwnn, nacl->nport_id);
+
+               return 1;
+       }
+       spin_unlock_irqrestore(sh->host_lock, flags);
+
+       return 0;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess)
+{
+       struct se_node_acl *se_nacl = sess->se_sess->se_node_acl;
+       struct se_portal_group *se_tpg = se_nacl->se_tpg;
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                               struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl,
+                               struct tcm_qla2xxx_nacl, se_node_acl);
+       void *node;
+
+       pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id);
+
+       node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id);
+       WARN_ON(node && (node != se_nacl));
+
+       pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n",
+           se_nacl, nacl->nport_wwnn, nacl->nport_id);
+}
+
+static void tcm_qla2xxx_put_sess(struct qla_tgt_sess *sess)
+{
+       target_put_session(sess->se_sess);
+}
+
+static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess)
+{
+       tcm_qla2xxx_shutdown_session(sess->se_sess);
+}
+
+static struct se_node_acl *tcm_qla2xxx_make_nodeacl(
+       struct se_portal_group *se_tpg,
+       struct config_group *group,
+       const char *name)
+{
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                               struct tcm_qla2xxx_lport, lport_wwn);
+       struct se_node_acl *se_nacl, *se_nacl_new;
+       struct tcm_qla2xxx_nacl *nacl;
+       u64 wwnn;
+       u32 qla2xxx_nexus_depth;
+       int rc;
+
+       if (tcm_qla2xxx_parse_wwn(name, &wwnn, 1) < 0)
+               return ERR_PTR(-EINVAL);
+
+       se_nacl_new = tcm_qla2xxx_alloc_fabric_acl(se_tpg);
+       if (!se_nacl_new)
+               return ERR_PTR(-ENOMEM);
+/* #warning FIXME: Hardcoded qla2xxx_nexus depth in tcm_qla2xxx_make_nodeacl */
+       qla2xxx_nexus_depth = 1;
+
+       /*
+        * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+        * when converting a NodeACL from demo mode -> explict
+        */
+       se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+                               name, qla2xxx_nexus_depth);
+       if (IS_ERR(se_nacl)) {
+               tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new);
+               return se_nacl;
+       }
+       /*
+        * Locate our struct tcm_qla2xxx_nacl and set the FC Nport WWPN
+        */
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       nacl->nport_wwnn = wwnn;
+       tcm_qla2xxx_format_wwn(&nacl->nport_name[0], TCM_QLA2XXX_NAMELEN, wwnn);
+       /*
+        * Setup a se_nacl handle based on an a matching struct fc_rport setup
+        * via drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
+        */
+       rc = tcm_qla2xxx_setup_nacl_from_rport(se_tpg, se_nacl, lport,
+                                       nacl, wwnn);
+       if (rc < 0) {
+               tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new);
+               return ERR_PTR(rc);
+       }
+
+       return se_nacl;
+}
+
+static void tcm_qla2xxx_drop_nodeacl(struct se_node_acl *se_acl)
+{
+       struct se_portal_group *se_tpg = se_acl->se_tpg;
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_acl,
+                               struct tcm_qla2xxx_nacl, se_node_acl);
+
+       core_tpg_del_initiator_node_acl(se_tpg, se_acl, 1);
+       kfree(nacl);
+}
+
+/* Start items for tcm_qla2xxx_tpg_attrib_cit */
+
+#define DEF_QLA_TPG_ATTRIB(name)                                       \
+                                                                       \
+static ssize_t tcm_qla2xxx_tpg_attrib_show_##name(                     \
+       struct se_portal_group *se_tpg,                                 \
+       char *page)                                                     \
+{                                                                      \
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,              \
+                       struct tcm_qla2xxx_tpg, se_tpg);                \
+                                                                       \
+       return sprintf(page, "%u\n", QLA_TPG_ATTRIB(tpg)->name);        \
+}                                                                      \
+                                                                       \
+static ssize_t tcm_qla2xxx_tpg_attrib_store_##name(                    \
+       struct se_portal_group *se_tpg,                                 \
+       const char *page,                                               \
+       size_t count)                                                   \
+{                                                                      \
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,              \
+                       struct tcm_qla2xxx_tpg, se_tpg);                \
+       unsigned long val;                                              \
+       int ret;                                                        \
+                                                                       \
+       ret = kstrtoul(page, 0, &val);                                  \
+       if (ret < 0) {                                                  \
+               pr_err("kstrtoul() failed with"                         \
+                               " ret: %d\n", ret);                     \
+               return -EINVAL;                                         \
+       }                                                               \
+       ret = tcm_qla2xxx_set_attrib_##name(tpg, val);                  \
+                                                                       \
+       return (!ret) ? count : -EINVAL;                                \
+}
+
+#define DEF_QLA_TPG_ATTR_BOOL(_name)                                   \
+                                                                       \
+static int tcm_qla2xxx_set_attrib_##_name(                             \
+       struct tcm_qla2xxx_tpg *tpg,                                    \
+       unsigned long val)                                              \
+{                                                                      \
+       struct tcm_qla2xxx_tpg_attrib *a = &tpg->tpg_attrib;            \
+                                                                       \
+       if ((val != 0) && (val != 1)) {                                 \
+               pr_err("Illegal boolean value %lu\n", val);             \
+               return -EINVAL;                                         \
+       }                                                               \
+                                                                       \
+       a->_name = val;                                                 \
+       return 0;                                                       \
+}
+
+#define QLA_TPG_ATTR(_name, _mode) \
+       TF_TPG_ATTRIB_ATTR(tcm_qla2xxx, _name, _mode);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_generate_node_acls
+ */
+DEF_QLA_TPG_ATTR_BOOL(generate_node_acls);
+DEF_QLA_TPG_ATTRIB(generate_node_acls);
+QLA_TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR);
+
+/*
+ Define tcm_qla2xxx_attrib_s_cache_dynamic_acls
+ */
+DEF_QLA_TPG_ATTR_BOOL(cache_dynamic_acls);
+DEF_QLA_TPG_ATTRIB(cache_dynamic_acls);
+QLA_TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_write_protect
+ */
+DEF_QLA_TPG_ATTR_BOOL(demo_mode_write_protect);
+DEF_QLA_TPG_ATTRIB(demo_mode_write_protect);
+QLA_TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_prod_mode_write_protect
+ */
+DEF_QLA_TPG_ATTR_BOOL(prod_mode_write_protect);
+DEF_QLA_TPG_ATTRIB(prod_mode_write_protect);
+QLA_TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_qla2xxx_tpg_attrib_attrs[] = {
+       &tcm_qla2xxx_tpg_attrib_generate_node_acls.attr,
+       &tcm_qla2xxx_tpg_attrib_cache_dynamic_acls.attr,
+       &tcm_qla2xxx_tpg_attrib_demo_mode_write_protect.attr,
+       &tcm_qla2xxx_tpg_attrib_prod_mode_write_protect.attr,
+       NULL,
+};
+
+/* End items for tcm_qla2xxx_tpg_attrib_cit */
+
+static ssize_t tcm_qla2xxx_tpg_show_enable(
+       struct se_portal_group *se_tpg,
+       char *page)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+
+       return snprintf(page, PAGE_SIZE, "%d\n",
+                       atomic_read(&tpg->lport_tpg_enabled));
+}
+
+static ssize_t tcm_qla2xxx_tpg_store_enable(
+       struct se_portal_group *se_tpg,
+       const char *page,
+       size_t count)
+{
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+       unsigned long op;
+       int rc;
+
+       rc = kstrtoul(page, 0, &op);
+       if (rc < 0) {
+               pr_err("kstrtoul() returned %d\n", rc);
+               return -EINVAL;
+       }
+       if ((op != 1) && (op != 0)) {
+               pr_err("Illegal value for tpg_enable: %lu\n", op);
+               return -EINVAL;
+       }
+
+       if (op) {
+               atomic_set(&tpg->lport_tpg_enabled, 1);
+               qlt_enable_vha(vha);
+       } else {
+               if (!ha->tgt.qla_tgt) {
+                       pr_err("truct qla_hw_data *ha->tgt.qla_tgt is NULL\n");
+                       return -ENODEV;
+               }
+               atomic_set(&tpg->lport_tpg_enabled, 0);
+               qlt_stop_phase1(ha->tgt.qla_tgt);
+       }
+
+       return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_qla2xxx, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_qla2xxx_tpg_attrs[] = {
+       &tcm_qla2xxx_tpg_enable.attr,
+       NULL,
+};
+
+static struct se_portal_group *tcm_qla2xxx_make_tpg(
+       struct se_wwn *wwn,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_tpg *tpg;
+       unsigned long tpgt;
+       int ret;
+
+       if (strstr(name, "tpgt_") != name)
+               return ERR_PTR(-EINVAL);
+       if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX)
+               return ERR_PTR(-EINVAL);
+
+       if (!lport->qla_npiv_vp && (tpgt != 1)) {
+               pr_err("In non NPIV mode, a single TPG=1 is used for HW port mappings\n");
+               return ERR_PTR(-ENOSYS);
+       }
+
+       tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL);
+       if (!tpg) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       tpg->lport = lport;
+       tpg->lport_tpgt = tpgt;
+       /*
+        * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic
+        * NodeACLs
+        */
+       QLA_TPG_ATTRIB(tpg)->generate_node_acls = 1;
+       QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect = 1;
+       QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls = 1;
+
+       ret = core_tpg_register(&tcm_qla2xxx_fabric_configfs->tf_ops, wwn,
+                               &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+       if (ret < 0) {
+               kfree(tpg);
+               return NULL;
+       }
+       /*
+        * Setup local TPG=1 pointer for non NPIV mode.
+        */
+       if (lport->qla_npiv_vp == NULL)
+               lport->tpg_1 = tpg;
+
+       return &tpg->se_tpg;
+}
+
+static void tcm_qla2xxx_drop_tpg(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       /*
+        * Call into qla2x_target.c LLD logic to shutdown the active
+        * FC Nexuses and disable target mode operation for this qla_hw_data
+        */
+       if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stop)
+               qlt_stop_phase1(ha->tgt.qla_tgt);
+
+       core_tpg_deregister(se_tpg);
+       /*
+        * Clear local TPG=1 pointer for non NPIV mode.
+        */
+       if (lport->qla_npiv_vp == NULL)
+               lport->tpg_1 = NULL;
+
+       kfree(tpg);
+}
+
+static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg(
+       struct se_wwn *wwn,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_tpg *tpg;
+       unsigned long tpgt;
+       int ret;
+
+       if (strstr(name, "tpgt_") != name)
+               return ERR_PTR(-EINVAL);
+       if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX)
+               return ERR_PTR(-EINVAL);
+
+       tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL);
+       if (!tpg) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       tpg->lport = lport;
+       tpg->lport_tpgt = tpgt;
+
+       ret = core_tpg_register(&tcm_qla2xxx_npiv_fabric_configfs->tf_ops, wwn,
+                               &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+       if (ret < 0) {
+               kfree(tpg);
+               return NULL;
+       }
+       return &tpg->se_tpg;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id(
+       scsi_qla_host_t *vha,
+       const uint8_t *s_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_nacl *nacl;
+       u32 key;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return NULL;
+       }
+
+       key = (((unsigned long)s_id[0] << 16) |
+              ((unsigned long)s_id[1] << 8) |
+              (unsigned long)s_id[2]);
+       pr_debug("find_sess_by_s_id: 0x%06x\n", key);
+
+       se_nacl = btree_lookup32(&lport->lport_fcport_map, key);
+       if (!se_nacl) {
+               pr_debug("Unable to locate s_id: 0x%06x\n", key);
+               return NULL;
+       }
+       pr_debug("find_sess_by_s_id: located se_nacl: %p, initiatorname: %s\n",
+           se_nacl, se_nacl->initiatorname);
+
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       if (!nacl->qla_tgt_sess) {
+               pr_err("Unable to locate struct qla_tgt_sess\n");
+               return NULL;
+       }
+
+       return nacl->qla_tgt_sess;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_set_sess_by_s_id(
+       struct tcm_qla2xxx_lport *lport,
+       struct se_node_acl *new_se_nacl,
+       struct tcm_qla2xxx_nacl *nacl,
+       struct se_session *se_sess,
+       struct qla_tgt_sess *qla_tgt_sess,
+       uint8_t *s_id)
+{
+       u32 key;
+       void *slot;
+       int rc;
+
+       key = (((unsigned long)s_id[0] << 16) |
+              ((unsigned long)s_id[1] << 8) |
+              (unsigned long)s_id[2]);
+       pr_debug("set_sess_by_s_id: %06x\n", key);
+
+       slot = btree_lookup32(&lport->lport_fcport_map, key);
+       if (!slot) {
+               if (new_se_nacl) {
+                       pr_debug("Setting up new fc_port entry to new_se_nacl\n");
+                       nacl->nport_id = key;
+                       rc = btree_insert32(&lport->lport_fcport_map, key,
+                                       new_se_nacl, GFP_ATOMIC);
+                       if (rc)
+                               printk(KERN_ERR "Unable to insert s_id into fcport_map: %06x\n",
+                                   (int)key);
+               } else {
+                       pr_debug("Wiping nonexisting fc_port entry\n");
+               }
+
+               qla_tgt_sess->se_sess = se_sess;
+               nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (nacl->qla_tgt_sess) {
+               if (new_se_nacl == NULL) {
+                       pr_debug("Clearing existing nacl->qla_tgt_sess and fc_port entry\n");
+                       btree_remove32(&lport->lport_fcport_map, key);
+                       nacl->qla_tgt_sess = NULL;
+                       return;
+               }
+               pr_debug("Replacing existing nacl->qla_tgt_sess and fc_port entry\n");
+               btree_update32(&lport->lport_fcport_map, key, new_se_nacl);
+               qla_tgt_sess->se_sess = se_sess;
+               nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (new_se_nacl == NULL) {
+               pr_debug("Clearing existing fc_port entry\n");
+               btree_remove32(&lport->lport_fcport_map, key);
+               return;
+       }
+
+       pr_debug("Replacing existing fc_port entry w/o active nacl->qla_tgt_sess\n");
+       btree_update32(&lport->lport_fcport_map, key, new_se_nacl);
+       qla_tgt_sess->se_sess = se_sess;
+       nacl->qla_tgt_sess = qla_tgt_sess;
+
+       pr_debug("Setup nacl->qla_tgt_sess %p by s_id for se_nacl: %p, initiatorname: %s\n",
+           nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id(
+       scsi_qla_host_t *vha,
+       const uint16_t loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_nacl *nacl;
+       struct tcm_qla2xxx_fc_loopid *fc_loopid;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return NULL;
+       }
+
+       pr_debug("find_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id);
+
+       fc_loopid = lport->lport_loopid_map + loop_id;
+       se_nacl = fc_loopid->se_nacl;
+       if (!se_nacl) {
+               pr_debug("Unable to locate se_nacl by loop_id: 0x%04x\n",
+                   loop_id);
+               return NULL;
+       }
+
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+
+       if (!nacl->qla_tgt_sess) {
+               pr_err("Unable to locate struct qla_tgt_sess\n");
+               return NULL;
+       }
+
+       return nacl->qla_tgt_sess;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_set_sess_by_loop_id(
+       struct tcm_qla2xxx_lport *lport,
+       struct se_node_acl *new_se_nacl,
+       struct tcm_qla2xxx_nacl *nacl,
+       struct se_session *se_sess,
+       struct qla_tgt_sess *qla_tgt_sess,
+       uint16_t loop_id)
+{
+       struct se_node_acl *saved_nacl;
+       struct tcm_qla2xxx_fc_loopid *fc_loopid;
+
+       pr_debug("set_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id);
+
+       fc_loopid = &((struct tcm_qla2xxx_fc_loopid *)
+                       lport->lport_loopid_map)[loop_id];
+
+       saved_nacl = fc_loopid->se_nacl;
+       if (!saved_nacl) {
+               pr_debug("Setting up new fc_loopid->se_nacl to new_se_nacl\n");
+               fc_loopid->se_nacl = new_se_nacl;
+               if (qla_tgt_sess->se_sess != se_sess)
+                       qla_tgt_sess->se_sess = se_sess;
+               if (nacl->qla_tgt_sess != qla_tgt_sess)
+                       nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (nacl->qla_tgt_sess) {
+               if (new_se_nacl == NULL) {
+                       pr_debug("Clearing nacl->qla_tgt_sess and fc_loopid->se_nacl\n");
+                       fc_loopid->se_nacl = NULL;
+                       nacl->qla_tgt_sess = NULL;
+                       return;
+               }
+
+               pr_debug("Replacing existing nacl->qla_tgt_sess and fc_loopid->se_nacl\n");
+               fc_loopid->se_nacl = new_se_nacl;
+               if (qla_tgt_sess->se_sess != se_sess)
+                       qla_tgt_sess->se_sess = se_sess;
+               if (nacl->qla_tgt_sess != qla_tgt_sess)
+                       nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (new_se_nacl == NULL) {
+               pr_debug("Clearing fc_loopid->se_nacl\n");
+               fc_loopid->se_nacl = NULL;
+               return;
+       }
+
+       pr_debug("Replacing existing fc_loopid->se_nacl w/o active nacl->qla_tgt_sess\n");
+       fc_loopid->se_nacl = new_se_nacl;
+       if (qla_tgt_sess->se_sess != se_sess)
+               qla_tgt_sess->se_sess = se_sess;
+       if (nacl->qla_tgt_sess != qla_tgt_sess)
+               nacl->qla_tgt_sess = qla_tgt_sess;
+
+       pr_debug("Setup nacl->qla_tgt_sess %p by loop_id for se_nacl: %p, initiatorname: %s\n",
+           nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
+}
+
+static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
+{
+       struct qla_tgt *tgt = sess->tgt;
+       struct qla_hw_data *ha = tgt->ha;
+       struct se_session *se_sess;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_lport *lport;
+       struct tcm_qla2xxx_nacl *nacl;
+       unsigned char be_sid[3];
+       unsigned long flags;
+
+       BUG_ON(in_interrupt());
+
+       se_sess = sess->se_sess;
+       if (!se_sess) {
+               pr_err("struct qla_tgt_sess->se_sess is NULL\n");
+               dump_stack();
+               return;
+       }
+       se_nacl = se_sess->se_node_acl;
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return;
+       }
+       target_wait_for_sess_cmds(se_sess, 0);
+       /*
+        * And now clear the se_nacl and session pointers from our HW lport
+        * mappings for fabric S_ID and LOOP_ID.
+        */
+       memset(&be_sid, 0, 3);
+       be_sid[0] = sess->s_id.b.domain;
+       be_sid[1] = sess->s_id.b.area;
+       be_sid[2] = sess->s_id.b.al_pa;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
+                       sess, be_sid);
+       tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
+                       sess, sess->loop_id);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       transport_deregister_session_configfs(sess->se_sess);
+       transport_deregister_session(sess->se_sess);
+}
+
+/*
+ * Called via qlt_create_sess():ha->qla2x_tmpl->check_initiator_node_acl()
+ * to locate struct se_node_acl
+ */
+static int tcm_qla2xxx_check_initiator_node_acl(
+       scsi_qla_host_t *vha,
+       unsigned char *fc_wwpn,
+       void *qla_tgt_sess,
+       uint8_t *s_id,
+       uint16_t loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct tcm_qla2xxx_tpg *tpg;
+       struct tcm_qla2xxx_nacl *nacl;
+       struct se_portal_group *se_tpg;
+       struct se_node_acl *se_nacl;
+       struct se_session *se_sess;
+       struct qla_tgt_sess *sess = qla_tgt_sess;
+       unsigned char port_name[36];
+       unsigned long flags;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return -EINVAL;
+       }
+       /*
+        * Locate the TPG=1 reference..
+        */
+       tpg = lport->tpg_1;
+       if (!tpg) {
+               pr_err("Unable to lcoate struct tcm_qla2xxx_lport->tpg_1\n");
+               return -EINVAL;
+       }
+       se_tpg = &tpg->se_tpg;
+
+       se_sess = transport_init_session();
+       if (IS_ERR(se_sess)) {
+               pr_err("Unable to initialize struct se_session\n");
+               return PTR_ERR(se_sess);
+       }
+       /*
+        * Format the FCP Initiator port_name into colon seperated values to
+        * match the format by tcm_qla2xxx explict ConfigFS NodeACLs.
+        */
+       memset(&port_name, 0, 36);
+       snprintf(port_name, 36, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+               fc_wwpn[0], fc_wwpn[1], fc_wwpn[2], fc_wwpn[3], fc_wwpn[4],
+               fc_wwpn[5], fc_wwpn[6], fc_wwpn[7]);
+       /*
+        * Locate our struct se_node_acl either from an explict NodeACL created
+        * via ConfigFS, or via running in TPG demo mode.
+        */
+       se_sess->se_node_acl = core_tpg_check_initiator_node_acl(se_tpg,
+                                       port_name);
+       if (!se_sess->se_node_acl) {
+               transport_free_session(se_sess);
+               return -EINVAL;
+       }
+       se_nacl = se_sess->se_node_acl;
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       /*
+        * And now setup the new se_nacl and session pointers into our HW lport
+        * mappings for fabric S_ID and LOOP_ID.
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tcm_qla2xxx_set_sess_by_s_id(lport, se_nacl, nacl, se_sess,
+                       qla_tgt_sess, s_id);
+       tcm_qla2xxx_set_sess_by_loop_id(lport, se_nacl, nacl, se_sess,
+                       qla_tgt_sess, loop_id);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       /*
+        * Finally register the new FC Nexus with TCM
+        */
+       __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess);
+
+       return 0;
+}
+
+/*
+ * Calls into tcm_qla2xxx used by qla2xxx LLD I/O path.
+ */
+static struct qla_tgt_func_tmpl tcm_qla2xxx_template = {
+       .handle_cmd             = tcm_qla2xxx_handle_cmd,
+       .handle_data            = tcm_qla2xxx_handle_data,
+       .handle_tmr             = tcm_qla2xxx_handle_tmr,
+       .free_cmd               = tcm_qla2xxx_free_cmd,
+       .free_mcmd              = tcm_qla2xxx_free_mcmd,
+       .free_session           = tcm_qla2xxx_free_session,
+       .check_initiator_node_acl = tcm_qla2xxx_check_initiator_node_acl,
+       .find_sess_by_s_id      = tcm_qla2xxx_find_sess_by_s_id,
+       .find_sess_by_loop_id   = tcm_qla2xxx_find_sess_by_loop_id,
+       .clear_nacl_from_fcport_map = tcm_qla2xxx_clear_nacl_from_fcport_map,
+       .put_sess               = tcm_qla2xxx_put_sess,
+       .shutdown_sess          = tcm_qla2xxx_shutdown_sess,
+};
+
+static int tcm_qla2xxx_init_lport(struct tcm_qla2xxx_lport *lport)
+{
+       int rc;
+
+       rc = btree_init32(&lport->lport_fcport_map);
+       if (rc) {
+               pr_err("Unable to initialize lport->lport_fcport_map btree\n");
+               return rc;
+       }
+
+       lport->lport_loopid_map = vmalloc(sizeof(struct tcm_qla2xxx_fc_loopid) *
+                               65536);
+       if (!lport->lport_loopid_map) {
+               pr_err("Unable to allocate lport->lport_loopid_map of %zu bytes\n",
+                   sizeof(struct tcm_qla2xxx_fc_loopid) * 65536);
+               btree_destroy32(&lport->lport_fcport_map);
+               return -ENOMEM;
+       }
+       memset(lport->lport_loopid_map, 0, sizeof(struct tcm_qla2xxx_fc_loopid)
+              * 65536);
+       pr_debug("qla2xxx: Allocated lport_loopid_map of %zu bytes\n",
+              sizeof(struct tcm_qla2xxx_fc_loopid) * 65536);
+       return 0;
+}
+
+static int tcm_qla2xxx_lport_register_cb(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       /*
+        * Setup local pointer to vha, NPIV VP pointer (if present) and
+        * vha->tcm_lport pointer
+        */
+       lport = (struct tcm_qla2xxx_lport *)ha->tgt.target_lport_ptr;
+       lport->qla_vha = vha;
+
+       return 0;
+}
+
+static struct se_wwn *tcm_qla2xxx_make_lport(
+       struct target_fabric_configfs *tf,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport;
+       u64 wwpn;
+       int ret = -ENODEV;
+
+       if (tcm_qla2xxx_parse_wwn(name, &wwpn, 1) < 0)
+               return ERR_PTR(-EINVAL);
+
+       lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL);
+       if (!lport) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_lport\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       lport->lport_wwpn = wwpn;
+       tcm_qla2xxx_format_wwn(&lport->lport_name[0], TCM_QLA2XXX_NAMELEN,
+                               wwpn);
+
+       ret = tcm_qla2xxx_init_lport(lport);
+       if (ret != 0)
+               goto out;
+
+       ret = qlt_lport_register(&tcm_qla2xxx_template, wwpn,
+                               tcm_qla2xxx_lport_register_cb, lport);
+       if (ret != 0)
+               goto out_lport;
+
+       return &lport->lport_wwn;
+out_lport:
+       vfree(lport->lport_loopid_map);
+       btree_destroy32(&lport->lport_fcport_map);
+out:
+       kfree(lport);
+       return ERR_PTR(ret);
+}
+
+static void tcm_qla2xxx_drop_lport(struct se_wwn *wwn)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct se_node_acl *node;
+       u32 key = 0;
+
+       /*
+        * Call into qla2x_target.c LLD logic to complete the
+        * shutdown of struct qla_tgt after the call to
+        * qlt_stop_phase1() from tcm_qla2xxx_drop_tpg() above..
+        */
+       if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stopped)
+               qlt_stop_phase2(ha->tgt.qla_tgt);
+
+       qlt_lport_deregister(vha);
+
+       vfree(lport->lport_loopid_map);
+       btree_for_each_safe32(&lport->lport_fcport_map, key, node)
+               btree_remove32(&lport->lport_fcport_map, key);
+       btree_destroy32(&lport->lport_fcport_map);
+       kfree(lport);
+}
+
+static struct se_wwn *tcm_qla2xxx_npiv_make_lport(
+       struct target_fabric_configfs *tf,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport;
+       u64 npiv_wwpn, npiv_wwnn;
+       int ret;
+
+       if (tcm_qla2xxx_npiv_parse_wwn(name, strlen(name)+1,
+                               &npiv_wwpn, &npiv_wwnn) < 0)
+               return ERR_PTR(-EINVAL);
+
+       lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL);
+       if (!lport) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_lport for NPIV\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       lport->lport_npiv_wwpn = npiv_wwpn;
+       lport->lport_npiv_wwnn = npiv_wwnn;
+       tcm_qla2xxx_npiv_format_wwn(&lport->lport_npiv_name[0],
+                       TCM_QLA2XXX_NAMELEN, npiv_wwpn, npiv_wwnn);
+
+/* FIXME: tcm_qla2xxx_npiv_make_lport */
+       ret = -ENOSYS;
+       if (ret != 0)
+               goto out;
+
+       return &lport->lport_wwn;
+out:
+       kfree(lport);
+       return ERR_PTR(ret);
+}
+
+static void tcm_qla2xxx_npiv_drop_lport(struct se_wwn *wwn)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct Scsi_Host *sh = vha->host;
+       /*
+        * Notify libfc that we want to release the lport->npiv_vport
+        */
+       fc_vport_terminate(lport->npiv_vport);
+
+       scsi_host_put(sh);
+       kfree(lport);
+}
+
+
+static ssize_t tcm_qla2xxx_wwn_show_attr_version(
+       struct target_fabric_configfs *tf,
+       char *page)
+{
+       return sprintf(page,
+           "TCM QLOGIC QLA2XXX NPIV capable fabric module %s on %s/%s on "
+           UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname,
+           utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(tcm_qla2xxx, version);
+
+static struct configfs_attribute *tcm_qla2xxx_wwn_attrs[] = {
+       &tcm_qla2xxx_wwn_version.attr,
+       NULL,
+};
+
+static struct target_core_fabric_ops tcm_qla2xxx_ops = {
+       .get_fabric_name                = tcm_qla2xxx_get_fabric_name,
+       .get_fabric_proto_ident         = tcm_qla2xxx_get_fabric_proto_ident,
+       .tpg_get_wwn                    = tcm_qla2xxx_get_fabric_wwn,
+       .tpg_get_tag                    = tcm_qla2xxx_get_tag,
+       .tpg_get_default_depth          = tcm_qla2xxx_get_default_depth,
+       .tpg_get_pr_transport_id        = tcm_qla2xxx_get_pr_transport_id,
+       .tpg_get_pr_transport_id_len    = tcm_qla2xxx_get_pr_transport_id_len,
+       .tpg_parse_pr_out_transport_id  = tcm_qla2xxx_parse_pr_out_transport_id,
+       .tpg_check_demo_mode            = tcm_qla2xxx_check_demo_mode,
+       .tpg_check_demo_mode_cache      = tcm_qla2xxx_check_demo_mode_cache,
+       .tpg_check_demo_mode_write_protect =
+                                       tcm_qla2xxx_check_demo_write_protect,
+       .tpg_check_prod_mode_write_protect =
+                                       tcm_qla2xxx_check_prod_write_protect,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
+       .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
+       .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
+       .new_cmd_map                    = NULL,
+       .check_stop_free                = tcm_qla2xxx_check_stop_free,
+       .release_cmd                    = tcm_qla2xxx_release_cmd,
+       .shutdown_session               = tcm_qla2xxx_shutdown_session,
+       .close_session                  = tcm_qla2xxx_close_session,
+       .sess_get_index                 = tcm_qla2xxx_sess_get_index,
+       .sess_get_initiator_sid         = NULL,
+       .write_pending                  = tcm_qla2xxx_write_pending,
+       .write_pending_status           = tcm_qla2xxx_write_pending_status,
+       .set_default_node_attributes    = tcm_qla2xxx_set_default_node_attrs,
+       .get_task_tag                   = tcm_qla2xxx_get_task_tag,
+       .get_cmd_state                  = tcm_qla2xxx_get_cmd_state,
+       .queue_data_in                  = tcm_qla2xxx_queue_data_in,
+       .queue_status                   = tcm_qla2xxx_queue_status,
+       .queue_tm_rsp                   = tcm_qla2xxx_queue_tm_rsp,
+       .get_fabric_sense_len           = tcm_qla2xxx_get_fabric_sense_len,
+       .set_fabric_sense_len           = tcm_qla2xxx_set_fabric_sense_len,
+       /*
+        * Setup function pointers for generic logic in
+        * target_core_fabric_configfs.c
+        */
+       .fabric_make_wwn                = tcm_qla2xxx_make_lport,
+       .fabric_drop_wwn                = tcm_qla2xxx_drop_lport,
+       .fabric_make_tpg                = tcm_qla2xxx_make_tpg,
+       .fabric_drop_tpg                = tcm_qla2xxx_drop_tpg,
+       .fabric_post_link               = NULL,
+       .fabric_pre_unlink              = NULL,
+       .fabric_make_np                 = NULL,
+       .fabric_drop_np                 = NULL,
+       .fabric_make_nodeacl            = tcm_qla2xxx_make_nodeacl,
+       .fabric_drop_nodeacl            = tcm_qla2xxx_drop_nodeacl,
+};
+
+static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = {
+       .get_fabric_name                = tcm_qla2xxx_npiv_get_fabric_name,
+       .get_fabric_proto_ident         = tcm_qla2xxx_get_fabric_proto_ident,
+       .tpg_get_wwn                    = tcm_qla2xxx_npiv_get_fabric_wwn,
+       .tpg_get_tag                    = tcm_qla2xxx_get_tag,
+       .tpg_get_default_depth          = tcm_qla2xxx_get_default_depth,
+       .tpg_get_pr_transport_id        = tcm_qla2xxx_get_pr_transport_id,
+       .tpg_get_pr_transport_id_len    = tcm_qla2xxx_get_pr_transport_id_len,
+       .tpg_parse_pr_out_transport_id  = tcm_qla2xxx_parse_pr_out_transport_id,
+       .tpg_check_demo_mode            = tcm_qla2xxx_check_false,
+       .tpg_check_demo_mode_cache      = tcm_qla2xxx_check_true,
+       .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_true,
+       .tpg_check_prod_mode_write_protect = tcm_qla2xxx_check_false,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
+       .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
+       .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
+       .release_cmd                    = tcm_qla2xxx_release_cmd,
+       .shutdown_session               = tcm_qla2xxx_shutdown_session,
+       .close_session                  = tcm_qla2xxx_close_session,
+       .sess_get_index                 = tcm_qla2xxx_sess_get_index,
+       .sess_get_initiator_sid         = NULL,
+       .write_pending                  = tcm_qla2xxx_write_pending,
+       .write_pending_status           = tcm_qla2xxx_write_pending_status,
+       .set_default_node_attributes    = tcm_qla2xxx_set_default_node_attrs,
+       .get_task_tag                   = tcm_qla2xxx_get_task_tag,
+       .get_cmd_state                  = tcm_qla2xxx_get_cmd_state,
+       .queue_data_in                  = tcm_qla2xxx_queue_data_in,
+       .queue_status                   = tcm_qla2xxx_queue_status,
+       .queue_tm_rsp                   = tcm_qla2xxx_queue_tm_rsp,
+       .get_fabric_sense_len           = tcm_qla2xxx_get_fabric_sense_len,
+       .set_fabric_sense_len           = tcm_qla2xxx_set_fabric_sense_len,
+       /*
+        * Setup function pointers for generic logic in
+        * target_core_fabric_configfs.c
+        */
+       .fabric_make_wwn                = tcm_qla2xxx_npiv_make_lport,
+       .fabric_drop_wwn                = tcm_qla2xxx_npiv_drop_lport,
+       .fabric_make_tpg                = tcm_qla2xxx_npiv_make_tpg,
+       .fabric_drop_tpg                = tcm_qla2xxx_drop_tpg,
+       .fabric_post_link               = NULL,
+       .fabric_pre_unlink              = NULL,
+       .fabric_make_np                 = NULL,
+       .fabric_drop_np                 = NULL,
+       .fabric_make_nodeacl            = tcm_qla2xxx_make_nodeacl,
+       .fabric_drop_nodeacl            = tcm_qla2xxx_drop_nodeacl,
+};
+
+static int tcm_qla2xxx_register_configfs(void)
+{
+       struct target_fabric_configfs *fabric, *npiv_fabric;
+       int ret;
+
+       pr_debug("TCM QLOGIC QLA2XXX fabric module %s on %s/%s on "
+           UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname,
+           utsname()->machine);
+       /*
+        * Register the top level struct config_item_type with TCM core
+        */
+       fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx");
+       if (IS_ERR(fabric)) {
+               pr_err("target_fabric_configfs_init() failed\n");
+               return PTR_ERR(fabric);
+       }
+       /*
+        * Setup fabric->tf_ops from our local tcm_qla2xxx_ops
+        */
+       fabric->tf_ops = tcm_qla2xxx_ops;
+       /*
+        * Setup default attribute lists for various fabric->tf_cit_tmpl
+        */
+       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs =
+                                               tcm_qla2xxx_tpg_attrib_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       /*
+        * Register the fabric for use within TCM
+        */
+       ret = target_fabric_configfs_register(fabric);
+       if (ret < 0) {
+               pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n");
+               return ret;
+       }
+       /*
+        * Setup our local pointer to *fabric
+        */
+       tcm_qla2xxx_fabric_configfs = fabric;
+       pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_fabric_configfs\n");
+
+       /*
+        * Register the top level struct config_item_type for NPIV with TCM core
+        */
+       npiv_fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx_npiv");
+       if (IS_ERR(npiv_fabric)) {
+               pr_err("target_fabric_configfs_init() failed\n");
+               ret = PTR_ERR(npiv_fabric);
+               goto out_fabric;
+       }
+       /*
+        * Setup fabric->tf_ops from our local tcm_qla2xxx_npiv_ops
+        */
+       npiv_fabric->tf_ops = tcm_qla2xxx_npiv_ops;
+       /*
+        * Setup default attribute lists for various npiv_fabric->tf_cit_tmpl
+        */
+       TF_CIT_TMPL(npiv_fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       /*
+        * Register the npiv_fabric for use within TCM
+        */
+       ret = target_fabric_configfs_register(npiv_fabric);
+       if (ret < 0) {
+               pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n");
+               goto out_fabric;
+       }
+       /*
+        * Setup our local pointer to *npiv_fabric
+        */
+       tcm_qla2xxx_npiv_fabric_configfs = npiv_fabric;
+       pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_npiv_fabric_configfs\n");
+
+       tcm_qla2xxx_free_wq = alloc_workqueue("tcm_qla2xxx_free",
+                                               WQ_MEM_RECLAIM, 0);
+       if (!tcm_qla2xxx_free_wq) {
+               ret = -ENOMEM;
+               goto out_fabric_npiv;
+       }
+
+       tcm_qla2xxx_cmd_wq = alloc_workqueue("tcm_qla2xxx_cmd", 0, 0);
+       if (!tcm_qla2xxx_cmd_wq) {
+               ret = -ENOMEM;
+               goto out_free_wq;
+       }
+
+       return 0;
+
+out_free_wq:
+       destroy_workqueue(tcm_qla2xxx_free_wq);
+out_fabric_npiv:
+       target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs);
+out_fabric:
+       target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs);
+       return ret;
+}
+
+static void tcm_qla2xxx_deregister_configfs(void)
+{
+       destroy_workqueue(tcm_qla2xxx_cmd_wq);
+       destroy_workqueue(tcm_qla2xxx_free_wq);
+
+       target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs);
+       tcm_qla2xxx_fabric_configfs = NULL;
+       pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_fabric_configfs\n");
+
+       target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs);
+       tcm_qla2xxx_npiv_fabric_configfs = NULL;
+       pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_npiv_fabric_configfs\n");
+}
+
+static int __init tcm_qla2xxx_init(void)
+{
+       int ret;
+
+       ret = tcm_qla2xxx_register_configfs();
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void __exit tcm_qla2xxx_exit(void)
+{
+       tcm_qla2xxx_deregister_configfs();
+}
+
+MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver");
+MODULE_LICENSE("GPL");
+module_init(tcm_qla2xxx_init);
+module_exit(tcm_qla2xxx_exit);
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h
new file mode 100644 (file)
index 0000000..8254981
--- /dev/null
@@ -0,0 +1,82 @@
+#include <target/target_core_base.h>
+#include <linux/btree.h>
+
+#define TCM_QLA2XXX_VERSION    "v0.1"
+/* length of ASCII WWPNs including pad */
+#define TCM_QLA2XXX_NAMELEN    32
+/* lenth of ASCII NPIV 'WWPN+WWNN' including pad */
+#define TCM_QLA2XXX_NPIV_NAMELEN 66
+
+#include "qla_target.h"
+
+struct tcm_qla2xxx_nacl {
+       /* From libfc struct fc_rport->port_id */
+       u32 nport_id;
+       /* Binary World Wide unique Node Name for remote FC Initiator Nport */
+       u64 nport_wwnn;
+       /* ASCII formatted WWPN for FC Initiator Nport */
+       char nport_name[TCM_QLA2XXX_NAMELEN];
+       /* Pointer to qla_tgt_sess */
+       struct qla_tgt_sess *qla_tgt_sess;
+       /* Pointer to TCM FC nexus */
+       struct se_session *nport_nexus;
+       /* Returned by tcm_qla2xxx_make_nodeacl() */
+       struct se_node_acl se_node_acl;
+};
+
+struct tcm_qla2xxx_tpg_attrib {
+       int generate_node_acls;
+       int cache_dynamic_acls;
+       int demo_mode_write_protect;
+       int prod_mode_write_protect;
+};
+
+struct tcm_qla2xxx_tpg {
+       /* FC lport target portal group tag for TCM */
+       u16 lport_tpgt;
+       /* Atomic bit to determine TPG active status */
+       atomic_t lport_tpg_enabled;
+       /* Pointer back to tcm_qla2xxx_lport */
+       struct tcm_qla2xxx_lport *lport;
+       /* Used by tcm_qla2xxx_tpg_attrib_cit */
+       struct tcm_qla2xxx_tpg_attrib tpg_attrib;
+       /* Returned by tcm_qla2xxx_make_tpg() */
+       struct se_portal_group se_tpg;
+};
+
+#define QLA_TPG_ATTRIB(tpg)    (&(tpg)->tpg_attrib)
+
+struct tcm_qla2xxx_fc_loopid {
+       struct se_node_acl *se_nacl;
+};
+
+struct tcm_qla2xxx_lport {
+       /* SCSI protocol the lport is providing */
+       u8 lport_proto_id;
+       /* Binary World Wide unique Port Name for FC Target Lport */
+       u64 lport_wwpn;
+       /* Binary World Wide unique Port Name for FC NPIV Target Lport */
+       u64 lport_npiv_wwpn;
+       /* Binary World Wide unique Node Name for FC NPIV Target Lport */
+       u64 lport_npiv_wwnn;
+       /* ASCII formatted WWPN for FC Target Lport */
+       char lport_name[TCM_QLA2XXX_NAMELEN];
+       /* ASCII formatted WWPN+WWNN for NPIV FC Target Lport */
+       char lport_npiv_name[TCM_QLA2XXX_NPIV_NAMELEN];
+       /* map for fc_port pointers in 24-bit FC Port ID space */
+       struct btree_head32 lport_fcport_map;
+       /* vmalloc-ed memory for fc_port pointers for 16-bit FC loop ID */
+       struct tcm_qla2xxx_fc_loopid *lport_loopid_map;
+       /* Pointer to struct scsi_qla_host from qla2xxx LLD */
+       struct scsi_qla_host *qla_vha;
+       /* Pointer to struct scsi_qla_host for NPIV VP from qla2xxx LLD */
+       struct scsi_qla_host *qla_npiv_vp;
+       /* Pointer to struct qla_tgt pointer */
+       struct qla_tgt lport_qla_tgt;
+       /* Pointer to struct fc_vport for NPIV vport from libfc */
+       struct fc_vport *npiv_vport;
+       /* Pointer to TPG=1 for non NPIV mode */
+       struct tcm_qla2xxx_tpg *tpg_1;
+       /* Returned by tcm_qla2xxx_make_lport() */
+       struct se_wwn lport_wwn;
+};
index 0b0a7d4..c681b2a 100644 (file)
@@ -9,6 +9,140 @@
 #include "ql4_glbl.h"
 #include "ql4_dbg.h"
 
+static ssize_t
+qla4_8xxx_sysfs_read_fw_dump(struct file *filep, struct kobject *kobj,
+                            struct bin_attribute *ba, char *buf, loff_t off,
+                            size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+                                              struct device, kobj)));
+
+       if (!is_qla8022(ha))
+               return -EINVAL;
+
+       if (!test_bit(AF_82XX_DUMP_READING, &ha->flags))
+               return 0;
+
+       return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
+                                      ha->fw_dump_size);
+}
+
+static ssize_t
+qla4_8xxx_sysfs_write_fw_dump(struct file *filep, struct kobject *kobj,
+                             struct bin_attribute *ba, char *buf, loff_t off,
+                             size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+                                              struct device, kobj)));
+       uint32_t dev_state;
+       long reading;
+       int ret = 0;
+
+       if (!is_qla8022(ha))
+               return -EINVAL;
+
+       if (off != 0)
+               return ret;
+
+       buf[1] = 0;
+       ret = kstrtol(buf, 10, &reading);
+       if (ret) {
+               ql4_printk(KERN_ERR, ha, "%s: Invalid input. Return err %d\n",
+                          __func__, ret);
+               return ret;
+       }
+
+       switch (reading) {
+       case 0:
+               /* clear dump collection flags */
+               if (test_and_clear_bit(AF_82XX_DUMP_READING, &ha->flags)) {
+                       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+                       /* Reload minidump template */
+                       qla4xxx_alloc_fw_dump(ha);
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "Firmware template reloaded\n"));
+               }
+               break;
+       case 1:
+               /* Set flag to read dump */
+               if (test_bit(AF_82XX_FW_DUMPED, &ha->flags) &&
+                   !test_bit(AF_82XX_DUMP_READING, &ha->flags)) {
+                       set_bit(AF_82XX_DUMP_READING, &ha->flags);
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "Raw firmware dump ready for read on (%ld).\n",
+                                         ha->host_no));
+               }
+               break;
+       case 2:
+               /* Reset HBA */
+               qla4_8xxx_idc_lock(ha);
+               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+               if (dev_state == QLA82XX_DEV_READY) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: Setting Need reset, reset_owner is 0x%x.\n",
+                                  __func__, ha->func_num);
+                       qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                                       QLA82XX_DEV_NEED_RESET);
+                       set_bit(AF_82XX_RST_OWNER, &ha->flags);
+               } else
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: Reset not performed as device state is 0x%x\n",
+                                  __func__, dev_state);
+
+               qla4_8xxx_idc_unlock(ha);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+
+       return count;
+}
+
+static struct bin_attribute sysfs_fw_dump_attr = {
+       .attr = {
+               .name = "fw_dump",
+               .mode = S_IRUSR | S_IWUSR,
+       },
+       .size = 0,
+       .read = qla4_8xxx_sysfs_read_fw_dump,
+       .write = qla4_8xxx_sysfs_write_fw_dump,
+};
+
+static struct sysfs_entry {
+       char *name;
+       struct bin_attribute *attr;
+} bin_file_entries[] = {
+       { "fw_dump", &sysfs_fw_dump_attr },
+       { NULL },
+};
+
+void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha)
+{
+       struct Scsi_Host *host = ha->host;
+       struct sysfs_entry *iter;
+       int ret;
+
+       for (iter = bin_file_entries; iter->name; iter++) {
+               ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
+                                           iter->attr);
+               if (ret)
+                       ql4_printk(KERN_ERR, ha,
+                                  "Unable to create sysfs %s binary attribute (%d).\n",
+                                  iter->name, ret);
+       }
+}
+
+void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha)
+{
+       struct Scsi_Host *host = ha->host;
+       struct sysfs_entry *iter;
+
+       for (iter = bin_file_entries; iter->name; iter++)
+               sysfs_remove_bin_file(&host->shost_gendev.kobj,
+                                     iter->attr);
+}
+
 /* Scsi_Host attributes. */
 static ssize_t
 qla4xxx_fw_version_show(struct device *dev,
index 7f2492e..96a5616 100644 (file)
@@ -398,6 +398,16 @@ struct isp_operations {
        int (*get_sys_info) (struct scsi_qla_host *);
 };
 
+struct ql4_mdump_size_table {
+       uint32_t size;
+       uint32_t size_cmask_02;
+       uint32_t size_cmask_04;
+       uint32_t size_cmask_08;
+       uint32_t size_cmask_10;
+       uint32_t size_cmask_FF;
+       uint32_t version;
+};
+
 /*qla4xxx ipaddress configuration details */
 struct ipaddress_config {
        uint16_t ipv4_options;
@@ -485,6 +495,10 @@ struct scsi_qla_host {
 #define AF_EEH_BUSY                    20 /* 0x00100000 */
 #define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */
 #define AF_BUILD_DDB_LIST              22 /* 0x00400000 */
+#define AF_82XX_FW_DUMPED              24 /* 0x01000000 */
+#define AF_82XX_RST_OWNER              25 /* 0x02000000 */
+#define AF_82XX_DUMP_READING           26 /* 0x04000000 */
+
        unsigned long dpc_flags;
 
 #define DPC_RESET_HA                   1 /* 0x00000002 */
@@ -662,6 +676,11 @@ struct scsi_qla_host {
 
        uint32_t nx_dev_init_timeout;
        uint32_t nx_reset_timeout;
+       void *fw_dump;
+       uint32_t fw_dump_size;
+       uint32_t fw_dump_capture_mask;
+       void *fw_dump_tmplt_hdr;
+       uint32_t fw_dump_tmplt_size;
 
        struct completion mbx_intr_comp;
 
@@ -936,4 +955,7 @@ static inline int ql4xxx_reset_active(struct scsi_qla_host *ha)
 #define PROCESS_ALL_AENS        0
 #define FLUSH_DDB_CHANGED_AENS  1
 
+/* Defines for udev events */
+#define QL4_UEVENT_CODE_FW_DUMP                0
+
 #endif /*_QLA4XXX_H */
index 210cd1d..7240948 100644 (file)
@@ -385,6 +385,11 @@ struct qla_flt_region {
 #define MBOX_CMD_GET_IP_ADDR_STATE             0x0091
 #define MBOX_CMD_SEND_IPV6_ROUTER_SOL          0x0092
 #define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR  0x0093
+#define MBOX_CMD_MINIDUMP                      0x0129
+
+/* Minidump subcommand */
+#define MINIDUMP_GET_SIZE_SUBCOMMAND           0x00
+#define MINIDUMP_GET_TMPLT_SUBCOMMAND          0x01
 
 /* Mailbox 1 */
 #define FW_STATE_READY                         0x0000
@@ -1190,4 +1195,27 @@ struct ql_iscsi_stats {
        uint8_t reserved2[264]; /* 0x0308 - 0x040F */
 };
 
+#define QLA82XX_DBG_STATE_ARRAY_LEN            16
+#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN         8
+#define QLA82XX_DBG_RSVD_ARRAY_LEN             8
+
+struct qla4_8xxx_minidump_template_hdr {
+       uint32_t entry_type;
+       uint32_t first_entry_offset;
+       uint32_t size_of_template;
+       uint32_t capture_debug_level;
+       uint32_t num_of_entries;
+       uint32_t version;
+       uint32_t driver_timestamp;
+       uint32_t checksum;
+
+       uint32_t driver_capture_mask;
+       uint32_t driver_info_word2;
+       uint32_t driver_info_word3;
+       uint32_t driver_info_word4;
+
+       uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN];
+       uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN];
+};
+
 #endif /*  _QLA4X_FW_H */
index 9105366..20b49d0 100644 (file)
@@ -196,10 +196,18 @@ int qla4xxx_bsg_request(struct bsg_job *bsg_job);
 int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job);
 
 void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry);
+int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
+                                 dma_addr_t phys_addr);
+int qla4xxx_req_template_size(struct scsi_qla_host *ha);
+void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha);
+void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha);
+void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
 extern int ql4xenablemsix;
+extern int ql4xmdcapmask;
+extern int ql4xenablemd;
 
 extern struct device_attribute *qla4xxx_host_attrs[];
 #endif /* _QLA4x_GBL_H */
index 90ee5d8..bf36723 100644 (file)
@@ -277,6 +277,94 @@ qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha)
        return ipv4_wait|ipv6_wait;
 }
 
+/**
+ * qla4xxx_alloc_fw_dump - Allocate memory for minidump data.
+ * @ha: pointer to host adapter structure.
+ **/
+void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha)
+{
+       int status;
+       uint32_t capture_debug_level;
+       int hdr_entry_bit, k;
+       void *md_tmp;
+       dma_addr_t md_tmp_dma;
+       struct qla4_8xxx_minidump_template_hdr *md_hdr;
+
+       if (ha->fw_dump) {
+               ql4_printk(KERN_WARNING, ha,
+                          "Firmware dump previously allocated.\n");
+               return;
+       }
+
+       status = qla4xxx_req_template_size(ha);
+       if (status != QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi%ld: Failed to get template size\n",
+                          ha->host_no);
+               return;
+       }
+
+       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+
+       /* Allocate memory for saving the template */
+       md_tmp = dma_alloc_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
+                                   &md_tmp_dma, GFP_KERNEL);
+
+       /* Request template */
+       status =  qla4xxx_get_minidump_template(ha, md_tmp_dma);
+       if (status != QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi%ld: Failed to get minidump template\n",
+                          ha->host_no);
+               goto alloc_cleanup;
+       }
+
+       md_hdr = (struct qla4_8xxx_minidump_template_hdr *)md_tmp;
+
+       capture_debug_level = md_hdr->capture_debug_level;
+
+       /* Get capture mask based on module loadtime setting. */
+       if (ql4xmdcapmask >= 0x3 && ql4xmdcapmask <= 0x7F)
+               ha->fw_dump_capture_mask = ql4xmdcapmask;
+       else
+               ha->fw_dump_capture_mask = capture_debug_level;
+
+       md_hdr->driver_capture_mask = ha->fw_dump_capture_mask;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Minimum num of entries = %d\n",
+                         md_hdr->num_of_entries));
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Dump template size  = %d\n",
+                         ha->fw_dump_tmplt_size));
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Selected Capture mask =0x%x\n",
+                         ha->fw_dump_capture_mask));
+
+       /* Calculate fw_dump_size */
+       for (hdr_entry_bit = 0x2, k = 1; (hdr_entry_bit & 0xFF);
+            hdr_entry_bit <<= 1, k++) {
+               if (hdr_entry_bit & ha->fw_dump_capture_mask)
+                       ha->fw_dump_size += md_hdr->capture_size_array[k];
+       }
+
+       /* Total firmware dump size including command header */
+       ha->fw_dump_size += ha->fw_dump_tmplt_size;
+       ha->fw_dump = vmalloc(ha->fw_dump_size);
+       if (!ha->fw_dump)
+               goto alloc_cleanup;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Minidump Tempalate Size = 0x%x KB\n",
+                         ha->fw_dump_tmplt_size));
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Total Minidump size = 0x%x KB\n", ha->fw_dump_size));
+
+       memcpy(ha->fw_dump, md_tmp, ha->fw_dump_tmplt_size);
+       ha->fw_dump_tmplt_hdr = ha->fw_dump;
+
+alloc_cleanup:
+       dma_free_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
+                         md_tmp, md_tmp_dma);
+}
+
 static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
 {
        uint32_t timeout_count;
@@ -445,9 +533,13 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
                              "control block\n", ha->host_no, __func__));
                return status;
        }
+
        if (!qla4xxx_fw_ready(ha))
                return status;
 
+       if (is_qla8022(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
+               qla4xxx_alloc_fw_dump(ha);
+
        return qla4xxx_get_firmware_status(ha);
 }
 
@@ -884,8 +976,8 @@ int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                switch (state) {
                case DDB_DS_SESSION_ACTIVE:
                case DDB_DS_DISCOVERY:
-                       ddb_entry->unblock_sess(ddb_entry->sess);
                        qla4xxx_update_session_conn_param(ha, ddb_entry);
+                       ddb_entry->unblock_sess(ddb_entry->sess);
                        status = QLA_SUCCESS;
                        break;
                case DDB_DS_SESSION_FAILED:
@@ -897,6 +989,7 @@ int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                }
                break;
        case DDB_DS_SESSION_ACTIVE:
+       case DDB_DS_DISCOVERY:
                switch (state) {
                case DDB_DS_SESSION_FAILED:
                        /*
index 7ac21da..cab8f66 100644 (file)
@@ -51,25 +51,6 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
                }
        }
 
-       if (is_qla8022(ha)) {
-               if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
-                       DEBUG2(ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: "
-                           "prematurely completing mbx cmd as firmware "
-                           "recovery detected\n", ha->host_no, __func__));
-                       return status;
-               }
-               /* Do not send any mbx cmd if h/w is in failed state*/
-               qla4_8xxx_idc_lock(ha);
-               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               qla4_8xxx_idc_unlock(ha);
-               if (dev_state == QLA82XX_DEV_FAILED) {
-                       ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: H/W is in "
-                           "failed state, do not send any mailbox commands\n",
-                           ha->host_no, __func__);
-                       return status;
-               }
-       }
-
        if ((is_aer_supported(ha)) &&
            (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))) {
                DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Perm failure on EEH, "
@@ -96,6 +77,25 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
                msleep(10);
        }
 
+       if (is_qla8022(ha)) {
+               if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
+                       DEBUG2(ql4_printk(KERN_WARNING, ha,
+                                         "scsi%ld: %s: prematurely completing mbx cmd as firmware recovery detected\n",
+                                         ha->host_no, __func__));
+                       goto mbox_exit;
+               }
+               /* Do not send any mbx cmd if h/w is in failed state*/
+               qla4_8xxx_idc_lock(ha);
+               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+               qla4_8xxx_idc_unlock(ha);
+               if (dev_state == QLA82XX_DEV_FAILED) {
+                       ql4_printk(KERN_WARNING, ha,
+                                  "scsi%ld: %s: H/W is in failed state, do not send any mailbox commands\n",
+                                  ha->host_no, __func__);
+                       goto mbox_exit;
+               }
+       }
+
        spin_lock_irqsave(&ha->hardware_lock, flags);
 
        ha->mbox_status_count = outCount;
@@ -270,6 +270,79 @@ mbox_exit:
        return status;
 }
 
+/**
+ * qla4xxx_get_minidump_template - Get the firmware template
+ * @ha: Pointer to host adapter structure.
+ * @phys_addr: dma address for template
+ *
+ * Obtain the minidump template from firmware during initialization
+ * as it may not be available when minidump is desired.
+ **/
+int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
+                                 dma_addr_t phys_addr)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       int status;
+
+       memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+       memset(&mbox_sts, 0, sizeof(mbox_sts));
+
+       mbox_cmd[0] = MBOX_CMD_MINIDUMP;
+       mbox_cmd[1] = MINIDUMP_GET_TMPLT_SUBCOMMAND;
+       mbox_cmd[2] = LSDW(phys_addr);
+       mbox_cmd[3] = MSDW(phys_addr);
+       mbox_cmd[4] = ha->fw_dump_tmplt_size;
+       mbox_cmd[5] = 0;
+
+       status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 2, &mbox_cmd[0],
+                                        &mbox_sts[0]);
+       if (status != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "scsi%ld: %s: Cmd = %08X, mbx[0] = 0x%04x, mbx[1] = 0x%04x\n",
+                                 ha->host_no, __func__, mbox_cmd[0],
+                                 mbox_sts[0], mbox_sts[1]));
+       }
+       return status;
+}
+
+/**
+ * qla4xxx_req_template_size - Get minidump template size from firmware.
+ * @ha: Pointer to host adapter structure.
+ **/
+int qla4xxx_req_template_size(struct scsi_qla_host *ha)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       int status;
+
+       memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+       memset(&mbox_sts, 0, sizeof(mbox_sts));
+
+       mbox_cmd[0] = MBOX_CMD_MINIDUMP;
+       mbox_cmd[1] = MINIDUMP_GET_SIZE_SUBCOMMAND;
+
+       status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
+                                        &mbox_sts[0]);
+       if (status == QLA_SUCCESS) {
+               ha->fw_dump_tmplt_size = mbox_sts[1];
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: sts[0]=0x%04x, template  size=0x%04x, size_cm_02=0x%04x, size_cm_04=0x%04x, size_cm_08=0x%04x, size_cm_10=0x%04x, size_cm_FF=0x%04x, version=0x%04x\n",
+                                 __func__, mbox_sts[0], mbox_sts[1],
+                                 mbox_sts[2], mbox_sts[3], mbox_sts[4],
+                                 mbox_sts[5], mbox_sts[6], mbox_sts[7]));
+               if (ha->fw_dump_tmplt_size == 0)
+                       status = QLA_ERROR;
+       } else {
+               ql4_printk(KERN_WARNING, ha,
+                          "%s: Error sts[0]=0x%04x, mbx[1]=0x%04x\n",
+                          __func__, mbox_sts[0], mbox_sts[1]);
+               status = QLA_ERROR;
+       }
+
+       return status;
+}
+
 void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha)
 {
        set_bit(AF_FW_RECOVERY, &ha->flags);
index e1e46b6..228b670 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/pci.h>
+#include <linux/ratelimit.h>
 #include "ql4_def.h"
 #include "ql4_glbl.h"
 
@@ -420,6 +421,38 @@ qla4_8xxx_rd_32(struct scsi_qla_host *ha, ulong off)
        return data;
 }
 
+/* Minidump related functions */
+static int qla4_8xxx_md_rw_32(struct scsi_qla_host *ha, uint32_t off,
+                             u32 data, uint8_t flag)
+{
+       uint32_t win_read, off_value, rval = QLA_SUCCESS;
+
+       off_value  = off & 0xFFFF0000;
+       writel(off_value, (void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
+
+       /* Read back value to make sure write has gone through before trying
+        * to use it.
+        */
+       win_read = readl((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
+       if (win_read != off_value) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: Written (0x%x) != Read (0x%x), off=0x%x\n",
+                                  __func__, off_value, win_read, off));
+               return QLA_ERROR;
+       }
+
+       off_value  = off & 0x0000FFFF;
+
+       if (flag)
+               writel(data, (void __iomem *)(off_value + CRB_INDIRECT_2M +
+                                             ha->nx_pcibase));
+       else
+               rval = readl((void __iomem *)(off_value + CRB_INDIRECT_2M +
+                                             ha->nx_pcibase));
+
+       return rval;
+}
+
 #define CRB_WIN_LOCK_TIMEOUT 100000000
 
 int qla4_8xxx_crb_win_lock(struct scsi_qla_host *ha)
@@ -1252,9 +1285,9 @@ qla4_8xxx_pci_mem_read_2M(struct scsi_qla_host *ha,
                }
 
                if (j >= MAX_CTL_CHECK) {
-                       if (printk_ratelimit())
-                               ql4_printk(KERN_ERR, ha,
-                                   "failed to read through agent\n");
+                       printk_ratelimited(KERN_ERR
+                                          "%s: failed to read through agent\n",
+                                          __func__);
                        break;
                }
 
@@ -1390,7 +1423,8 @@ qla4_8xxx_pci_mem_write_2M(struct scsi_qla_host *ha,
                if (j >= MAX_CTL_CHECK) {
                        if (printk_ratelimit())
                                ql4_printk(KERN_ERR, ha,
-                                   "failed to write through agent\n");
+                                          "%s: failed to read through agent\n",
+                                          __func__);
                        ret = -1;
                        break;
                }
@@ -1462,6 +1496,8 @@ qla4_8xxx_set_drv_active(struct scsi_qla_host *ha)
 
        drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        drv_active |= (1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
+                  __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
 }
 
@@ -1472,6 +1508,8 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha)
 
        drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        drv_active &= ~(1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
+                  __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
 }
 
@@ -1497,6 +1535,8 @@ qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha)
 
        drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
        drv_state |= (1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
+                  __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
 }
 
@@ -1507,6 +1547,8 @@ qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha)
 
        drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
        drv_state &= ~(1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
+                  __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
 }
 
@@ -1601,6 +1643,629 @@ static void qla4_8xxx_rom_lock_recovery(struct scsi_qla_host *ha)
        qla4_8xxx_rom_unlock(ha);
 }
 
+static void qla4_8xxx_minidump_process_rdcrb(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_crb *crb_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       crb_hdr = (struct qla82xx_minidump_entry_crb *)entry_hdr;
+       r_addr = crb_hdr->addr;
+       r_stride = crb_hdr->crb_strd.addr_stride;
+       loop_cnt = crb_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+               *data_ptr++ = cpu_to_le32(r_addr);
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += r_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static int qla4_8xxx_minidump_process_l2tag(struct scsi_qla_host *ha,
+                                struct qla82xx_minidump_entry_hdr *entry_hdr,
+                                uint32_t **d_ptr)
+{
+       uint32_t addr, r_addr, c_addr, t_r_addr;
+       uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+       unsigned long p_wait, w_time, p_mask;
+       uint32_t c_value_w, c_value_r;
+       struct qla82xx_minidump_entry_cache *cache_hdr;
+       int rval = QLA_ERROR;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
+
+       loop_count = cache_hdr->op_count;
+       r_addr = cache_hdr->read_addr;
+       c_addr = cache_hdr->control_addr;
+       c_value_w = cache_hdr->cache_ctrl.write_value;
+
+       t_r_addr = cache_hdr->tag_reg_addr;
+       t_value = cache_hdr->addr_ctrl.init_tag_value;
+       r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+       p_wait = cache_hdr->cache_ctrl.poll_wait;
+       p_mask = cache_hdr->cache_ctrl.poll_mask;
+
+       for (i = 0; i < loop_count; i++) {
+               qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
+
+               if (c_value_w)
+                       qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
+
+               if (p_mask) {
+                       w_time = jiffies + p_wait;
+                       do {
+                               c_value_r = qla4_8xxx_md_rw_32(ha, c_addr,
+                                                               0, 0);
+                               if ((c_value_r & p_mask) == 0) {
+                                       break;
+                               } else if (time_after_eq(jiffies, w_time)) {
+                                       /* capturing dump failed */
+                                       return rval;
+                               }
+                       } while (1);
+               }
+
+               addr = r_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       addr += cache_hdr->read_ctrl.read_addr_stride;
+               }
+
+               t_value += cache_hdr->addr_ctrl.tag_value_stride;
+       }
+       *d_ptr = data_ptr;
+       return QLA_SUCCESS;
+}
+
+static int qla4_8xxx_minidump_process_control(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr)
+{
+       struct qla82xx_minidump_entry_crb *crb_entry;
+       uint32_t read_value, opcode, poll_time, addr, index, rval = QLA_SUCCESS;
+       uint32_t crb_addr;
+       unsigned long wtime;
+       struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
+       int i;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
+                                               ha->fw_dump_tmplt_hdr;
+       crb_entry = (struct qla82xx_minidump_entry_crb *)entry_hdr;
+
+       crb_addr = crb_entry->addr;
+       for (i = 0; i < crb_entry->op_count; i++) {
+               opcode = crb_entry->crb_ctrl.opcode;
+               if (opcode & QLA82XX_DBG_OPCODE_WR) {
+                       qla4_8xxx_md_rw_32(ha, crb_addr,
+                                          crb_entry->value_1, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_WR;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_RW) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_RW;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_AND) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       read_value &= crb_entry->value_2;
+                       opcode &= ~QLA82XX_DBG_OPCODE_AND;
+                       if (opcode & QLA82XX_DBG_OPCODE_OR) {
+                               read_value |= crb_entry->value_3;
+                               opcode &= ~QLA82XX_DBG_OPCODE_OR;
+                       }
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_OR) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       read_value |= crb_entry->value_3;
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_OR;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_POLL) {
+                       poll_time = crb_entry->crb_strd.poll_timeout;
+                       wtime = jiffies + poll_time;
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+
+                       do {
+                               if ((read_value & crb_entry->value_2) ==
+                                   crb_entry->value_1)
+                                       break;
+                               else if (time_after_eq(jiffies, wtime)) {
+                                       /* capturing dump failed */
+                                       rval = QLA_ERROR;
+                                       break;
+                               } else
+                                       read_value = qla4_8xxx_md_rw_32(ha,
+                                                               crb_addr, 0, 0);
+                       } while (1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_POLL;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
+                       if (crb_entry->crb_strd.state_index_a) {
+                               index = crb_entry->crb_strd.state_index_a;
+                               addr = tmplt_hdr->saved_state_array[index];
+                       } else {
+                               addr = crb_addr;
+                       }
+
+                       read_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       index = crb_entry->crb_ctrl.state_index_v;
+                       tmplt_hdr->saved_state_array[index] = read_value;
+                       opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
+                       if (crb_entry->crb_strd.state_index_a) {
+                               index = crb_entry->crb_strd.state_index_a;
+                               addr = tmplt_hdr->saved_state_array[index];
+                       } else {
+                               addr = crb_addr;
+                       }
+
+                       if (crb_entry->crb_ctrl.state_index_v) {
+                               index = crb_entry->crb_ctrl.state_index_v;
+                               read_value =
+                                       tmplt_hdr->saved_state_array[index];
+                       } else {
+                               read_value = crb_entry->value_1;
+                       }
+
+                       qla4_8xxx_md_rw_32(ha, addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
+                       index = crb_entry->crb_ctrl.state_index_v;
+                       read_value = tmplt_hdr->saved_state_array[index];
+                       read_value <<= crb_entry->crb_ctrl.shl;
+                       read_value >>= crb_entry->crb_ctrl.shr;
+                       if (crb_entry->value_2)
+                               read_value &= crb_entry->value_2;
+                       read_value |= crb_entry->value_3;
+                       read_value += crb_entry->value_1;
+                       tmplt_hdr->saved_state_array[index] = read_value;
+                       opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE;
+               }
+               crb_addr += crb_entry->crb_strd.addr_stride;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s\n", __func__));
+       return rval;
+}
+
+static void qla4_8xxx_minidump_process_rdocm(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_rdocm *ocm_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       ocm_hdr = (struct qla82xx_minidump_entry_rdocm *)entry_hdr;
+       r_addr = ocm_hdr->read_addr;
+       r_stride = ocm_hdr->read_addr_stride;
+       loop_cnt = ocm_hdr->op_count;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: r_addr: 0x%x, r_stride: 0x%x, loop_cnt: 0x%x\n",
+                         __func__, r_addr, r_stride, loop_cnt));
+
+       for (i = 0; i < loop_cnt; i++) {
+               r_value = readl((void __iomem *)(r_addr + ha->nx_pcibase));
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += r_stride;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%lx\n",
+                         __func__, (loop_cnt * sizeof(uint32_t))));
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_rdmux(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_mux *mux_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       mux_hdr = (struct qla82xx_minidump_entry_mux *)entry_hdr;
+       r_addr = mux_hdr->read_addr;
+       s_addr = mux_hdr->select_addr;
+       s_stride = mux_hdr->select_value_stride;
+       s_value = mux_hdr->select_value;
+       loop_cnt = mux_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, s_addr, s_value, 1);
+               r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+               *data_ptr++ = cpu_to_le32(s_value);
+               *data_ptr++ = cpu_to_le32(r_value);
+               s_value += s_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_l1cache(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t addr, r_addr, c_addr, t_r_addr;
+       uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+       uint32_t c_value_w;
+       struct qla82xx_minidump_entry_cache *cache_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
+       loop_count = cache_hdr->op_count;
+       r_addr = cache_hdr->read_addr;
+       c_addr = cache_hdr->control_addr;
+       c_value_w = cache_hdr->cache_ctrl.write_value;
+
+       t_r_addr = cache_hdr->tag_reg_addr;
+       t_value = cache_hdr->addr_ctrl.init_tag_value;
+       r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+
+       for (i = 0; i < loop_count; i++) {
+               qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
+               qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
+               addr = r_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       addr += cache_hdr->read_ctrl.read_addr_stride;
+               }
+               t_value += cache_hdr->addr_ctrl.tag_value_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_queue(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t s_addr, r_addr;
+       uint32_t r_stride, r_value, r_cnt, qid = 0;
+       uint32_t i, k, loop_cnt;
+       struct qla82xx_minidump_entry_queue *q_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       q_hdr = (struct qla82xx_minidump_entry_queue *)entry_hdr;
+       s_addr = q_hdr->select_addr;
+       r_cnt = q_hdr->rd_strd.read_addr_cnt;
+       r_stride = q_hdr->rd_strd.read_addr_stride;
+       loop_cnt = q_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, s_addr, qid, 1);
+               r_addr = q_hdr->read_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       r_addr += r_stride;
+               }
+               qid += q_hdr->q_strd.queue_id_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+#define MD_DIRECT_ROM_WINDOW           0x42110030
+#define MD_DIRECT_ROM_READ_BASE                0x42150000
+
+static void qla4_8xxx_minidump_process_rdrom(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_value;
+       uint32_t i, loop_cnt;
+       struct qla82xx_minidump_entry_rdrom *rom_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       rom_hdr = (struct qla82xx_minidump_entry_rdrom *)entry_hdr;
+       r_addr = rom_hdr->read_addr;
+       loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: flash_addr: 0x%x, read_data_size: 0x%x\n",
+                          __func__, r_addr, loop_cnt));
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW,
+                                  (r_addr & 0xFFFF0000), 1);
+               r_value = qla4_8xxx_md_rw_32(ha,
+                                            MD_DIRECT_ROM_READ_BASE +
+                                            (r_addr & 0x0000FFFF), 0, 0);
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += sizeof(uint32_t);
+       }
+       *d_ptr = data_ptr;
+}
+
+#define MD_MIU_TEST_AGT_CTRL           0x41000090
+#define MD_MIU_TEST_AGT_ADDR_LO                0x41000094
+#define MD_MIU_TEST_AGT_ADDR_HI                0x41000098
+
+static int qla4_8xxx_minidump_process_rdmem(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_value, r_data;
+       uint32_t i, j, loop_cnt;
+       struct qla82xx_minidump_entry_rdmem *m_hdr;
+       unsigned long flags;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       m_hdr = (struct qla82xx_minidump_entry_rdmem *)entry_hdr;
+       r_addr = m_hdr->read_addr;
+       loop_cnt = m_hdr->read_data_size/16;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: Read addr: 0x%x, read_data_size: 0x%x\n",
+                         __func__, r_addr, m_hdr->read_data_size));
+
+       if (r_addr & 0xf) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "[%s]: Read addr 0x%x not 16 bytes alligned\n",
+                                 __func__, r_addr));
+               return QLA_ERROR;
+       }
+
+       if (m_hdr->read_data_size % 16) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "[%s]: Read data[0x%x] not multiple of 16 bytes\n",
+                                 __func__, m_hdr->read_data_size));
+               return QLA_ERROR;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n",
+                         __func__, r_addr, m_hdr->read_data_size, loop_cnt));
+
+       write_lock_irqsave(&ha->hw_lock, flags);
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1);
+               r_value = 0;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1);
+               r_value = MIU_TA_CTL_ENABLE;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+               r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+
+               for (j = 0; j < MAX_CTL_CHECK; j++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL,
+                                                    0, 0);
+                       if ((r_value & MIU_TA_CTL_BUSY) == 0)
+                               break;
+               }
+
+               if (j >= MAX_CTL_CHECK) {
+                       printk_ratelimited(KERN_ERR
+                                          "%s: failed to read through agent\n",
+                                           __func__);
+                       write_unlock_irqrestore(&ha->hw_lock, flags);
+                       return QLA_SUCCESS;
+               }
+
+               for (j = 0; j < 4; j++) {
+                       r_data = qla4_8xxx_md_rw_32(ha,
+                                                   MD_MIU_TEST_AGT_RDDATA[j],
+                                                   0, 0);
+                       *data_ptr++ = cpu_to_le32(r_data);
+               }
+
+               r_addr += 16;
+       }
+       write_unlock_irqrestore(&ha->hw_lock, flags);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%x\n",
+                         __func__, (loop_cnt * 16)));
+
+       *d_ptr = data_ptr;
+       return QLA_SUCCESS;
+}
+
+static void ql4_8xxx_mark_entry_skipped(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               int index)
+{
+       entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG;
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "scsi(%ld): Skipping entry[%d]: ETYPE[0x%x]-ELEVEL[0x%x]\n",
+                         ha->host_no, index, entry_hdr->entry_type,
+                         entry_hdr->d_ctrl.entry_capture_mask));
+}
+
+/**
+ * qla82xx_collect_md_data - Retrieve firmware minidump data.
+ * @ha: pointer to adapter structure
+ **/
+static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
+{
+       int num_entry_hdr = 0;
+       struct qla82xx_minidump_entry_hdr *entry_hdr;
+       struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
+       uint32_t *data_ptr;
+       uint32_t data_collected = 0;
+       int i, rval = QLA_ERROR;
+       uint64_t now;
+       uint32_t timestamp;
+
+       if (!ha->fw_dump) {
+               ql4_printk(KERN_INFO, ha, "%s(%ld) No buffer to dump\n",
+                          __func__, ha->host_no);
+               return rval;
+       }
+
+       tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
+                                               ha->fw_dump_tmplt_hdr;
+       data_ptr = (uint32_t *)((uint8_t *)ha->fw_dump +
+                                               ha->fw_dump_tmplt_size);
+       data_collected += ha->fw_dump_tmplt_size;
+
+       num_entry_hdr = tmplt_hdr->num_of_entries;
+       ql4_printk(KERN_INFO, ha, "[%s]: starting data ptr: %p\n",
+                  __func__, data_ptr);
+       ql4_printk(KERN_INFO, ha,
+                  "[%s]: no of entry headers in Template: 0x%x\n",
+                  __func__, num_entry_hdr);
+       ql4_printk(KERN_INFO, ha, "[%s]: Capture Mask obtained: 0x%x\n",
+                  __func__, ha->fw_dump_capture_mask);
+       ql4_printk(KERN_INFO, ha, "[%s]: Total_data_size 0x%x, %d obtained\n",
+                  __func__, ha->fw_dump_size, ha->fw_dump_size);
+
+       /* Update current timestamp before taking dump */
+       now = get_jiffies_64();
+       timestamp = (u32)(jiffies_to_msecs(now) / 1000);
+       tmplt_hdr->driver_timestamp = timestamp;
+
+       entry_hdr = (struct qla82xx_minidump_entry_hdr *)
+                                       (((uint8_t *)ha->fw_dump_tmplt_hdr) +
+                                        tmplt_hdr->first_entry_offset);
+
+       /* Walk through the entry headers - validate/perform required action */
+       for (i = 0; i < num_entry_hdr; i++) {
+               if (data_collected >= ha->fw_dump_size) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "Data collected: [0x%x], Total Dump size: [0x%x]\n",
+                                  data_collected, ha->fw_dump_size);
+                       return rval;
+               }
+
+               if (!(entry_hdr->d_ctrl.entry_capture_mask &
+                     ha->fw_dump_capture_mask)) {
+                       entry_hdr->d_ctrl.driver_flags |=
+                                               QLA82XX_DBG_SKIPPED_FLAG;
+                       goto skip_nxt_entry;
+               }
+
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "Data collected: [0x%x], Dump size left:[0x%x]\n",
+                                 data_collected,
+                                 (ha->fw_dump_size - data_collected)));
+
+               /* Decode the entry type and take required action to capture
+                * debug data
+                */
+               switch (entry_hdr->entry_type) {
+               case QLA82XX_RDEND:
+                       ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
+               case QLA82XX_CNTRL:
+                       rval = qla4_8xxx_minidump_process_control(ha,
+                                                                 entry_hdr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_RDCRB:
+                       qla4_8xxx_minidump_process_rdcrb(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDMEM:
+                       rval = qla4_8xxx_minidump_process_rdmem(ha, entry_hdr,
+                                                               &data_ptr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_BOARD:
+               case QLA82XX_RDROM:
+                       qla4_8xxx_minidump_process_rdrom(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_L2DTG:
+               case QLA82XX_L2ITG:
+               case QLA82XX_L2DAT:
+               case QLA82XX_L2INS:
+                       rval = qla4_8xxx_minidump_process_l2tag(ha, entry_hdr,
+                                                               &data_ptr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_L1DAT:
+               case QLA82XX_L1INS:
+                       qla4_8xxx_minidump_process_l1cache(ha, entry_hdr,
+                                                          &data_ptr);
+                       break;
+               case QLA82XX_RDOCM:
+                       qla4_8xxx_minidump_process_rdocm(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDMUX:
+                       qla4_8xxx_minidump_process_rdmux(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_QUEUE:
+                       qla4_8xxx_minidump_process_queue(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDNOP:
+               default:
+                       ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
+               }
+
+               data_collected = (uint8_t *)data_ptr -
+                                ((uint8_t *)((uint8_t *)ha->fw_dump +
+                                               ha->fw_dump_tmplt_size));
+skip_nxt_entry:
+               /*  next entry in the template */
+               entry_hdr = (struct qla82xx_minidump_entry_hdr *)
+                               (((uint8_t *)entry_hdr) +
+                                entry_hdr->entry_size);
+       }
+
+       if ((data_collected + ha->fw_dump_tmplt_size) != ha->fw_dump_size) {
+               ql4_printk(KERN_INFO, ha,
+                          "Dump data mismatch: Data collected: [0x%x], total_data_size:[0x%x]\n",
+                          data_collected, ha->fw_dump_size);
+               goto md_failed;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s Last entry: 0x%x\n",
+                         __func__, i));
+md_failed:
+       return rval;
+}
+
+/**
+ * qla4_8xxx_uevent_emit - Send uevent when the firmware dump is ready.
+ * @ha: pointer to adapter structure
+ **/
+static void qla4_8xxx_uevent_emit(struct scsi_qla_host *ha, u32 code)
+{
+       char event_string[40];
+       char *envp[] = { event_string, NULL };
+
+       switch (code) {
+       case QL4_UEVENT_CODE_FW_DUMP:
+               snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+                        ha->host_no);
+               break;
+       default:
+               /*do nothing*/
+               break;
+       }
+
+       kobject_uevent_env(&(&ha->pdev->dev)->kobj, KOBJ_CHANGE, envp);
+}
+
 /**
  * qla4_8xxx_device_bootstrap - Initialize device, set DEV_READY, start fw
  * @ha: pointer to adapter structure
@@ -1659,6 +2324,15 @@ dev_initialize:
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION);
 
        qla4_8xxx_idc_unlock(ha);
+       if (ql4xenablemd && test_bit(AF_FW_RECOVERY, &ha->flags) &&
+           !test_and_set_bit(AF_82XX_FW_DUMPED, &ha->flags)) {
+               if (!qla4_8xxx_collect_md_data(ha)) {
+                       qla4_8xxx_uevent_emit(ha, QL4_UEVENT_CODE_FW_DUMP);
+               } else {
+                       ql4_printk(KERN_INFO, ha, "Unable to collect minidump\n");
+                       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+               }
+       }
        rval = qla4_8xxx_try_start_fw(ha);
        qla4_8xxx_idc_lock(ha);
 
@@ -1686,6 +2360,7 @@ static void
 qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
 {
        uint32_t dev_state, drv_state, drv_active;
+       uint32_t active_mask = 0xFFFFFFFF;
        unsigned long reset_timeout;
 
        ql4_printk(KERN_INFO, ha,
@@ -1697,7 +2372,14 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                qla4_8xxx_idc_lock(ha);
        }
 
-       qla4_8xxx_set_rst_ready(ha);
+       if (!test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s(%ld): reset acknowledged\n",
+                                 __func__, ha->host_no));
+               qla4_8xxx_set_rst_ready(ha);
+       } else {
+               active_mask = (~(1 << (ha->func_num * 4)));
+       }
 
        /* wait for 10 seconds for reset ack from all functions */
        reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
@@ -1709,12 +2391,24 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                "%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
                __func__, ha->host_no, drv_state, drv_active);
 
-       while (drv_state != drv_active) {
+       while (drv_state != (drv_active & active_mask)) {
                if (time_after_eq(jiffies, reset_timeout)) {
-                       printk("%s: RESET TIMEOUT!\n", DRIVER_NAME);
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: RESET TIMEOUT! drv_state: 0x%08x, drv_active: 0x%08x\n",
+                                  DRIVER_NAME, drv_state, drv_active);
                        break;
                }
 
+               /*
+                * When reset_owner times out, check which functions
+                * acked/did not ack
+                */
+               if (test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
+                                  __func__, ha->host_no, drv_state,
+                                  drv_active);
+               }
                qla4_8xxx_idc_unlock(ha);
                msleep(1000);
                qla4_8xxx_idc_lock(ha);
@@ -1723,14 +2417,18 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        }
 
+       /* Clear RESET OWNER as we are not going to use it any further */
+       clear_bit(AF_82XX_RST_OWNER, &ha->flags);
+
        dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-       ql4_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state,
-               dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+       ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n", dev_state,
+                  dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
 
        /* Force to DEV_COLD unless someone else is starting a reset */
        if (dev_state != QLA82XX_DEV_INITIALIZING) {
                ql4_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n");
                qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
+               qla4_8xxx_set_rst_ready(ha);
        }
 }
 
@@ -1765,8 +2463,9 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        }
 
        dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-       ql4_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state,
-               dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
+                         dev_state, dev_state < MAX_STATES ?
+                         qdev_state[dev_state] : "Unknown"));
 
        /* wait for 30 seconds for device to go ready */
        dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
@@ -1775,15 +2474,19 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        while (1) {
 
                if (time_after_eq(jiffies, dev_init_timeout)) {
-                       ql4_printk(KERN_WARNING, ha, "Device init failed!\n");
+                       ql4_printk(KERN_WARNING, ha,
+                                  "%s: Device Init Failed 0x%x = %s\n",
+                                  DRIVER_NAME,
+                                  dev_state, dev_state < MAX_STATES ?
+                                  qdev_state[dev_state] : "Unknown");
                        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                                QLA82XX_DEV_FAILED);
                }
 
                dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               ql4_printk(KERN_INFO, ha,
-                   "2:Device state is 0x%x = %s\n", dev_state,
-                   dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+               ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
+                          dev_state, dev_state < MAX_STATES ?
+                          qdev_state[dev_state] : "Unknown");
 
                /* NOTE: Make sure idc unlocked upon exit of switch statement */
                switch (dev_state) {
@@ -2184,6 +2887,7 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
                ql4_printk(KERN_INFO, ha, "HW State: NEED RESET\n");
                qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                    QLA82XX_DEV_NEED_RESET);
+               set_bit(AF_82XX_RST_OWNER, &ha->flags);
        } else
                ql4_printk(KERN_INFO, ha, "HW State: DEVICE INITIALIZING\n");
 
@@ -2195,8 +2899,10 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
        qla4_8xxx_clear_rst_ready(ha);
        qla4_8xxx_idc_unlock(ha);
 
-       if (rval == QLA_SUCCESS)
+       if (rval == QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha, "Clearing AF_RECOVERY in qla4_8xxx_isp_reset\n");
                clear_bit(AF_FW_RECOVERY, &ha->flags);
+       }
 
        return rval;
 }
index dc7500e..3025847 100644 (file)
@@ -792,4 +792,196 @@ struct crb_addr_pair {
 #define MIU_TEST_AGT_WRDATA_UPPER_LO   (0x0b0)
 #define        MIU_TEST_AGT_WRDATA_UPPER_HI    (0x0b4)
 
+/* Minidump related */
+
+/* Entry Type Defines */
+#define QLA82XX_RDNOP  0
+#define QLA82XX_RDCRB  1
+#define QLA82XX_RDMUX  2
+#define QLA82XX_QUEUE  3
+#define QLA82XX_BOARD  4
+#define QLA82XX_RDOCM  6
+#define QLA82XX_PREGS  7
+#define QLA82XX_L1DTG  8
+#define QLA82XX_L1ITG  9
+#define QLA82XX_L1DAT  11
+#define QLA82XX_L1INS  12
+#define QLA82XX_L2DTG  21
+#define QLA82XX_L2ITG  22
+#define QLA82XX_L2DAT  23
+#define QLA82XX_L2INS  24
+#define QLA82XX_RDROM  71
+#define QLA82XX_RDMEM  72
+#define QLA82XX_CNTRL  98
+#define QLA82XX_RDEND  255
+
+/* Opcodes for Control Entries.
+ * These Flags are bit fields.
+ */
+#define QLA82XX_DBG_OPCODE_WR          0x01
+#define QLA82XX_DBG_OPCODE_RW          0x02
+#define QLA82XX_DBG_OPCODE_AND         0x04
+#define QLA82XX_DBG_OPCODE_OR          0x08
+#define QLA82XX_DBG_OPCODE_POLL                0x10
+#define QLA82XX_DBG_OPCODE_RDSTATE     0x20
+#define QLA82XX_DBG_OPCODE_WRSTATE     0x40
+#define QLA82XX_DBG_OPCODE_MDSTATE     0x80
+
+/* Driver Flags */
+#define QLA82XX_DBG_SKIPPED_FLAG       0x80 /* driver skipped this entry  */
+#define QLA82XX_DBG_SIZE_ERR_FLAG      0x40 /* Entry vs Capture size
+                                             * mismatch */
+
+/* Driver_code is for driver to write some info about the entry
+ * currently not used.
+ */
+struct qla82xx_minidump_entry_hdr {
+       uint32_t entry_type;
+       uint32_t entry_size;
+       uint32_t entry_capture_size;
+       struct {
+               uint8_t entry_capture_mask;
+               uint8_t entry_code;
+               uint8_t driver_code;
+               uint8_t driver_flags;
+       } d_ctrl;
+};
+
+/*  Read CRB entry header */
+struct qla82xx_minidump_entry_crb {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t addr;
+       struct {
+               uint8_t addr_stride;
+               uint8_t state_index_a;
+               uint16_t poll_timeout;
+       } crb_strd;
+       uint32_t data_size;
+       uint32_t op_count;
+
+       struct {
+               uint8_t opcode;
+               uint8_t state_index_v;
+               uint8_t shl;
+               uint8_t shr;
+       } crb_ctrl;
+
+       uint32_t value_1;
+       uint32_t value_2;
+       uint32_t value_3;
+};
+
+struct qla82xx_minidump_entry_cache {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t tag_reg_addr;
+       struct {
+               uint16_t tag_value_stride;
+               uint16_t init_tag_value;
+       } addr_ctrl;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t control_addr;
+       struct {
+               uint16_t write_value;
+               uint8_t poll_mask;
+               uint8_t poll_wait;
+       } cache_ctrl;
+       uint32_t read_addr;
+       struct {
+               uint8_t read_addr_stride;
+               uint8_t read_addr_cnt;
+               uint16_t rsvd_1;
+       } read_ctrl;
+};
+
+/* Read OCM */
+struct qla82xx_minidump_entry_rdocm {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd_0;
+       uint32_t rsvd_1;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t rsvd_2;
+       uint32_t rsvd_3;
+       uint32_t read_addr;
+       uint32_t read_addr_stride;
+};
+
+/* Read Memory */
+struct qla82xx_minidump_entry_rdmem {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd[6];
+       uint32_t read_addr;
+       uint32_t read_data_size;
+};
+
+/* Read ROM */
+struct qla82xx_minidump_entry_rdrom {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd[6];
+       uint32_t read_addr;
+       uint32_t read_data_size;
+};
+
+/* Mux entry */
+struct qla82xx_minidump_entry_mux {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t select_addr;
+       uint32_t rsvd_0;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t select_value;
+       uint32_t select_value_stride;
+       uint32_t read_addr;
+       uint32_t rsvd_1;
+};
+
+/* Queue entry */
+struct qla82xx_minidump_entry_queue {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t select_addr;
+       struct {
+               uint16_t queue_id_stride;
+               uint16_t rsvd_0;
+       } q_strd;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t rsvd_1;
+       uint32_t rsvd_2;
+       uint32_t read_addr;
+       struct {
+               uint8_t read_addr_stride;
+               uint8_t read_addr_cnt;
+               uint16_t rsvd_3;
+       } rd_strd;
+};
+
+#define QLA82XX_MINIDUMP_OCM0_SIZE             (256 * 1024)
+#define QLA82XX_MINIDUMP_L1C_SIZE              (256 * 1024)
+#define QLA82XX_MINIDUMP_L2C_SIZE              1572864
+#define QLA82XX_MINIDUMP_COMMON_STR_SIZE       0
+#define QLA82XX_MINIDUMP_FCOE_STR_SIZE         0
+#define QLA82XX_MINIDUMP_MEM_SIZE              0
+#define QLA82XX_MAX_ENTRY_HDR                  4
+
+struct qla82xx_minidump {
+       uint32_t md_ocm0_data[QLA82XX_MINIDUMP_OCM0_SIZE];
+       uint32_t md_l1c_data[QLA82XX_MINIDUMP_L1C_SIZE];
+       uint32_t md_l2c_data[QLA82XX_MINIDUMP_L2C_SIZE];
+       uint32_t md_cs_data[QLA82XX_MINIDUMP_COMMON_STR_SIZE];
+       uint32_t md_fcoes_data[QLA82XX_MINIDUMP_FCOE_STR_SIZE];
+       uint32_t md_mem_data[QLA82XX_MINIDUMP_MEM_SIZE];
+};
+
+#define MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE       0x129
+#define RQST_TMPLT_SIZE                                0x0
+#define RQST_TMPLT                             0x1
+#define MD_DIRECT_ROM_WINDOW                   0x42110030
+#define MD_DIRECT_ROM_READ_BASE                        0x42150000
+#define MD_MIU_TEST_AGT_CTRL                   0x41000090
+#define MD_MIU_TEST_AGT_ADDR_LO                        0x41000094
+#define MD_MIU_TEST_AGT_ADDR_HI                        0x41000098
+
+static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
+                               0x410000AC, 0x410000B8, 0x410000BC };
 #endif
index ee47820..cd15678 100644 (file)
@@ -68,12 +68,34 @@ MODULE_PARM_DESC(ql4xmaxqdepth,
                 " Maximum queue depth to report for target devices.\n"
                 "\t\t  Default: 32.");
 
+static int ql4xqfulltracking = 1;
+module_param(ql4xqfulltracking, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ql4xqfulltracking,
+                " Enable or disable dynamic tracking and adjustment of\n"
+                "\t\t scsi device queue depth.\n"
+                "\t\t  0 - Disable.\n"
+                "\t\t  1 - Enable. (Default)");
+
 static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO;
 module_param(ql4xsess_recovery_tmo, int, S_IRUGO);
 MODULE_PARM_DESC(ql4xsess_recovery_tmo,
                " Target Session Recovery Timeout.\n"
                "\t\t  Default: 120 sec.");
 
+int ql4xmdcapmask = 0x1F;
+module_param(ql4xmdcapmask, int, S_IRUGO);
+MODULE_PARM_DESC(ql4xmdcapmask,
+                " Set the Minidump driver capture mask level.\n"
+                "\t\t  Default is 0x1F.\n"
+                "\t\t  Can be set to 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F");
+
+int ql4xenablemd = 1;
+module_param(ql4xenablemd, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ql4xenablemd,
+                " Set to enable minidump.\n"
+                "\t\t  0 - disable minidump\n"
+                "\t\t  1 - enable minidump (Default)");
+
 static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
 /*
  * SCSI host template entry points
@@ -140,6 +162,8 @@ static int qla4xxx_slave_configure(struct scsi_device *device);
 static void qla4xxx_slave_destroy(struct scsi_device *sdev);
 static umode_t ql4_attr_is_visible(int param_type, int param);
 static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type);
+static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                     int reason);
 
 static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
     QLA82XX_LEGACY_INTR_CONFIG;
@@ -159,6 +183,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
        .slave_configure        = qla4xxx_slave_configure,
        .slave_alloc            = qla4xxx_slave_alloc,
        .slave_destroy          = qla4xxx_slave_destroy,
+       .change_queue_depth     = qla4xxx_change_queue_depth,
 
        .this_id                = -1,
        .cmd_per_lun            = 3,
@@ -1555,19 +1580,53 @@ static void qla4xxx_session_destroy(struct iscsi_cls_session *cls_sess)
        struct iscsi_session *sess;
        struct ddb_entry *ddb_entry;
        struct scsi_qla_host *ha;
-       unsigned long flags;
+       unsigned long flags, wtime;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       uint32_t ddb_state;
+       int ret;
 
        DEBUG2(printk(KERN_INFO "Func: %s\n", __func__));
        sess = cls_sess->dd_data;
        ddb_entry = sess->dd_data;
        ha = ddb_entry->ha;
 
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Unable to allocate dma buffer\n", __func__);
+               goto destroy_session;
+       }
+
+       wtime = jiffies + (HZ * LOGOUT_TOV);
+       do {
+               ret = qla4xxx_get_fwddb_entry(ha, ddb_entry->fw_ddb_index,
+                                             fw_ddb_entry, fw_ddb_entry_dma,
+                                             NULL, NULL, &ddb_state, NULL,
+                                             NULL, NULL);
+               if (ret == QLA_ERROR)
+                       goto destroy_session;
+
+               if ((ddb_state == DDB_DS_NO_CONNECTION_ACTIVE) ||
+                   (ddb_state == DDB_DS_SESSION_FAILED))
+                       goto destroy_session;
+
+               schedule_timeout_uninterruptible(HZ);
+       } while ((time_after(wtime, jiffies)));
+
+destroy_session:
        qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        qla4xxx_free_ddb(ha, ddb_entry);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
        iscsi_session_teardown(cls_sess);
+
+       if (fw_ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                 fw_ddb_entry, fw_ddb_entry_dma);
 }
 
 static struct iscsi_cls_conn *
@@ -2220,6 +2279,9 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
                dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
                                  ha->queues_dma);
 
+        if (ha->fw_dump)
+               vfree(ha->fw_dump);
+
        ha->queues_len = 0;
        ha->queues = NULL;
        ha->queues_dma = 0;
@@ -2229,6 +2291,8 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
        ha->response_dma = 0;
        ha->shadow_regs = NULL;
        ha->shadow_regs_dma = 0;
+       ha->fw_dump = NULL;
+       ha->fw_dump_size = 0;
 
        /* Free srb pool. */
        if (ha->srb_mempool)
@@ -5023,6 +5087,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
 
        set_bit(AF_INIT_DONE, &ha->flags);
 
+       qla4_8xxx_alloc_sysfs_attr(ha);
+
        printk(KERN_INFO
               " QLogic iSCSI HBA Driver version: %s\n"
               "  QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
@@ -5149,6 +5215,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
                iscsi_boot_destroy_kset(ha->boot_kset);
 
        qla4xxx_destroy_fw_ddb_session(ha);
+       qla4_8xxx_free_sysfs_attr(ha);
 
        scsi_remove_host(ha->host);
 
@@ -5217,6 +5284,15 @@ static void qla4xxx_slave_destroy(struct scsi_device *sdev)
        scsi_deactivate_tcq(sdev, 1);
 }
 
+static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                     int reason)
+{
+       if (!ql4xqfulltracking)
+               return -EOPNOTSUPP;
+
+       return iscsi_change_queue_depth(sdev, qdepth, reason);
+}
+
 /**
  * qla4xxx_del_from_active_array - returns an active srb
  * @ha: Pointer to host adapter structure.
index 97b30c1..cc1cc35 100644 (file)
@@ -5,4 +5,4 @@
  * See LICENSE.qla4xxx for copyright and licensing details.
  */
 
-#define QLA4XXX_DRIVER_VERSION "5.02.00-k16"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k17"
index 62ddfd3..6dfb978 100644 (file)
@@ -1378,16 +1378,19 @@ static int scsi_lld_busy(struct request_queue *q)
 {
        struct scsi_device *sdev = q->queuedata;
        struct Scsi_Host *shost;
-       struct scsi_target *starget;
 
        if (!sdev)
                return 0;
 
        shost = sdev->host;
-       starget = scsi_target(sdev);
 
-       if (scsi_host_in_recovery(shost) || scsi_host_is_busy(shost) ||
-           scsi_target_is_busy(starget) || scsi_device_is_busy(sdev))
+       /*
+        * Ignore host/starget busy state.
+        * Since block layer does not have a concept of fairness across
+        * multiple queues, congestion of host/starget needs to be handled
+        * in SCSI layer.
+        */
+       if (scsi_host_in_recovery(shost) || scsi_device_is_busy(sdev))
                return 1;
 
        return 0;
index f661a41..d4201de 100644 (file)
@@ -24,8 +24,11 @@ static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
        err = scsi_device_quiesce(to_scsi_device(dev));
        if (err == 0) {
                drv = dev->driver;
-               if (drv && drv->suspend)
+               if (drv && drv->suspend) {
                        err = drv->suspend(dev, msg);
+                       if (err)
+                               scsi_device_resume(to_scsi_device(dev));
+               }
        }
        dev_dbg(dev, "scsi suspend: %d\n", err);
        return err;
index 01b0374..2e5fe58 100644 (file)
@@ -147,7 +147,7 @@ int scsi_complete_async_scans(void)
 
        do {
                if (list_empty(&scanning_hosts))
-                       return 0;
+                       goto out;
                /* If we can't get memory immediately, that's OK.  Just
                 * sleep a little.  Even if we never get memory, the async
                 * scans will finish eventually.
@@ -179,8 +179,11 @@ int scsi_complete_async_scans(void)
        }
  done:
        spin_unlock(&async_scan_lock);
-
        kfree(data);
+
+ out:
+       async_synchronize_full_domain(&scsi_sd_probe_domain);
+
        return 0;
 }
 
index 74708fc..ae78148 100644 (file)
@@ -12,7 +12,7 @@
 
 #include <linux/module.h>
 #include <linux/device.h>
-#include <scsi/scsi_scan.h>
+#include "scsi_priv.h"
 
 static int __init wait_scan_init(void)
 {
index 4e010b7..6a4fd00 100644 (file)
@@ -1836,7 +1836,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        err = pci_request_regions(pdev, UFSHCD);
        if (err < 0) {
                dev_err(&pdev->dev, "request regions failed\n");
-               goto out_disable;
+               goto out_host_put;
        }
 
        hba->mmio_base = pci_ioremap_bar(pdev, 0);
@@ -1925,8 +1925,9 @@ out_iounmap:
        iounmap(hba->mmio_base);
 out_release_regions:
        pci_release_regions(pdev);
-out_disable:
+out_host_put:
        scsi_host_put(host);
+out_disable:
        pci_clear_master(pdev);
        pci_disable_device(pdev);
 out_error:
index 69adc80..67feac9 100644 (file)
@@ -6,11 +6,17 @@
 
 /* Legacy tty mutex glue */
 
+enum {
+       TTY_MUTEX_NORMAL,
+       TTY_MUTEX_NESTED,
+};
+
 /*
  * Getting the big tty mutex.
  */
 
-void __lockfunc tty_lock(struct tty_struct *tty)
+static void __lockfunc tty_lock_nested(struct tty_struct *tty,
+                                      unsigned int subclass)
 {
        if (tty->magic != TTY_MAGIC) {
                printk(KERN_ERR "L Bad %p\n", tty);
@@ -18,7 +24,12 @@ void __lockfunc tty_lock(struct tty_struct *tty)
                return;
        }
        tty_kref_get(tty);
-       mutex_lock(&tty->legacy_mutex);
+       mutex_lock_nested(&tty->legacy_mutex, subclass);
+}
+
+void __lockfunc tty_lock(struct tty_struct *tty)
+{
+       return tty_lock_nested(tty, TTY_MUTEX_NORMAL);
 }
 EXPORT_SYMBOL(tty_lock);
 
@@ -43,11 +54,11 @@ void __lockfunc tty_lock_pair(struct tty_struct *tty,
 {
        if (tty < tty2) {
                tty_lock(tty);
-               tty_lock(tty2);
+               tty_lock_nested(tty2, TTY_MUTEX_NESTED);
        } else {
                if (tty2 && tty2 != tty)
                        tty_lock(tty2);
-               tty_lock(tty);
+               tty_lock_nested(tty, TTY_MUTEX_NESTED);
        }
 }
 EXPORT_SYMBOL(tty_lock_pair);
index f4aadd1..0c85fae 100644 (file)
@@ -145,50 +145,6 @@ struct dio {
 
 static struct kmem_cache *dio_cache __read_mostly;
 
-static void __inode_dio_wait(struct inode *inode)
-{
-       wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
-       DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
-
-       do {
-               prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
-               if (atomic_read(&inode->i_dio_count))
-                       schedule();
-       } while (atomic_read(&inode->i_dio_count));
-       finish_wait(wq, &q.wait);
-}
-
-/**
- * inode_dio_wait - wait for outstanding DIO requests to finish
- * @inode: inode to wait for
- *
- * Waits for all pending direct I/O requests to finish so that we can
- * proceed with a truncate or equivalent operation.
- *
- * Must be called under a lock that serializes taking new references
- * to i_dio_count, usually by inode->i_mutex.
- */
-void inode_dio_wait(struct inode *inode)
-{
-       if (atomic_read(&inode->i_dio_count))
-               __inode_dio_wait(inode);
-}
-EXPORT_SYMBOL(inode_dio_wait);
-
-/*
- * inode_dio_done - signal finish of a direct I/O requests
- * @inode: inode the direct I/O happens on
- *
- * This is called once we've finished processing a direct I/O request,
- * and is used to wake up callers waiting for direct I/O to be quiesced.
- */
-void inode_dio_done(struct inode *inode)
-{
-       if (atomic_dec_and_test(&inode->i_dio_count))
-               wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
-}
-EXPORT_SYMBOL(inode_dio_done);
-
 /*
  * How many pages are in the queue?
  */
index 6bc8761..c474c1d 100644 (file)
@@ -1748,3 +1748,50 @@ bool inode_owner_or_capable(const struct inode *inode)
        return false;
 }
 EXPORT_SYMBOL(inode_owner_or_capable);
+
+/*
+ * Direct i/o helper functions
+ */
+static void __inode_dio_wait(struct inode *inode)
+{
+       wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
+       DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
+
+       do {
+               prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
+               if (atomic_read(&inode->i_dio_count))
+                       schedule();
+       } while (atomic_read(&inode->i_dio_count));
+       finish_wait(wq, &q.wait);
+}
+
+/**
+ * inode_dio_wait - wait for outstanding DIO requests to finish
+ * @inode: inode to wait for
+ *
+ * Waits for all pending direct I/O requests to finish so that we can
+ * proceed with a truncate or equivalent operation.
+ *
+ * Must be called under a lock that serializes taking new references
+ * to i_dio_count, usually by inode->i_mutex.
+ */
+void inode_dio_wait(struct inode *inode)
+{
+       if (atomic_read(&inode->i_dio_count))
+               __inode_dio_wait(inode);
+}
+EXPORT_SYMBOL(inode_dio_wait);
+
+/*
+ * inode_dio_done - signal finish of a direct I/O requests
+ * @inode: inode the direct I/O happens on
+ *
+ * This is called once we've finished processing a direct I/O request,
+ * and is used to wake up callers waiting for direct I/O to be quiesced.
+ */
+void inode_dio_done(struct inode *inode)
+{
+       if (atomic_dec_and_test(&inode->i_dio_count))
+               wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
+}
+EXPORT_SYMBOL(inode_dio_done);
index 23d170b..ad2775d 100644 (file)
@@ -454,6 +454,12 @@ out:
        return result;
 }
 
+static void nfs_inode_dio_write_done(struct inode *inode)
+{
+       nfs_zap_mapping(inode, inode->i_mapping);
+       inode_dio_done(inode);
+}
+
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
 static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
 {
@@ -564,7 +570,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)
                        nfs_direct_write_reschedule(dreq);
                        break;
                default:
-                       nfs_zap_mapping(dreq->inode, dreq->inode->i_mapping);
+                       nfs_inode_dio_write_done(dreq->inode);
                        nfs_direct_complete(dreq);
        }
 }
@@ -581,7 +587,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)
 
 static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
 {
-       nfs_zap_mapping(inode, inode->i_mapping);
+       nfs_inode_dio_write_done(inode);
        nfs_direct_complete(dreq);
 }
 #endif
@@ -766,14 +772,16 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
                                               loff_t pos)
 {
        struct nfs_pageio_descriptor desc;
+       struct inode *inode = dreq->inode;
        ssize_t result = 0;
        size_t requested_bytes = 0;
        unsigned long seg;
 
-       nfs_pageio_init_write(&desc, dreq->inode, FLUSH_COND_STABLE,
+       nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE,
                              &nfs_direct_write_completion_ops);
        desc.pg_dreq = dreq;
        get_dreq(dreq);
+       atomic_inc(&inode->i_dio_count);
 
        for (seg = 0; seg < nr_segs; seg++) {
                const struct iovec *vec = &iov[seg];
@@ -793,6 +801,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
         * generic layer handle the completion.
         */
        if (requested_bytes == 0) {
+               inode_dio_done(inode);
                nfs_direct_req_release(dreq);
                return result < 0 ? result : -EIO;
        }
index 2f6f78c..e605d69 100644 (file)
@@ -418,8 +418,10 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                return 0;
 
        /* Write all dirty data */
-       if (S_ISREG(inode->i_mode))
+       if (S_ISREG(inode->i_mode)) {
+               nfs_inode_dio_wait(inode);
                nfs_wb_all(inode);
+       }
 
        fattr = nfs_alloc_fattr();
        if (fattr == NULL)
@@ -503,6 +505,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 
        /* Flush out writes to the server in order to update c/mtime.  */
        if (S_ISREG(inode->i_mode)) {
+               nfs_inode_dio_wait(inode);
                err = filemap_write_and_wait(inode->i_mapping);
                if (err)
                        goto out;
index 1848a72..18f99ef 100644 (file)
@@ -369,6 +369,10 @@ extern int nfs_migrate_page(struct address_space *,
 /* direct.c */
 void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
                              struct nfs_direct_req *dreq);
+static inline void nfs_inode_dio_wait(struct inode *inode)
+{
+       inode_dio_wait(inode);
+}
 
 /* nfs4proc.c */
 extern void __nfs4_read_done_cb(struct nfs_read_data *);
index 53f91b1..2c85a0f 100644 (file)
@@ -8,6 +8,7 @@ header-y += int-ll64.h
 header-y += ioctl.h
 header-y += ioctls.h
 header-y += ipcbuf.h
+header-y += kvm_para.h
 header-y += mman-common.h
 header-y += mman.h
 header-y += msgbuf.h
index 038076b..598a589 100644 (file)
@@ -2453,8 +2453,6 @@ enum {
 };
 
 void dio_end_io(struct bio *bio, int error);
-void inode_dio_wait(struct inode *inode);
-void inode_dio_done(struct inode *inode);
 
 ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        struct block_device *bdev, const struct iovec *iov, loff_t offset,
@@ -2469,12 +2467,11 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
                                    offset, nr_segs, get_block, NULL, NULL,
                                    DIO_LOCKING | DIO_SKIP_HOLES);
 }
-#else
-static inline void inode_dio_wait(struct inode *inode)
-{
-}
 #endif
 
+void inode_dio_wait(struct inode *inode);
+void inode_dio_done(struct inode *inode);
+
 extern const struct file_operations generic_ro_fops;
 
 #define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
index 73c28de..7a11401 100644 (file)
@@ -110,6 +110,9 @@ extern int lockdep_genl_is_held(void);
 #define genl_dereference(p)                                    \
        rcu_dereference_protected(p, lockdep_genl_is_held())
 
+#define MODULE_ALIAS_GENL_FAMILY(family)\
+ MODULE_ALIAS_NET_PF_PROTO_NAME(PF_NETLINK, NETLINK_GENERIC, "-family-" family)
+
 #endif /* __KERNEL__ */
 
 #endif /* __LINUX_GENERIC_NETLINK_H */
index 2d7510f..e9ac2df 100644 (file)
@@ -313,5 +313,8 @@ extern int kernel_sock_shutdown(struct socket *sock,
        MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \
                     "-type-" __stringify(type))
 
+#define MODULE_ALIAS_NET_PF_PROTO_NAME(pf, proto, name) \
+       MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \
+                    name)
 #endif /* __KERNEL__ */
 #endif /* _LINUX_NET_H */
index e7fd468..d94cb14 100644 (file)
@@ -2795,15 +2795,15 @@ do {                                                            \
 #define netif_info(priv, type, dev, fmt, args...)              \
        netif_level(info, priv, type, dev, fmt, ##args)
 
-#if defined(DEBUG)
-#define netif_dbg(priv, type, dev, format, args...)            \
-       netif_printk(priv, type, KERN_DEBUG, dev, format, ##args)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
 #define netif_dbg(priv, type, netdev, format, args...)         \
 do {                                                           \
        if (netif_msg_##type(priv))                             \
                dynamic_netdev_dbg(netdev, format, ##args);     \
 } while (0)
+#elif defined(DEBUG)
+#define netif_dbg(priv, type, dev, format, args...)            \
+       netif_printk(priv, type, KERN_DEBUG, dev, format, ##args)
 #else
 #define netif_dbg(priv, type, dev, format, args...)                    \
 ({                                                                     \
index 4f75e53..241065c 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/power_supply.h>
 
 enum data_source {
+       CM_BATTERY_PRESENT,
+       CM_NO_BATTERY,
        CM_FUEL_GAUGE,
        CM_CHARGER_STAT,
 };
@@ -29,6 +31,16 @@ enum polling_modes {
        CM_POLL_CHARGING_ONLY,
 };
 
+enum cm_event_types {
+       CM_EVENT_UNKNOWN = 0,
+       CM_EVENT_BATT_FULL,
+       CM_EVENT_BATT_IN,
+       CM_EVENT_BATT_OUT,
+       CM_EVENT_EXT_PWR_IN_OUT,
+       CM_EVENT_CHG_START_STOP,
+       CM_EVENT_OTHERS,
+};
+
 /**
  * struct charger_global_desc
  * @rtc_name: the name of RTC used to wake up the system from suspend.
@@ -38,11 +50,18 @@ enum polling_modes {
  *     rtc_only_wakeup() returning false.
  *     If the RTC given to CM is the only wakeup reason,
  *     rtc_only_wakeup should return true.
+ * @assume_timer_stops_in_suspend:
+ *     Assume that the jiffy timer stops in suspend-to-RAM.
+ *     When enabled, CM does not rely on jiffies value in
+ *     suspend_again and assumes that jiffies value does not
+ *     change during suspend.
  */
 struct charger_global_desc {
        char *rtc_name;
 
        bool (*rtc_only_wakeup)(void);
+
+       bool assume_timer_stops_in_suspend;
 };
 
 /**
@@ -50,6 +69,11 @@ struct charger_global_desc {
  * @psy_name: the name of power-supply-class for charger manager
  * @polling_mode:
  *     Determine which polling mode will be used
+ * @fullbatt_vchkdrop_ms:
+ * @fullbatt_vchkdrop_uV:
+ *     Check voltage drop after the battery is fully charged.
+ *     If it has dropped more than fullbatt_vchkdrop_uV after
+ *     fullbatt_vchkdrop_ms, CM will restart charging.
  * @fullbatt_uV: voltage in microvolt
  *     If it is not being charged and VBATT >= fullbatt_uV,
  *     it is assumed to be full.
@@ -76,6 +100,8 @@ struct charger_desc {
        enum polling_modes polling_mode;
        unsigned int polling_interval_ms;
 
+       unsigned int fullbatt_vchkdrop_ms;
+       unsigned int fullbatt_vchkdrop_uV;
        unsigned int fullbatt_uV;
 
        enum data_source battery_present;
@@ -101,6 +127,11 @@ struct charger_desc {
  * @fuel_gauge: power_supply for fuel gauge
  * @charger_stat: array of power_supply for chargers
  * @charger_enabled: the state of charger
+ * @fullbatt_vchk_jiffies_at:
+ *     jiffies at the time full battery check will occur.
+ * @fullbatt_vchk_uV: voltage in microvolt
+ *     criteria for full battery
+ * @fullbatt_vchk_work: work queue for full battery check
  * @emergency_stop:
  *     When setting true, stop charging
  * @last_temp_mC: the measured temperature in milli-Celsius
@@ -121,6 +152,10 @@ struct charger_manager {
 
        bool charger_enabled;
 
+       unsigned long fullbatt_vchk_jiffies_at;
+       unsigned int fullbatt_vchk_uV;
+       struct delayed_work fullbatt_vchk_work;
+
        int emergency_stop;
        int last_temp_mC;
 
@@ -134,14 +169,13 @@ struct charger_manager {
 #ifdef CONFIG_CHARGER_MANAGER
 extern int setup_charger_manager(struct charger_global_desc *gd);
 extern bool cm_suspend_again(void);
+extern void cm_notify_event(struct power_supply *psy,
+                               enum cm_event_types type, char *msg);
 #else
-static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
-{ }
-
-static bool __maybe_unused cm_suspend_again(void)
-{
-       return false;
-}
+static inline int setup_charger_manager(struct charger_global_desc *gd)
+{ return 0; }
+static inline bool cm_suspend_again(void) { return false; }
+static inline void cm_notify_event(struct power_supply *psy,
+                               enum cm_event_types type, char *msg) { }
 #endif
-
 #endif /* _CHARGER_MANAGER_H */
index e01b167..89dd84f 100644 (file)
@@ -116,6 +116,18 @@ enum max17042_register {
        MAX17042_VFSOC          = 0xFF,
 };
 
+/* Registers specific to max17047/50 */
+enum max17047_register {
+       MAX17047_QRTbl00        = 0x12,
+       MAX17047_FullSOCThr     = 0x13,
+       MAX17047_QRTbl10        = 0x22,
+       MAX17047_QRTbl20        = 0x32,
+       MAX17047_V_empty        = 0x3A,
+       MAX17047_QRTbl30        = 0x42,
+};
+
+enum max170xx_chip_type {MAX17042, MAX17047};
+
 /*
  * used for setting a register to a desired value
  * addr : address for a register
@@ -144,6 +156,7 @@ struct max17042_config_data {
        u16     shdntimer;      /* 0x03F */
 
        /* App data */
+       u16     full_soc_thresh;        /* 0x13 */
        u16     design_cap;     /* 0x18 */
        u16     ichgt_term;     /* 0x1E */
 
@@ -162,6 +175,10 @@ struct max17042_config_data {
        u16     lavg_empty;     /* 0x36 */
        u16     dqacc;          /* 0x45 */
        u16     dpacc;          /* 0x46 */
+       u16     qrtbl00;        /* 0x12 */
+       u16     qrtbl10;        /* 0x22 */
+       u16     qrtbl20;        /* 0x32 */
+       u16     qrtbl30;        /* 0x42 */
 
        /* Cell technology from power_supply.h */
        u16     cell_technology;
index c38c13d..3b912be 100644 (file)
@@ -96,6 +96,7 @@ enum power_supply_property {
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
        POWER_SUPPLY_PROP_CURRENT_MAX,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CURRENT_AVG,
@@ -211,7 +212,7 @@ extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 
-#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE)
+#ifdef CONFIG_POWER_SUPPLY
 extern int power_supply_is_system_supplied(void);
 #else
 static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
@@ -261,6 +262,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
        case POWER_SUPPLY_PROP_POWER_NOW:
                return 1;
        default:
index 0e50171..b534a1b 100644 (file)
@@ -1896,8 +1896,6 @@ static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
 {
        int delta = 0;
 
-       if (headroom < NET_SKB_PAD)
-               headroom = NET_SKB_PAD;
        if (headroom > skb_headroom(skb))
                delta = headroom - skb_headroom(skb);
 
index bed833d..8197ead 100644 (file)
@@ -60,6 +60,7 @@ struct dst_entry {
 #define DST_NOCOUNT            0x0020
 #define DST_NOPEER             0x0040
 #define DST_FAKE_RTABLE                0x0080
+#define DST_XFRM_TUNNEL                0x0100
 
        short                   error;
        short                   obsolete;
diff --git a/include/scsi/fcoe_sysfs.h b/include/scsi/fcoe_sysfs.h
new file mode 100644 (file)
index 0000000..604cb9b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011-2012 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef FCOE_SYSFS
+#define FCOE_SYSFS
+
+#include <linux/if_ether.h>
+#include <linux/device.h>
+#include <scsi/fc/fc_fcoe.h>
+
+struct fcoe_ctlr_device;
+struct fcoe_fcf_device;
+
+struct fcoe_sysfs_function_template {
+       void (*get_fcoe_ctlr_link_fail)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_vlink_fail)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_miss_fka)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_mode)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_fcf_selected)(struct fcoe_fcf_device *);
+       void (*get_fcoe_fcf_vlan_id)(struct fcoe_fcf_device *);
+};
+
+#define dev_to_ctlr(d)                                 \
+       container_of((d), struct fcoe_ctlr_device, dev)
+
+enum fip_conn_type {
+       FIP_CONN_TYPE_UNKNOWN,
+       FIP_CONN_TYPE_FABRIC,
+       FIP_CONN_TYPE_VN2VN,
+};
+
+struct fcoe_ctlr_device {
+       u32                             id;
+
+       struct device                   dev;
+       struct fcoe_sysfs_function_template *f;
+
+       struct list_head                fcfs;
+       char                            work_q_name[20];
+       struct workqueue_struct         *work_q;
+       char                            devloss_work_q_name[20];
+       struct workqueue_struct         *devloss_work_q;
+       struct mutex                    lock;
+
+       int                             fcf_dev_loss_tmo;
+       enum fip_conn_type              mode;
+
+       /* expected in host order for displaying */
+       struct fcoe_fc_els_lesb         lesb;
+};
+
+static inline void *fcoe_ctlr_device_priv(const struct fcoe_ctlr_device *ctlr)
+{
+       return (void *)(ctlr + 1);
+}
+
+/* fcf states */
+enum fcf_state {
+       FCOE_FCF_STATE_UNKNOWN,
+       FCOE_FCF_STATE_DISCONNECTED,
+       FCOE_FCF_STATE_CONNECTED,
+       FCOE_FCF_STATE_DELETED,
+};
+
+struct fcoe_fcf_device {
+       u32                 id;
+       struct device       dev;
+       struct list_head    peers;
+       struct work_struct  delete_work;
+       struct delayed_work dev_loss_work;
+       u32                 dev_loss_tmo;
+       void                *priv;
+       enum fcf_state      state;
+
+       u64                 fabric_name;
+       u64                 switch_name;
+       u32                 fc_map;
+       u16                 vfid;
+       u8                  mac[ETH_ALEN];
+       u8                  priority;
+       u32                 fka_period;
+       u8                  selected;
+       u16                 vlan_id;
+};
+
+#define dev_to_fcf(d)                                  \
+       container_of((d), struct fcoe_fcf_device, dev)
+/* parentage should never be missing */
+#define fcoe_fcf_dev_to_ctlr_dev(x)            \
+       dev_to_ctlr((x)->dev.parent)
+#define fcoe_fcf_device_priv(x)                        \
+       ((x)->priv)
+
+struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,
+                           struct fcoe_sysfs_function_template *f,
+                           int priv_size);
+void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *);
+struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *,
+                                           struct fcoe_fcf_device *);
+void fcoe_fcf_device_delete(struct fcoe_fcf_device *);
+
+int __init fcoe_sysfs_setup(void);
+void __exit fcoe_sysfs_teardown(void);
+
+#endif /* FCOE_SYSFS */
index cfdb55f..22b07cc 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/random.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/libfc.h>
+#include <scsi/fcoe_sysfs.h>
 
 #define FCOE_MAX_CMD_LEN       16      /* Supported CDB length */
 
@@ -158,9 +159,25 @@ struct fcoe_ctlr {
        spinlock_t ctlr_lock;
 };
 
+/**
+ * fcoe_ctlr_priv() - Return the private data from a fcoe_ctlr
+ * @cltr: The fcoe_ctlr whose private data will be returned
+ */
+static inline void *fcoe_ctlr_priv(const struct fcoe_ctlr *ctlr)
+{
+       return (void *)(ctlr + 1);
+}
+
+#define fcoe_ctlr_to_ctlr_dev(x)                                       \
+       (struct fcoe_ctlr_device *)(((struct fcoe_ctlr_device *)(x)) - 1)
+
 /**
  * struct fcoe_fcf - Fibre-Channel Forwarder
  * @list:       list linkage
+ * @event_work:  Work for FC Transport actions queue
+ * @event:       The event to be processed
+ * @fip:         The controller that the FCF was discovered on
+ * @fcf_dev:     The associated fcoe_fcf_device instance
  * @time:       system time (jiffies) when an advertisement was last received
  * @switch_name: WWN of switch from advertisement
  * @fabric_name: WWN of fabric from advertisement
@@ -182,6 +199,9 @@ struct fcoe_ctlr {
  */
 struct fcoe_fcf {
        struct list_head list;
+       struct work_struct event_work;
+       struct fcoe_ctlr *fip;
+       struct fcoe_fcf_device *fcf_dev;
        unsigned long time;
 
        u64 switch_name;
@@ -198,6 +218,9 @@ struct fcoe_fcf {
        u8 fd_flags:1;
 };
 
+#define fcoe_fcf_to_fcf_dev(x)                 \
+       ((x)->fcf_dev)
+
 /**
  * struct fcoe_rport - VN2VN remote port
  * @time:      time of create or last beacon packet received from node
@@ -333,6 +356,10 @@ void fcoe_queue_timer(ulong lport);
 int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen,
                           struct fcoe_percpu_s *fps);
 
+/* FCoE Sysfs helpers */
+void fcoe_fcf_get_selected(struct fcoe_fcf_device *);
+void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *);
+
 /**
  * struct netdev_list
  * A mapping from netdevice to fcoe_transport
index 3252e7e..ea5fb9f 100644 (file)
@@ -468,3 +468,4 @@ module_exit(exit_net_drop_monitor);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
+MODULE_ALIAS_GENL_FAMILY("NET_DM");
index 89a47b3..cb982a6 100644 (file)
@@ -459,28 +459,22 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
        struct esp_data *esp = x->data;
        u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
        u32 align = max_t(u32, blksize, esp->padlen);
-       u32 rem;
-
-       mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
-       rem = mtu & (align - 1);
-       mtu &= ~(align - 1);
+       unsigned int net_adj;
 
        switch (x->props.mode) {
-       case XFRM_MODE_TUNNEL:
-               break;
-       default:
        case XFRM_MODE_TRANSPORT:
-               /* The worst case */
-               mtu -= blksize - 4;
-               mtu += min_t(u32, blksize - 4, rem);
-               break;
        case XFRM_MODE_BEET:
-               /* The worst case. */
-               mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
+               net_adj = sizeof(struct iphdr);
                break;
+       case XFRM_MODE_TUNNEL:
+               net_adj = 0;
+               break;
+       default:
+               BUG();
        }
 
-       return mtu - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
+                net_adj) & ~(align - 1)) + (net_adj - 2);
 }
 
 static void esp4_err(struct sk_buff *skb, u32 info)
index 1e62b75..db1521f 100644 (file)
@@ -413,19 +413,15 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
        struct esp_data *esp = x->data;
        u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
        u32 align = max_t(u32, blksize, esp->padlen);
-       u32 rem;
+       unsigned int net_adj;
 
-       mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
-       rem = mtu & (align - 1);
-       mtu &= ~(align - 1);
-
-       if (x->props.mode != XFRM_MODE_TUNNEL) {
-               u32 padsize = ((blksize - 1) & 7) + 1;
-               mtu -= blksize - padsize;
-               mtu += min_t(u32, blksize - padsize, rem);
-       }
+       if (x->props.mode != XFRM_MODE_TUNNEL)
+               net_adj = sizeof(struct ipv6hdr);
+       else
+               net_adj = 0;
 
-       return mtu - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
+                net_adj) & ~(align - 1)) + (net_adj - 2);
 }
 
 static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
index d99fdc6..17b8c67 100644 (file)
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
        return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
 }
 
+static void ip6_append_data_mtu(int *mtu,
+                               int *maxfraglen,
+                               unsigned int fragheaderlen,
+                               struct sk_buff *skb,
+                               struct rt6_info *rt)
+{
+       if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
+               if (skb == NULL) {
+                       /* first fragment, reserve header_len */
+                       *mtu = *mtu - rt->dst.header_len;
+
+               } else {
+                       /*
+                        * this fragment is not first, the headers
+                        * space is regarded as data space.
+                        */
+                       *mtu = dst_mtu(rt->dst.path);
+               }
+               *maxfraglen = ((*mtu - fragheaderlen) & ~7)
+                             + fragheaderlen - sizeof(struct frag_hdr);
+       }
+}
+
 int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        int offset, int len, int odd, struct sk_buff *skb),
        void *from, int length, int transhdrlen,
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct inet_cork *cork;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *skb_prev = NULL;
        unsigned int maxfraglen, fragheaderlen;
        int exthdrlen;
        int dst_exthdrlen;
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                inet->cork.fl.u.ip6 = *fl6;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
-               mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
-                     rt->dst.dev->mtu : dst_mtu(&rt->dst);
+               if (rt->dst.flags & DST_XFRM_TUNNEL)
+                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                             rt->dst.dev->mtu : dst_mtu(&rt->dst);
+               else
+                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                             rt->dst.dev->mtu : dst_mtu(rt->dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                                mtu = np->frag_size;
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                        unsigned int fraglen;
                        unsigned int fraggap;
                        unsigned int alloclen;
-                       struct sk_buff *skb_prev;
 alloc_new_skb:
-                       skb_prev = skb;
-
                        /* There's no room in the current skb */
-                       if (skb_prev)
-                               fraggap = skb_prev->len - maxfraglen;
+                       if (skb)
+                               fraggap = skb->len - maxfraglen;
                        else
                                fraggap = 0;
+                       /* update mtu and maxfraglen if necessary */
+                       if (skb == NULL || skb_prev == NULL)
+                               ip6_append_data_mtu(&mtu, &maxfraglen,
+                                                   fragheaderlen, skb, rt);
+
+                       skb_prev = skb;
 
                        /*
                         * If remaining data exceeds the mtu,
                         * we know we need more fragment(s).
                         */
                        datalen = length + fraggap;
-                       if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
-                               datalen = maxfraglen - fragheaderlen;
 
-                       fraglen = datalen + fragheaderlen;
+                       if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
+                               datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
                        if ((flags & MSG_MORE) &&
                            !(rt->dst.dev->features&NETIF_F_SG))
                                alloclen = mtu;
@@ -1377,13 +1406,16 @@ alloc_new_skb:
 
                        alloclen += dst_exthdrlen;
 
-                       /*
-                        * The last fragment gets additional space at tail.
-                        * Note: we overallocate on fragments with MSG_MODE
-                        * because we have no idea if we're the last one.
-                        */
-                       if (datalen == length + fraggap)
-                               alloclen += rt->dst.trailer_len;
+                       if (datalen != length + fraggap) {
+                               /*
+                                * this is not the last fragment, the trailer
+                                * space is regarded as data space.
+                                */
+                               datalen += rt->dst.trailer_len;
+                       }
+
+                       alloclen += rt->dst.trailer_len;
+                       fraglen = datalen + fragheaderlen;
 
                        /*
                         * We just reserve space for fragment header.
index 889f5d1..70614e7 100644 (file)
@@ -239,9 +239,16 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr;
-       int ret = -EINVAL;
+       int ret;
        int chk_addr_ret;
 
+       if (!sock_flag(sk, SOCK_ZAPPED))
+               return -EINVAL;
+       if (addr_len < sizeof(struct sockaddr_l2tpip))
+               return -EINVAL;
+       if (addr->l2tp_family != AF_INET)
+               return -EINVAL;
+
        ret = -EADDRINUSE;
        read_lock_bh(&l2tp_ip_lock);
        if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id))
@@ -272,6 +279,8 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip_lock);
        ret = 0;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+
 out:
        release_sock(sk);
 
@@ -288,6 +297,9 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
        struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
        int rc;
 
+       if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */
+               return -EINVAL;
+
        if (addr_len < sizeof(*lsa))
                return -EINVAL;
 
@@ -311,6 +323,14 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
        return rc;
 }
 
+static int l2tp_ip_disconnect(struct sock *sk, int flags)
+{
+       if (sock_flag(sk, SOCK_ZAPPED))
+               return 0;
+
+       return udp_disconnect(sk, flags);
+}
+
 static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
                           int *uaddr_len, int peer)
 {
@@ -530,7 +550,7 @@ static struct proto l2tp_ip_prot = {
        .close             = l2tp_ip_close,
        .bind              = l2tp_ip_bind,
        .connect           = l2tp_ip_connect,
-       .disconnect        = udp_disconnect,
+       .disconnect        = l2tp_ip_disconnect,
        .ioctl             = udp_ioctl,
        .destroy           = l2tp_ip_destroy_sock,
        .setsockopt        = ip_setsockopt,
index 0291d8d..35e1e4b 100644 (file)
@@ -258,6 +258,10 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        int addr_type;
        int err;
 
+       if (!sock_flag(sk, SOCK_ZAPPED))
+               return -EINVAL;
+       if (addr->l2tp_family != AF_INET6)
+               return -EINVAL;
        if (addr_len < sizeof(*addr))
                return -EINVAL;
 
@@ -331,6 +335,7 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip6_lock);
 
+       sock_reset_flag(sk, SOCK_ZAPPED);
        release_sock(sk);
        return 0;
 
@@ -354,6 +359,9 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
        int     addr_type;
        int rc;
 
+       if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */
+               return -EINVAL;
+
        if (addr_len < sizeof(*lsa))
                return -EINVAL;
 
@@ -383,6 +391,14 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
        return rc;
 }
 
+static int l2tp_ip6_disconnect(struct sock *sk, int flags)
+{
+       if (sock_flag(sk, SOCK_ZAPPED))
+               return 0;
+
+       return udp_disconnect(sk, flags);
+}
+
 static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
                            int *uaddr_len, int peer)
 {
@@ -689,7 +705,7 @@ static struct proto l2tp_ip6_prot = {
        .close             = l2tp_ip6_close,
        .bind              = l2tp_ip6_bind,
        .connect           = l2tp_ip6_connect,
-       .disconnect        = udp_disconnect,
+       .disconnect        = l2tp_ip6_disconnect,
        .ioctl             = udp_ioctl,
        .destroy           = l2tp_ip6_destroy_sock,
        .setsockopt        = ipv6_setsockopt,
index 8577264..ddc553e 100644 (file)
@@ -923,5 +923,4 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 MODULE_DESCRIPTION("L2TP netlink");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.0");
-MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK) "-proto-" \
-            __stringify(NETLINK_GENERIC) "-type-" "l2tp");
+MODULE_ALIAS_GENL_FAMILY("l2tp");
index b3b3c26..04c3063 100644 (file)
@@ -1522,6 +1522,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
         * anymore. The timeout will be reset if the frame is ACKed by
         * the AP.
         */
+       ifmgd->probe_send_count++;
+
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
                ifmgd->nullfunc_failed = false;
                ieee80211_send_nullfunc(sdata->local, sdata, 0);
@@ -1538,7 +1540,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                                         0, (u32) -1, true, false);
        }
 
-       ifmgd->probe_send_count++;
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
        run_again(ifmgd, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
index 5f827a6..847215b 100644 (file)
@@ -153,7 +153,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
        if (ieee80211_is_data_qos(hdr->frame_control) &&
-           *(ieee80211_get_qos_ctl(hdr)) | IEEE80211_QOS_CTL_ACK_POLICY_NOACK)
+           *(ieee80211_get_qos_ctl(hdr)) & IEEE80211_QOS_CTL_ACK_POLICY_NOACK)
                dur = 0;
        else
                /* Time needed to transmit ACK
index 22f2216..a44c680 100644 (file)
@@ -1371,6 +1371,12 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                }
        }
 
+       /* add back keys */
+       list_for_each_entry(sdata, &local->interfaces, list)
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_enable_keys(sdata);
+
+ wake_up:
        /*
         * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
         * sessions can be established after a resume.
@@ -1392,12 +1398,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_unlock(&local->sta_mtx);
        }
 
-       /* add back keys */
-       list_for_each_entry(sdata, &local->interfaces, list)
-               if (ieee80211_sdata_running(sdata))
-                       ieee80211_enable_keys(sdata);
-
- wake_up:
        ieee80211_wake_queues_by_reason(hw,
                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
index 8340ace..2cc7c1e 100644 (file)
@@ -836,7 +836,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
 #ifdef CONFIG_MODULES
                if (res == NULL) {
                        genl_unlock();
-                       request_module("net-pf-%d-proto-%d-type-%s",
+                       request_module("net-pf-%d-proto-%d-family-%s",
                                       PF_NETLINK, NETLINK_GENERIC, name);
                        genl_lock();
                        res = genl_family_find_byname(name);
index edfaaaf..8d2b3d5 100644 (file)
@@ -186,8 +186,7 @@ struct rds_ib_device {
        struct work_struct      free_work;
 };
 
-#define pcidev_to_node(pcidev) pcibus_to_node(pcidev->bus)
-#define ibdev_to_node(ibdev) pcidev_to_node(to_pci_dev(ibdev->dma_device))
+#define ibdev_to_node(ibdev) dev_to_node(ibdev->dma_device)
 #define rdsibdev_to_node(rdsibdev) ibdev_to_node(rdsibdev->dev)
 
 /* bits for i_ack_flags */
index 61ceae0..a157a2e 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 config WAN_ROUTER
-       tristate "WAN router"
+       tristate "WAN router (DEPRECATED)"
        depends on EXPERIMENTAL
        ---help---
          Wide Area Networks (WANs), such as X.25, frame relay and leased
index c53e8f4..ccfbd32 100644 (file)
@@ -1921,6 +1921,9 @@ no_transform:
        }
 ok:
        xfrm_pols_put(pols, drop_pols);
+       if (dst && dst->xfrm &&
+           dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
+               dst->flags |= DST_XFRM_TUNNEL;
        return dst;
 
 nopol: