diff --git a/[refs] b/[refs]
index fc4b0286aa08..b5a31b18e1fa 100644
--- a/[refs]
+++ b/[refs]
@@ -1,2 +1,2 @@
---
-refs/heads/master: 66f3e6afa8e48486c4dd535d616fbfe04569fbd4
+refs/heads/master: 8d735b4148d46446e64d72b22ef0344ee8dc02fa
diff --git a/trunk/Documentation/DocBook/mac80211.tmpl b/trunk/Documentation/DocBook/mac80211.tmpl
index 8af6d9626878..fbeaffc1dcc3 100644
--- a/trunk/Documentation/DocBook/mac80211.tmpl
+++ b/trunk/Documentation/DocBook/mac80211.tmpl
@@ -227,6 +227,12 @@ usage should require reading the full document.
!Pinclude/net/mac80211.h Powersave support
+
+ Beacon filter support
+!Pinclude/net/mac80211.h Beacon filter support
+!Finclude/net/mac80211.h ieee80211_beacon_loss
+
+
Multiple queues and QoS support
TBD
diff --git a/trunk/Documentation/devices.txt b/trunk/Documentation/devices.txt
index 2be08240ee80..62254d4510c6 100644
--- a/trunk/Documentation/devices.txt
+++ b/trunk/Documentation/devices.txt
@@ -3145,6 +3145,12 @@ Your cooperation is appreciated.
1 = /dev/blockrom1 Second ROM card's translation layer interface
...
+260 char OSD (Object-based-device) SCSI Device
+ 0 = /dev/osd0 First OSD Device
+ 1 = /dev/osd1 Second OSD Device
+ ...
+ 255 = /dev/osd255 256th OSD Device
+
**** ADDITIONAL /dev DIRECTORY ENTRIES
This section details additional entries that should or may exist in
diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt
index e47c0ff8ba7a..02ea3773535e 100644
--- a/trunk/Documentation/feature-removal-schedule.txt
+++ b/trunk/Documentation/feature-removal-schedule.txt
@@ -6,20 +6,47 @@ be removed from this file.
---------------------------
-What: old static regulatory information and ieee80211_regdom module parameter
-When: 2.6.29
+What: The ieee80211_regdom module parameter
+When: March 2010 / desktop catchup
+
+Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code,
+ and currently serves as an option for users to define an
+ ISO / IEC 3166 alpha2 code for the country they are currently
+ present in. Although there are userspace API replacements for this
+ through nl80211 distributions haven't yet caught up with implementing
+ decent alternatives through standard GUIs. Although available as an
+ option through iw or wpa_supplicant its just a matter of time before
+ distributions pick up good GUI options for this. The ideal solution
+ would actually consist of intelligent designs which would do this for
+ the user automatically even when travelling through different countries.
+ Until then we leave this module parameter as a compromise.
+
+ When userspace improves with reasonable widely-available alternatives for
+ this we will no longer need this module parameter. This entry hopes that
+ by the super-futuristically looking date of "March 2010" we will have
+ such replacements widely available.
+
+Who: Luis R. Rodriguez
+
+---------------------------
+
+What: CONFIG_WIRELESS_OLD_REGULATORY - old static regulatory information
+When: March 2010 / desktop catchup
+
Why: The old regulatory infrastructure has been replaced with a new one
which does not require statically defined regulatory domains. We do
not want to keep static regulatory domains in the kernel due to the
the dynamic nature of regulatory law and localization. We kept around
the old static definitions for the regulatory domains of:
+
* US
* JP
* EU
+
and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was
- set. We also kept around the ieee80211_regdom module parameter in case
- some applications were relying on it. Changing regulatory domains
- can now be done instead by using nl80211, as is done with iw.
+ set. We will remove this option once the standard Linux desktop catches
+ up with the new userspace APIs we have implemented.
+
Who: Luis R. Rodriguez
---------------------------
diff --git a/trunk/Documentation/scsi/osd.txt b/trunk/Documentation/scsi/osd.txt
new file mode 100644
index 000000000000..da162f7fd5f5
--- /dev/null
+++ b/trunk/Documentation/scsi/osd.txt
@@ -0,0 +1,198 @@
+The OSD Standard
+================
+OSD (Object-Based Storage Device) is a T10 SCSI command set that is designed
+to provide efficient operation of input/output logical units that manage the
+allocation, placement, and accessing of variable-size data-storage containers,
+called objects. Objects are intended to contain operating system and application
+constructs. Each object has associated attributes attached to it, which are
+integral part of the object and provide metadata about the object. The standard
+defines some common obligatory attributes, but user attributes can be added as
+needed.
+
+See: http://www.t10.org/ftp/t10/drafts/osd2/ for the latest draft for OSD 2
+or search the web for "OSD SCSI"
+
+OSD in the Linux Kernel
+=======================
+osd-initiator:
+ The main component of OSD in Kernel is the osd-initiator library. Its main
+user is intended to be the pNFS-over-objects layout driver, which uses objects
+as its back-end data storage. Other clients are the other osd parts listed below.
+
+osd-uld:
+ This is a SCSI ULD that registers for OSD type devices and provides a testing
+platform, both for the in-kernel initiator as well as connected targets. It
+currently has no useful user-mode API, though it could have if need be.
+
+exofs:
+ Is an OSD based Linux file system. It uses the osd-initiator and osd-uld,
+to export a usable file system for users.
+See Documentation/filesystems/exofs.txt for more details
+
+osd target:
+ There are no current plans for an OSD target implementation in kernel. For all
+needs, a user-mode target that is based on the scsi tgt target framework is
+available from Ohio Supercomputer Center (OSC) at:
+http://www.open-osd.org/bin/view/Main/OscOsdProject
+There are several other target implementations. See http://open-osd.org for more
+links.
+
+Files and Folders
+=================
+This is the complete list of files included in this work:
+include/scsi/
+ osd_initiator.h Main API for the initiator library
+ osd_types.h Common OSD types
+ osd_sec.h Security Manager API
+ osd_protocol.h Wire definitions of the OSD standard protocol
+ osd_attributes.h Wire definitions of OSD attributes
+
+drivers/scsi/osd/
+ osd_initiator.c OSD-Initiator library implementation
+ osd_uld.c The OSD scsi ULD
+ osd_ktest.{h,c} In-kernel test suite (called by osd_uld)
+ osd_debug.h Some printk macros
+ Makefile For both in-tree and out-of-tree compilation
+ Kconfig Enables inclusion of the different pieces
+ osd_test.c User-mode application to call the kernel tests
+
+The OSD-Initiator Library
+=========================
+osd_initiator is a low level implementation of an osd initiator encoder.
+But even though, it should be intuitive and easy to use. Perhaps over time an
+higher lever will form that automates some of the more common recipes.
+
+init/fini:
+- osd_dev_init() associates a scsi_device with an osd_dev structure
+ and initializes some global pools. This should be done once per scsi_device
+ (OSD LUN). The osd_dev structure is needed for calling osd_start_request().
+
+- osd_dev_fini() cleans up before a osd_dev/scsi_device destruction.
+
+OSD commands encoding, execution, and decoding of results:
+
+struct osd_request's is used to iteratively encode an OSD command and carry
+its state throughout execution. Each request goes through these stages:
+
+a. osd_start_request() allocates the request.
+
+b. Any of the osd_req_* methods is used to encode a request of the specified
+ type.
+
+c. osd_req_add_{get,set}_attr_* may be called to add get/set attributes to the
+ CDB. "List" or "Page" mode can be used exclusively. The attribute-list API
+ can be called multiple times on the same request. However, only one
+ attribute-page can be read, as mandated by the OSD standard.
+
+d. osd_finalize_request() computes offsets into the data-in and data-out buffers
+ and signs the request using the provided capability key and integrity-
+ check parameters.
+
+e. osd_execute_request() may be called to execute the request via the block
+ layer and wait for its completion. The request can be executed
+ asynchronously by calling the block layer API directly.
+
+f. After execution, osd_req_decode_sense() can be called to decode the request's
+ sense information.
+
+g. osd_req_decode_get_attr() may be called to retrieve osd_add_get_attr_list()
+ values.
+
+h. osd_end_request() must be called to deallocate the request and any resource
+ associated with it. Note that osd_end_request cleans up the request at any
+ stage and it must always be called after a successful osd_start_request().
+
+osd_request's structure:
+
+The OSD standard defines a complex structure of IO segments pointed to by
+members in the CDB. Up to 3 segments can be deployed in the IN-Buffer and up to
+4 in the OUT-Buffer. The ASCII illustration below depicts a secure-read with
+associated get+set of attributes-lists. Other combinations very on the same
+basic theme. From no-segments-used up to all-segments-used.
+
+|________OSD-CDB__________|
+| |
+|read_len (offset=0) -|---------\
+| | |
+|get_attrs_list_length | |
+|get_attrs_list_offset -|----\ |
+| | | |
+|retrieved_attrs_alloc_len| | |
+|retrieved_attrs_offset -|----|----|-\
+| | | | |
+|set_attrs_list_length | | | |
+|set_attrs_list_offset -|-\ | | |
+| | | | | |
+|in_data_integ_offset -|-|--|----|-|-\
+|out_data_integ_offset -|-|--|--\ | | |
+\_________________________/ | | | | | |
+ | | | | | |
+|_______OUT-BUFFER________| | | | | | |
+| Set attr list | | | | | |
+| | | | | | |
+|-------------------------| | | | | |
+| Get attr descriptors |<---/ | | | |
+| | | | | |
+|-------------------------| | | | |
+| Out-data integrity |<------/ | | |
+| | | | |
+\_________________________/ | | |
+ | | |
+|________IN-BUFFER________| | | |
+| In-Data read |<--------/ | |
+| | | |
+|-------------------------| | |
+| Get attr list |<----------/ |
+| | |
+|-------------------------| |
+| In-data integrity |<------------/
+| |
+\_________________________/
+
+A block device request can carry bidirectional payload by means of associating
+a bidi_read request with a main write-request. Each in/out request is described
+by a chain of BIOs associated with each request.
+The CDB is of a SCSI VARLEN CDB format, as described by OSD standard.
+The OSD standard also mandates alignment restrictions at start of each segment.
+
+In the code, in struct osd_request, there are two _osd_io_info structures to
+describe the IN/OUT buffers above, two BIOs for the data payload and up to five
+_osd_req_data_segment structures to hold the different segments allocation and
+information.
+
+Important: We have chosen to disregard the assumption that a BIO-chain (and
+the resulting sg-list) describes a linear memory buffer. Meaning only first and
+last scatter chain can be incomplete and all the middle chains are of PAGE_SIZE.
+For us, a scatter-gather-list, as its name implies and as used by the Networking
+layer, is to describe a vector of buffers that will be transferred to/from the
+wire. It works very well with current iSCSI transport. iSCSI is currently the
+only deployed OSD transport. In the future we anticipate SAS and FC attached OSD
+devices as well.
+
+The OSD Testing ULD
+===================
+TODO: More user-mode control on tests.
+
+Authors, Mailing list
+=====================
+Please communicate with us on any deployment of osd, whether using this code
+or not.
+
+Any problems, questions, bug reports, lonely OSD nights, please email:
+ OSD Dev List
+
+More up-to-date information can be found on:
+http://open-osd.org
+
+Boaz Harrosh
+Benny Halevy
+
+References
+==========
+Weber, R., "SCSI Object-Based Storage Device Commands",
+T10/1355-D ANSI/INCITS 400-2004,
+http://www.t10.org/ftp/t10/drafts/osd/osd-r10.pdf
+
+Weber, R., "SCSI Object-Based Storage Device Commands -2 (OSD-2)"
+T10/1729-D, Working Draft, rev. 3
+http://www.t10.org/ftp/t10/drafts/osd2/osd2r03.pdf
diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS
index fa7be04b0cf0..d8a4c8d0a554 100644
--- a/trunk/MAINTAINERS
+++ b/trunk/MAINTAINERS
@@ -765,6 +765,14 @@ L: linux-wireless@vger.kernel.org
L: ath9k-devel@lists.ath9k.org
S: Supported
+ATHEROS AR9170 WIRELESS DRIVER
+P: Christian Lamparter
+M: chunkeey@web.de
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/ar9170
+S: Maintained
+F: drivers/net/wireless/ar9170/
+
ATI_REMOTE2 DRIVER
P: Ville Syrjala
M: syrjala@sci.fi
@@ -3302,6 +3310,16 @@ L: orinoco-devel@lists.sourceforge.net
W: http://www.nongnu.org/orinoco/
S: Maintained
+OSD LIBRARY
+P: Boaz Harrosh
+M: bharrosh@panasas.com
+P: Benny Halevy
+M: bhalevy@panasas.com
+L: osd-dev@open-osd.org
+W: http://open-osd.org
+T: git://git.open-osd.org/open-osd.git
+S: Maintained
+
P54 WIRELESS DRIVER
P: Michael Wu
M: flamingice@sourmilk.net
@@ -3602,7 +3620,7 @@ S: Maintained
RALINK RT2X00 WIRELESS LAN DRIVER
P: rt2x00 project
L: linux-wireless@vger.kernel.org
-L: rt2400-devel@lists.sourceforge.net
+L: users@rt2x00.serialmonkey.com
W: http://rt2x00.serialmonkey.com/
S: Maintained
T: git kernel.org:/pub/scm/linux/kernel/git/ivd/rt2x00.git
diff --git a/trunk/arch/alpha/kernel/entry.S b/trunk/arch/alpha/kernel/entry.S
index e4a54b615894..b45d913a51c3 100644
--- a/trunk/arch/alpha/kernel/entry.S
+++ b/trunk/arch/alpha/kernel/entry.S
@@ -903,8 +903,9 @@ sys_alpha_pipe:
stq $26, 0($sp)
.prologue 0
+ mov $31, $17
lda $16, 8($sp)
- jsr $26, do_pipe
+ jsr $26, do_pipe_flags
ldq $26, 0($sp)
bne $0, 1f
diff --git a/trunk/arch/alpha/kernel/osf_sys.c b/trunk/arch/alpha/kernel/osf_sys.c
index ae41f097864b..42ee05981e71 100644
--- a/trunk/arch/alpha/kernel/osf_sys.c
+++ b/trunk/arch/alpha/kernel/osf_sys.c
@@ -46,8 +46,6 @@
#include
#include
-extern int do_pipe(int *);
-
/*
* Brk needs to return an error. Still support Linux's brk(0) query idiom,
* which OSF programs just shouldn't be doing. We're still not quite
diff --git a/trunk/arch/ia64/ia32/ia32_entry.S b/trunk/arch/ia64/ia32/ia32_entry.S
index a46f8395e9a5..af9405cd70e5 100644
--- a/trunk/arch/ia64/ia32/ia32_entry.S
+++ b/trunk/arch/ia64/ia32/ia32_entry.S
@@ -240,7 +240,7 @@ ia32_syscall_table:
data8 sys_ni_syscall
data8 sys_umask /* 60 */
data8 sys_chroot
- data8 sys_ustat
+ data8 compat_sys_ustat
data8 sys_dup2
data8 sys_getppid
data8 sys_getpgrp /* 65 */
diff --git a/trunk/arch/ia64/kernel/perfmon.c b/trunk/arch/ia64/kernel/perfmon.c
index 0e499757309b..5c0f408cfd71 100644
--- a/trunk/arch/ia64/kernel/perfmon.c
+++ b/trunk/arch/ia64/kernel/perfmon.c
@@ -2196,7 +2196,7 @@ pfmfs_delete_dentry(struct dentry *dentry)
return 1;
}
-static struct dentry_operations pfmfs_dentry_operations = {
+static const struct dentry_operations pfmfs_dentry_operations = {
.d_delete = pfmfs_delete_dentry,
};
diff --git a/trunk/arch/mips/kernel/linux32.c b/trunk/arch/mips/kernel/linux32.c
index 49aac6e17df9..2a472713de8e 100644
--- a/trunk/arch/mips/kernel/linux32.c
+++ b/trunk/arch/mips/kernel/linux32.c
@@ -355,40 +355,6 @@ SYSCALL_DEFINE1(32_personality, unsigned long, personality)
return ret;
}
-/* ustat compatibility */
-struct ustat32 {
- compat_daddr_t f_tfree;
- compat_ino_t f_tinode;
- char f_fname[6];
- char f_fpack[6];
-};
-
-extern asmlinkage long sys_ustat(dev_t dev, struct ustat __user * ubuf);
-
-SYSCALL_DEFINE2(32_ustat, dev_t, dev, struct ustat32 __user *, ubuf32)
-{
- int err;
- struct ustat tmp;
- struct ustat32 tmp32;
- mm_segment_t old_fs = get_fs();
-
- set_fs(KERNEL_DS);
- err = sys_ustat(dev, (struct ustat __user *)&tmp);
- set_fs(old_fs);
-
- if (err)
- goto out;
-
- memset(&tmp32, 0, sizeof(struct ustat32));
- tmp32.f_tfree = tmp.f_tfree;
- tmp32.f_tinode = tmp.f_tinode;
-
- err = copy_to_user(ubuf32, &tmp32, sizeof(struct ustat32)) ? -EFAULT : 0;
-
-out:
- return err;
-}
-
SYSCALL_DEFINE4(32_sendfile, long, out_fd, long, in_fd,
compat_off_t __user *, offset, s32, count)
{
diff --git a/trunk/arch/mips/kernel/scall64-n32.S b/trunk/arch/mips/kernel/scall64-n32.S
index 7438e92f8a01..f61d6b0e5731 100644
--- a/trunk/arch/mips/kernel/scall64-n32.S
+++ b/trunk/arch/mips/kernel/scall64-n32.S
@@ -253,7 +253,7 @@ EXPORT(sysn32_call_table)
PTR compat_sys_utime /* 6130 */
PTR sys_mknod
PTR sys_32_personality
- PTR sys_32_ustat
+ PTR compat_sys_ustat
PTR compat_sys_statfs
PTR compat_sys_fstatfs /* 6135 */
PTR sys_sysfs
diff --git a/trunk/arch/mips/kernel/scall64-o32.S b/trunk/arch/mips/kernel/scall64-o32.S
index b0fef4ff9827..60997f1f69d4 100644
--- a/trunk/arch/mips/kernel/scall64-o32.S
+++ b/trunk/arch/mips/kernel/scall64-o32.S
@@ -265,7 +265,7 @@ sys_call_table:
PTR sys_olduname
PTR sys_umask /* 4060 */
PTR sys_chroot
- PTR sys_32_ustat
+ PTR compat_sys_ustat
PTR sys_dup2
PTR sys_getppid
PTR sys_getpgrp /* 4065 */
diff --git a/trunk/arch/parisc/kernel/syscall_table.S b/trunk/arch/parisc/kernel/syscall_table.S
index 303d2b647e41..03b9a01bc16c 100644
--- a/trunk/arch/parisc/kernel/syscall_table.S
+++ b/trunk/arch/parisc/kernel/syscall_table.S
@@ -130,7 +130,7 @@
ENTRY_OURS(newuname)
ENTRY_SAME(umask) /* 60 */
ENTRY_SAME(chroot)
- ENTRY_SAME(ustat)
+ ENTRY_COMP(ustat)
ENTRY_SAME(dup2)
ENTRY_SAME(getppid)
ENTRY_SAME(getpgrp) /* 65 */
diff --git a/trunk/arch/powerpc/include/asm/systbl.h b/trunk/arch/powerpc/include/asm/systbl.h
index 72353f6070a4..fe166491e9dc 100644
--- a/trunk/arch/powerpc/include/asm/systbl.h
+++ b/trunk/arch/powerpc/include/asm/systbl.h
@@ -65,7 +65,7 @@ SYSCALL(ni_syscall)
SYSX(sys_ni_syscall,sys_olduname, sys_olduname)
COMPAT_SYS_SPU(umask)
SYSCALL_SPU(chroot)
-SYSCALL(ustat)
+COMPAT_SYS(ustat)
SYSCALL_SPU(dup2)
SYSCALL_SPU(getppid)
SYSCALL_SPU(getpgrp)
diff --git a/trunk/arch/s390/kernel/compat_wrapper.S b/trunk/arch/s390/kernel/compat_wrapper.S
index 62c706eb0de6..87cf5a79a351 100644
--- a/trunk/arch/s390/kernel/compat_wrapper.S
+++ b/trunk/arch/s390/kernel/compat_wrapper.S
@@ -252,7 +252,7 @@ sys32_chroot_wrapper:
sys32_ustat_wrapper:
llgfr %r2,%r2 # dev_t
llgtr %r3,%r3 # struct ustat *
- jg sys_ustat
+ jg compat_sys_ustat
.globl sys32_dup2_wrapper
sys32_dup2_wrapper:
diff --git a/trunk/arch/sparc/kernel/smp_64.c b/trunk/arch/sparc/kernel/smp_64.c
index 6cd1a5b65067..79457f682b5a 100644
--- a/trunk/arch/sparc/kernel/smp_64.c
+++ b/trunk/arch/sparc/kernel/smp_64.c
@@ -1031,7 +1031,7 @@ void smp_fetch_global_regs(void)
* If the address space is non-shared (ie. mm->count == 1) we avoid
* cross calls when we want to flush the currently running process's
* tlb state. This is done by clearing all cpu bits except the current
- * processor's in current->active_mm->cpu_vm_mask and performing the
+ * processor's in current->mm->cpu_vm_mask and performing the
* flush locally only. This will force any subsequent cpus which run
* this task to flush the context from the local tlb if the process
* migrates to another cpu (again).
@@ -1074,7 +1074,7 @@ void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long
u32 ctx = CTX_HWBITS(mm->context);
int cpu = get_cpu();
- if (mm == current->active_mm && atomic_read(&mm->mm_users) == 1)
+ if (mm == current->mm && atomic_read(&mm->mm_users) == 1)
mm->cpu_vm_mask = cpumask_of_cpu(cpu);
else
smp_cross_call_masked(&xcall_flush_tlb_pending,
diff --git a/trunk/arch/sparc/kernel/systbls_64.S b/trunk/arch/sparc/kernel/systbls_64.S
index f93c42a2b522..a8000b1cda74 100644
--- a/trunk/arch/sparc/kernel/systbls_64.S
+++ b/trunk/arch/sparc/kernel/systbls_64.S
@@ -51,7 +51,7 @@ sys_call_table32:
/*150*/ .word sys_nis_syscall, sys_inotify_init, sys_inotify_add_watch, sys_poll, sys_getdents64
.word compat_sys_fcntl64, sys_inotify_rm_watch, compat_sys_statfs, compat_sys_fstatfs, sys_oldumount
/*160*/ .word compat_sys_sched_setaffinity, compat_sys_sched_getaffinity, sys32_getdomainname, sys32_setdomainname, sys_nis_syscall
- .word sys_quotactl, sys_set_tid_address, compat_sys_mount, sys_ustat, sys32_setxattr
+ .word sys_quotactl, sys_set_tid_address, compat_sys_mount, compat_sys_ustat, sys32_setxattr
/*170*/ .word sys32_lsetxattr, sys32_fsetxattr, sys_getxattr, sys_lgetxattr, compat_sys_getdents
.word sys_setsid, sys_fchdir, sys32_fgetxattr, sys_listxattr, sys_llistxattr
/*180*/ .word sys32_flistxattr, sys_removexattr, sys_lremovexattr, compat_sys_sigpending, sys_ni_syscall
diff --git a/trunk/arch/sparc/kernel/time_64.c b/trunk/arch/sparc/kernel/time_64.c
index 642562d83ec4..4ee2e48c4b39 100644
--- a/trunk/arch/sparc/kernel/time_64.c
+++ b/trunk/arch/sparc/kernel/time_64.c
@@ -724,12 +724,14 @@ void timer_interrupt(int irq, struct pt_regs *regs)
unsigned long tick_mask = tick_ops->softint_mask;
int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(sparc64_events, cpu);
+ struct irq_desc *desc;
clear_softint(tick_mask);
irq_enter();
- kstat_this_cpu.irqs[0]++;
+ desc = irq_to_desc(0);
+ kstat_incr_irqs_this_cpu(0, desc);
if (unlikely(!evt->event_handler)) {
printk(KERN_WARNING
diff --git a/trunk/arch/um/drivers/net_kern.c b/trunk/arch/um/drivers/net_kern.c
index fde510b664d3..434224e2229f 100644
--- a/trunk/arch/um/drivers/net_kern.c
+++ b/trunk/arch/um/drivers/net_kern.c
@@ -86,7 +86,7 @@ static int uml_net_rx(struct net_device *dev)
drop_skb->dev = dev;
/* Read a packet into drop_skb and don't do anything with it. */
(*lp->read)(lp->fd, drop_skb, lp);
- lp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
return 0;
}
@@ -99,8 +99,8 @@ static int uml_net_rx(struct net_device *dev)
skb_trim(skb, pkt_len);
skb->protocol = (*lp->protocol)(skb);
- lp->stats.rx_bytes += skb->len;
- lp->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
netif_rx(skb);
return pkt_len;
}
@@ -224,8 +224,8 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
len = (*lp->write)(lp->fd, skb, lp);
if (len == skb->len) {
- lp->stats.tx_packets++;
- lp->stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies;
netif_start_queue(dev);
@@ -234,7 +234,7 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
else if (len == 0) {
netif_start_queue(dev);
- lp->stats.tx_dropped++;
+ dev->stats.tx_dropped++;
}
else {
netif_start_queue(dev);
@@ -248,12 +248,6 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static struct net_device_stats *uml_net_get_stats(struct net_device *dev)
-{
- struct uml_net_private *lp = netdev_priv(dev);
- return &lp->stats;
-}
-
static void uml_net_set_multicast_list(struct net_device *dev)
{
return;
@@ -377,6 +371,18 @@ static void net_device_release(struct device *dev)
free_netdev(netdev);
}
+static const struct net_device_ops uml_netdev_ops = {
+ .ndo_open = uml_net_open,
+ .ndo_stop = uml_net_close,
+ .ndo_start_xmit = uml_net_start_xmit,
+ .ndo_set_multicast_list = uml_net_set_multicast_list,
+ .ndo_tx_timeout = uml_net_tx_timeout,
+ .ndo_set_mac_address = uml_net_set_mac,
+ .ndo_change_mtu = uml_net_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/*
* Ensures that platform_driver_register is called only once by
* eth_configure. Will be set in an initcall.
@@ -473,14 +479,7 @@ static void eth_configure(int n, void *init, char *mac,
set_ether_mac(dev, device->mac);
dev->mtu = transport->user->mtu;
- dev->open = uml_net_open;
- dev->hard_start_xmit = uml_net_start_xmit;
- dev->stop = uml_net_close;
- dev->get_stats = uml_net_get_stats;
- dev->set_multicast_list = uml_net_set_multicast_list;
- dev->tx_timeout = uml_net_tx_timeout;
- dev->set_mac_address = uml_net_set_mac;
- dev->change_mtu = uml_net_change_mtu;
+ dev->netdev_ops = ¨_netdev_ops;
dev->ethtool_ops = ¨_net_ethtool_ops;
dev->watchdog_timeo = (HZ >> 1);
dev->irq = UM_ETH_IRQ;
diff --git a/trunk/arch/um/include/shared/net_kern.h b/trunk/arch/um/include/shared/net_kern.h
index d843c7924a7c..5c367f22595b 100644
--- a/trunk/arch/um/include/shared/net_kern.h
+++ b/trunk/arch/um/include/shared/net_kern.h
@@ -26,7 +26,7 @@ struct uml_net_private {
spinlock_t lock;
struct net_device *dev;
struct timer_list tl;
- struct net_device_stats stats;
+
struct work_struct work;
int fd;
unsigned char mac[ETH_ALEN];
diff --git a/trunk/arch/x86/ia32/ia32entry.S b/trunk/arch/x86/ia32/ia32entry.S
index 5a0d76dc56a4..8ef8876666b2 100644
--- a/trunk/arch/x86/ia32/ia32entry.S
+++ b/trunk/arch/x86/ia32/ia32entry.S
@@ -557,7 +557,7 @@ ia32_sys_call_table:
.quad sys32_olduname
.quad sys_umask /* 60 */
.quad sys_chroot
- .quad sys32_ustat
+ .quad compat_sys_ustat
.quad sys_dup2
.quad sys_getppid
.quad sys_getpgrp /* 65 */
diff --git a/trunk/arch/x86/ia32/sys_ia32.c b/trunk/arch/x86/ia32/sys_ia32.c
index 6c0d7f6231af..efac92fd1efb 100644
--- a/trunk/arch/x86/ia32/sys_ia32.c
+++ b/trunk/arch/x86/ia32/sys_ia32.c
@@ -638,28 +638,6 @@ long sys32_uname(struct old_utsname __user *name)
return err ? -EFAULT : 0;
}
-long sys32_ustat(unsigned dev, struct ustat32 __user *u32p)
-{
- struct ustat u;
- mm_segment_t seg;
- int ret;
-
- seg = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_ustat(dev, (struct ustat __user *)&u);
- set_fs(seg);
- if (ret < 0)
- return ret;
-
- if (!access_ok(VERIFY_WRITE, u32p, sizeof(struct ustat32)) ||
- __put_user((__u32) u.f_tfree, &u32p->f_tfree) ||
- __put_user((__u32) u.f_tinode, &u32p->f_tfree) ||
- __copy_to_user(&u32p->f_fname, u.f_fname, sizeof(u.f_fname)) ||
- __copy_to_user(&u32p->f_fpack, u.f_fpack, sizeof(u.f_fpack)))
- ret = -EFAULT;
- return ret;
-}
-
asmlinkage long sys32_execve(char __user *name, compat_uptr_t __user *argv,
compat_uptr_t __user *envp, struct pt_regs *regs)
{
diff --git a/trunk/arch/x86/include/asm/ia32.h b/trunk/arch/x86/include/asm/ia32.h
index 50ca486fd88c..1f7e62517284 100644
--- a/trunk/arch/x86/include/asm/ia32.h
+++ b/trunk/arch/x86/include/asm/ia32.h
@@ -129,13 +129,6 @@ typedef struct compat_siginfo {
} _sifields;
} compat_siginfo_t;
-struct ustat32 {
- __u32 f_tfree;
- compat_ino_t f_tinode;
- char f_fname[6];
- char f_fpack[6];
-};
-
#define IA32_STACK_TOP IA32_PAGE_OFFSET
#ifdef __KERNEL__
diff --git a/trunk/arch/x86/include/asm/sys_ia32.h b/trunk/arch/x86/include/asm/sys_ia32.h
index ffb08be2a530..72a6dcd1299b 100644
--- a/trunk/arch/x86/include/asm/sys_ia32.h
+++ b/trunk/arch/x86/include/asm/sys_ia32.h
@@ -70,8 +70,6 @@ struct old_utsname;
asmlinkage long sys32_olduname(struct oldold_utsname __user *);
long sys32_uname(struct old_utsname __user *);
-long sys32_ustat(unsigned, struct ustat32 __user *);
-
asmlinkage long sys32_execve(char __user *, compat_uptr_t __user *,
compat_uptr_t __user *, struct pt_regs *);
asmlinkage long sys32_clone(unsigned int, unsigned int, struct pt_regs *);
diff --git a/trunk/block/cmd-filter.c b/trunk/block/cmd-filter.c
index 504b275e1b90..572bbc2f900d 100644
--- a/trunk/block/cmd-filter.c
+++ b/trunk/block/cmd-filter.c
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/trunk/drivers/char/agp/intel-agp.c b/trunk/drivers/char/agp/intel-agp.c
index 4373adb2119a..9d9490e22e07 100644
--- a/trunk/drivers/char/agp/intel-agp.c
+++ b/trunk/drivers/char/agp/intel-agp.c
@@ -26,6 +26,10 @@
#define PCI_DEVICE_ID_INTEL_82965GME_IG 0x2A12
#define PCI_DEVICE_ID_INTEL_82945GME_HB 0x27AC
#define PCI_DEVICE_ID_INTEL_82945GME_IG 0x27AE
+#define PCI_DEVICE_ID_INTEL_IGDGM_HB 0xA010
+#define PCI_DEVICE_ID_INTEL_IGDGM_IG 0xA011
+#define PCI_DEVICE_ID_INTEL_IGDG_HB 0xA000
+#define PCI_DEVICE_ID_INTEL_IGDG_IG 0xA001
#define PCI_DEVICE_ID_INTEL_G33_HB 0x29C0
#define PCI_DEVICE_ID_INTEL_G33_IG 0x29C2
#define PCI_DEVICE_ID_INTEL_Q35_HB 0x29B0
@@ -60,7 +64,12 @@
#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB)
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDGM_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDG_HB)
+
+#define IS_IGD (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDGM_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDG_HB)
#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_E_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
@@ -510,7 +519,7 @@ static void intel_i830_init_gtt_entries(void)
size = 512;
}
size += 4; /* add in BIOS popup space */
- } else if (IS_G33) {
+ } else if (IS_G33 && !IS_IGD) {
/* G33's GTT size defined in gmch_ctrl */
switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) {
case G33_PGETBL_SIZE_1M:
@@ -526,7 +535,7 @@ static void intel_i830_init_gtt_entries(void)
size = 512;
}
size += 4;
- } else if (IS_G4X) {
+ } else if (IS_G4X || IS_IGD) {
/* On 4 series hardware, GTT stolen is separate from graphics
* stolen, ignore it in stolen gtt entries counting. However,
* 4KB of the stolen memory doesn't get mapped to the GTT.
@@ -2161,6 +2170,10 @@ static const struct intel_driver_description {
NULL, &intel_g33_driver },
{ PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33",
NULL, &intel_g33_driver },
+ { PCI_DEVICE_ID_INTEL_IGDGM_HB, PCI_DEVICE_ID_INTEL_IGDGM_IG, 0, "IGD",
+ NULL, &intel_g33_driver },
+ { PCI_DEVICE_ID_INTEL_IGDG_HB, PCI_DEVICE_ID_INTEL_IGDG_IG, 0, "IGD",
+ NULL, &intel_g33_driver },
{ PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0,
"Mobile IntelĀ® GM45 Express", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_IGD_E_HB, PCI_DEVICE_ID_INTEL_IGD_E_IG, 0,
@@ -2355,6 +2368,8 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_82945G_HB),
ID(PCI_DEVICE_ID_INTEL_82945GM_HB),
ID(PCI_DEVICE_ID_INTEL_82945GME_HB),
+ ID(PCI_DEVICE_ID_INTEL_IGDGM_HB),
+ ID(PCI_DEVICE_ID_INTEL_IGDG_HB),
ID(PCI_DEVICE_ID_INTEL_82946GZ_HB),
ID(PCI_DEVICE_ID_INTEL_82G35_HB),
ID(PCI_DEVICE_ID_INTEL_82965Q_HB),
diff --git a/trunk/drivers/firewire/fw-card.c b/trunk/drivers/firewire/fw-card.c
index a5dd7a665aa8..8b8c8c22f0fc 100644
--- a/trunk/drivers/firewire/fw-card.c
+++ b/trunk/drivers/firewire/fw-card.c
@@ -63,8 +63,7 @@ static int descriptor_count;
#define BIB_CMC ((1) << 30)
#define BIB_IMC ((1) << 31)
-static u32 *
-generate_config_rom(struct fw_card *card, size_t *config_rom_length)
+static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
{
struct fw_descriptor *desc;
static u32 config_rom[256];
@@ -128,8 +127,7 @@ generate_config_rom(struct fw_card *card, size_t *config_rom_length)
return config_rom;
}
-static void
-update_config_roms(void)
+static void update_config_roms(void)
{
struct fw_card *card;
u32 *config_rom;
@@ -141,8 +139,7 @@ update_config_roms(void)
}
}
-int
-fw_core_add_descriptor(struct fw_descriptor *desc)
+int fw_core_add_descriptor(struct fw_descriptor *desc)
{
size_t i;
@@ -171,8 +168,7 @@ fw_core_add_descriptor(struct fw_descriptor *desc)
return 0;
}
-void
-fw_core_remove_descriptor(struct fw_descriptor *desc)
+void fw_core_remove_descriptor(struct fw_descriptor *desc)
{
mutex_lock(&card_mutex);
@@ -185,12 +181,30 @@ fw_core_remove_descriptor(struct fw_descriptor *desc)
mutex_unlock(&card_mutex);
}
+static int set_broadcast_channel(struct device *dev, void *data)
+{
+ fw_device_set_broadcast_channel(fw_device(dev), (long)data);
+ return 0;
+}
+
+static void allocate_broadcast_channel(struct fw_card *card, int generation)
+{
+ int channel, bandwidth = 0;
+
+ fw_iso_resource_manage(card, generation, 1ULL << 31,
+ &channel, &bandwidth, true);
+ if (channel == 31) {
+ card->broadcast_channel_allocated = true;
+ device_for_each_child(card->device, (void *)(long)generation,
+ set_broadcast_channel);
+ }
+}
+
static const char gap_count_table[] = {
63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
};
-void
-fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
+void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
{
int scheduled;
@@ -200,37 +214,38 @@ fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
fw_card_put(card);
}
-static void
-fw_card_bm_work(struct work_struct *work)
+static void fw_card_bm_work(struct work_struct *work)
{
struct fw_card *card = container_of(work, struct fw_card, work.work);
struct fw_device *root_device;
- struct fw_node *root_node, *local_node;
+ struct fw_node *root_node;
unsigned long flags;
- int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
+ int root_id, new_root_id, irm_id, local_id;
+ int gap_count, generation, grace, rcode;
bool do_reset = false;
bool root_device_is_running;
bool root_device_is_cmc;
__be32 lock_data[2];
spin_lock_irqsave(&card->lock, flags);
- local_node = card->local_node;
- root_node = card->root_node;
- if (local_node == NULL) {
+ if (card->local_node == NULL) {
spin_unlock_irqrestore(&card->lock, flags);
goto out_put_card;
}
- fw_node_get(local_node);
- fw_node_get(root_node);
generation = card->generation;
+ root_node = card->root_node;
+ fw_node_get(root_node);
root_device = root_node->data;
root_device_is_running = root_device &&
atomic_read(&root_device->state) == FW_DEVICE_RUNNING;
root_device_is_cmc = root_device && root_device->cmc;
- root_id = root_node->node_id;
- grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
+ root_id = root_node->node_id;
+ irm_id = card->irm_node->node_id;
+ local_id = card->local_node->node_id;
+
+ grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 8));
if (is_next_generation(generation, card->bm_generation) ||
(card->bm_generation != generation && grace)) {
@@ -246,16 +261,15 @@ fw_card_bm_work(struct work_struct *work)
* next generation.
*/
- irm_id = card->irm_node->node_id;
if (!card->irm_node->link_on) {
- new_root_id = local_node->node_id;
+ new_root_id = local_id;
fw_notify("IRM has link off, making local node (%02x) root.\n",
new_root_id);
goto pick_me;
}
lock_data[0] = cpu_to_be32(0x3f);
- lock_data[1] = cpu_to_be32(local_node->node_id);
+ lock_data[1] = cpu_to_be32(local_id);
spin_unlock_irqrestore(&card->lock, flags);
@@ -269,9 +283,14 @@ fw_card_bm_work(struct work_struct *work)
goto out;
if (rcode == RCODE_COMPLETE &&
- lock_data[0] != cpu_to_be32(0x3f))
- /* Somebody else is BM, let them do the work. */
+ lock_data[0] != cpu_to_be32(0x3f)) {
+
+ /* Somebody else is BM. Only act as IRM. */
+ if (local_id == irm_id)
+ allocate_broadcast_channel(card, generation);
+
goto out;
+ }
spin_lock_irqsave(&card->lock, flags);
@@ -282,19 +301,18 @@ fw_card_bm_work(struct work_struct *work)
* do a bus reset and pick the local node as
* root, and thus, IRM.
*/
- new_root_id = local_node->node_id;
+ new_root_id = local_id;
fw_notify("BM lock failed, making local node (%02x) root.\n",
new_root_id);
goto pick_me;
}
} else if (card->bm_generation != generation) {
/*
- * OK, we weren't BM in the last generation, and it's
- * less than 100ms since last bus reset. Reschedule
- * this task 100ms from now.
+ * We weren't BM in the last generation, and the last
+ * bus reset is less than 125ms ago. Reschedule this job.
*/
spin_unlock_irqrestore(&card->lock, flags);
- fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 10));
+ fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8));
goto out;
}
@@ -310,7 +328,7 @@ fw_card_bm_work(struct work_struct *work)
* Either link_on is false, or we failed to read the
* config rom. In either case, pick another root.
*/
- new_root_id = local_node->node_id;
+ new_root_id = local_id;
} else if (!root_device_is_running) {
/*
* If we haven't probed this device yet, bail out now
@@ -332,7 +350,7 @@ fw_card_bm_work(struct work_struct *work)
* successfully read the config rom, but it's not
* cycle master capable.
*/
- new_root_id = local_node->node_id;
+ new_root_id = local_id;
}
pick_me:
@@ -363,25 +381,28 @@ fw_card_bm_work(struct work_struct *work)
card->index, new_root_id, gap_count);
fw_send_phy_config(card, new_root_id, generation, gap_count);
fw_core_initiate_bus_reset(card, 1);
+ /* Will allocate broadcast channel after the reset. */
+ } else {
+ if (local_id == irm_id)
+ allocate_broadcast_channel(card, generation);
}
+
out:
fw_node_put(root_node);
- fw_node_put(local_node);
out_put_card:
fw_card_put(card);
}
-static void
-flush_timer_callback(unsigned long data)
+static void flush_timer_callback(unsigned long data)
{
struct fw_card *card = (struct fw_card *)data;
fw_flush_transactions(card);
}
-void
-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
- struct device *device)
+void fw_card_initialize(struct fw_card *card,
+ const struct fw_card_driver *driver,
+ struct device *device)
{
static atomic_t index = ATOMIC_INIT(-1);
@@ -406,13 +427,12 @@ fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
}
EXPORT_SYMBOL(fw_card_initialize);
-int
-fw_card_add(struct fw_card *card,
- u32 max_receive, u32 link_speed, u64 guid)
+int fw_card_add(struct fw_card *card,
+ u32 max_receive, u32 link_speed, u64 guid)
{
u32 *config_rom;
size_t length;
- int err;
+ int ret;
card->max_receive = max_receive;
card->link_speed = link_speed;
@@ -423,13 +443,14 @@ fw_card_add(struct fw_card *card,
list_add_tail(&card->link, &card_list);
mutex_unlock(&card_mutex);
- err = card->driver->enable(card, config_rom, length);
- if (err < 0) {
+ ret = card->driver->enable(card, config_rom, length);
+ if (ret < 0) {
mutex_lock(&card_mutex);
list_del(&card->link);
mutex_unlock(&card_mutex);
}
- return err;
+
+ return ret;
}
EXPORT_SYMBOL(fw_card_add);
@@ -442,23 +463,20 @@ EXPORT_SYMBOL(fw_card_add);
* dummy driver just fails all IO.
*/
-static int
-dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
+static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
{
BUG();
return -1;
}
-static int
-dummy_update_phy_reg(struct fw_card *card, int address,
- int clear_bits, int set_bits)
+static int dummy_update_phy_reg(struct fw_card *card, int address,
+ int clear_bits, int set_bits)
{
return -ENODEV;
}
-static int
-dummy_set_config_rom(struct fw_card *card,
- u32 *config_rom, size_t length)
+static int dummy_set_config_rom(struct fw_card *card,
+ u32 *config_rom, size_t length)
{
/*
* We take the card out of card_list before setting the dummy
@@ -468,27 +486,23 @@ dummy_set_config_rom(struct fw_card *card,
return -1;
}
-static void
-dummy_send_request(struct fw_card *card, struct fw_packet *packet)
+static void dummy_send_request(struct fw_card *card, struct fw_packet *packet)
{
packet->callback(packet, card, -ENODEV);
}
-static void
-dummy_send_response(struct fw_card *card, struct fw_packet *packet)
+static void dummy_send_response(struct fw_card *card, struct fw_packet *packet)
{
packet->callback(packet, card, -ENODEV);
}
-static int
-dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
{
return -ENOENT;
}
-static int
-dummy_enable_phys_dma(struct fw_card *card,
- int node_id, int generation)
+static int dummy_enable_phys_dma(struct fw_card *card,
+ int node_id, int generation)
{
return -ENODEV;
}
@@ -503,16 +517,14 @@ static struct fw_card_driver dummy_driver = {
.enable_phys_dma = dummy_enable_phys_dma,
};
-void
-fw_card_release(struct kref *kref)
+void fw_card_release(struct kref *kref)
{
struct fw_card *card = container_of(kref, struct fw_card, kref);
complete(&card->done);
}
-void
-fw_core_remove_card(struct fw_card *card)
+void fw_core_remove_card(struct fw_card *card)
{
card->driver->update_phy_reg(card, 4,
PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
@@ -536,8 +548,7 @@ fw_core_remove_card(struct fw_card *card)
}
EXPORT_SYMBOL(fw_core_remove_card);
-int
-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
+int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
{
int reg = short_reset ? 5 : 1;
int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
diff --git a/trunk/drivers/firewire/fw-cdev.c b/trunk/drivers/firewire/fw-cdev.c
index ed03234cbea8..7eb6594cc3e5 100644
--- a/trunk/drivers/firewire/fw-cdev.c
+++ b/trunk/drivers/firewire/fw-cdev.c
@@ -18,87 +18,162 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include
-#include
-#include
-#include
+#include
+#include
#include
-#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
+#include
#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+
#include
#include
-#include "fw-transaction.h"
-#include "fw-topology.h"
+
#include "fw-device.h"
+#include "fw-topology.h"
+#include "fw-transaction.h"
+
+struct client {
+ u32 version;
+ struct fw_device *device;
+
+ spinlock_t lock;
+ bool in_shutdown;
+ struct idr resource_idr;
+ struct list_head event_list;
+ wait_queue_head_t wait;
+ u64 bus_reset_closure;
+
+ struct fw_iso_context *iso_context;
+ u64 iso_closure;
+ struct fw_iso_buffer buffer;
+ unsigned long vm_start;
-struct client;
-struct client_resource {
struct list_head link;
- void (*release)(struct client *client, struct client_resource *r);
- u32 handle;
+ struct kref kref;
};
+static inline void client_get(struct client *client)
+{
+ kref_get(&client->kref);
+}
+
+static void client_release(struct kref *kref)
+{
+ struct client *client = container_of(kref, struct client, kref);
+
+ fw_device_put(client->device);
+ kfree(client);
+}
+
+static void client_put(struct client *client)
+{
+ kref_put(&client->kref, client_release);
+}
+
+struct client_resource;
+typedef void (*client_resource_release_fn_t)(struct client *,
+ struct client_resource *);
+struct client_resource {
+ client_resource_release_fn_t release;
+ int handle;
+};
+
+struct address_handler_resource {
+ struct client_resource resource;
+ struct fw_address_handler handler;
+ __u64 closure;
+ struct client *client;
+};
+
+struct outbound_transaction_resource {
+ struct client_resource resource;
+ struct fw_transaction transaction;
+};
+
+struct inbound_transaction_resource {
+ struct client_resource resource;
+ struct fw_request *request;
+ void *data;
+ size_t length;
+};
+
+struct descriptor_resource {
+ struct client_resource resource;
+ struct fw_descriptor descriptor;
+ u32 data[0];
+};
+
+struct iso_resource {
+ struct client_resource resource;
+ struct client *client;
+ /* Schedule work and access todo only with client->lock held. */
+ struct delayed_work work;
+ enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
+ ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
+ int generation;
+ u64 channels;
+ s32 bandwidth;
+ struct iso_resource_event *e_alloc, *e_dealloc;
+};
+
+static void schedule_iso_resource(struct iso_resource *);
+static void release_iso_resource(struct client *, struct client_resource *);
+
/*
* dequeue_event() just kfree()'s the event, so the event has to be
- * the first field in the struct.
+ * the first field in a struct XYZ_event.
*/
-
struct event {
struct { void *data; size_t size; } v[2];
struct list_head link;
};
-struct bus_reset {
+struct bus_reset_event {
struct event event;
struct fw_cdev_event_bus_reset reset;
};
-struct response {
+struct outbound_transaction_event {
struct event event;
- struct fw_transaction transaction;
struct client *client;
- struct client_resource resource;
+ struct outbound_transaction_resource r;
struct fw_cdev_event_response response;
};
-struct iso_interrupt {
+struct inbound_transaction_event {
struct event event;
- struct fw_cdev_event_iso_interrupt interrupt;
+ struct fw_cdev_event_request request;
};
-struct client {
- u32 version;
- struct fw_device *device;
- spinlock_t lock;
- u32 resource_handle;
- struct list_head resource_list;
- struct list_head event_list;
- wait_queue_head_t wait;
- u64 bus_reset_closure;
-
- struct fw_iso_context *iso_context;
- u64 iso_closure;
- struct fw_iso_buffer buffer;
- unsigned long vm_start;
+struct iso_interrupt_event {
+ struct event event;
+ struct fw_cdev_event_iso_interrupt interrupt;
+};
- struct list_head link;
+struct iso_resource_event {
+ struct event event;
+ struct fw_cdev_event_iso_resource resource;
};
-static inline void __user *
-u64_to_uptr(__u64 value)
+static inline void __user *u64_to_uptr(__u64 value)
{
return (void __user *)(unsigned long)value;
}
-static inline __u64
-uptr_to_u64(void __user *ptr)
+static inline __u64 uptr_to_u64(void __user *ptr)
{
return (__u64)(unsigned long)ptr;
}
@@ -107,7 +182,6 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
{
struct fw_device *device;
struct client *client;
- unsigned long flags;
device = fw_device_get_by_devt(inode->i_rdev);
if (device == NULL)
@@ -125,16 +199,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
}
client->device = device;
- INIT_LIST_HEAD(&client->event_list);
- INIT_LIST_HEAD(&client->resource_list);
spin_lock_init(&client->lock);
+ idr_init(&client->resource_idr);
+ INIT_LIST_HEAD(&client->event_list);
init_waitqueue_head(&client->wait);
+ kref_init(&client->kref);
file->private_data = client;
- spin_lock_irqsave(&device->card->lock, flags);
+ mutex_lock(&device->client_list_mutex);
list_add_tail(&client->link, &device->client_list);
- spin_unlock_irqrestore(&device->card->lock, flags);
+ mutex_unlock(&device->client_list_mutex);
return 0;
}
@@ -150,68 +225,69 @@ static void queue_event(struct client *client, struct event *event,
event->v[1].size = size1;
spin_lock_irqsave(&client->lock, flags);
- list_add_tail(&event->link, &client->event_list);
+ if (client->in_shutdown)
+ kfree(event);
+ else
+ list_add_tail(&event->link, &client->event_list);
spin_unlock_irqrestore(&client->lock, flags);
wake_up_interruptible(&client->wait);
}
-static int
-dequeue_event(struct client *client, char __user *buffer, size_t count)
+static int dequeue_event(struct client *client,
+ char __user *buffer, size_t count)
{
- unsigned long flags;
struct event *event;
size_t size, total;
- int i, retval;
+ int i, ret;
- retval = wait_event_interruptible(client->wait,
- !list_empty(&client->event_list) ||
- fw_device_is_shutdown(client->device));
- if (retval < 0)
- return retval;
+ ret = wait_event_interruptible(client->wait,
+ !list_empty(&client->event_list) ||
+ fw_device_is_shutdown(client->device));
+ if (ret < 0)
+ return ret;
if (list_empty(&client->event_list) &&
fw_device_is_shutdown(client->device))
return -ENODEV;
- spin_lock_irqsave(&client->lock, flags);
- event = container_of(client->event_list.next, struct event, link);
+ spin_lock_irq(&client->lock);
+ event = list_first_entry(&client->event_list, struct event, link);
list_del(&event->link);
- spin_unlock_irqrestore(&client->lock, flags);
+ spin_unlock_irq(&client->lock);
total = 0;
for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
size = min(event->v[i].size, count - total);
if (copy_to_user(buffer + total, event->v[i].data, size)) {
- retval = -EFAULT;
+ ret = -EFAULT;
goto out;
}
total += size;
}
- retval = total;
+ ret = total;
out:
kfree(event);
- return retval;
+ return ret;
}
-static ssize_t
-fw_device_op_read(struct file *file,
- char __user *buffer, size_t count, loff_t *offset)
+static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *offset)
{
struct client *client = file->private_data;
return dequeue_event(client, buffer, count);
}
-/* caller must hold card->lock so that node pointers can be dereferenced here */
-static void
-fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
- struct client *client)
+static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
+ struct client *client)
{
struct fw_card *card = client->device->card;
+ spin_lock_irq(&card->lock);
+
event->closure = client->bus_reset_closure;
event->type = FW_CDEV_EVENT_BUS_RESET;
event->generation = client->device->generation;
@@ -220,39 +296,49 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
event->bm_node_id = 0; /* FIXME: We don't track the BM. */
event->irm_node_id = card->irm_node->node_id;
event->root_node_id = card->root_node->node_id;
+
+ spin_unlock_irq(&card->lock);
}
-static void
-for_each_client(struct fw_device *device,
- void (*callback)(struct client *client))
+static void for_each_client(struct fw_device *device,
+ void (*callback)(struct client *client))
{
- struct fw_card *card = device->card;
struct client *c;
- unsigned long flags;
-
- spin_lock_irqsave(&card->lock, flags);
+ mutex_lock(&device->client_list_mutex);
list_for_each_entry(c, &device->client_list, link)
callback(c);
+ mutex_unlock(&device->client_list_mutex);
+}
+
+static int schedule_reallocations(int id, void *p, void *data)
+{
+ struct client_resource *r = p;
- spin_unlock_irqrestore(&card->lock, flags);
+ if (r->release == release_iso_resource)
+ schedule_iso_resource(container_of(r,
+ struct iso_resource, resource));
+ return 0;
}
-static void
-queue_bus_reset_event(struct client *client)
+static void queue_bus_reset_event(struct client *client)
{
- struct bus_reset *bus_reset;
+ struct bus_reset_event *e;
- bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC);
- if (bus_reset == NULL) {
+ e = kzalloc(sizeof(*e), GFP_KERNEL);
+ if (e == NULL) {
fw_notify("Out of memory when allocating bus reset event\n");
return;
}
- fill_bus_reset_event(&bus_reset->reset, client);
+ fill_bus_reset_event(&e->reset, client);
+
+ queue_event(client, &e->event,
+ &e->reset, sizeof(e->reset), NULL, 0);
- queue_event(client, &bus_reset->event,
- &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0);
+ spin_lock_irq(&client->lock);
+ idr_for_each(&client->resource_idr, schedule_reallocations, client);
+ spin_unlock_irq(&client->lock);
}
void fw_device_cdev_update(struct fw_device *device)
@@ -274,11 +360,11 @@ static int ioctl_get_info(struct client *client, void *buffer)
{
struct fw_cdev_get_info *get_info = buffer;
struct fw_cdev_event_bus_reset bus_reset;
- struct fw_card *card = client->device->card;
unsigned long ret = 0;
client->version = get_info->version;
get_info->version = FW_CDEV_VERSION;
+ get_info->card = client->device->card->index;
down_read(&fw_device_rwsem);
@@ -300,49 +386,61 @@ static int ioctl_get_info(struct client *client, void *buffer)
client->bus_reset_closure = get_info->bus_reset_closure;
if (get_info->bus_reset != 0) {
void __user *uptr = u64_to_uptr(get_info->bus_reset);
- unsigned long flags;
- spin_lock_irqsave(&card->lock, flags);
fill_bus_reset_event(&bus_reset, client);
- spin_unlock_irqrestore(&card->lock, flags);
-
if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
return -EFAULT;
}
- get_info->card = card->index;
-
return 0;
}
-static void
-add_client_resource(struct client *client, struct client_resource *resource)
+static int add_client_resource(struct client *client,
+ struct client_resource *resource, gfp_t gfp_mask)
{
unsigned long flags;
+ int ret;
+
+ retry:
+ if (idr_pre_get(&client->resource_idr, gfp_mask) == 0)
+ return -ENOMEM;
spin_lock_irqsave(&client->lock, flags);
- list_add_tail(&resource->link, &client->resource_list);
- resource->handle = client->resource_handle++;
+ if (client->in_shutdown)
+ ret = -ECANCELED;
+ else
+ ret = idr_get_new(&client->resource_idr, resource,
+ &resource->handle);
+ if (ret >= 0) {
+ client_get(client);
+ if (resource->release == release_iso_resource)
+ schedule_iso_resource(container_of(resource,
+ struct iso_resource, resource));
+ }
spin_unlock_irqrestore(&client->lock, flags);
+
+ if (ret == -EAGAIN)
+ goto retry;
+
+ return ret < 0 ? ret : 0;
}
-static int
-release_client_resource(struct client *client, u32 handle,
- struct client_resource **resource)
+static int release_client_resource(struct client *client, u32 handle,
+ client_resource_release_fn_t release,
+ struct client_resource **resource)
{
struct client_resource *r;
- unsigned long flags;
- spin_lock_irqsave(&client->lock, flags);
- list_for_each_entry(r, &client->resource_list, link) {
- if (r->handle == handle) {
- list_del(&r->link);
- break;
- }
- }
- spin_unlock_irqrestore(&client->lock, flags);
+ spin_lock_irq(&client->lock);
+ if (client->in_shutdown)
+ r = NULL;
+ else
+ r = idr_find(&client->resource_idr, handle);
+ if (r && r->release == release)
+ idr_remove(&client->resource_idr, handle);
+ spin_unlock_irq(&client->lock);
- if (&r->link == &client->resource_list)
+ if (!(r && r->release == release))
return -EINVAL;
if (resource)
@@ -350,203 +448,239 @@ release_client_resource(struct client *client, u32 handle,
else
r->release(client, r);
+ client_put(client);
+
return 0;
}
-static void
-release_transaction(struct client *client, struct client_resource *resource)
+static void release_transaction(struct client *client,
+ struct client_resource *resource)
{
- struct response *response =
- container_of(resource, struct response, resource);
+ struct outbound_transaction_resource *r = container_of(resource,
+ struct outbound_transaction_resource, resource);
- fw_cancel_transaction(client->device->card, &response->transaction);
+ fw_cancel_transaction(client->device->card, &r->transaction);
}
-static void
-complete_transaction(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
+static void complete_transaction(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
{
- struct response *response = data;
- struct client *client = response->client;
+ struct outbound_transaction_event *e = data;
+ struct fw_cdev_event_response *rsp = &e->response;
+ struct client *client = e->client;
unsigned long flags;
- struct fw_cdev_event_response *r = &response->response;
- if (length < r->length)
- r->length = length;
+ if (length < rsp->length)
+ rsp->length = length;
if (rcode == RCODE_COMPLETE)
- memcpy(r->data, payload, r->length);
+ memcpy(rsp->data, payload, rsp->length);
spin_lock_irqsave(&client->lock, flags);
- list_del(&response->resource.link);
+ /*
+ * 1. If called while in shutdown, the idr tree must be left untouched.
+ * The idr handle will be removed and the client reference will be
+ * dropped later.
+ * 2. If the call chain was release_client_resource ->
+ * release_transaction -> complete_transaction (instead of a normal
+ * conclusion of the transaction), i.e. if this resource was already
+ * unregistered from the idr, the client reference will be dropped
+ * by release_client_resource and we must not drop it here.
+ */
+ if (!client->in_shutdown &&
+ idr_find(&client->resource_idr, e->r.resource.handle)) {
+ idr_remove(&client->resource_idr, e->r.resource.handle);
+ /* Drop the idr's reference */
+ client_put(client);
+ }
spin_unlock_irqrestore(&client->lock, flags);
- r->type = FW_CDEV_EVENT_RESPONSE;
- r->rcode = rcode;
+ rsp->type = FW_CDEV_EVENT_RESPONSE;
+ rsp->rcode = rcode;
/*
- * In the case that sizeof(*r) doesn't align with the position of the
+ * In the case that sizeof(*rsp) doesn't align with the position of the
* data, and the read is short, preserve an extra copy of the data
* to stay compatible with a pre-2.6.27 bug. Since the bug is harmless
* for short reads and some apps depended on it, this is both safe
* and prudent for compatibility.
*/
- if (r->length <= sizeof(*r) - offsetof(typeof(*r), data))
- queue_event(client, &response->event, r, sizeof(*r),
- r->data, r->length);
+ if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
+ queue_event(client, &e->event, rsp, sizeof(*rsp),
+ rsp->data, rsp->length);
else
- queue_event(client, &response->event, r, sizeof(*r) + r->length,
+ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
NULL, 0);
+
+ /* Drop the transaction callback's reference */
+ client_put(client);
}
-static int ioctl_send_request(struct client *client, void *buffer)
+static int init_request(struct client *client,
+ struct fw_cdev_send_request *request,
+ int destination_id, int speed)
{
- struct fw_device *device = client->device;
- struct fw_cdev_send_request *request = buffer;
- struct response *response;
+ struct outbound_transaction_event *e;
+ int ret;
- /* What is the biggest size we'll accept, really? */
- if (request->length > 4096)
- return -EINVAL;
+ if (request->tcode != TCODE_STREAM_DATA &&
+ (request->length > 4096 || request->length > 512 << speed))
+ return -EIO;
- response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL);
- if (response == NULL)
+ e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
+ if (e == NULL)
return -ENOMEM;
- response->client = client;
- response->response.length = request->length;
- response->response.closure = request->closure;
+ e->client = client;
+ e->response.length = request->length;
+ e->response.closure = request->closure;
if (request->data &&
- copy_from_user(response->response.data,
+ copy_from_user(e->response.data,
u64_to_uptr(request->data), request->length)) {
- kfree(response);
- return -EFAULT;
+ ret = -EFAULT;
+ goto failed;
}
- response->resource.release = release_transaction;
- add_client_resource(client, &response->resource);
+ e->r.resource.release = release_transaction;
+ ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
+ if (ret < 0)
+ goto failed;
- fw_send_request(device->card, &response->transaction,
- request->tcode & 0x1f,
- device->node->node_id,
- request->generation,
- device->max_speed,
- request->offset,
- response->response.data, request->length,
- complete_transaction, response);
+ /* Get a reference for the transaction callback */
+ client_get(client);
- if (request->data)
- return sizeof(request) + request->length;
- else
- return sizeof(request);
+ fw_send_request(client->device->card, &e->r.transaction,
+ request->tcode, destination_id, request->generation,
+ speed, request->offset, e->response.data,
+ request->length, complete_transaction, e);
+ return 0;
+
+ failed:
+ kfree(e);
+
+ return ret;
}
-struct address_handler {
- struct fw_address_handler handler;
- __u64 closure;
- struct client *client;
- struct client_resource resource;
-};
+static int ioctl_send_request(struct client *client, void *buffer)
+{
+ struct fw_cdev_send_request *request = buffer;
-struct request {
- struct fw_request *request;
- void *data;
- size_t length;
- struct client_resource resource;
-};
+ switch (request->tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ case TCODE_READ_QUADLET_REQUEST:
+ case TCODE_READ_BLOCK_REQUEST:
+ case TCODE_LOCK_MASK_SWAP:
+ case TCODE_LOCK_COMPARE_SWAP:
+ case TCODE_LOCK_FETCH_ADD:
+ case TCODE_LOCK_LITTLE_ADD:
+ case TCODE_LOCK_BOUNDED_ADD:
+ case TCODE_LOCK_WRAP_ADD:
+ case TCODE_LOCK_VENDOR_DEPENDENT:
+ break;
+ default:
+ return -EINVAL;
+ }
-struct request_event {
- struct event event;
- struct fw_cdev_event_request request;
-};
+ return init_request(client, request, client->device->node_id,
+ client->device->max_speed);
+}
-static void
-release_request(struct client *client, struct client_resource *resource)
+static void release_request(struct client *client,
+ struct client_resource *resource)
{
- struct request *request =
- container_of(resource, struct request, resource);
+ struct inbound_transaction_resource *r = container_of(resource,
+ struct inbound_transaction_resource, resource);
- fw_send_response(client->device->card, request->request,
+ fw_send_response(client->device->card, r->request,
RCODE_CONFLICT_ERROR);
- kfree(request);
+ kfree(r);
}
-static void
-handle_request(struct fw_card *card, struct fw_request *r,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void handle_request(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
- struct address_handler *handler = callback_data;
- struct request *request;
- struct request_event *e;
- struct client *client = handler->client;
+ struct address_handler_resource *handler = callback_data;
+ struct inbound_transaction_resource *r;
+ struct inbound_transaction_event *e;
+ int ret;
- request = kmalloc(sizeof(*request), GFP_ATOMIC);
+ r = kmalloc(sizeof(*r), GFP_ATOMIC);
e = kmalloc(sizeof(*e), GFP_ATOMIC);
- if (request == NULL || e == NULL) {
- kfree(request);
- kfree(e);
- fw_send_response(card, r, RCODE_CONFLICT_ERROR);
- return;
- }
+ if (r == NULL || e == NULL)
+ goto failed;
- request->request = r;
- request->data = payload;
- request->length = length;
+ r->request = request;
+ r->data = payload;
+ r->length = length;
- request->resource.release = release_request;
- add_client_resource(client, &request->resource);
+ r->resource.release = release_request;
+ ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
+ if (ret < 0)
+ goto failed;
e->request.type = FW_CDEV_EVENT_REQUEST;
e->request.tcode = tcode;
e->request.offset = offset;
e->request.length = length;
- e->request.handle = request->resource.handle;
+ e->request.handle = r->resource.handle;
e->request.closure = handler->closure;
- queue_event(client, &e->event,
+ queue_event(handler->client, &e->event,
&e->request, sizeof(e->request), payload, length);
+ return;
+
+ failed:
+ kfree(r);
+ kfree(e);
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
}
-static void
-release_address_handler(struct client *client,
- struct client_resource *resource)
+static void release_address_handler(struct client *client,
+ struct client_resource *resource)
{
- struct address_handler *handler =
- container_of(resource, struct address_handler, resource);
+ struct address_handler_resource *r =
+ container_of(resource, struct address_handler_resource, resource);
- fw_core_remove_address_handler(&handler->handler);
- kfree(handler);
+ fw_core_remove_address_handler(&r->handler);
+ kfree(r);
}
static int ioctl_allocate(struct client *client, void *buffer)
{
struct fw_cdev_allocate *request = buffer;
- struct address_handler *handler;
+ struct address_handler_resource *r;
struct fw_address_region region;
+ int ret;
- handler = kmalloc(sizeof(*handler), GFP_KERNEL);
- if (handler == NULL)
+ r = kmalloc(sizeof(*r), GFP_KERNEL);
+ if (r == NULL)
return -ENOMEM;
region.start = request->offset;
region.end = request->offset + request->length;
- handler->handler.length = request->length;
- handler->handler.address_callback = handle_request;
- handler->handler.callback_data = handler;
- handler->closure = request->closure;
- handler->client = client;
-
- if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) {
- kfree(handler);
- return -EBUSY;
+ r->handler.length = request->length;
+ r->handler.address_callback = handle_request;
+ r->handler.callback_data = r;
+ r->closure = request->closure;
+ r->client = client;
+
+ ret = fw_core_add_address_handler(&r->handler, ®ion);
+ if (ret < 0) {
+ kfree(r);
+ return ret;
}
- handler->resource.release = release_address_handler;
- add_client_resource(client, &handler->resource);
- request->handle = handler->resource.handle;
+ r->resource.release = release_address_handler;
+ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
+ if (ret < 0) {
+ release_address_handler(client, &r->resource);
+ return ret;
+ }
+ request->handle = r->resource.handle;
return 0;
}
@@ -555,18 +689,22 @@ static int ioctl_deallocate(struct client *client, void *buffer)
{
struct fw_cdev_deallocate *request = buffer;
- return release_client_resource(client, request->handle, NULL);
+ return release_client_resource(client, request->handle,
+ release_address_handler, NULL);
}
static int ioctl_send_response(struct client *client, void *buffer)
{
struct fw_cdev_send_response *request = buffer;
struct client_resource *resource;
- struct request *r;
+ struct inbound_transaction_resource *r;
- if (release_client_resource(client, request->handle, &resource) < 0)
+ if (release_client_resource(client, request->handle,
+ release_request, &resource) < 0)
return -EINVAL;
- r = container_of(resource, struct request, resource);
+
+ r = container_of(resource, struct inbound_transaction_resource,
+ resource);
if (request->length < r->length)
r->length = request->length;
if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
@@ -588,85 +726,92 @@ static int ioctl_initiate_bus_reset(struct client *client, void *buffer)
return fw_core_initiate_bus_reset(client->device->card, short_reset);
}
-struct descriptor {
- struct fw_descriptor d;
- struct client_resource resource;
- u32 data[0];
-};
-
static void release_descriptor(struct client *client,
struct client_resource *resource)
{
- struct descriptor *descriptor =
- container_of(resource, struct descriptor, resource);
+ struct descriptor_resource *r =
+ container_of(resource, struct descriptor_resource, resource);
- fw_core_remove_descriptor(&descriptor->d);
- kfree(descriptor);
+ fw_core_remove_descriptor(&r->descriptor);
+ kfree(r);
}
static int ioctl_add_descriptor(struct client *client, void *buffer)
{
struct fw_cdev_add_descriptor *request = buffer;
- struct descriptor *descriptor;
- int retval;
+ struct fw_card *card = client->device->card;
+ struct descriptor_resource *r;
+ int ret;
+
+ /* Access policy: Allow this ioctl only on local nodes' device files. */
+ spin_lock_irq(&card->lock);
+ ret = client->device->node_id != card->local_node->node_id;
+ spin_unlock_irq(&card->lock);
+ if (ret)
+ return -ENOSYS;
if (request->length > 256)
return -EINVAL;
- descriptor =
- kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL);
- if (descriptor == NULL)
+ r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL);
+ if (r == NULL)
return -ENOMEM;
- if (copy_from_user(descriptor->data,
+ if (copy_from_user(r->data,
u64_to_uptr(request->data), request->length * 4)) {
- kfree(descriptor);
- return -EFAULT;
+ ret = -EFAULT;
+ goto failed;
}
- descriptor->d.length = request->length;
- descriptor->d.immediate = request->immediate;
- descriptor->d.key = request->key;
- descriptor->d.data = descriptor->data;
+ r->descriptor.length = request->length;
+ r->descriptor.immediate = request->immediate;
+ r->descriptor.key = request->key;
+ r->descriptor.data = r->data;
- retval = fw_core_add_descriptor(&descriptor->d);
- if (retval < 0) {
- kfree(descriptor);
- return retval;
- }
+ ret = fw_core_add_descriptor(&r->descriptor);
+ if (ret < 0)
+ goto failed;
- descriptor->resource.release = release_descriptor;
- add_client_resource(client, &descriptor->resource);
- request->handle = descriptor->resource.handle;
+ r->resource.release = release_descriptor;
+ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
+ if (ret < 0) {
+ fw_core_remove_descriptor(&r->descriptor);
+ goto failed;
+ }
+ request->handle = r->resource.handle;
return 0;
+ failed:
+ kfree(r);
+
+ return ret;
}
static int ioctl_remove_descriptor(struct client *client, void *buffer)
{
struct fw_cdev_remove_descriptor *request = buffer;
- return release_client_resource(client, request->handle, NULL);
+ return release_client_resource(client, request->handle,
+ release_descriptor, NULL);
}
-static void
-iso_callback(struct fw_iso_context *context, u32 cycle,
- size_t header_length, void *header, void *data)
+static void iso_callback(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
{
struct client *client = data;
- struct iso_interrupt *irq;
+ struct iso_interrupt_event *e;
- irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC);
- if (irq == NULL)
+ e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
+ if (e == NULL)
return;
- irq->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
- irq->interrupt.closure = client->iso_closure;
- irq->interrupt.cycle = cycle;
- irq->interrupt.header_length = header_length;
- memcpy(irq->interrupt.header, header, header_length);
- queue_event(client, &irq->event, &irq->interrupt,
- sizeof(irq->interrupt) + header_length, NULL, 0);
+ e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
+ e->interrupt.closure = client->iso_closure;
+ e->interrupt.cycle = cycle;
+ e->interrupt.header_length = header_length;
+ memcpy(e->interrupt.header, header, header_length);
+ queue_event(client, &e->event, &e->interrupt,
+ sizeof(e->interrupt) + header_length, NULL, 0);
}
static int ioctl_create_iso_context(struct client *client, void *buffer)
@@ -871,6 +1016,261 @@ static int ioctl_get_cycle_timer(struct client *client, void *buffer)
return 0;
}
+static void iso_resource_work(struct work_struct *work)
+{
+ struct iso_resource_event *e;
+ struct iso_resource *r =
+ container_of(work, struct iso_resource, work.work);
+ struct client *client = r->client;
+ int generation, channel, bandwidth, todo;
+ bool skip, free, success;
+
+ spin_lock_irq(&client->lock);
+ generation = client->device->generation;
+ todo = r->todo;
+ /* Allow 1000ms grace period for other reallocations. */
+ if (todo == ISO_RES_ALLOC &&
+ time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
+ if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3)))
+ client_get(client);
+ skip = true;
+ } else {
+ /* We could be called twice within the same generation. */
+ skip = todo == ISO_RES_REALLOC &&
+ r->generation == generation;
+ }
+ free = todo == ISO_RES_DEALLOC ||
+ todo == ISO_RES_ALLOC_ONCE ||
+ todo == ISO_RES_DEALLOC_ONCE;
+ r->generation = generation;
+ spin_unlock_irq(&client->lock);
+
+ if (skip)
+ goto out;
+
+ bandwidth = r->bandwidth;
+
+ fw_iso_resource_manage(client->device->card, generation,
+ r->channels, &channel, &bandwidth,
+ todo == ISO_RES_ALLOC ||
+ todo == ISO_RES_REALLOC ||
+ todo == ISO_RES_ALLOC_ONCE);
+ /*
+ * Is this generation outdated already? As long as this resource sticks
+ * in the idr, it will be scheduled again for a newer generation or at
+ * shutdown.
+ */
+ if (channel == -EAGAIN &&
+ (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
+ goto out;
+
+ success = channel >= 0 || bandwidth > 0;
+
+ spin_lock_irq(&client->lock);
+ /*
+ * Transit from allocation to reallocation, except if the client
+ * requested deallocation in the meantime.
+ */
+ if (r->todo == ISO_RES_ALLOC)
+ r->todo = ISO_RES_REALLOC;
+ /*
+ * Allocation or reallocation failure? Pull this resource out of the
+ * idr and prepare for deletion, unless the client is shutting down.
+ */
+ if (r->todo == ISO_RES_REALLOC && !success &&
+ !client->in_shutdown &&
+ idr_find(&client->resource_idr, r->resource.handle)) {
+ idr_remove(&client->resource_idr, r->resource.handle);
+ client_put(client);
+ free = true;
+ }
+ spin_unlock_irq(&client->lock);
+
+ if (todo == ISO_RES_ALLOC && channel >= 0)
+ r->channels = 1ULL << channel;
+
+ if (todo == ISO_RES_REALLOC && success)
+ goto out;
+
+ if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
+ e = r->e_alloc;
+ r->e_alloc = NULL;
+ } else {
+ e = r->e_dealloc;
+ r->e_dealloc = NULL;
+ }
+ e->resource.handle = r->resource.handle;
+ e->resource.channel = channel;
+ e->resource.bandwidth = bandwidth;
+
+ queue_event(client, &e->event,
+ &e->resource, sizeof(e->resource), NULL, 0);
+
+ if (free) {
+ cancel_delayed_work(&r->work);
+ kfree(r->e_alloc);
+ kfree(r->e_dealloc);
+ kfree(r);
+ }
+ out:
+ client_put(client);
+}
+
+static void schedule_iso_resource(struct iso_resource *r)
+{
+ client_get(r->client);
+ if (!schedule_delayed_work(&r->work, 0))
+ client_put(r->client);
+}
+
+static void release_iso_resource(struct client *client,
+ struct client_resource *resource)
+{
+ struct iso_resource *r =
+ container_of(resource, struct iso_resource, resource);
+
+ spin_lock_irq(&client->lock);
+ r->todo = ISO_RES_DEALLOC;
+ schedule_iso_resource(r);
+ spin_unlock_irq(&client->lock);
+}
+
+static int init_iso_resource(struct client *client,
+ struct fw_cdev_allocate_iso_resource *request, int todo)
+{
+ struct iso_resource_event *e1, *e2;
+ struct iso_resource *r;
+ int ret;
+
+ if ((request->channels == 0 && request->bandwidth == 0) ||
+ request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL ||
+ request->bandwidth < 0)
+ return -EINVAL;
+
+ r = kmalloc(sizeof(*r), GFP_KERNEL);
+ e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
+ e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
+ if (r == NULL || e1 == NULL || e2 == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ INIT_DELAYED_WORK(&r->work, iso_resource_work);
+ r->client = client;
+ r->todo = todo;
+ r->generation = -1;
+ r->channels = request->channels;
+ r->bandwidth = request->bandwidth;
+ r->e_alloc = e1;
+ r->e_dealloc = e2;
+
+ e1->resource.closure = request->closure;
+ e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
+ e2->resource.closure = request->closure;
+ e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
+
+ if (todo == ISO_RES_ALLOC) {
+ r->resource.release = release_iso_resource;
+ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
+ if (ret < 0)
+ goto fail;
+ } else {
+ r->resource.release = NULL;
+ r->resource.handle = -1;
+ schedule_iso_resource(r);
+ }
+ request->handle = r->resource.handle;
+
+ return 0;
+ fail:
+ kfree(r);
+ kfree(e1);
+ kfree(e2);
+
+ return ret;
+}
+
+static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
+{
+ struct fw_cdev_allocate_iso_resource *request = buffer;
+
+ return init_iso_resource(client, request, ISO_RES_ALLOC);
+}
+
+static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
+{
+ struct fw_cdev_deallocate *request = buffer;
+
+ return release_client_resource(client, request->handle,
+ release_iso_resource, NULL);
+}
+
+static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
+{
+ struct fw_cdev_allocate_iso_resource *request = buffer;
+
+ return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
+}
+
+static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
+{
+ struct fw_cdev_allocate_iso_resource *request = buffer;
+
+ return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
+}
+
+/*
+ * Returns a speed code: Maximum speed to or from this device,
+ * limited by the device's link speed, the local node's link speed,
+ * and all PHY port speeds between the two links.
+ */
+static int ioctl_get_speed(struct client *client, void *buffer)
+{
+ return client->device->max_speed;
+}
+
+static int ioctl_send_broadcast_request(struct client *client, void *buffer)
+{
+ struct fw_cdev_send_request *request = buffer;
+
+ switch (request->tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Security policy: Only allow accesses to Units Space. */
+ if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
+ return -EACCES;
+
+ return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
+}
+
+static int ioctl_send_stream_packet(struct client *client, void *buffer)
+{
+ struct fw_cdev_send_stream_packet *p = buffer;
+ struct fw_cdev_send_request request;
+ int dest;
+
+ if (p->speed > client->device->card->link_speed ||
+ p->length > 1024 << p->speed)
+ return -EIO;
+
+ if (p->tag > 3 || p->channel > 63 || p->sy > 15)
+ return -EINVAL;
+
+ dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
+ request.tcode = TCODE_STREAM_DATA;
+ request.length = p->length;
+ request.closure = p->closure;
+ request.data = p->data;
+ request.generation = p->generation;
+
+ return init_request(client, &request, dest, p->speed);
+}
+
static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_get_info,
ioctl_send_request,
@@ -885,13 +1285,20 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_start_iso,
ioctl_stop_iso,
ioctl_get_cycle_timer,
+ ioctl_allocate_iso_resource,
+ ioctl_deallocate_iso_resource,
+ ioctl_allocate_iso_resource_once,
+ ioctl_deallocate_iso_resource_once,
+ ioctl_get_speed,
+ ioctl_send_broadcast_request,
+ ioctl_send_stream_packet,
};
-static int
-dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
+static int dispatch_ioctl(struct client *client,
+ unsigned int cmd, void __user *arg)
{
char buffer[256];
- int retval;
+ int ret;
if (_IOC_TYPE(cmd) != '#' ||
_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
@@ -903,9 +1310,9 @@ dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
return -EFAULT;
}
- retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
- if (retval < 0)
- return retval;
+ ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
+ if (ret < 0)
+ return ret;
if (_IOC_DIR(cmd) & _IOC_READ) {
if (_IOC_SIZE(cmd) > sizeof(buffer) ||
@@ -913,12 +1320,11 @@ dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
return -EFAULT;
}
- return retval;
+ return ret;
}
-static long
-fw_device_op_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static long fw_device_op_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct client *client = file->private_data;
@@ -929,9 +1335,8 @@ fw_device_op_ioctl(struct file *file,
}
#ifdef CONFIG_COMPAT
-static long
-fw_device_op_compat_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static long fw_device_op_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct client *client = file->private_data;
@@ -947,7 +1352,7 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
struct client *client = file->private_data;
enum dma_data_direction direction;
unsigned long size;
- int page_count, retval;
+ int page_count, ret;
if (fw_device_is_shutdown(client->device))
return -ENODEV;
@@ -973,48 +1378,57 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
else
direction = DMA_FROM_DEVICE;
- retval = fw_iso_buffer_init(&client->buffer, client->device->card,
- page_count, direction);
- if (retval < 0)
- return retval;
+ ret = fw_iso_buffer_init(&client->buffer, client->device->card,
+ page_count, direction);
+ if (ret < 0)
+ return ret;
- retval = fw_iso_buffer_map(&client->buffer, vma);
- if (retval < 0)
+ ret = fw_iso_buffer_map(&client->buffer, vma);
+ if (ret < 0)
fw_iso_buffer_destroy(&client->buffer, client->device->card);
- return retval;
+ return ret;
+}
+
+static int shutdown_resource(int id, void *p, void *data)
+{
+ struct client_resource *r = p;
+ struct client *client = data;
+
+ r->release(client, r);
+ client_put(client);
+
+ return 0;
}
static int fw_device_op_release(struct inode *inode, struct file *file)
{
struct client *client = file->private_data;
struct event *e, *next_e;
- struct client_resource *r, *next_r;
- unsigned long flags;
- if (client->buffer.pages)
- fw_iso_buffer_destroy(&client->buffer, client->device->card);
+ mutex_lock(&client->device->client_list_mutex);
+ list_del(&client->link);
+ mutex_unlock(&client->device->client_list_mutex);
if (client->iso_context)
fw_iso_context_destroy(client->iso_context);
- list_for_each_entry_safe(r, next_r, &client->resource_list, link)
- r->release(client, r);
+ if (client->buffer.pages)
+ fw_iso_buffer_destroy(&client->buffer, client->device->card);
- /*
- * FIXME: We should wait for the async tasklets to stop
- * running before freeing the memory.
- */
+ /* Freeze client->resource_idr and client->event_list */
+ spin_lock_irq(&client->lock);
+ client->in_shutdown = true;
+ spin_unlock_irq(&client->lock);
+
+ idr_for_each(&client->resource_idr, shutdown_resource, client);
+ idr_remove_all(&client->resource_idr);
+ idr_destroy(&client->resource_idr);
list_for_each_entry_safe(e, next_e, &client->event_list, link)
kfree(e);
- spin_lock_irqsave(&client->device->card->lock, flags);
- list_del(&client->link);
- spin_unlock_irqrestore(&client->device->card->lock, flags);
-
- fw_device_put(client->device);
- kfree(client);
+ client_put(client);
return 0;
}
diff --git a/trunk/drivers/firewire/fw-device.c b/trunk/drivers/firewire/fw-device.c
index bf53acb45652..a47e2129d83d 100644
--- a/trunk/drivers/firewire/fw-device.c
+++ b/trunk/drivers/firewire/fw-device.c
@@ -18,22 +18,26 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include
-#include
-#include
-#include
-#include
+#include
#include
+#include
+#include
#include
#include
-#include
+#include
+#include
+#include
#include
#include
+#include
+#include
+#include
+
#include
-#include
-#include "fw-transaction.h"
-#include "fw-topology.h"
+
#include "fw-device.h"
+#include "fw-topology.h"
+#include "fw-transaction.h"
void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
{
@@ -132,8 +136,7 @@ static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size)
vendor, model, specifier_id, version);
}
-static int
-fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct fw_unit *unit = fw_unit(dev);
char modalias[64];
@@ -152,27 +155,6 @@ struct bus_type fw_bus_type = {
};
EXPORT_SYMBOL(fw_bus_type);
-static void fw_device_release(struct device *dev)
-{
- struct fw_device *device = fw_device(dev);
- struct fw_card *card = device->card;
- unsigned long flags;
-
- /*
- * Take the card lock so we don't set this to NULL while a
- * FW_NODE_UPDATED callback is being handled or while the
- * bus manager work looks at this node.
- */
- spin_lock_irqsave(&card->lock, flags);
- device->node->data = NULL;
- spin_unlock_irqrestore(&card->lock, flags);
-
- fw_node_put(device->node);
- kfree(device->config_rom);
- kfree(device);
- fw_card_put(card);
-}
-
int fw_device_enable_phys_dma(struct fw_device *device)
{
int generation = device->generation;
@@ -191,8 +173,8 @@ struct config_rom_attribute {
u32 key;
};
-static ssize_t
-show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
+static ssize_t show_immediate(struct device *dev,
+ struct device_attribute *dattr, char *buf)
{
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
@@ -223,8 +205,8 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
#define IMMEDIATE_ATTR(name, key) \
{ __ATTR(name, S_IRUGO, show_immediate, NULL), key }
-static ssize_t
-show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
+static ssize_t show_text_leaf(struct device *dev,
+ struct device_attribute *dattr, char *buf)
{
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
@@ -293,10 +275,9 @@ static struct config_rom_attribute config_rom_attributes[] = {
TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
};
-static void
-init_fw_attribute_group(struct device *dev,
- struct device_attribute *attrs,
- struct fw_attribute_group *group)
+static void init_fw_attribute_group(struct device *dev,
+ struct device_attribute *attrs,
+ struct fw_attribute_group *group)
{
struct device_attribute *attr;
int i, j;
@@ -319,9 +300,8 @@ init_fw_attribute_group(struct device *dev,
dev->groups = group->groups;
}
-static ssize_t
-modalias_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fw_unit *unit = fw_unit(dev);
int length;
@@ -332,9 +312,8 @@ modalias_show(struct device *dev,
return length + 1;
}
-static ssize_t
-rom_index_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t rom_index_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fw_device *device = fw_device(dev->parent);
struct fw_unit *unit = fw_unit(dev);
@@ -349,8 +328,8 @@ static struct device_attribute fw_unit_attributes[] = {
__ATTR_NULL,
};
-static ssize_t
-config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t config_rom_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fw_device *device = fw_device(dev);
size_t length;
@@ -363,8 +342,8 @@ config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
return length;
}
-static ssize_t
-guid_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t guid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fw_device *device = fw_device(dev);
int ret;
@@ -383,8 +362,8 @@ static struct device_attribute fw_device_attributes[] = {
__ATTR_NULL,
};
-static int
-read_rom(struct fw_device *device, int generation, int index, u32 *data)
+static int read_rom(struct fw_device *device,
+ int generation, int index, u32 *data)
{
int rcode;
@@ -539,7 +518,7 @@ static int read_bus_info_block(struct fw_device *device, int generation)
kfree(old_rom);
ret = 0;
- device->cmc = rom[2] & 1 << 30;
+ device->cmc = rom[2] >> 30 & 1;
out:
kfree(rom);
@@ -679,11 +658,53 @@ static void fw_device_shutdown(struct work_struct *work)
fw_device_put(device);
}
+static void fw_device_release(struct device *dev)
+{
+ struct fw_device *device = fw_device(dev);
+ struct fw_card *card = device->card;
+ unsigned long flags;
+
+ /*
+ * Take the card lock so we don't set this to NULL while a
+ * FW_NODE_UPDATED callback is being handled or while the
+ * bus manager work looks at this node.
+ */
+ spin_lock_irqsave(&card->lock, flags);
+ device->node->data = NULL;
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ fw_node_put(device->node);
+ kfree(device->config_rom);
+ kfree(device);
+ fw_card_put(card);
+}
+
static struct device_type fw_device_type = {
- .release = fw_device_release,
+ .release = fw_device_release,
};
-static void fw_device_update(struct work_struct *work);
+static int update_unit(struct device *dev, void *data)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_driver *driver = (struct fw_driver *)dev->driver;
+
+ if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
+ down(&dev->sem);
+ driver->update(unit);
+ up(&dev->sem);
+ }
+
+ return 0;
+}
+
+static void fw_device_update(struct work_struct *work)
+{
+ struct fw_device *device =
+ container_of(work, struct fw_device, work.work);
+
+ fw_device_cdev_update(device);
+ device_for_each_child(&device->device, NULL, update_unit);
+}
/*
* If a device was pending for deletion because its node went away but its
@@ -735,12 +756,50 @@ static int lookup_existing_device(struct device *dev, void *data)
return match;
}
+enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, };
+
+void fw_device_set_broadcast_channel(struct fw_device *device, int generation)
+{
+ struct fw_card *card = device->card;
+ __be32 data;
+ int rcode;
+
+ if (!card->broadcast_channel_allocated)
+ return;
+
+ if (device->bc_implemented == BC_UNKNOWN) {
+ rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST,
+ device->node_id, generation, device->max_speed,
+ CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+ &data, 4);
+ switch (rcode) {
+ case RCODE_COMPLETE:
+ if (data & cpu_to_be32(1 << 31)) {
+ device->bc_implemented = BC_IMPLEMENTED;
+ break;
+ }
+ /* else fall through to case address error */
+ case RCODE_ADDRESS_ERROR:
+ device->bc_implemented = BC_UNIMPLEMENTED;
+ }
+ }
+
+ if (device->bc_implemented == BC_IMPLEMENTED) {
+ data = cpu_to_be32(BROADCAST_CHANNEL_INITIAL |
+ BROADCAST_CHANNEL_VALID);
+ fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST,
+ device->node_id, generation, device->max_speed,
+ CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+ &data, 4);
+ }
+}
+
static void fw_device_init(struct work_struct *work)
{
struct fw_device *device =
container_of(work, struct fw_device, work.work);
struct device *revived_dev;
- int minor, err;
+ int minor, ret;
/*
* All failure paths here set node->data to NULL, so that we
@@ -776,12 +835,12 @@ static void fw_device_init(struct work_struct *work)
fw_device_get(device);
down_write(&fw_device_rwsem);
- err = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
+ ret = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
idr_get_new(&fw_device_idr, device, &minor) :
-ENOMEM;
up_write(&fw_device_rwsem);
- if (err < 0)
+ if (ret < 0)
goto error;
device->device.bus = &fw_bus_type;
@@ -828,6 +887,8 @@ static void fw_device_init(struct work_struct *work)
device->config_rom[3], device->config_rom[4],
1 << device->max_speed);
device->config_rom_retries = 0;
+
+ fw_device_set_broadcast_channel(device, device->generation);
}
/*
@@ -851,29 +912,6 @@ static void fw_device_init(struct work_struct *work)
put_device(&device->device); /* our reference */
}
-static int update_unit(struct device *dev, void *data)
-{
- struct fw_unit *unit = fw_unit(dev);
- struct fw_driver *driver = (struct fw_driver *)dev->driver;
-
- if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
- down(&dev->sem);
- driver->update(unit);
- up(&dev->sem);
- }
-
- return 0;
-}
-
-static void fw_device_update(struct work_struct *work)
-{
- struct fw_device *device =
- container_of(work, struct fw_device, work.work);
-
- fw_device_cdev_update(device);
- device_for_each_child(&device->device, NULL, update_unit);
-}
-
enum {
REREAD_BIB_ERROR,
REREAD_BIB_GONE,
@@ -894,7 +932,7 @@ static int reread_bus_info_block(struct fw_device *device, int generation)
if (i == 0 && q == 0)
return REREAD_BIB_GONE;
- if (i > device->config_rom_length || q != device->config_rom[i])
+ if (q != device->config_rom[i])
return REREAD_BIB_CHANGED;
}
@@ -1004,6 +1042,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
device->node = fw_node_get(node);
device->node_id = node->node_id;
device->generation = card->generation;
+ mutex_init(&device->client_list_mutex);
INIT_LIST_HEAD(&device->client_list);
/*
diff --git a/trunk/drivers/firewire/fw-device.h b/trunk/drivers/firewire/fw-device.h
index 8ef6ec2ca21c..97588937c018 100644
--- a/trunk/drivers/firewire/fw-device.h
+++ b/trunk/drivers/firewire/fw-device.h
@@ -19,10 +19,17 @@
#ifndef __fw_device_h
#define __fw_device_h
+#include
#include
-#include
#include
+#include
+#include
+#include
#include
+#include
+#include
+#include
+
#include
enum fw_device_state {
@@ -38,6 +45,9 @@ struct fw_attribute_group {
struct attribute *attrs[11];
};
+struct fw_node;
+struct fw_card;
+
/*
* Note, fw_device.generation always has to be read before fw_device.node_id.
* Use SMP memory barriers to ensure this. Otherwise requests will be sent
@@ -61,13 +71,18 @@ struct fw_device {
int node_id;
int generation;
unsigned max_speed;
- bool cmc;
struct fw_card *card;
struct device device;
+
+ struct mutex client_list_mutex;
struct list_head client_list;
+
u32 *config_rom;
size_t config_rom_length;
int config_rom_retries;
+ unsigned cmc:1;
+ unsigned bc_implemented:2;
+
struct delayed_work work;
struct fw_attribute_group attribute_group;
};
@@ -96,6 +111,7 @@ static inline void fw_device_put(struct fw_device *device)
struct fw_device *fw_device_get_by_devt(dev_t devt);
int fw_device_enable_phys_dma(struct fw_device *device);
+void fw_device_set_broadcast_channel(struct fw_device *device, int generation);
void fw_device_cdev_update(struct fw_device *device);
void fw_device_cdev_remove(struct fw_device *device);
@@ -176,8 +192,7 @@ struct fw_driver {
const struct fw_device_id *id_table;
};
-static inline struct fw_driver *
-fw_driver(struct device_driver *drv)
+static inline struct fw_driver *fw_driver(struct device_driver *drv)
{
return container_of(drv, struct fw_driver, driver);
}
diff --git a/trunk/drivers/firewire/fw-iso.c b/trunk/drivers/firewire/fw-iso.c
index e14c03dc0065..2baf1007253e 100644
--- a/trunk/drivers/firewire/fw-iso.c
+++ b/trunk/drivers/firewire/fw-iso.c
@@ -1,5 +1,7 @@
/*
- * Isochronous IO functionality
+ * Isochronous I/O functionality:
+ * - Isochronous DMA context management
+ * - Isochronous bus resource management (channels, bandwidth), client side
*
* Copyright (C) 2006 Kristian Hoegsberg
*
@@ -18,21 +20,25 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include
-#include
#include
-#include
+#include
+#include
+#include
#include
+#include
+#include
-#include "fw-transaction.h"
#include "fw-topology.h"
-#include "fw-device.h"
+#include "fw-transaction.h"
-int
-fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
- int page_count, enum dma_data_direction direction)
+/*
+ * Isochronous DMA context management
+ */
+
+int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
+ int page_count, enum dma_data_direction direction)
{
- int i, j, retval = -ENOMEM;
+ int i, j;
dma_addr_t address;
buffer->page_count = page_count;
@@ -69,19 +75,21 @@ fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
kfree(buffer->pages);
out:
buffer->pages = NULL;
- return retval;
+
+ return -ENOMEM;
}
int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma)
{
unsigned long uaddr;
- int i, retval;
+ int i, err;
uaddr = vma->vm_start;
for (i = 0; i < buffer->page_count; i++) {
- retval = vm_insert_page(vma, uaddr, buffer->pages[i]);
- if (retval)
- return retval;
+ err = vm_insert_page(vma, uaddr, buffer->pages[i]);
+ if (err)
+ return err;
+
uaddr += PAGE_SIZE;
}
@@ -105,14 +113,14 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
buffer->pages = NULL;
}
-struct fw_iso_context *
-fw_iso_context_create(struct fw_card *card, int type,
- int channel, int speed, size_t header_size,
- fw_iso_callback_t callback, void *callback_data)
+struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
+ int type, int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data)
{
struct fw_iso_context *ctx;
- ctx = card->driver->allocate_iso_context(card, type, header_size);
+ ctx = card->driver->allocate_iso_context(card,
+ type, channel, header_size);
if (IS_ERR(ctx))
return ctx;
@@ -134,25 +142,186 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx)
card->driver->free_iso_context(ctx);
}
-int
-fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags)
+int fw_iso_context_start(struct fw_iso_context *ctx,
+ int cycle, int sync, int tags)
{
return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
}
-int
-fw_iso_context_queue(struct fw_iso_context *ctx,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload)
+int fw_iso_context_queue(struct fw_iso_context *ctx,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
{
struct fw_card *card = ctx->card;
return card->driver->queue_iso(ctx, packet, buffer, payload);
}
-int
-fw_iso_context_stop(struct fw_iso_context *ctx)
+int fw_iso_context_stop(struct fw_iso_context *ctx)
{
return ctx->card->driver->stop_iso(ctx);
}
+
+/*
+ * Isochronous bus resource management (channels, bandwidth), client side
+ */
+
+static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
+ int bandwidth, bool allocate)
+{
+ __be32 data[2];
+ int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
+
+ /*
+ * On a 1394a IRM with low contention, try < 1 is enough.
+ * On a 1394-1995 IRM, we need at least try < 2.
+ * Let's just do try < 5.
+ */
+ for (try = 0; try < 5; try++) {
+ new = allocate ? old - bandwidth : old + bandwidth;
+ if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
+ break;
+
+ data[0] = cpu_to_be32(old);
+ data[1] = cpu_to_be32(new);
+ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
+ irm_id, generation, SCODE_100,
+ CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
+ data, sizeof(data))) {
+ case RCODE_GENERATION:
+ /* A generation change frees all bandwidth. */
+ return allocate ? -EAGAIN : bandwidth;
+
+ case RCODE_COMPLETE:
+ if (be32_to_cpup(data) == old)
+ return bandwidth;
+
+ old = be32_to_cpup(data);
+ /* Fall through. */
+ }
+ }
+
+ return -EIO;
+}
+
+static int manage_channel(struct fw_card *card, int irm_id, int generation,
+ u32 channels_mask, u64 offset, bool allocate)
+{
+ __be32 data[2], c, all, old;
+ int i, retry = 5;
+
+ old = all = allocate ? cpu_to_be32(~0) : 0;
+
+ for (i = 0; i < 32; i++) {
+ if (!(channels_mask & 1 << i))
+ continue;
+
+ c = cpu_to_be32(1 << (31 - i));
+ if ((old & c) != (all & c))
+ continue;
+
+ data[0] = old;
+ data[1] = old ^ c;
+ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
+ irm_id, generation, SCODE_100,
+ offset, data, sizeof(data))) {
+ case RCODE_GENERATION:
+ /* A generation change frees all channels. */
+ return allocate ? -EAGAIN : i;
+
+ case RCODE_COMPLETE:
+ if (data[0] == old)
+ return i;
+
+ old = data[0];
+
+ /* Is the IRM 1394a-2000 compliant? */
+ if ((data[0] & c) == (data[1] & c))
+ continue;
+
+ /* 1394-1995 IRM, fall through to retry. */
+ default:
+ if (retry--)
+ i--;
+ }
+ }
+
+ return -EIO;
+}
+
+static void deallocate_channel(struct fw_card *card, int irm_id,
+ int generation, int channel)
+{
+ u32 mask;
+ u64 offset;
+
+ mask = channel < 32 ? 1 << channel : 1 << (channel - 32);
+ offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
+ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
+
+ manage_channel(card, irm_id, generation, mask, offset, false);
+}
+
+/**
+ * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth
+ *
+ * In parameters: card, generation, channels_mask, bandwidth, allocate
+ * Out parameters: channel, bandwidth
+ * This function blocks (sleeps) during communication with the IRM.
+ *
+ * Allocates or deallocates at most one channel out of channels_mask.
+ * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0.
+ * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for
+ * channel 0 and LSB for channel 63.)
+ * Allocates or deallocates as many bandwidth allocation units as specified.
+ *
+ * Returns channel < 0 if no channel was allocated or deallocated.
+ * Returns bandwidth = 0 if no bandwidth was allocated or deallocated.
+ *
+ * If generation is stale, deallocations succeed but allocations fail with
+ * channel = -EAGAIN.
+ *
+ * If channel allocation fails, no bandwidth will be allocated either.
+ * If bandwidth allocation fails, no channel will be allocated either.
+ * But deallocations of channel and bandwidth are tried independently
+ * of each other's success.
+ */
+void fw_iso_resource_manage(struct fw_card *card, int generation,
+ u64 channels_mask, int *channel, int *bandwidth,
+ bool allocate)
+{
+ u32 channels_hi = channels_mask; /* channels 31...0 */
+ u32 channels_lo = channels_mask >> 32; /* channels 63...32 */
+ int irm_id, ret, c = -EINVAL;
+
+ spin_lock_irq(&card->lock);
+ irm_id = card->irm_node->node_id;
+ spin_unlock_irq(&card->lock);
+
+ if (channels_hi)
+ c = manage_channel(card, irm_id, generation, channels_hi,
+ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, allocate);
+ if (channels_lo && c < 0) {
+ c = manage_channel(card, irm_id, generation, channels_lo,
+ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, allocate);
+ if (c >= 0)
+ c += 32;
+ }
+ *channel = c;
+
+ if (allocate && channels_mask != 0 && c < 0)
+ *bandwidth = 0;
+
+ if (*bandwidth == 0)
+ return;
+
+ ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
+ if (ret < 0)
+ *bandwidth = 0;
+
+ if (allocate && ret < 0 && c >= 0) {
+ deallocate_channel(card, irm_id, generation, c);
+ *channel = ret;
+ }
+}
diff --git a/trunk/drivers/firewire/fw-ohci.c b/trunk/drivers/firewire/fw-ohci.c
index 6d19828a93a5..1180d0be0bb4 100644
--- a/trunk/drivers/firewire/fw-ohci.c
+++ b/trunk/drivers/firewire/fw-ohci.c
@@ -205,6 +205,7 @@ struct fw_ohci {
u32 it_context_mask;
struct iso_context *it_context_list;
+ u64 ir_context_channels;
u32 ir_context_mask;
struct iso_context *ir_context_list;
};
@@ -441,9 +442,8 @@ static inline void flush_writes(const struct fw_ohci *ohci)
reg_read(ohci, OHCI1394_Version);
}
-static int
-ohci_update_phy_reg(struct fw_card *card, int addr,
- int clear_bits, int set_bits)
+static int ohci_update_phy_reg(struct fw_card *card, int addr,
+ int clear_bits, int set_bits)
{
struct fw_ohci *ohci = fw_ohci(card);
u32 val, old;
@@ -658,8 +658,8 @@ static void ar_context_tasklet(unsigned long data)
}
}
-static int
-ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs)
+static int ar_context_init(struct ar_context *ctx,
+ struct fw_ohci *ohci, u32 regs)
{
struct ar_buffer ab;
@@ -690,8 +690,7 @@ static void ar_context_run(struct ar_context *ctx)
flush_writes(ctx->ohci);
}
-static struct descriptor *
-find_branch_descriptor(struct descriptor *d, int z)
+static struct descriptor *find_branch_descriptor(struct descriptor *d, int z)
{
int b, key;
@@ -751,8 +750,7 @@ static void context_tasklet(unsigned long data)
* Allocate a new buffer and add it to the list of free buffers for this
* context. Must be called with ohci->lock held.
*/
-static int
-context_add_buffer(struct context *ctx)
+static int context_add_buffer(struct context *ctx)
{
struct descriptor_buffer *desc;
dma_addr_t uninitialized_var(bus_addr);
@@ -781,9 +779,8 @@ context_add_buffer(struct context *ctx)
return 0;
}
-static int
-context_init(struct context *ctx, struct fw_ohci *ohci,
- u32 regs, descriptor_callback_t callback)
+static int context_init(struct context *ctx, struct fw_ohci *ohci,
+ u32 regs, descriptor_callback_t callback)
{
ctx->ohci = ohci;
ctx->regs = regs;
@@ -814,8 +811,7 @@ context_init(struct context *ctx, struct fw_ohci *ohci,
return 0;
}
-static void
-context_release(struct context *ctx)
+static void context_release(struct context *ctx)
{
struct fw_card *card = &ctx->ohci->card;
struct descriptor_buffer *desc, *tmp;
@@ -827,8 +823,8 @@ context_release(struct context *ctx)
}
/* Must be called with ohci->lock held */
-static struct descriptor *
-context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus)
+static struct descriptor *context_get_descriptors(struct context *ctx,
+ int z, dma_addr_t *d_bus)
{
struct descriptor *d = NULL;
struct descriptor_buffer *desc = ctx->buffer_tail;
@@ -912,8 +908,8 @@ struct driver_data {
* Must always be called with the ochi->lock held to ensure proper
* generation handling and locking around packet queue manipulation.
*/
-static int
-at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
+static int at_context_queue_packet(struct context *ctx,
+ struct fw_packet *packet)
{
struct fw_ohci *ohci = ctx->ohci;
dma_addr_t d_bus, uninitialized_var(payload_bus);
@@ -940,7 +936,9 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
*/
header = (__le32 *) &d[1];
- if (packet->header_length > 8) {
+ switch (packet->header_length) {
+ case 16:
+ case 12:
header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
(packet->speed << 16));
header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
@@ -954,12 +952,27 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
header[3] = (__force __le32) packet->header[3];
d[0].req_count = cpu_to_le16(packet->header_length);
- } else {
+ break;
+
+ case 8:
header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) |
(packet->speed << 16));
header[1] = cpu_to_le32(packet->header[0]);
header[2] = cpu_to_le32(packet->header[1]);
d[0].req_count = cpu_to_le16(12);
+ break;
+
+ case 4:
+ header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
+ (packet->speed << 16));
+ header[1] = cpu_to_le32(packet->header[0] & 0xffff0000);
+ d[0].req_count = cpu_to_le16(8);
+ break;
+
+ default:
+ /* BUG(); */
+ packet->ack = RCODE_SEND_ERROR;
+ return -1;
}
driver_data = (struct driver_data *) &d[3];
@@ -1095,8 +1108,8 @@ static int handle_at_packet(struct context *context,
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
-static void
-handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
+static void handle_local_rom(struct fw_ohci *ohci,
+ struct fw_packet *packet, u32 csr)
{
struct fw_packet response;
int tcode, length, i;
@@ -1122,8 +1135,8 @@ handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
fw_core_handle_response(&ohci->card, &response);
}
-static void
-handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
+static void handle_local_lock(struct fw_ohci *ohci,
+ struct fw_packet *packet, u32 csr)
{
struct fw_packet response;
int tcode, length, ext_tcode, sel;
@@ -1164,8 +1177,7 @@ handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
fw_core_handle_response(&ohci->card, &response);
}
-static void
-handle_local_request(struct context *ctx, struct fw_packet *packet)
+static void handle_local_request(struct context *ctx, struct fw_packet *packet)
{
u64 offset;
u32 csr;
@@ -1205,11 +1217,10 @@ handle_local_request(struct context *ctx, struct fw_packet *packet)
}
}
-static void
-at_context_transmit(struct context *ctx, struct fw_packet *packet)
+static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
{
unsigned long flags;
- int retval;
+ int ret;
spin_lock_irqsave(&ctx->ohci->lock, flags);
@@ -1220,10 +1231,10 @@ at_context_transmit(struct context *ctx, struct fw_packet *packet)
return;
}
- retval = at_context_queue_packet(ctx, packet);
+ ret = at_context_queue_packet(ctx, packet);
spin_unlock_irqrestore(&ctx->ohci->lock, flags);
- if (retval < 0)
+ if (ret < 0)
packet->callback(packet, &ctx->ohci->card, packet->ack);
}
@@ -1590,12 +1601,12 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
return 0;
}
-static int
-ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
+static int ohci_set_config_rom(struct fw_card *card,
+ u32 *config_rom, size_t length)
{
struct fw_ohci *ohci;
unsigned long flags;
- int retval = -EBUSY;
+ int ret = -EBUSY;
__be32 *next_config_rom;
dma_addr_t uninitialized_var(next_config_rom_bus);
@@ -1649,7 +1660,7 @@ ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
reg_write(ohci, OHCI1394_ConfigROMmap,
ohci->next_config_rom_bus);
- retval = 0;
+ ret = 0;
}
spin_unlock_irqrestore(&ohci->lock, flags);
@@ -1661,13 +1672,13 @@ ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
* controller could need to access it before the bus reset
* takes effect.
*/
- if (retval == 0)
+ if (ret == 0)
fw_core_initiate_bus_reset(&ohci->card, 1);
else
dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
next_config_rom, next_config_rom_bus);
- return retval;
+ return ret;
}
static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
@@ -1689,7 +1700,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
struct fw_ohci *ohci = fw_ohci(card);
struct context *ctx = &ohci->at_request_ctx;
struct driver_data *driver_data = packet->driver_data;
- int retval = -ENOENT;
+ int ret = -ENOENT;
tasklet_disable(&ctx->tasklet);
@@ -1704,23 +1715,22 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
driver_data->packet = NULL;
packet->ack = RCODE_CANCELLED;
packet->callback(packet, &ohci->card, packet->ack);
- retval = 0;
-
+ ret = 0;
out:
tasklet_enable(&ctx->tasklet);
- return retval;
+ return ret;
}
-static int
-ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
+static int ohci_enable_phys_dma(struct fw_card *card,
+ int node_id, int generation)
{
#ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA
return 0;
#else
struct fw_ohci *ohci = fw_ohci(card);
unsigned long flags;
- int n, retval = 0;
+ int n, ret = 0;
/*
* FIXME: Make sure this bitmask is cleared when we clear the busReset
@@ -1730,7 +1740,7 @@ ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
spin_lock_irqsave(&ohci->lock, flags);
if (ohci->generation != generation) {
- retval = -ESTALE;
+ ret = -ESTALE;
goto out;
}
@@ -1748,12 +1758,12 @@ ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
flush_writes(ohci);
out:
spin_unlock_irqrestore(&ohci->lock, flags);
- return retval;
+
+ return ret;
#endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */
}
-static u64
-ohci_get_bus_time(struct fw_card *card)
+static u64 ohci_get_bus_time(struct fw_card *card)
{
struct fw_ohci *ohci = fw_ohci(card);
u32 cycle_time;
@@ -1765,6 +1775,28 @@ ohci_get_bus_time(struct fw_card *card)
return bus_time;
}
+static void copy_iso_headers(struct iso_context *ctx, void *p)
+{
+ int i = ctx->header_length;
+
+ if (i + ctx->base.header_size > PAGE_SIZE)
+ return;
+
+ /*
+ * The iso header is byteswapped to little endian by
+ * the controller, but the remaining header quadlets
+ * are big endian. We want to present all the headers
+ * as big endian, so we have to swap the first quadlet.
+ */
+ if (ctx->base.header_size > 0)
+ *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+ if (ctx->base.header_size > 4)
+ *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p);
+ if (ctx->base.header_size > 8)
+ memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8);
+ ctx->header_length += ctx->base.header_size;
+}
+
static int handle_ir_dualbuffer_packet(struct context *context,
struct descriptor *d,
struct descriptor *last)
@@ -1775,7 +1807,6 @@ static int handle_ir_dualbuffer_packet(struct context *context,
__le32 *ir_header;
size_t header_length;
void *p, *end;
- int i;
if (db->first_res_count != 0 && db->second_res_count != 0) {
if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) {
@@ -1788,25 +1819,14 @@ static int handle_ir_dualbuffer_packet(struct context *context,
header_length = le16_to_cpu(db->first_req_count) -
le16_to_cpu(db->first_res_count);
- i = ctx->header_length;
p = db + 1;
end = p + header_length;
- while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
- /*
- * The iso header is byteswapped to little endian by
- * the controller, but the remaining header quadlets
- * are big endian. We want to present all the headers
- * as big endian, so we have to swap the first
- * quadlet.
- */
- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
- i += ctx->base.header_size;
+ while (p < end) {
+ copy_iso_headers(ctx, p);
ctx->excess_bytes +=
(le32_to_cpu(*(__le32 *)(p + 4)) >> 16) & 0xffff;
- p += ctx->base.header_size + 4;
+ p += max(ctx->base.header_size, (size_t)8);
}
- ctx->header_length = i;
ctx->excess_bytes -= le16_to_cpu(db->second_req_count) -
le16_to_cpu(db->second_res_count);
@@ -1832,7 +1852,6 @@ static int handle_ir_packet_per_buffer(struct context *context,
struct descriptor *pd;
__le32 *ir_header;
void *p;
- int i;
for (pd = d; pd <= last; pd++) {
if (pd->transfer_status)
@@ -1842,21 +1861,8 @@ static int handle_ir_packet_per_buffer(struct context *context,
/* Descriptor(s) not done yet, stop iteration */
return 0;
- i = ctx->header_length;
- p = last + 1;
-
- if (ctx->base.header_size > 0 &&
- i + ctx->base.header_size <= PAGE_SIZE) {
- /*
- * The iso header is byteswapped to little endian by
- * the controller, but the remaining header quadlets
- * are big endian. We want to present all the headers
- * as big endian, so we have to swap the first quadlet.
- */
- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
- ctx->header_length += ctx->base.header_size;
- }
+ p = last + 1;
+ copy_iso_headers(ctx, p);
if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) {
ir_header = (__le32 *) p;
@@ -1888,21 +1894,24 @@ static int handle_it_packet(struct context *context,
return 1;
}
-static struct fw_iso_context *
-ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
+static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
+ int type, int channel, size_t header_size)
{
struct fw_ohci *ohci = fw_ohci(card);
struct iso_context *ctx, *list;
descriptor_callback_t callback;
+ u64 *channels, dont_care = ~0ULL;
u32 *mask, regs;
unsigned long flags;
- int index, retval = -ENOMEM;
+ int index, ret = -ENOMEM;
if (type == FW_ISO_CONTEXT_TRANSMIT) {
+ channels = &dont_care;
mask = &ohci->it_context_mask;
list = ohci->it_context_list;
callback = handle_it_packet;
} else {
+ channels = &ohci->ir_context_channels;
mask = &ohci->ir_context_mask;
list = ohci->ir_context_list;
if (ohci->use_dualbuffer)
@@ -1912,9 +1921,11 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
}
spin_lock_irqsave(&ohci->lock, flags);
- index = ffs(*mask) - 1;
- if (index >= 0)
+ index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1;
+ if (index >= 0) {
+ *channels &= ~(1ULL << channel);
*mask &= ~(1 << index);
+ }
spin_unlock_irqrestore(&ohci->lock, flags);
if (index < 0)
@@ -1932,8 +1943,8 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
if (ctx->header == NULL)
goto out;
- retval = context_init(&ctx->context, ohci, regs, callback);
- if (retval < 0)
+ ret = context_init(&ctx->context, ohci, regs, callback);
+ if (ret < 0)
goto out_with_header;
return &ctx->base;
@@ -1945,7 +1956,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
*mask |= 1 << index;
spin_unlock_irqrestore(&ohci->lock, flags);
- return ERR_PTR(retval);
+ return ERR_PTR(ret);
}
static int ohci_start_iso(struct fw_iso_context *base,
@@ -2024,16 +2035,16 @@ static void ohci_free_iso_context(struct fw_iso_context *base)
} else {
index = ctx - ohci->ir_context_list;
ohci->ir_context_mask |= 1 << index;
+ ohci->ir_context_channels |= 1ULL << base->channel;
}
spin_unlock_irqrestore(&ohci->lock, flags);
}
-static int
-ohci_queue_iso_transmit(struct fw_iso_context *base,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload)
+static int ohci_queue_iso_transmit(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
{
struct iso_context *ctx = container_of(base, struct iso_context, base);
struct descriptor *d, *last, *pd;
@@ -2128,11 +2139,10 @@ ohci_queue_iso_transmit(struct fw_iso_context *base,
return 0;
}
-static int
-ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload)
+static int ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
{
struct iso_context *ctx = container_of(base, struct iso_context, base);
struct db_descriptor *db = NULL;
@@ -2151,11 +2161,11 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
z = 2;
/*
- * The OHCI controller puts the status word in the header
- * buffer too, so we need 4 extra bytes per packet.
+ * The OHCI controller puts the isochronous header and trailer in the
+ * buffer, so we need at least 8 bytes.
*/
packet_count = p->header_length / ctx->base.header_size;
- header_size = packet_count * (ctx->base.header_size + 4);
+ header_size = packet_count * max(ctx->base.header_size, (size_t)8);
/* Get header size in number of descriptors. */
header_z = DIV_ROUND_UP(header_size, sizeof(*d));
@@ -2173,7 +2183,8 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
db = (struct db_descriptor *) d;
db->control = cpu_to_le16(DESCRIPTOR_STATUS |
DESCRIPTOR_BRANCH_ALWAYS);
- db->first_size = cpu_to_le16(ctx->base.header_size + 4);
+ db->first_size =
+ cpu_to_le16(max(ctx->base.header_size, (size_t)8));
if (p->skip && rest == p->payload_length) {
db->control |= cpu_to_le16(DESCRIPTOR_WAIT);
db->first_req_count = db->first_size;
@@ -2208,11 +2219,10 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
return 0;
}
-static int
-ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload)
+static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
{
struct iso_context *ctx = container_of(base, struct iso_context, base);
struct descriptor *d = NULL, *pd = NULL;
@@ -2223,11 +2233,11 @@ ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
int page, offset, packet_count, header_size, payload_per_buffer;
/*
- * The OHCI controller puts the status word in the
- * buffer too, so we need 4 extra bytes per packet.
+ * The OHCI controller puts the isochronous header and trailer in the
+ * buffer, so we need at least 8 bytes.
*/
packet_count = p->header_length / ctx->base.header_size;
- header_size = ctx->base.header_size + 4;
+ header_size = max(ctx->base.header_size, (size_t)8);
/* Get header size in number of descriptors. */
header_z = DIV_ROUND_UP(header_size, sizeof(*d));
@@ -2286,29 +2296,27 @@ ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
return 0;
}
-static int
-ohci_queue_iso(struct fw_iso_context *base,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload)
+static int ohci_queue_iso(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
{
struct iso_context *ctx = container_of(base, struct iso_context, base);
unsigned long flags;
- int retval;
+ int ret;
spin_lock_irqsave(&ctx->context.ohci->lock, flags);
if (base->type == FW_ISO_CONTEXT_TRANSMIT)
- retval = ohci_queue_iso_transmit(base, packet, buffer, payload);
+ ret = ohci_queue_iso_transmit(base, packet, buffer, payload);
else if (ctx->context.ohci->use_dualbuffer)
- retval = ohci_queue_iso_receive_dualbuffer(base, packet,
- buffer, payload);
+ ret = ohci_queue_iso_receive_dualbuffer(base, packet,
+ buffer, payload);
else
- retval = ohci_queue_iso_receive_packet_per_buffer(base, packet,
- buffer,
- payload);
+ ret = ohci_queue_iso_receive_packet_per_buffer(base, packet,
+ buffer, payload);
spin_unlock_irqrestore(&ctx->context.ohci->lock, flags);
- return retval;
+ return ret;
}
static const struct fw_card_driver ohci_driver = {
@@ -2357,8 +2365,8 @@ static void ohci_pmac_off(struct pci_dev *dev)
#define ohci_pmac_off(dev)
#endif /* CONFIG_PPC_PMAC */
-static int __devinit
-pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+static int __devinit pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
struct fw_ohci *ohci;
u32 bus_options, max_receive, link_speed, version;
@@ -2440,6 +2448,7 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
ohci->it_context_list = kzalloc(size, GFP_KERNEL);
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
+ ohci->ir_context_channels = ~0ULL;
ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);
@@ -2467,11 +2476,12 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
reg_read(ohci, OHCI1394_GUIDLo);
err = fw_card_add(&ohci->card, max_receive, link_speed, guid);
- if (err < 0)
+ if (err)
goto fail_self_id;
fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n",
dev_name(&dev->dev), version >> 16, version & 0xff);
+
return 0;
fail_self_id:
diff --git a/trunk/drivers/firewire/fw-sbp2.c b/trunk/drivers/firewire/fw-sbp2.c
index c71c4419d9e8..2bcf51557c72 100644
--- a/trunk/drivers/firewire/fw-sbp2.c
+++ b/trunk/drivers/firewire/fw-sbp2.c
@@ -392,20 +392,18 @@ static const struct {
}
};
-static void
-free_orb(struct kref *kref)
+static void free_orb(struct kref *kref)
{
struct sbp2_orb *orb = container_of(kref, struct sbp2_orb, kref);
kfree(orb);
}
-static void
-sbp2_status_write(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
struct sbp2_logical_unit *lu = callback_data;
struct sbp2_orb *orb;
@@ -451,9 +449,8 @@ sbp2_status_write(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_COMPLETE);
}
-static void
-complete_transaction(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
+static void complete_transaction(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
{
struct sbp2_orb *orb = data;
unsigned long flags;
@@ -482,9 +479,8 @@ complete_transaction(struct fw_card *card, int rcode,
kref_put(&orb->kref, free_orb);
}
-static void
-sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
- int node_id, int generation, u64 offset)
+static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
+ int node_id, int generation, u64 offset)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
unsigned long flags;
@@ -531,8 +527,8 @@ static int sbp2_cancel_orbs(struct sbp2_logical_unit *lu)
return retval;
}
-static void
-complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
+static void complete_management_orb(struct sbp2_orb *base_orb,
+ struct sbp2_status *status)
{
struct sbp2_management_orb *orb =
container_of(base_orb, struct sbp2_management_orb, base);
@@ -542,10 +538,9 @@ complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
complete(&orb->done);
}
-static int
-sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
- int generation, int function, int lun_or_login_id,
- void *response)
+static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
+ int generation, int function,
+ int lun_or_login_id, void *response)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct sbp2_management_orb *orb;
@@ -652,9 +647,8 @@ static void sbp2_agent_reset(struct sbp2_logical_unit *lu)
&d, sizeof(d));
}
-static void
-complete_agent_reset_write_no_wait(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
+static void complete_agent_reset_write_no_wait(struct fw_card *card,
+ int rcode, void *payload, size_t length, void *data)
{
kfree(data);
}
@@ -1299,8 +1293,7 @@ static void sbp2_unmap_scatterlist(struct device *card_device,
sizeof(orb->page_table), DMA_TO_DEVICE);
}
-static unsigned int
-sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
+static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
{
int sam_status;
@@ -1337,8 +1330,8 @@ sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
}
}
-static void
-complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
+static void complete_command_orb(struct sbp2_orb *base_orb,
+ struct sbp2_status *status)
{
struct sbp2_command_orb *orb =
container_of(base_orb, struct sbp2_command_orb, base);
@@ -1384,9 +1377,8 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
orb->done(orb->cmd);
}
-static int
-sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
- struct sbp2_logical_unit *lu)
+static int sbp2_map_scatterlist(struct sbp2_command_orb *orb,
+ struct fw_device *device, struct sbp2_logical_unit *lu)
{
struct scatterlist *sg = scsi_sglist(orb->cmd);
int i, n;
@@ -1584,9 +1576,8 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd)
* This is the concatenation of target port identifier and logical unit
* identifier as per SAM-2...SAM-4 annex A.
*/
-static ssize_t
-sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct sbp2_logical_unit *lu;
diff --git a/trunk/drivers/firewire/fw-topology.c b/trunk/drivers/firewire/fw-topology.c
index 8dd6703b55cd..d0deecc4de93 100644
--- a/trunk/drivers/firewire/fw-topology.c
+++ b/trunk/drivers/firewire/fw-topology.c
@@ -314,9 +314,8 @@ typedef void (*fw_node_callback_t)(struct fw_card * card,
struct fw_node * node,
struct fw_node * parent);
-static void
-for_each_fw_node(struct fw_card *card, struct fw_node *root,
- fw_node_callback_t callback)
+static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
+ fw_node_callback_t callback)
{
struct list_head list;
struct fw_node *node, *next, *child, *parent;
@@ -349,9 +348,8 @@ for_each_fw_node(struct fw_card *card, struct fw_node *root,
fw_node_put(node);
}
-static void
-report_lost_node(struct fw_card *card,
- struct fw_node *node, struct fw_node *parent)
+static void report_lost_node(struct fw_card *card,
+ struct fw_node *node, struct fw_node *parent)
{
fw_node_event(card, node, FW_NODE_DESTROYED);
fw_node_put(node);
@@ -360,9 +358,8 @@ report_lost_node(struct fw_card *card,
card->bm_retries = 0;
}
-static void
-report_found_node(struct fw_card *card,
- struct fw_node *node, struct fw_node *parent)
+static void report_found_node(struct fw_card *card,
+ struct fw_node *node, struct fw_node *parent)
{
int b_path = (node->phy_speed == SCODE_BETA);
@@ -415,8 +412,7 @@ static void move_tree(struct fw_node *node0, struct fw_node *node1, int port)
* found, lost or updated. Update the nodes in the card topology tree
* as we go.
*/
-static void
-update_tree(struct fw_card *card, struct fw_node *root)
+static void update_tree(struct fw_card *card, struct fw_node *root)
{
struct list_head list0, list1;
struct fw_node *node0, *node1, *next1;
@@ -497,8 +493,8 @@ update_tree(struct fw_card *card, struct fw_node *root)
}
}
-static void
-update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
+static void update_topology_map(struct fw_card *card,
+ u32 *self_ids, int self_id_count)
{
int node_count;
@@ -510,10 +506,8 @@ update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
fw_compute_block_crc(card->topology_map);
}
-void
-fw_core_handle_bus_reset(struct fw_card *card,
- int node_id, int generation,
- int self_id_count, u32 * self_ids)
+void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
+ int self_id_count, u32 *self_ids)
{
struct fw_node *local_node;
unsigned long flags;
@@ -532,6 +526,7 @@ fw_core_handle_bus_reset(struct fw_card *card,
spin_lock_irqsave(&card->lock, flags);
+ card->broadcast_channel_allocated = false;
card->node_id = node_id;
/*
* Update node_id before generation to prevent anybody from using
diff --git a/trunk/drivers/firewire/fw-topology.h b/trunk/drivers/firewire/fw-topology.h
index addb9f8ea776..3c497bb4fae4 100644
--- a/trunk/drivers/firewire/fw-topology.h
+++ b/trunk/drivers/firewire/fw-topology.h
@@ -19,6 +19,11 @@
#ifndef __fw_topology_h
#define __fw_topology_h
+#include
+#include
+
+#include
+
enum {
FW_NODE_CREATED,
FW_NODE_UPDATED,
@@ -51,26 +56,22 @@ struct fw_node {
struct fw_node *ports[0];
};
-static inline struct fw_node *
-fw_node_get(struct fw_node *node)
+static inline struct fw_node *fw_node_get(struct fw_node *node)
{
atomic_inc(&node->ref_count);
return node;
}
-static inline void
-fw_node_put(struct fw_node *node)
+static inline void fw_node_put(struct fw_node *node)
{
if (atomic_dec_and_test(&node->ref_count))
kfree(node);
}
-void
-fw_destroy_nodes(struct fw_card *card);
-
-int
-fw_compute_block_crc(u32 *block);
+struct fw_card;
+void fw_destroy_nodes(struct fw_card *card);
+int fw_compute_block_crc(u32 *block);
#endif /* __fw_topology_h */
diff --git a/trunk/drivers/firewire/fw-transaction.c b/trunk/drivers/firewire/fw-transaction.c
index 699ac041f39a..283dac6d327d 100644
--- a/trunk/drivers/firewire/fw-transaction.c
+++ b/trunk/drivers/firewire/fw-transaction.c
@@ -64,10 +64,8 @@
#define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
#define PHY_IDENTIFIER(id) ((id) << 30)
-static int
-close_transaction(struct fw_transaction *transaction,
- struct fw_card *card, int rcode,
- u32 *payload, size_t length)
+static int close_transaction(struct fw_transaction *transaction,
+ struct fw_card *card, int rcode)
{
struct fw_transaction *t;
unsigned long flags;
@@ -83,7 +81,7 @@ close_transaction(struct fw_transaction *transaction,
spin_unlock_irqrestore(&card->lock, flags);
if (&t->link != &card->transaction_list) {
- t->callback(card, rcode, payload, length, t->callback_data);
+ t->callback(card, rcode, NULL, 0, t->callback_data);
return 0;
}
@@ -94,9 +92,8 @@ close_transaction(struct fw_transaction *transaction,
* Only valid for transactions that are potentially pending (ie have
* been sent).
*/
-int
-fw_cancel_transaction(struct fw_card *card,
- struct fw_transaction *transaction)
+int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction)
{
/*
* Cancel the packet transmission if it's still queued. That
@@ -112,20 +109,19 @@ fw_cancel_transaction(struct fw_card *card,
* if the transaction is still pending and remove it in that case.
*/
- return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0);
+ return close_transaction(transaction, card, RCODE_CANCELLED);
}
EXPORT_SYMBOL(fw_cancel_transaction);
-static void
-transmit_complete_callback(struct fw_packet *packet,
- struct fw_card *card, int status)
+static void transmit_complete_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
{
struct fw_transaction *t =
container_of(packet, struct fw_transaction, packet);
switch (status) {
case ACK_COMPLETE:
- close_transaction(t, card, RCODE_COMPLETE, NULL, 0);
+ close_transaction(t, card, RCODE_COMPLETE);
break;
case ACK_PENDING:
t->timestamp = packet->timestamp;
@@ -133,31 +129,42 @@ transmit_complete_callback(struct fw_packet *packet,
case ACK_BUSY_X:
case ACK_BUSY_A:
case ACK_BUSY_B:
- close_transaction(t, card, RCODE_BUSY, NULL, 0);
+ close_transaction(t, card, RCODE_BUSY);
break;
case ACK_DATA_ERROR:
- close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0);
+ close_transaction(t, card, RCODE_DATA_ERROR);
break;
case ACK_TYPE_ERROR:
- close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0);
+ close_transaction(t, card, RCODE_TYPE_ERROR);
break;
default:
/*
* In this case the ack is really a juju specific
* rcode, so just forward that to the callback.
*/
- close_transaction(t, card, status, NULL, 0);
+ close_transaction(t, card, status);
break;
}
}
-static void
-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
int destination_id, int source_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length)
{
int ext_tcode;
+ if (tcode == TCODE_STREAM_DATA) {
+ packet->header[0] =
+ HEADER_DATA_LENGTH(length) |
+ destination_id |
+ HEADER_TCODE(TCODE_STREAM_DATA);
+ packet->header_length = 4;
+ packet->payload = payload;
+ packet->payload_length = length;
+
+ goto common;
+ }
+
if (tcode > 0x10) {
ext_tcode = tcode & ~0x10;
tcode = TCODE_LOCK_REQUEST;
@@ -204,7 +211,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
packet->payload_length = 0;
break;
}
-
+ common:
packet->speed = speed;
packet->generation = generation;
packet->ack = 0;
@@ -246,13 +253,14 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
* @param callback function to be called when the transaction is completed
* @param callback_data pointer to arbitrary data, which will be
* passed to the callback
+ *
+ * In case of asynchronous stream packets i.e. TCODE_STREAM_DATA, the caller
+ * needs to synthesize @destination_id with fw_stream_packet_destination_id().
*/
-void
-fw_send_request(struct fw_card *card, struct fw_transaction *t,
- int tcode, int destination_id, int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length,
- fw_transaction_callback_t callback, void *callback_data)
+void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
+ int destination_id, int generation, int speed,
+ unsigned long long offset, void *payload, size_t length,
+ fw_transaction_callback_t callback, void *callback_data)
{
unsigned long flags;
int tlabel;
@@ -322,16 +330,16 @@ static void transaction_callback(struct fw_card *card, int rcode,
* Returns the RCODE.
*/
int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
- int generation, int speed, unsigned long long offset,
- void *data, size_t length)
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
{
struct transaction_callback_data d;
struct fw_transaction t;
init_completion(&d.done);
- d.payload = data;
+ d.payload = payload;
fw_send_request(card, &t, tcode, destination_id, generation, speed,
- offset, data, length, transaction_callback, &d);
+ offset, payload, length, transaction_callback, &d);
wait_for_completion(&d.done);
return d.rcode;
@@ -399,9 +407,8 @@ void fw_flush_transactions(struct fw_card *card)
}
}
-static struct fw_address_handler *
-lookup_overlapping_address_handler(struct list_head *list,
- unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_overlapping_address_handler(
+ struct list_head *list, unsigned long long offset, size_t length)
{
struct fw_address_handler *handler;
@@ -414,9 +421,8 @@ lookup_overlapping_address_handler(struct list_head *list,
return NULL;
}
-static struct fw_address_handler *
-lookup_enclosing_address_handler(struct list_head *list,
- unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_enclosing_address_handler(
+ struct list_head *list, unsigned long long offset, size_t length)
{
struct fw_address_handler *handler;
@@ -449,36 +455,44 @@ const struct fw_address_region fw_unit_space_region =
#endif /* 0 */
/**
- * Allocate a range of addresses in the node space of the OHCI
- * controller. When a request is received that falls within the
- * specified address range, the specified callback is invoked. The
- * parameters passed to the callback give the details of the
- * particular request.
+ * fw_core_add_address_handler - register for incoming requests
+ * @handler: callback
+ * @region: region in the IEEE 1212 node space address range
+ *
+ * region->start, ->end, and handler->length have to be quadlet-aligned.
+ *
+ * When a request is received that falls within the specified address range,
+ * the specified callback is invoked. The parameters passed to the callback
+ * give the details of the particular request.
*
* Return value: 0 on success, non-zero otherwise.
* The start offset of the handler's address region is determined by
* fw_core_add_address_handler() and is returned in handler->offset.
- * The offset is quadlet-aligned.
*/
-int
-fw_core_add_address_handler(struct fw_address_handler *handler,
- const struct fw_address_region *region)
+int fw_core_add_address_handler(struct fw_address_handler *handler,
+ const struct fw_address_region *region)
{
struct fw_address_handler *other;
unsigned long flags;
int ret = -EBUSY;
+ if (region->start & 0xffff000000000003ULL ||
+ region->end & 0xffff000000000003ULL ||
+ region->start >= region->end ||
+ handler->length & 3 ||
+ handler->length == 0)
+ return -EINVAL;
+
spin_lock_irqsave(&address_handler_lock, flags);
- handler->offset = roundup(region->start, 4);
+ handler->offset = region->start;
while (handler->offset + handler->length <= region->end) {
other =
lookup_overlapping_address_handler(&address_handler_list,
handler->offset,
handler->length);
if (other != NULL) {
- handler->offset =
- roundup(other->offset + other->length, 4);
+ handler->offset += other->length;
} else {
list_add_tail(&handler->link, &address_handler_list);
ret = 0;
@@ -493,12 +507,7 @@ fw_core_add_address_handler(struct fw_address_handler *handler,
EXPORT_SYMBOL(fw_core_add_address_handler);
/**
- * Deallocate a range of addresses allocated with fw_allocate. This
- * will call the associated callback one last time with a the special
- * tcode TCODE_DEALLOCATE, to let the client destroy the registered
- * callback data. For convenience, the callback parameters offset and
- * length are set to the start and the length respectively for the
- * deallocated region, payload is set to NULL.
+ * fw_core_remove_address_handler - unregister an address handler
*/
void fw_core_remove_address_handler(struct fw_address_handler *handler)
{
@@ -518,9 +527,8 @@ struct fw_request {
u32 data[0];
};
-static void
-free_response_callback(struct fw_packet *packet,
- struct fw_card *card, int status)
+static void free_response_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
{
struct fw_request *request;
@@ -528,9 +536,8 @@ free_response_callback(struct fw_packet *packet,
kfree(request);
}
-void
-fw_fill_response(struct fw_packet *response, u32 *request_header,
- int rcode, void *payload, size_t length)
+void fw_fill_response(struct fw_packet *response, u32 *request_header,
+ int rcode, void *payload, size_t length)
{
int tcode, tlabel, extended_tcode, source, destination;
@@ -588,8 +595,7 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
}
EXPORT_SYMBOL(fw_fill_response);
-static struct fw_request *
-allocate_request(struct fw_packet *p)
+static struct fw_request *allocate_request(struct fw_packet *p)
{
struct fw_request *request;
u32 *data, length;
@@ -649,8 +655,8 @@ allocate_request(struct fw_packet *p)
return request;
}
-void
-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
+void fw_send_response(struct fw_card *card,
+ struct fw_request *request, int rcode)
{
/* unified transaction or broadcast transaction: don't respond */
if (request->ack != ACK_PENDING ||
@@ -670,8 +676,7 @@ fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
}
EXPORT_SYMBOL(fw_send_response);
-void
-fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
{
struct fw_address_handler *handler;
struct fw_request *request;
@@ -719,8 +724,7 @@ fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
}
EXPORT_SYMBOL(fw_core_handle_request);
-void
-fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
{
struct fw_transaction *t;
unsigned long flags;
@@ -793,12 +797,10 @@ static const struct fw_address_region topology_map_region =
{ .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
.end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
-static void
-handle_topology_map(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void handle_topology_map(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ int speed, unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
int i, start, end;
__be32 *map;
@@ -832,12 +834,10 @@ static const struct fw_address_region registers_region =
{ .start = CSR_REGISTER_BASE,
.end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
-static void
-handle_registers(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void handle_registers(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ int speed, unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
int reg = offset & ~CSR_REGISTER_BASE;
unsigned long long bus_time;
@@ -939,11 +939,11 @@ static struct fw_descriptor model_id_descriptor = {
static int __init fw_core_init(void)
{
- int retval;
+ int ret;
- retval = bus_register(&fw_bus_type);
- if (retval < 0)
- return retval;
+ ret = bus_register(&fw_bus_type);
+ if (ret < 0)
+ return ret;
fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
if (fw_cdev_major < 0) {
@@ -951,19 +951,10 @@ static int __init fw_core_init(void)
return fw_cdev_major;
}
- retval = fw_core_add_address_handler(&topology_map,
- &topology_map_region);
- BUG_ON(retval < 0);
-
- retval = fw_core_add_address_handler(®isters,
- ®isters_region);
- BUG_ON(retval < 0);
-
- /* Add the vendor textual descriptor. */
- retval = fw_core_add_descriptor(&vendor_id_descriptor);
- BUG_ON(retval < 0);
- retval = fw_core_add_descriptor(&model_id_descriptor);
- BUG_ON(retval < 0);
+ fw_core_add_address_handler(&topology_map, &topology_map_region);
+ fw_core_add_address_handler(®isters, ®isters_region);
+ fw_core_add_descriptor(&vendor_id_descriptor);
+ fw_core_add_descriptor(&model_id_descriptor);
return 0;
}
diff --git a/trunk/drivers/firewire/fw-transaction.h b/trunk/drivers/firewire/fw-transaction.h
index 1d78e9cc5940..dfa799068f89 100644
--- a/trunk/drivers/firewire/fw-transaction.h
+++ b/trunk/drivers/firewire/fw-transaction.h
@@ -82,14 +82,14 @@
#define CSR_SPEED_MAP 0x2000
#define CSR_SPEED_MAP_END 0x3000
+#define BANDWIDTH_AVAILABLE_INITIAL 4915
#define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31)
#define BROADCAST_CHANNEL_VALID (1 << 30)
#define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
#define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
-static inline void
-fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
+static inline void fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
{
u32 *dst = _dst;
__be32 *src = _src;
@@ -99,8 +99,7 @@ fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
dst[i] = be32_to_cpu(src[i]);
}
-static inline void
-fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
+static inline void fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
{
fw_memcpy_from_be32(_dst, _src, size);
}
@@ -125,8 +124,7 @@ typedef void (*fw_packet_callback_t)(struct fw_packet *packet,
struct fw_card *card, int status);
typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
- void *data,
- size_t length,
+ void *data, size_t length,
void *callback_data);
/*
@@ -141,12 +139,6 @@ typedef void (*fw_address_callback_t)(struct fw_card *card,
void *data, size_t length,
void *callback_data);
-typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle,
- int node_id, int generation,
- u32 *self_ids,
- int self_id_count,
- void *callback_data);
-
struct fw_packet {
int speed;
int generation;
@@ -187,12 +179,6 @@ struct fw_transaction {
void *callback_data;
};
-static inline struct fw_packet *
-fw_packet(struct list_head *l)
-{
- return list_entry(l, struct fw_packet, link);
-}
-
struct fw_address_handler {
u64 offset;
size_t length;
@@ -201,7 +187,6 @@ struct fw_address_handler {
struct list_head link;
};
-
struct fw_address_region {
u64 start;
u64 end;
@@ -255,6 +240,7 @@ struct fw_card {
int bm_retries;
int bm_generation;
+ bool broadcast_channel_allocated;
u32 broadcast_channel;
u32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4];
};
@@ -315,10 +301,8 @@ struct fw_iso_packet {
struct fw_iso_context;
typedef void (*fw_iso_callback_t)(struct fw_iso_context *context,
- u32 cycle,
- size_t header_length,
- void *header,
- void *data);
+ u32 cycle, size_t header_length,
+ void *header, void *data);
/*
* An iso buffer is just a set of pages mapped for DMA in the
@@ -344,36 +328,25 @@ struct fw_iso_context {
void *callback_data;
};
-int
-fw_iso_buffer_init(struct fw_iso_buffer *buffer,
- struct fw_card *card,
- int page_count,
- enum dma_data_direction direction);
-int
-fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
-void
-fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
-
-struct fw_iso_context *
-fw_iso_context_create(struct fw_card *card, int type,
- int channel, int speed, size_t header_size,
- fw_iso_callback_t callback, void *callback_data);
-
-void
-fw_iso_context_destroy(struct fw_iso_context *ctx);
-
-int
-fw_iso_context_queue(struct fw_iso_context *ctx,
- struct fw_iso_packet *packet,
- struct fw_iso_buffer *buffer,
- unsigned long payload);
-
-int
-fw_iso_context_start(struct fw_iso_context *ctx,
- int cycle, int sync, int tags);
-
-int
-fw_iso_context_stop(struct fw_iso_context *ctx);
+int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
+ int page_count, enum dma_data_direction direction);
+int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
+void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
+
+struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
+ int type, int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data);
+int fw_iso_context_queue(struct fw_iso_context *ctx,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload);
+int fw_iso_context_start(struct fw_iso_context *ctx,
+ int cycle, int sync, int tags);
+int fw_iso_context_stop(struct fw_iso_context *ctx);
+void fw_iso_context_destroy(struct fw_iso_context *ctx);
+
+void fw_iso_resource_manage(struct fw_card *card, int generation,
+ u64 channels_mask, int *channel, int *bandwidth, bool allocate);
struct fw_card_driver {
/*
@@ -415,7 +388,7 @@ struct fw_card_driver {
struct fw_iso_context *
(*allocate_iso_context)(struct fw_card *card,
- int type, size_t header_size);
+ int type, int channel, size_t header_size);
void (*free_iso_context)(struct fw_iso_context *ctx);
int (*start_iso)(struct fw_iso_context *ctx,
@@ -429,54 +402,45 @@ struct fw_card_driver {
int (*stop_iso)(struct fw_iso_context *ctx);
};
-int
-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
+int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
-void
-fw_send_request(struct fw_card *card, struct fw_transaction *t,
+void fw_send_request(struct fw_card *card, struct fw_transaction *t,
int tcode, int destination_id, int generation, int speed,
- unsigned long long offset, void *data, size_t length,
+ unsigned long long offset, void *payload, size_t length,
fw_transaction_callback_t callback, void *callback_data);
-
-int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
- int generation, int speed, unsigned long long offset,
- void *data, size_t length);
-
int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction);
-
void fw_flush_transactions(struct fw_card *card);
-
+int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length);
void fw_send_phy_config(struct fw_card *card,
int node_id, int generation, int gap_count);
+static inline int fw_stream_packet_destination_id(int tag, int channel, int sy)
+{
+ return tag << 14 | channel << 8 | sy;
+}
+
/*
* Called by the topology code to inform the device code of node
* activity; found, lost, or updated nodes.
*/
-void
-fw_node_event(struct fw_card *card, struct fw_node *node, int event);
+void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
/* API used by card level drivers */
-void
-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
- struct device *device);
-int
-fw_card_add(struct fw_card *card,
- u32 max_receive, u32 link_speed, u64 guid);
-
-void
-fw_core_remove_card(struct fw_card *card);
-
-void
-fw_core_handle_bus_reset(struct fw_card *card,
- int node_id, int generation,
- int self_id_count, u32 *self_ids);
-void
-fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
-
-void
-fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+void fw_card_initialize(struct fw_card *card,
+ const struct fw_card_driver *driver, struct device *device);
+int fw_card_add(struct fw_card *card,
+ u32 max_receive, u32 link_speed, u64 guid);
+void fw_core_remove_card(struct fw_card *card);
+void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
+ int generation, int self_id_count, u32 *self_ids);
+void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
+void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+
+extern int fw_irm_set_broadcast_channel_register(struct device *dev,
+ void *data);
#endif /* __fw_transaction_h */
diff --git a/trunk/drivers/gpu/drm/Makefile b/trunk/drivers/gpu/drm/Makefile
index 30022c4a5c12..4ec5061fa584 100644
--- a/trunk/drivers/gpu/drm/Makefile
+++ b/trunk/drivers/gpu/drm/Makefile
@@ -10,7 +10,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \
- drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o
+ drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o \
+ drm_info.o drm_debugfs.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
diff --git a/trunk/drivers/gpu/drm/drm_debugfs.c b/trunk/drivers/gpu/drm/drm_debugfs.c
new file mode 100644
index 000000000000..c77c6c6d9d2c
--- /dev/null
+++ b/trunk/drivers/gpu/drm/drm_debugfs.c
@@ -0,0 +1,235 @@
+/**
+ * \file drm_debugfs.c
+ * debugfs support for DRM
+ *
+ * \author Ben Gamari
+ */
+
+/*
+ * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com
+ *
+ * Copyright 2008 Ben Gamari
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include
+#include
+#include "drmP.h"
+
+#if defined(CONFIG_DEBUG_FS)
+
+/***************************************************
+ * Initialization, etc.
+ **************************************************/
+
+static struct drm_info_list drm_debugfs_list[] = {
+ {"name", drm_name_info, 0},
+ {"vm", drm_vm_info, 0},
+ {"clients", drm_clients_info, 0},
+ {"queues", drm_queues_info, 0},
+ {"bufs", drm_bufs_info, 0},
+ {"gem_names", drm_gem_name_info, DRIVER_GEM},
+ {"gem_objects", drm_gem_object_info, DRIVER_GEM},
+#if DRM_DEBUG_CODE
+ {"vma", drm_vma_info, 0},
+#endif
+};
+#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
+
+
+static int drm_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct drm_info_node *node = inode->i_private;
+
+ return single_open(file, node->info_ent->show, node);
+}
+
+
+static const struct file_operations drm_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+/**
+ * Initialize a given set of debugfs files for a device
+ *
+ * \param files The array of files to create
+ * \param count The number of files given
+ * \param root DRI debugfs dir entry.
+ * \param minor device minor number
+ * \return Zero on success, non-zero on failure
+ *
+ * Create a given set of debugfs files represented by an array of
+ * gdm_debugfs_lists in the given root directory.
+ */
+int drm_debugfs_create_files(struct drm_info_list *files, int count,
+ struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+ struct drm_info_node *tmp;
+ char name[64];
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ u32 features = files[i].driver_features;
+
+ if (features != 0 &&
+ (dev->driver->driver_features & features) != features)
+ continue;
+
+ tmp = drm_alloc(sizeof(struct drm_info_node),
+ _DRM_DRIVER);
+ ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
+ root, tmp, &drm_debugfs_fops);
+ if (!ent) {
+ DRM_ERROR("Cannot create /debugfs/dri/%s/%s\n",
+ name, files[i].name);
+ drm_free(tmp, sizeof(struct drm_info_node),
+ _DRM_DRIVER);
+ ret = -1;
+ goto fail;
+ }
+
+ tmp->minor = minor;
+ tmp->dent = ent;
+ tmp->info_ent = &files[i];
+ list_add(&(tmp->list), &(minor->debugfs_nodes.list));
+ }
+ return 0;
+
+fail:
+ drm_debugfs_remove_files(files, count, minor);
+ return ret;
+}
+EXPORT_SYMBOL(drm_debugfs_create_files);
+
+/**
+ * Initialize the DRI debugfs filesystem for a device
+ *
+ * \param dev DRM device
+ * \param minor device minor number
+ * \param root DRI debugfs dir entry.
+ *
+ * Create the DRI debugfs root entry "/debugfs/dri", the device debugfs root entry
+ * "/debugfs/dri/%minor%/", and each entry in debugfs_list as
+ * "/debugfs/dri/%minor%/%name%".
+ */
+int drm_debugfs_init(struct drm_minor *minor, int minor_id,
+ struct dentry *root)
+{
+ struct drm_device *dev = minor->dev;
+ char name[64];
+ int ret;
+
+ INIT_LIST_HEAD(&minor->debugfs_nodes.list);
+ sprintf(name, "%d", minor_id);
+ minor->debugfs_root = debugfs_create_dir(name, root);
+ if (!minor->debugfs_root) {
+ DRM_ERROR("Cannot create /debugfs/dri/%s\n", name);
+ return -1;
+ }
+
+ ret = drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES,
+ minor->debugfs_root, minor);
+ if (ret) {
+ debugfs_remove(minor->debugfs_root);
+ minor->debugfs_root = NULL;
+ DRM_ERROR("Failed to create core drm debugfs files\n");
+ return ret;
+ }
+
+ if (dev->driver->debugfs_init) {
+ ret = dev->driver->debugfs_init(minor);
+ if (ret) {
+ DRM_ERROR("DRM: Driver failed to initialize "
+ "/debugfs/dri.\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Remove a list of debugfs files
+ *
+ * \param files The list of files
+ * \param count The number of files
+ * \param minor The minor of which we should remove the files
+ * \return always zero.
+ *
+ * Remove all debugfs entries created by debugfs_init().
+ */
+int drm_debugfs_remove_files(struct drm_info_list *files, int count,
+ struct drm_minor *minor)
+{
+ struct list_head *pos, *q;
+ struct drm_info_node *tmp;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ list_for_each_safe(pos, q, &minor->debugfs_nodes.list) {
+ tmp = list_entry(pos, struct drm_info_node, list);
+ if (tmp->info_ent == &files[i]) {
+ debugfs_remove(tmp->dent);
+ list_del(pos);
+ drm_free(tmp, sizeof(struct drm_info_node),
+ _DRM_DRIVER);
+ }
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_debugfs_remove_files);
+
+/**
+ * Cleanup the debugfs filesystem resources.
+ *
+ * \param minor device minor number.
+ * \return always zero.
+ *
+ * Remove all debugfs entries created by debugfs_init().
+ */
+int drm_debugfs_cleanup(struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+
+ if (!minor->debugfs_root)
+ return 0;
+
+ if (dev->driver->debugfs_cleanup)
+ dev->driver->debugfs_cleanup(minor);
+
+ drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor);
+
+ debugfs_remove(minor->debugfs_root);
+ minor->debugfs_root = NULL;
+
+ return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/trunk/drivers/gpu/drm/drm_drv.c b/trunk/drivers/gpu/drm/drm_drv.c
index 14c7a23dc157..ed32edb17166 100644
--- a/trunk/drivers/gpu/drm/drm_drv.c
+++ b/trunk/drivers/gpu/drm/drm_drv.c
@@ -46,9 +46,11 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
+#include
#include "drmP.h"
#include "drm_core.h"
+
static int drm_version(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -178,7 +180,7 @@ int drm_lastclose(struct drm_device * dev)
/* Clear AGP information */
if (drm_core_has_AGP(dev) && dev->agp &&
- !drm_core_check_feature(dev, DRIVER_MODESET)) {
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
struct drm_agp_mem *entry, *tempe;
/* Remove AGP resources, but leave dev->agp
@@ -382,6 +384,13 @@ static int __init drm_core_init(void)
goto err_p3;
}
+ drm_debugfs_root = debugfs_create_dir("dri", NULL);
+ if (!drm_debugfs_root) {
+ DRM_ERROR("Cannot create /debugfs/dri\n");
+ ret = -1;
+ goto err_p3;
+ }
+
drm_mem_init();
DRM_INFO("Initialized %s %d.%d.%d %s\n",
@@ -400,6 +409,7 @@ static int __init drm_core_init(void)
static void __exit drm_core_exit(void)
{
remove_proc_entry("dri", NULL);
+ debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
unregister_chrdev(DRM_MAJOR, "drm");
diff --git a/trunk/drivers/gpu/drm/drm_info.c b/trunk/drivers/gpu/drm/drm_info.c
new file mode 100644
index 000000000000..fc98952b9033
--- /dev/null
+++ b/trunk/drivers/gpu/drm/drm_info.c
@@ -0,0 +1,328 @@
+/**
+ * \file drm_info.c
+ * DRM info file implementations
+ *
+ * \author Ben Gamari
+ */
+
+/*
+ * Created: Sun Dec 21 13:09:50 2008 by bgamari@gmail.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * Copyright 2008 Ben Gamari
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include
+#include "drmP.h"
+
+/**
+ * Called when "/proc/dri/.../name" is read.
+ *
+ * Prints the device name together with the bus id if available.
+ */
+int drm_name_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_minor *minor = node->minor;
+ struct drm_device *dev = minor->dev;
+ struct drm_master *master = minor->master;
+
+ if (!master)
+ return 0;
+
+ if (master->unique) {
+ seq_printf(m, "%s %s %s\n",
+ dev->driver->pci_driver.name,
+ pci_name(dev->pdev), master->unique);
+ } else {
+ seq_printf(m, "%s %s\n", dev->driver->pci_driver.name,
+ pci_name(dev->pdev));
+ }
+
+ return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../vm" is read.
+ *
+ * Prints information about all mappings in drm_device::maplist.
+ */
+int drm_vm_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_map *map;
+ struct drm_map_list *r_list;
+
+ /* Hardcoded from _DRM_FRAME_BUFFER,
+ _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and
+ _DRM_SCATTER_GATHER and _DRM_CONSISTENT */
+ const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" };
+ const char *type;
+ int i;
+
+ mutex_lock(&dev->struct_mutex);
+ seq_printf(m, "slot offset size type flags address mtrr\n\n");
+ i = 0;
+ list_for_each_entry(r_list, &dev->maplist, head) {
+ map = r_list->map;
+ if (!map)
+ continue;
+ if (map->type < 0 || map->type > 5)
+ type = "??";
+ else
+ type = types[map->type];
+
+ seq_printf(m, "%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ",
+ i,
+ map->offset,
+ map->size, type, map->flags,
+ (unsigned long) r_list->user_token);
+ if (map->mtrr < 0)
+ seq_printf(m, "none\n");
+ else
+ seq_printf(m, "%4d\n", map->mtrr);
+ i++;
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../queues" is read.
+ */
+int drm_queues_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ int i;
+ struct drm_queue *q;
+
+ mutex_lock(&dev->struct_mutex);
+ seq_printf(m, " ctx/flags use fin"
+ " blk/rw/rwf wait flushed queued"
+ " locks\n\n");
+ for (i = 0; i < dev->queue_count; i++) {
+ q = dev->queuelist[i];
+ atomic_inc(&q->use_count);
+ seq_printf(m, "%5d/0x%03x %5d %5d"
+ " %5d/%c%c/%c%c%c %5Zd\n",
+ i,
+ q->flags,
+ atomic_read(&q->use_count),
+ atomic_read(&q->finalization),
+ atomic_read(&q->block_count),
+ atomic_read(&q->block_read) ? 'r' : '-',
+ atomic_read(&q->block_write) ? 'w' : '-',
+ waitqueue_active(&q->read_queue) ? 'r' : '-',
+ waitqueue_active(&q->write_queue) ? 'w' : '-',
+ waitqueue_active(&q->flush_queue) ? 'f' : '-',
+ DRM_BUFCOUNT(&q->waitlist));
+ atomic_dec(&q->use_count);
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../bufs" is read.
+ */
+int drm_bufs_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_device_dma *dma;
+ int i, seg_pages;
+
+ mutex_lock(&dev->struct_mutex);
+ dma = dev->dma;
+ if (!dma) {
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+ }
+
+ seq_printf(m, " o size count free segs pages kB\n\n");
+ for (i = 0; i <= DRM_MAX_ORDER; i++) {
+ if (dma->bufs[i].buf_count) {
+ seg_pages = dma->bufs[i].seg_count * (1 << dma->bufs[i].page_order);
+ seq_printf(m, "%2d %8d %5d %5d %5d %5d %5ld\n",
+ i,
+ dma->bufs[i].buf_size,
+ dma->bufs[i].buf_count,
+ atomic_read(&dma->bufs[i].freelist.count),
+ dma->bufs[i].seg_count,
+ seg_pages,
+ seg_pages * PAGE_SIZE / 1024);
+ }
+ }
+ seq_printf(m, "\n");
+ for (i = 0; i < dma->buf_count; i++) {
+ if (i && !(i % 32))
+ seq_printf(m, "\n");
+ seq_printf(m, " %d", dma->buflist[i]->list);
+ }
+ seq_printf(m, "\n");
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../vblank" is read.
+ */
+int drm_vblank_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ int crtc;
+
+ mutex_lock(&dev->struct_mutex);
+ for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+ seq_printf(m, "CRTC %d enable: %d\n",
+ crtc, atomic_read(&dev->vblank_refcount[crtc]));
+ seq_printf(m, "CRTC %d counter: %d\n",
+ crtc, drm_vblank_count(dev, crtc));
+ seq_printf(m, "CRTC %d last wait: %d\n",
+ crtc, dev->last_vblank_wait[crtc]);
+ seq_printf(m, "CRTC %d in modeset: %d\n",
+ crtc, dev->vblank_inmodeset[crtc]);
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../clients" is read.
+ *
+ */
+int drm_clients_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_file *priv;
+
+ mutex_lock(&dev->struct_mutex);
+ seq_printf(m, "a dev pid uid magic ioctls\n\n");
+ list_for_each_entry(priv, &dev->filelist, lhead) {
+ seq_printf(m, "%c %3d %5d %5d %10u %10lu\n",
+ priv->authenticated ? 'y' : 'n',
+ priv->minor->index,
+ priv->pid,
+ priv->uid, priv->magic, priv->ioctl_count);
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+
+int drm_gem_one_name_info(int id, void *ptr, void *data)
+{
+ struct drm_gem_object *obj = ptr;
+ struct seq_file *m = data;
+
+ seq_printf(m, "name %d size %zd\n", obj->name, obj->size);
+
+ seq_printf(m, "%6d %8zd %7d %8d\n",
+ obj->name, obj->size,
+ atomic_read(&obj->handlecount.refcount),
+ atomic_read(&obj->refcount.refcount));
+ return 0;
+}
+
+int drm_gem_name_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+
+ seq_printf(m, " name size handles refcount\n");
+ idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m);
+ return 0;
+}
+
+int drm_gem_object_info(struct seq_file *m, void* data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+
+ seq_printf(m, "%d objects\n", atomic_read(&dev->object_count));
+ seq_printf(m, "%d object bytes\n", atomic_read(&dev->object_memory));
+ seq_printf(m, "%d pinned\n", atomic_read(&dev->pin_count));
+ seq_printf(m, "%d pin bytes\n", atomic_read(&dev->pin_memory));
+ seq_printf(m, "%d gtt bytes\n", atomic_read(&dev->gtt_memory));
+ seq_printf(m, "%d gtt total\n", dev->gtt_total);
+ return 0;
+}
+
+#if DRM_DEBUG_CODE
+
+int drm_vma_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_vma_entry *pt;
+ struct vm_area_struct *vma;
+#if defined(__i386__)
+ unsigned int pgprot;
+#endif
+
+ mutex_lock(&dev->struct_mutex);
+ seq_printf(m, "vma use count: %d, high_memory = %p, 0x%08lx\n",
+ atomic_read(&dev->vma_count),
+ high_memory, virt_to_phys(high_memory));
+
+ list_for_each_entry(pt, &dev->vmalist, head) {
+ vma = pt->vma;
+ if (!vma)
+ continue;
+ seq_printf(m,
+ "\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx000",
+ pt->pid, vma->vm_start, vma->vm_end,
+ vma->vm_flags & VM_READ ? 'r' : '-',
+ vma->vm_flags & VM_WRITE ? 'w' : '-',
+ vma->vm_flags & VM_EXEC ? 'x' : '-',
+ vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+ vma->vm_flags & VM_LOCKED ? 'l' : '-',
+ vma->vm_flags & VM_IO ? 'i' : '-',
+ vma->vm_pgoff);
+
+#if defined(__i386__)
+ pgprot = pgprot_val(vma->vm_page_prot);
+ seq_printf(m, " %c%c%c%c%c%c%c%c%c",
+ pgprot & _PAGE_PRESENT ? 'p' : '-',
+ pgprot & _PAGE_RW ? 'w' : 'r',
+ pgprot & _PAGE_USER ? 'u' : 's',
+ pgprot & _PAGE_PWT ? 't' : 'b',
+ pgprot & _PAGE_PCD ? 'u' : 'c',
+ pgprot & _PAGE_ACCESSED ? 'a' : '-',
+ pgprot & _PAGE_DIRTY ? 'd' : '-',
+ pgprot & _PAGE_PSE ? 'm' : 'k',
+ pgprot & _PAGE_GLOBAL ? 'g' : 'l');
+#endif
+ seq_printf(m, "\n");
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+#endif
+
diff --git a/trunk/drivers/gpu/drm/drm_proc.c b/trunk/drivers/gpu/drm/drm_proc.c
index 8df849f66830..9b3c5af61e98 100644
--- a/trunk/drivers/gpu/drm/drm_proc.c
+++ b/trunk/drivers/gpu/drm/drm_proc.c
@@ -37,697 +37,196 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
+#include
#include "drmP.h"
-static int drm_name_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_vm_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_clients_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_queues_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_bufs_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_vblank_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_gem_name_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int drm_gem_object_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-#if DRM_DEBUG_CODE
-static int drm_vma_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-#endif
+
+/***************************************************
+ * Initialization, etc.
+ **************************************************/
/**
* Proc file list.
*/
-static struct drm_proc_list {
- const char *name; /**< file name */
- int (*f) (char *, char **, off_t, int, int *, void *); /**< proc callback*/
- u32 driver_features; /**< Required driver features for this entry */
-} drm_proc_list[] = {
+static struct drm_info_list drm_proc_list[] = {
{"name", drm_name_info, 0},
- {"mem", drm_mem_info, 0},
{"vm", drm_vm_info, 0},
{"clients", drm_clients_info, 0},
{"queues", drm_queues_info, 0},
{"bufs", drm_bufs_info, 0},
- {"vblank", drm_vblank_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
{"gem_objects", drm_gem_object_info, DRIVER_GEM},
#if DRM_DEBUG_CODE
- {"vma", drm_vma_info},
+ {"vma", drm_vma_info, 0},
#endif
};
-
#define DRM_PROC_ENTRIES ARRAY_SIZE(drm_proc_list)
+static int drm_proc_open(struct inode *inode, struct file *file)
+{
+ struct drm_info_node* node = PDE(inode)->data;
+
+ return single_open(file, node->info_ent->show, node);
+}
+
+static const struct file_operations drm_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
/**
- * Initialize the DRI proc filesystem for a device.
+ * Initialize a given set of proc files for a device
*
- * \param dev DRM device.
- * \param minor device minor number.
+ * \param files The array of files to create
+ * \param count The number of files given
* \param root DRI proc dir entry.
- * \param dev_root resulting DRI device proc dir entry.
- * \return root entry pointer on success, or NULL on failure.
+ * \param minor device minor number
+ * \return Zero on success, non-zero on failure
*
- * Create the DRI proc root entry "/proc/dri", the device proc root entry
- * "/proc/dri/%minor%/", and each entry in proc_list as
- * "/proc/dri/%minor%/%name%".
+ * Create a given set of proc files represented by an array of
+ * gdm_proc_lists in the given root directory.
*/
-int drm_proc_init(struct drm_minor *minor, int minor_id,
- struct proc_dir_entry *root)
+int drm_proc_create_files(struct drm_info_list *files, int count,
+ struct proc_dir_entry *root, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct proc_dir_entry *ent;
- int i, j, ret;
+ struct drm_info_node *tmp;
char name[64];
+ int i, ret;
- sprintf(name, "%d", minor_id);
- minor->dev_root = proc_mkdir(name, root);
- if (!minor->dev_root) {
- DRM_ERROR("Cannot create /proc/dri/%s\n", name);
- return -1;
- }
-
- for (i = 0; i < DRM_PROC_ENTRIES; i++) {
- u32 features = drm_proc_list[i].driver_features;
+ for (i = 0; i < count; i++) {
+ u32 features = files[i].driver_features;
if (features != 0 &&
(dev->driver->driver_features & features) != features)
continue;
- ent = create_proc_entry(drm_proc_list[i].name,
- S_IFREG | S_IRUGO, minor->dev_root);
+ tmp = drm_alloc(sizeof(struct drm_info_node), _DRM_DRIVER);
+ ent = create_proc_entry(files[i].name, S_IFREG | S_IRUGO, root);
if (!ent) {
DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
- name, drm_proc_list[i].name);
+ name, files[i].name);
+ drm_free(tmp, sizeof(struct drm_info_node),
+ _DRM_DRIVER);
ret = -1;
goto fail;
}
- ent->read_proc = drm_proc_list[i].f;
- ent->data = minor;
- }
- if (dev->driver->proc_init) {
- ret = dev->driver->proc_init(minor);
- if (ret) {
- DRM_ERROR("DRM: Driver failed to initialize "
- "/proc/dri.\n");
- goto fail;
- }
+ ent->proc_fops = &drm_proc_fops;
+ ent->data = tmp;
+ tmp->minor = minor;
+ tmp->info_ent = &files[i];
+ list_add(&(tmp->list), &(minor->proc_nodes.list));
}
-
return 0;
- fail:
- for (j = 0; j < i; j++)
- remove_proc_entry(drm_proc_list[i].name,
- minor->dev_root);
- remove_proc_entry(name, root);
- minor->dev_root = NULL;
+fail:
+ for (i = 0; i < count; i++)
+ remove_proc_entry(drm_proc_list[i].name, minor->proc_root);
return ret;
}
/**
- * Cleanup the proc filesystem resources.
+ * Initialize the DRI proc filesystem for a device
*
- * \param minor device minor number.
+ * \param dev DRM device
+ * \param minor device minor number
* \param root DRI proc dir entry.
- * \param dev_root DRI device proc dir entry.
- * \return always zero.
+ * \param dev_root resulting DRI device proc dir entry.
+ * \return root entry pointer on success, or NULL on failure.
*
- * Remove all proc entries created by proc_init().
+ * Create the DRI proc root entry "/proc/dri", the device proc root entry
+ * "/proc/dri/%minor%/", and each entry in proc_list as
+ * "/proc/dri/%minor%/%name%".
*/
-int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root)
+int drm_proc_init(struct drm_minor *minor, int minor_id,
+ struct proc_dir_entry *root)
{
struct drm_device *dev = minor->dev;
- int i;
char name[64];
+ int ret;
- if (!root || !minor->dev_root)
- return 0;
-
- if (dev->driver->proc_cleanup)
- dev->driver->proc_cleanup(minor);
-
- for (i = 0; i < DRM_PROC_ENTRIES; i++)
- remove_proc_entry(drm_proc_list[i].name, minor->dev_root);
- sprintf(name, "%d", minor->index);
- remove_proc_entry(name, root);
-
- return 0;
-}
-
-/**
- * Called when "/proc/dri/.../name" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- *
- * Prints the device name together with the bus id if available.
- */
-static int drm_name_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_master *master = minor->master;
- struct drm_device *dev = minor->dev;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
+ INIT_LIST_HEAD(&minor->proc_nodes.list);
+ sprintf(name, "%d", minor_id);
+ minor->proc_root = proc_mkdir(name, root);
+ if (!minor->proc_root) {
+ DRM_ERROR("Cannot create /proc/dri/%s\n", name);
+ return -1;
}
- if (!master)
- return 0;
-
- *start = &buf[offset];
- *eof = 0;
-
- if (master->unique) {
- DRM_PROC_PRINT("%s %s %s\n",
- dev->driver->pci_driver.name,
- pci_name(dev->pdev), master->unique);
- } else {
- DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name,
- pci_name(dev->pdev));
+ ret = drm_proc_create_files(drm_proc_list, DRM_PROC_ENTRIES,
+ minor->proc_root, minor);
+ if (ret) {
+ remove_proc_entry(name, root);
+ minor->proc_root = NULL;
+ DRM_ERROR("Failed to create core drm proc files\n");
+ return ret;
}
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Called when "/proc/dri/.../vm" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- *
- * Prints information about all mappings in drm_device::maplist.
- */
-static int drm__vm_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
- struct drm_map *map;
- struct drm_map_list *r_list;
-
- /* Hardcoded from _DRM_FRAME_BUFFER,
- _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and
- _DRM_SCATTER_GATHER and _DRM_CONSISTENT */
- const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" };
- const char *type;
- int i;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
-
- DRM_PROC_PRINT("slot offset size type flags "
- "address mtrr\n\n");
- i = 0;
- list_for_each_entry(r_list, &dev->maplist, head) {
- map = r_list->map;
- if (!map)
- continue;
- if (map->type < 0 || map->type > 5)
- type = "??";
- else
- type = types[map->type];
- DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ",
- i,
- map->offset,
- map->size, type, map->flags,
- (unsigned long) r_list->user_token);
- if (map->mtrr < 0) {
- DRM_PROC_PRINT("none\n");
- } else {
- DRM_PROC_PRINT("%4d\n", map->mtrr);
+ if (dev->driver->proc_init) {
+ ret = dev->driver->proc_init(minor);
+ if (ret) {
+ DRM_ERROR("DRM: Driver failed to initialize "
+ "/proc/dri.\n");
+ return ret;
}
- i++;
- }
-
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Simply calls _vm_info() while holding the drm_device::struct_mutex lock.
- */
-static int drm_vm_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__vm_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
-}
-
-/**
- * Called when "/proc/dri/.../queues" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- */
-static int drm__queues_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
- int i;
- struct drm_queue *q;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
}
-
- *start = &buf[offset];
- *eof = 0;
-
- DRM_PROC_PRINT(" ctx/flags use fin"
- " blk/rw/rwf wait flushed queued"
- " locks\n\n");
- for (i = 0; i < dev->queue_count; i++) {
- q = dev->queuelist[i];
- atomic_inc(&q->use_count);
- DRM_PROC_PRINT_RET(atomic_dec(&q->use_count),
- "%5d/0x%03x %5d %5d"
- " %5d/%c%c/%c%c%c %5Zd\n",
- i,
- q->flags,
- atomic_read(&q->use_count),
- atomic_read(&q->finalization),
- atomic_read(&q->block_count),
- atomic_read(&q->block_read) ? 'r' : '-',
- atomic_read(&q->block_write) ? 'w' : '-',
- waitqueue_active(&q->read_queue) ? 'r' : '-',
- waitqueue_active(&q->
- write_queue) ? 'w' : '-',
- waitqueue_active(&q->
- flush_queue) ? 'f' : '-',
- DRM_BUFCOUNT(&q->waitlist));
- atomic_dec(&q->use_count);
- }
-
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Simply calls _queues_info() while holding the drm_device::struct_mutex lock.
- */
-static int drm_queues_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__queues_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
+ return 0;
}
-/**
- * Called when "/proc/dri/.../bufs" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- */
-static int drm__bufs_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
+int drm_proc_remove_files(struct drm_info_list *files, int count,
+ struct drm_minor *minor)
{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
- struct drm_device_dma *dma = dev->dma;
+ struct list_head *pos, *q;
+ struct drm_info_node *tmp;
int i;
- if (!dma || offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
-
- DRM_PROC_PRINT(" o size count free segs pages kB\n\n");
- for (i = 0; i <= DRM_MAX_ORDER; i++) {
- if (dma->bufs[i].buf_count)
- DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n",
- i,
- dma->bufs[i].buf_size,
- dma->bufs[i].buf_count,
- atomic_read(&dma->bufs[i]
- .freelist.count),
- dma->bufs[i].seg_count,
- dma->bufs[i].seg_count
- * (1 << dma->bufs[i].page_order),
- (dma->bufs[i].seg_count
- * (1 << dma->bufs[i].page_order))
- * PAGE_SIZE / 1024);
- }
- DRM_PROC_PRINT("\n");
- for (i = 0; i < dma->buf_count; i++) {
- if (i && !(i % 32))
- DRM_PROC_PRINT("\n");
- DRM_PROC_PRINT(" %d", dma->buflist[i]->list);
+ for (i = 0; i < count; i++) {
+ list_for_each_safe(pos, q, &minor->proc_nodes.list) {
+ tmp = list_entry(pos, struct drm_info_node, list);
+ if (tmp->info_ent == &files[i]) {
+ remove_proc_entry(files[i].name,
+ minor->proc_root);
+ list_del(pos);
+ drm_free(tmp, sizeof(struct drm_info_node),
+ _DRM_DRIVER);
+ }
+ }
}
- DRM_PROC_PRINT("\n");
-
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Simply calls _bufs_info() while holding the drm_device::struct_mutex lock.
- */
-static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__bufs_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
+ return 0;
}
/**
- * Called when "/proc/dri/.../vblank" is read.
+ * Cleanup the proc filesystem resources.
*
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- */
-static int drm__vblank_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
- int crtc;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
-
- for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
- DRM_PROC_PRINT("CRTC %d enable: %d\n",
- crtc, atomic_read(&dev->vblank_refcount[crtc]));
- DRM_PROC_PRINT("CRTC %d counter: %d\n",
- crtc, drm_vblank_count(dev, crtc));
- DRM_PROC_PRINT("CRTC %d last wait: %d\n",
- crtc, dev->last_vblank_wait[crtc]);
- DRM_PROC_PRINT("CRTC %d in modeset: %d\n",
- crtc, dev->vblank_inmodeset[crtc]);
- }
-
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Simply calls _vblank_info() while holding the drm_device::struct_mutex lock.
- */
-static int drm_vblank_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__vblank_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
-}
-
-/**
- * Called when "/proc/dri/.../clients" is read.
+ * \param minor device minor number.
+ * \param root DRI proc dir entry.
+ * \param dev_root DRI device proc dir entry.
+ * \return always zero.
*
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param request requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
+ * Remove all proc entries created by proc_init().
*/
-static int drm__clients_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
+int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root)
{
- struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
- int len = 0;
- struct drm_file *priv;
+ char name[64];
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
+ if (!root || !minor->proc_root)
return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
-
- DRM_PROC_PRINT("a dev pid uid magic ioctls\n\n");
- list_for_each_entry(priv, &dev->filelist, lhead) {
- DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
- priv->authenticated ? 'y' : 'n',
- priv->minor->index,
- priv->pid,
- priv->uid, priv->magic, priv->ioctl_count);
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-/**
- * Simply calls _clients_info() while holding the drm_device::struct_mutex lock.
- */
-static int drm_clients_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__clients_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
-}
-
-struct drm_gem_name_info_data {
- int len;
- char *buf;
- int eof;
-};
+ if (dev->driver->proc_cleanup)
+ dev->driver->proc_cleanup(minor);
-static int drm_gem_one_name_info(int id, void *ptr, void *data)
-{
- struct drm_gem_object *obj = ptr;
- struct drm_gem_name_info_data *nid = data;
+ drm_proc_remove_files(drm_proc_list, DRM_PROC_ENTRIES, minor);
- DRM_INFO("name %d size %zd\n", obj->name, obj->size);
- if (nid->eof)
- return 0;
+ sprintf(name, "%d", minor->index);
+ remove_proc_entry(name, root);
- nid->len += sprintf(&nid->buf[nid->len],
- "%6d %8zd %7d %8d\n",
- obj->name, obj->size,
- atomic_read(&obj->handlecount.refcount),
- atomic_read(&obj->refcount.refcount));
- if (nid->len > DRM_PROC_LIMIT) {
- nid->eof = 1;
- return 0;
- }
return 0;
}
-static int drm_gem_name_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- struct drm_gem_name_info_data nid;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- nid.len = sprintf(buf, " name size handles refcount\n");
- nid.buf = buf;
- nid.eof = 0;
- idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, &nid);
-
- *start = &buf[offset];
- *eof = 0;
- if (nid.len > request + offset)
- return request;
- *eof = 1;
- return nid.len - offset;
-}
-
-static int drm_gem_object_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("%d objects\n", atomic_read(&dev->object_count));
- DRM_PROC_PRINT("%d object bytes\n", atomic_read(&dev->object_memory));
- DRM_PROC_PRINT("%d pinned\n", atomic_read(&dev->pin_count));
- DRM_PROC_PRINT("%d pin bytes\n", atomic_read(&dev->pin_memory));
- DRM_PROC_PRINT("%d gtt bytes\n", atomic_read(&dev->gtt_memory));
- DRM_PROC_PRINT("%d gtt total\n", dev->gtt_total);
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-#if DRM_DEBUG_CODE
-
-static int drm__vma_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int len = 0;
- struct drm_vma_entry *pt;
- struct vm_area_struct *vma;
-#if defined(__i386__)
- unsigned int pgprot;
-#endif
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
-
- DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n",
- atomic_read(&dev->vma_count),
- high_memory, virt_to_phys(high_memory));
- list_for_each_entry(pt, &dev->vmalist, head) {
- if (!(vma = pt->vma))
- continue;
- DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx000",
- pt->pid,
- vma->vm_start,
- vma->vm_end,
- vma->vm_flags & VM_READ ? 'r' : '-',
- vma->vm_flags & VM_WRITE ? 'w' : '-',
- vma->vm_flags & VM_EXEC ? 'x' : '-',
- vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
- vma->vm_flags & VM_LOCKED ? 'l' : '-',
- vma->vm_flags & VM_IO ? 'i' : '-',
- vma->vm_pgoff);
-
-#if defined(__i386__)
- pgprot = pgprot_val(vma->vm_page_prot);
- DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c",
- pgprot & _PAGE_PRESENT ? 'p' : '-',
- pgprot & _PAGE_RW ? 'w' : 'r',
- pgprot & _PAGE_USER ? 'u' : 's',
- pgprot & _PAGE_PWT ? 't' : 'b',
- pgprot & _PAGE_PCD ? 'u' : 'c',
- pgprot & _PAGE_ACCESSED ? 'a' : '-',
- pgprot & _PAGE_DIRTY ? 'd' : '-',
- pgprot & _PAGE_PSE ? 'm' : 'k',
- pgprot & _PAGE_GLOBAL ? 'g' : 'l');
-#endif
- DRM_PROC_PRINT("\n");
- }
-
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int drm_vma_info(char *buf, char **start, off_t offset, int request,
- int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- int ret;
-
- mutex_lock(&dev->struct_mutex);
- ret = drm__vma_info(buf, start, offset, request, eof, data);
- mutex_unlock(&dev->struct_mutex);
- return ret;
-}
-#endif
diff --git a/trunk/drivers/gpu/drm/drm_stub.c b/trunk/drivers/gpu/drm/drm_stub.c
index 7c8b15b22bf2..48f33be8fd0f 100644
--- a/trunk/drivers/gpu/drm/drm_stub.c
+++ b/trunk/drivers/gpu/drm/drm_stub.c
@@ -50,6 +50,7 @@ struct idr drm_minors_idr;
struct class *drm_class;
struct proc_dir_entry *drm_proc_root;
+struct dentry *drm_debugfs_root;
static int drm_minor_get_id(struct drm_device *dev, int type)
{
@@ -313,7 +314,15 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
goto err_mem;
}
} else
- new_minor->dev_root = NULL;
+ new_minor->proc_root = NULL;
+
+#if defined(CONFIG_DEBUG_FS)
+ ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
+ if (ret) {
+ DRM_ERROR("DRM: Failed to initialize /debugfs/dri.\n");
+ goto err_g2;
+ }
+#endif
ret = drm_sysfs_device_add(new_minor);
if (ret) {
@@ -451,6 +460,10 @@ int drm_put_minor(struct drm_minor **minor_p)
if (minor->type == DRM_MINOR_LEGACY)
drm_proc_cleanup(minor, drm_proc_root);
+#if defined(CONFIG_DEBUG_FS)
+ drm_debugfs_cleanup(minor);
+#endif
+
drm_sysfs_device_remove(minor);
idr_remove(&drm_minors_idr, minor->index);
diff --git a/trunk/drivers/gpu/drm/i915/Makefile b/trunk/drivers/gpu/drm/i915/Makefile
index 793cba39d832..51c5a050aa73 100644
--- a/trunk/drivers/gpu/drm/i915/Makefile
+++ b/trunk/drivers/gpu/drm/i915/Makefile
@@ -7,7 +7,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
i915_suspend.o \
i915_gem.o \
i915_gem_debug.o \
- i915_gem_proc.o \
+ i915_gem_debugfs.o \
i915_gem_tiling.o \
intel_display.o \
intel_crt.o \
diff --git a/trunk/drivers/gpu/drm/i915/i915_dma.c b/trunk/drivers/gpu/drm/i915/i915_dma.c
index 6d21b9e48b89..a818b377e1f7 100644
--- a/trunk/drivers/gpu/drm/i915/i915_dma.c
+++ b/trunk/drivers/gpu/drm/i915/i915_dma.c
@@ -41,7 +41,6 @@
int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD;
u32 last_acthd = I915_READ(acthd_reg);
@@ -58,8 +57,12 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
if (ring->space >= n)
return 0;
- if (master_priv->sarea_priv)
- master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ if (dev->primary->master) {
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ }
+
if (ring->head != last_head)
i = 0;
@@ -356,7 +359,7 @@ static int validate_cmd(int cmd)
return ret;
}
-static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords)
+static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i;
@@ -370,8 +373,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
for (i = 0; i < dwords;) {
int cmd, sz;
- if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))
- return -EINVAL;
+ cmd = buffer[i];
if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
return -EINVAL;
@@ -379,11 +381,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
OUT_RING(cmd);
while (++i, --sz) {
- if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
- sizeof(cmd))) {
- return -EINVAL;
- }
- OUT_RING(cmd);
+ OUT_RING(buffer[i]);
}
}
@@ -397,17 +395,13 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor
int
i915_emit_box(struct drm_device *dev,
- struct drm_clip_rect __user *boxes,
+ struct drm_clip_rect *boxes,
int i, int DR1, int DR4)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_clip_rect box;
+ struct drm_clip_rect box = boxes[i];
RING_LOCALS;
- if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
- return -EFAULT;
- }
-
if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {
DRM_ERROR("Bad box %d,%d..%d,%d\n",
box.x1, box.y1, box.x2, box.y2);
@@ -460,7 +454,9 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
}
static int i915_dispatch_cmdbuffer(struct drm_device * dev,
- drm_i915_cmdbuffer_t * cmd)
+ drm_i915_cmdbuffer_t *cmd,
+ struct drm_clip_rect *cliprects,
+ void *cmdbuf)
{
int nbox = cmd->num_cliprects;
int i = 0, count, ret;
@@ -476,13 +472,13 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
for (i = 0; i < count; i++) {
if (i < nbox) {
- ret = i915_emit_box(dev, cmd->cliprects, i,
+ ret = i915_emit_box(dev, cliprects, i,
cmd->DR1, cmd->DR4);
if (ret)
return ret;
}
- ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4);
+ ret = i915_emit_cmds(dev, cmdbuf, cmd->sz / 4);
if (ret)
return ret;
}
@@ -492,10 +488,10 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
}
static int i915_dispatch_batchbuffer(struct drm_device * dev,
- drm_i915_batchbuffer_t * batch)
+ drm_i915_batchbuffer_t * batch,
+ struct drm_clip_rect *cliprects)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_clip_rect __user *boxes = batch->cliprects;
int nbox = batch->num_cliprects;
int i = 0, count;
RING_LOCALS;
@@ -511,7 +507,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
for (i = 0; i < count; i++) {
if (i < nbox) {
- int ret = i915_emit_box(dev, boxes, i,
+ int ret = i915_emit_box(dev, cliprects, i,
batch->DR1, batch->DR4);
if (ret)
return ret;
@@ -626,6 +622,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
master_priv->sarea_priv;
drm_i915_batchbuffer_t *batch = data;
int ret;
+ struct drm_clip_rect *cliprects = NULL;
if (!dev_priv->allow_batchbuffer) {
DRM_ERROR("Batchbuffer ioctl disabled\n");
@@ -637,17 +634,35 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
- if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects,
- batch->num_cliprects *
- sizeof(struct drm_clip_rect)))
- return -EFAULT;
+ if (batch->num_cliprects < 0)
+ return -EINVAL;
+
+ if (batch->num_cliprects) {
+ cliprects = drm_calloc(batch->num_cliprects,
+ sizeof(struct drm_clip_rect),
+ DRM_MEM_DRIVER);
+ if (cliprects == NULL)
+ return -ENOMEM;
+
+ ret = copy_from_user(cliprects, batch->cliprects,
+ batch->num_cliprects *
+ sizeof(struct drm_clip_rect));
+ if (ret != 0)
+ goto fail_free;
+ }
mutex_lock(&dev->struct_mutex);
- ret = i915_dispatch_batchbuffer(dev, batch);
+ ret = i915_dispatch_batchbuffer(dev, batch, cliprects);
mutex_unlock(&dev->struct_mutex);
if (sarea_priv)
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+
+fail_free:
+ drm_free(cliprects,
+ batch->num_cliprects * sizeof(struct drm_clip_rect),
+ DRM_MEM_DRIVER);
+
return ret;
}
@@ -659,6 +674,8 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
master_priv->sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data;
+ struct drm_clip_rect *cliprects = NULL;
+ void *batch_data;
int ret;
DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
@@ -666,25 +683,50 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
- if (cmdbuf->num_cliprects &&
- DRM_VERIFYAREA_READ(cmdbuf->cliprects,
- cmdbuf->num_cliprects *
- sizeof(struct drm_clip_rect))) {
- DRM_ERROR("Fault accessing cliprects\n");
- return -EFAULT;
+ if (cmdbuf->num_cliprects < 0)
+ return -EINVAL;
+
+ batch_data = drm_alloc(cmdbuf->sz, DRM_MEM_DRIVER);
+ if (batch_data == NULL)
+ return -ENOMEM;
+
+ ret = copy_from_user(batch_data, cmdbuf->buf, cmdbuf->sz);
+ if (ret != 0)
+ goto fail_batch_free;
+
+ if (cmdbuf->num_cliprects) {
+ cliprects = drm_calloc(cmdbuf->num_cliprects,
+ sizeof(struct drm_clip_rect),
+ DRM_MEM_DRIVER);
+ if (cliprects == NULL)
+ goto fail_batch_free;
+
+ ret = copy_from_user(cliprects, cmdbuf->cliprects,
+ cmdbuf->num_cliprects *
+ sizeof(struct drm_clip_rect));
+ if (ret != 0)
+ goto fail_clip_free;
}
mutex_lock(&dev->struct_mutex);
- ret = i915_dispatch_cmdbuffer(dev, cmdbuf);
+ ret = i915_dispatch_cmdbuffer(dev, cmdbuf, cliprects, batch_data);
mutex_unlock(&dev->struct_mutex);
if (ret) {
DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
- return ret;
+ goto fail_batch_free;
}
if (sarea_priv)
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
- return 0;
+
+fail_batch_free:
+ drm_free(batch_data, cmdbuf->sz, DRM_MEM_DRIVER);
+fail_clip_free:
+ drm_free(cliprects,
+ cmdbuf->num_cliprects * sizeof(struct drm_clip_rect),
+ DRM_MEM_DRIVER);
+
+ return ret;
}
static int i915_flip_bufs(struct drm_device *dev, void *data,
diff --git a/trunk/drivers/gpu/drm/i915/i915_drv.c b/trunk/drivers/gpu/drm/i915/i915_drv.c
index b293ef0bae71..dcb91f5df6e3 100644
--- a/trunk/drivers/gpu/drm/i915/i915_drv.c
+++ b/trunk/drivers/gpu/drm/i915/i915_drv.c
@@ -150,8 +150,10 @@ static struct drm_driver driver = {
.get_reg_ofs = drm_core_get_reg_ofs,
.master_create = i915_master_create,
.master_destroy = i915_master_destroy,
- .proc_init = i915_gem_proc_init,
- .proc_cleanup = i915_gem_proc_cleanup,
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = i915_gem_debugfs_init,
+ .debugfs_cleanup = i915_gem_debugfs_cleanup,
+#endif
.gem_init_object = i915_gem_init_object,
.gem_free_object = i915_gem_free_object,
.gem_vm_ops = &i915_gem_vm_ops,
diff --git a/trunk/drivers/gpu/drm/i915/i915_drv.h b/trunk/drivers/gpu/drm/i915/i915_drv.h
index d6cc9861e0a1..c1685d0c704f 100644
--- a/trunk/drivers/gpu/drm/i915/i915_drv.h
+++ b/trunk/drivers/gpu/drm/i915/i915_drv.h
@@ -404,7 +404,8 @@ struct drm_i915_gem_object {
/** AGP memory structure for our GTT binding. */
DRM_AGP_MEM *agp_mem;
- struct page **page_list;
+ struct page **pages;
+ int pages_refcount;
/**
* Current offset of the object in GTT space.
@@ -519,7 +520,7 @@ extern int i915_driver_device_is_agp(struct drm_device * dev);
extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
extern int i915_emit_box(struct drm_device *dev,
- struct drm_clip_rect __user *boxes,
+ struct drm_clip_rect *boxes,
int i, int DR1, int DR4);
/* i915_irq.c */
@@ -604,8 +605,6 @@ int i915_gem_get_tiling(struct drm_device *dev, void *data,
int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_gem_load(struct drm_device *dev);
-int i915_gem_proc_init(struct drm_minor *minor);
-void i915_gem_proc_cleanup(struct drm_minor *minor);
int i915_gem_init_object(struct drm_gem_object *obj);
void i915_gem_free_object(struct drm_gem_object *obj);
int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment);
@@ -649,6 +648,10 @@ void i915_gem_dump_object(struct drm_gem_object *obj, int len,
const char *where, uint32_t mark);
void i915_dump_lru(struct drm_device *dev, const char *where);
+/* i915_debugfs.c */
+int i915_gem_debugfs_init(struct drm_minor *minor);
+void i915_gem_debugfs_cleanup(struct drm_minor *minor);
+
/* i915_suspend.c */
extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev);
@@ -784,15 +787,21 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
(dev)->pci_device == 0x2E22 || \
IS_GM45(dev))
+#define IS_IGDG(dev) ((dev)->pci_device == 0xa001)
+#define IS_IGDGM(dev) ((dev)->pci_device == 0xa011)
+#define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev))
+
#define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \
(dev)->pci_device == 0x29B2 || \
- (dev)->pci_device == 0x29D2)
+ (dev)->pci_device == 0x29D2 || \
+ (IS_IGD(dev)))
#define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \
IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev))
#define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \
- IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev))
+ IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev) || \
+ IS_IGD(dev))
#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev))
/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
diff --git a/trunk/drivers/gpu/drm/i915/i915_gem.c b/trunk/drivers/gpu/drm/i915/i915_gem.c
index 37427e4016cb..b52cba0f16d2 100644
--- a/trunk/drivers/gpu/drm/i915/i915_gem.c
+++ b/trunk/drivers/gpu/drm/i915/i915_gem.c
@@ -43,8 +43,8 @@ static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
uint64_t offset,
uint64_t size);
static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj);
-static int i915_gem_object_get_page_list(struct drm_gem_object *obj);
-static void i915_gem_object_free_page_list(struct drm_gem_object *obj);
+static int i915_gem_object_get_pages(struct drm_gem_object *obj);
+static void i915_gem_object_put_pages(struct drm_gem_object *obj);
static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
unsigned alignment);
@@ -136,6 +136,224 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
return 0;
}
+static inline int
+fast_shmem_read(struct page **pages,
+ loff_t page_base, int page_offset,
+ char __user *data,
+ int length)
+{
+ char __iomem *vaddr;
+ int ret;
+
+ vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
+ if (vaddr == NULL)
+ return -ENOMEM;
+ ret = __copy_to_user_inatomic(data, vaddr + page_offset, length);
+ kunmap_atomic(vaddr, KM_USER0);
+
+ return ret;
+}
+
+static inline int
+slow_shmem_copy(struct page *dst_page,
+ int dst_offset,
+ struct page *src_page,
+ int src_offset,
+ int length)
+{
+ char *dst_vaddr, *src_vaddr;
+
+ dst_vaddr = kmap_atomic(dst_page, KM_USER0);
+ if (dst_vaddr == NULL)
+ return -ENOMEM;
+
+ src_vaddr = kmap_atomic(src_page, KM_USER1);
+ if (src_vaddr == NULL) {
+ kunmap_atomic(dst_vaddr, KM_USER0);
+ return -ENOMEM;
+ }
+
+ memcpy(dst_vaddr + dst_offset, src_vaddr + src_offset, length);
+
+ kunmap_atomic(src_vaddr, KM_USER1);
+ kunmap_atomic(dst_vaddr, KM_USER0);
+
+ return 0;
+}
+
+/**
+ * This is the fast shmem pread path, which attempts to copy_from_user directly
+ * from the backing pages of the object to the user's address space. On a
+ * fault, it fails so we can fall back to i915_gem_shmem_pwrite_slow().
+ */
+static int
+i915_gem_shmem_pread_fast(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pread *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ ssize_t remain;
+ loff_t offset, page_base;
+ char __user *user_data;
+ int page_offset, page_length;
+ int ret;
+
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ remain = args->size;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_get_pages(obj);
+ if (ret != 0)
+ goto fail_unlock;
+
+ ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset,
+ args->size);
+ if (ret != 0)
+ goto fail_put_pages;
+
+ obj_priv = obj->driver_private;
+ offset = args->offset;
+
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * page_base = page offset within aperture
+ * page_offset = offset within page
+ * page_length = bytes to copy for this page
+ */
+ page_base = (offset & ~(PAGE_SIZE-1));
+ page_offset = offset & (PAGE_SIZE-1);
+ page_length = remain;
+ if ((page_offset + remain) > PAGE_SIZE)
+ page_length = PAGE_SIZE - page_offset;
+
+ ret = fast_shmem_read(obj_priv->pages,
+ page_base, page_offset,
+ user_data, page_length);
+ if (ret)
+ goto fail_put_pages;
+
+ remain -= page_length;
+ user_data += page_length;
+ offset += page_length;
+ }
+
+fail_put_pages:
+ i915_gem_object_put_pages(obj);
+fail_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+/**
+ * This is the fallback shmem pread path, which allocates temporary storage
+ * in kernel space to copy_to_user into outside of the struct_mutex, so we
+ * can copy out of the object's backing pages while holding the struct mutex
+ * and not take page faults.
+ */
+static int
+i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pread *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct mm_struct *mm = current->mm;
+ struct page **user_pages;
+ ssize_t remain;
+ loff_t offset, pinned_pages, i;
+ loff_t first_data_page, last_data_page, num_pages;
+ int shmem_page_index, shmem_page_offset;
+ int data_page_index, data_page_offset;
+ int page_length;
+ int ret;
+ uint64_t data_ptr = args->data_ptr;
+
+ remain = args->size;
+
+ /* Pin the user pages containing the data. We can't fault while
+ * holding the struct mutex, yet we want to hold it while
+ * dereferencing the user data.
+ */
+ first_data_page = data_ptr / PAGE_SIZE;
+ last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
+ num_pages = last_data_page - first_data_page + 1;
+
+ user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ if (user_pages == NULL)
+ return -ENOMEM;
+
+ down_read(&mm->mmap_sem);
+ pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
+ num_pages, 0, 0, user_pages, NULL);
+ up_read(&mm->mmap_sem);
+ if (pinned_pages < num_pages) {
+ ret = -EFAULT;
+ goto fail_put_user_pages;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_get_pages(obj);
+ if (ret != 0)
+ goto fail_unlock;
+
+ ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset,
+ args->size);
+ if (ret != 0)
+ goto fail_put_pages;
+
+ obj_priv = obj->driver_private;
+ offset = args->offset;
+
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * shmem_page_index = page number within shmem file
+ * shmem_page_offset = offset within page in shmem file
+ * data_page_index = page number in get_user_pages return
+ * data_page_offset = offset with data_page_index page.
+ * page_length = bytes to copy for this page
+ */
+ shmem_page_index = offset / PAGE_SIZE;
+ shmem_page_offset = offset & ~PAGE_MASK;
+ data_page_index = data_ptr / PAGE_SIZE - first_data_page;
+ data_page_offset = data_ptr & ~PAGE_MASK;
+
+ page_length = remain;
+ if ((shmem_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - shmem_page_offset;
+ if ((data_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - data_page_offset;
+
+ ret = slow_shmem_copy(user_pages[data_page_index],
+ data_page_offset,
+ obj_priv->pages[shmem_page_index],
+ shmem_page_offset,
+ page_length);
+ if (ret)
+ goto fail_put_pages;
+
+ remain -= page_length;
+ data_ptr += page_length;
+ offset += page_length;
+ }
+
+fail_put_pages:
+ i915_gem_object_put_pages(obj);
+fail_unlock:
+ mutex_unlock(&dev->struct_mutex);
+fail_put_user_pages:
+ for (i = 0; i < pinned_pages; i++) {
+ SetPageDirty(user_pages[i]);
+ page_cache_release(user_pages[i]);
+ }
+ kfree(user_pages);
+
+ return ret;
+}
+
/**
* Reads data from the object referenced by handle.
*
@@ -148,8 +366,6 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_pread *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
- ssize_t read;
- loff_t offset;
int ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
@@ -167,33 +383,13 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- mutex_lock(&dev->struct_mutex);
-
- ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset,
- args->size);
- if (ret != 0) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
-
- offset = args->offset;
-
- read = vfs_read(obj->filp, (char __user *)(uintptr_t)args->data_ptr,
- args->size, &offset);
- if (read != args->size) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- if (read < 0)
- return read;
- else
- return -EINVAL;
- }
+ ret = i915_gem_shmem_pread_fast(dev, obj, args, file_priv);
+ if (ret != 0)
+ ret = i915_gem_shmem_pread_slow(dev, obj, args, file_priv);
drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return 0;
+ return ret;
}
/* This is the fast write path which cannot handle
@@ -223,29 +419,51 @@ fast_user_write(struct io_mapping *mapping,
*/
static inline int
-slow_user_write(struct io_mapping *mapping,
- loff_t page_base, int page_offset,
- char __user *user_data,
- int length)
+slow_kernel_write(struct io_mapping *mapping,
+ loff_t gtt_base, int gtt_offset,
+ struct page *user_page, int user_offset,
+ int length)
{
- char __iomem *vaddr;
+ char *src_vaddr, *dst_vaddr;
unsigned long unwritten;
- vaddr = io_mapping_map_wc(mapping, page_base);
- if (vaddr == NULL)
- return -EFAULT;
- unwritten = __copy_from_user(vaddr + page_offset,
- user_data, length);
- io_mapping_unmap(vaddr);
+ dst_vaddr = io_mapping_map_atomic_wc(mapping, gtt_base);
+ src_vaddr = kmap_atomic(user_page, KM_USER1);
+ unwritten = __copy_from_user_inatomic_nocache(dst_vaddr + gtt_offset,
+ src_vaddr + user_offset,
+ length);
+ kunmap_atomic(src_vaddr, KM_USER1);
+ io_mapping_unmap_atomic(dst_vaddr);
if (unwritten)
return -EFAULT;
return 0;
}
+static inline int
+fast_shmem_write(struct page **pages,
+ loff_t page_base, int page_offset,
+ char __user *data,
+ int length)
+{
+ char __iomem *vaddr;
+
+ vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
+ if (vaddr == NULL)
+ return -ENOMEM;
+ __copy_from_user_inatomic(vaddr + page_offset, data, length);
+ kunmap_atomic(vaddr, KM_USER0);
+
+ return 0;
+}
+
+/**
+ * This is the fast pwrite path, where we copy the data directly from the
+ * user into the GTT, uncached.
+ */
static int
-i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
- struct drm_i915_gem_pwrite *args,
- struct drm_file *file_priv)
+i915_gem_gtt_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -273,7 +491,6 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
obj_priv = obj->driver_private;
offset = obj_priv->gtt_offset + args->offset;
- obj_priv->dirty = 1;
while (remain > 0) {
/* Operation in this page
@@ -292,16 +509,11 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
page_offset, user_data, page_length);
/* If we get a fault while copying data, then (presumably) our
- * source page isn't available. In this case, use the
- * non-atomic function
+ * source page isn't available. Return the error and we'll
+ * retry in the slow path.
*/
- if (ret) {
- ret = slow_user_write (dev_priv->mm.gtt_mapping,
- page_base, page_offset,
- user_data, page_length);
- if (ret)
- goto fail;
- }
+ if (ret)
+ goto fail;
remain -= page_length;
user_data += page_length;
@@ -315,39 +527,284 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
return ret;
}
+/**
+ * This is the fallback GTT pwrite path, which uses get_user_pages to pin
+ * the memory and maps it using kmap_atomic for copying.
+ *
+ * This code resulted in x11perf -rgb10text consuming about 10% more CPU
+ * than using i915_gem_gtt_pwrite_fast on a G45 (32-bit).
+ */
static int
-i915_gem_shmem_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
- struct drm_i915_gem_pwrite *args,
- struct drm_file *file_priv)
+i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ ssize_t remain;
+ loff_t gtt_page_base, offset;
+ loff_t first_data_page, last_data_page, num_pages;
+ loff_t pinned_pages, i;
+ struct page **user_pages;
+ struct mm_struct *mm = current->mm;
+ int gtt_page_offset, data_page_offset, data_page_index, page_length;
int ret;
- loff_t offset;
- ssize_t written;
+ uint64_t data_ptr = args->data_ptr;
+
+ remain = args->size;
+
+ /* Pin the user pages containing the data. We can't fault while
+ * holding the struct mutex, and all of the pwrite implementations
+ * want to hold it while dereferencing the user data.
+ */
+ first_data_page = data_ptr / PAGE_SIZE;
+ last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
+ num_pages = last_data_page - first_data_page + 1;
+
+ user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ if (user_pages == NULL)
+ return -ENOMEM;
+
+ down_read(&mm->mmap_sem);
+ pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
+ num_pages, 0, 0, user_pages, NULL);
+ up_read(&mm->mmap_sem);
+ if (pinned_pages < num_pages) {
+ ret = -EFAULT;
+ goto out_unpin_pages;
+ }
mutex_lock(&dev->struct_mutex);
+ ret = i915_gem_object_pin(obj, 0);
+ if (ret)
+ goto out_unlock;
+
+ ret = i915_gem_object_set_to_gtt_domain(obj, 1);
+ if (ret)
+ goto out_unpin_object;
+
+ obj_priv = obj->driver_private;
+ offset = obj_priv->gtt_offset + args->offset;
+
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * gtt_page_base = page offset within aperture
+ * gtt_page_offset = offset within page in aperture
+ * data_page_index = page number in get_user_pages return
+ * data_page_offset = offset with data_page_index page.
+ * page_length = bytes to copy for this page
+ */
+ gtt_page_base = offset & PAGE_MASK;
+ gtt_page_offset = offset & ~PAGE_MASK;
+ data_page_index = data_ptr / PAGE_SIZE - first_data_page;
+ data_page_offset = data_ptr & ~PAGE_MASK;
+
+ page_length = remain;
+ if ((gtt_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - gtt_page_offset;
+ if ((data_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - data_page_offset;
+
+ ret = slow_kernel_write(dev_priv->mm.gtt_mapping,
+ gtt_page_base, gtt_page_offset,
+ user_pages[data_page_index],
+ data_page_offset,
+ page_length);
+
+ /* If we get a fault while copying data, then (presumably) our
+ * source page isn't available. Return the error and we'll
+ * retry in the slow path.
+ */
+ if (ret)
+ goto out_unpin_object;
+
+ remain -= page_length;
+ offset += page_length;
+ data_ptr += page_length;
+ }
+
+out_unpin_object:
+ i915_gem_object_unpin(obj);
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+out_unpin_pages:
+ for (i = 0; i < pinned_pages; i++)
+ page_cache_release(user_pages[i]);
+ kfree(user_pages);
+
+ return ret;
+}
+
+/**
+ * This is the fast shmem pwrite path, which attempts to directly
+ * copy_from_user into the kmapped pages backing the object.
+ */
+static int
+i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ ssize_t remain;
+ loff_t offset, page_base;
+ char __user *user_data;
+ int page_offset, page_length;
+ int ret;
+
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ remain = args->size;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_get_pages(obj);
+ if (ret != 0)
+ goto fail_unlock;
ret = i915_gem_object_set_to_cpu_domain(obj, 1);
- if (ret) {
- mutex_unlock(&dev->struct_mutex);
- return ret;
+ if (ret != 0)
+ goto fail_put_pages;
+
+ obj_priv = obj->driver_private;
+ offset = args->offset;
+ obj_priv->dirty = 1;
+
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * page_base = page offset within aperture
+ * page_offset = offset within page
+ * page_length = bytes to copy for this page
+ */
+ page_base = (offset & ~(PAGE_SIZE-1));
+ page_offset = offset & (PAGE_SIZE-1);
+ page_length = remain;
+ if ((page_offset + remain) > PAGE_SIZE)
+ page_length = PAGE_SIZE - page_offset;
+
+ ret = fast_shmem_write(obj_priv->pages,
+ page_base, page_offset,
+ user_data, page_length);
+ if (ret)
+ goto fail_put_pages;
+
+ remain -= page_length;
+ user_data += page_length;
+ offset += page_length;
}
+fail_put_pages:
+ i915_gem_object_put_pages(obj);
+fail_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+/**
+ * This is the fallback shmem pwrite path, which uses get_user_pages to pin
+ * the memory and maps it using kmap_atomic for copying.
+ *
+ * This avoids taking mmap_sem for faulting on the user's address while the
+ * struct_mutex is held.
+ */
+static int
+i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct mm_struct *mm = current->mm;
+ struct page **user_pages;
+ ssize_t remain;
+ loff_t offset, pinned_pages, i;
+ loff_t first_data_page, last_data_page, num_pages;
+ int shmem_page_index, shmem_page_offset;
+ int data_page_index, data_page_offset;
+ int page_length;
+ int ret;
+ uint64_t data_ptr = args->data_ptr;
+
+ remain = args->size;
+
+ /* Pin the user pages containing the data. We can't fault while
+ * holding the struct mutex, and all of the pwrite implementations
+ * want to hold it while dereferencing the user data.
+ */
+ first_data_page = data_ptr / PAGE_SIZE;
+ last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
+ num_pages = last_data_page - first_data_page + 1;
+
+ user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
+ if (user_pages == NULL)
+ return -ENOMEM;
+
+ down_read(&mm->mmap_sem);
+ pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
+ num_pages, 0, 0, user_pages, NULL);
+ up_read(&mm->mmap_sem);
+ if (pinned_pages < num_pages) {
+ ret = -EFAULT;
+ goto fail_put_user_pages;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_get_pages(obj);
+ if (ret != 0)
+ goto fail_unlock;
+
+ ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+ if (ret != 0)
+ goto fail_put_pages;
+
+ obj_priv = obj->driver_private;
offset = args->offset;
+ obj_priv->dirty = 1;
- written = vfs_write(obj->filp,
- (char __user *)(uintptr_t) args->data_ptr,
- args->size, &offset);
- if (written != args->size) {
- mutex_unlock(&dev->struct_mutex);
- if (written < 0)
- return written;
- else
- return -EINVAL;
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * shmem_page_index = page number within shmem file
+ * shmem_page_offset = offset within page in shmem file
+ * data_page_index = page number in get_user_pages return
+ * data_page_offset = offset with data_page_index page.
+ * page_length = bytes to copy for this page
+ */
+ shmem_page_index = offset / PAGE_SIZE;
+ shmem_page_offset = offset & ~PAGE_MASK;
+ data_page_index = data_ptr / PAGE_SIZE - first_data_page;
+ data_page_offset = data_ptr & ~PAGE_MASK;
+
+ page_length = remain;
+ if ((shmem_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - shmem_page_offset;
+ if ((data_page_offset + page_length) > PAGE_SIZE)
+ page_length = PAGE_SIZE - data_page_offset;
+
+ ret = slow_shmem_copy(obj_priv->pages[shmem_page_index],
+ shmem_page_offset,
+ user_pages[data_page_index],
+ data_page_offset,
+ page_length);
+ if (ret)
+ goto fail_put_pages;
+
+ remain -= page_length;
+ data_ptr += page_length;
+ offset += page_length;
}
+fail_put_pages:
+ i915_gem_object_put_pages(obj);
+fail_unlock:
mutex_unlock(&dev->struct_mutex);
+fail_put_user_pages:
+ for (i = 0; i < pinned_pages; i++)
+ page_cache_release(user_pages[i]);
+ kfree(user_pages);
- return 0;
+ return ret;
}
/**
@@ -388,10 +845,19 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
if (obj_priv->phys_obj)
ret = i915_gem_phys_pwrite(dev, obj, args, file_priv);
else if (obj_priv->tiling_mode == I915_TILING_NONE &&
- dev->gtt_total != 0)
- ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv);
- else
- ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv);
+ dev->gtt_total != 0) {
+ ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file_priv);
+ if (ret == -EFAULT) {
+ ret = i915_gem_gtt_pwrite_slow(dev, obj, args,
+ file_priv);
+ }
+ } else {
+ ret = i915_gem_shmem_pwrite_fast(dev, obj, args, file_priv);
+ if (ret == -EFAULT) {
+ ret = i915_gem_shmem_pwrite_slow(dev, obj, args,
+ file_priv);
+ }
+ }
#if WATCH_PWRITE
if (ret)
@@ -816,29 +1282,30 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
}
static void
-i915_gem_object_free_page_list(struct drm_gem_object *obj)
+i915_gem_object_put_pages(struct drm_gem_object *obj)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
int page_count = obj->size / PAGE_SIZE;
int i;
- if (obj_priv->page_list == NULL)
- return;
+ BUG_ON(obj_priv->pages_refcount == 0);
+ if (--obj_priv->pages_refcount != 0)
+ return;
for (i = 0; i < page_count; i++)
- if (obj_priv->page_list[i] != NULL) {
+ if (obj_priv->pages[i] != NULL) {
if (obj_priv->dirty)
- set_page_dirty(obj_priv->page_list[i]);
- mark_page_accessed(obj_priv->page_list[i]);
- page_cache_release(obj_priv->page_list[i]);
+ set_page_dirty(obj_priv->pages[i]);
+ mark_page_accessed(obj_priv->pages[i]);
+ page_cache_release(obj_priv->pages[i]);
}
obj_priv->dirty = 0;
- drm_free(obj_priv->page_list,
+ drm_free(obj_priv->pages,
page_count * sizeof(struct page *),
DRM_MEM_DRIVER);
- obj_priv->page_list = NULL;
+ obj_priv->pages = NULL;
}
static void
@@ -1290,7 +1757,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
i915_gem_clear_fence_reg(obj);
- i915_gem_object_free_page_list(obj);
+ i915_gem_object_put_pages(obj);
if (obj_priv->gtt_space) {
atomic_dec(&dev->gtt_count);
@@ -1409,7 +1876,7 @@ i915_gem_evict_everything(struct drm_device *dev)
}
static int
-i915_gem_object_get_page_list(struct drm_gem_object *obj)
+i915_gem_object_get_pages(struct drm_gem_object *obj)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
int page_count, i;
@@ -1418,18 +1885,19 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
struct page *page;
int ret;
- if (obj_priv->page_list)
+ if (obj_priv->pages_refcount++ != 0)
return 0;
/* Get the list of pages out of our struct file. They'll be pinned
* at this point until we release them.
*/
page_count = obj->size / PAGE_SIZE;
- BUG_ON(obj_priv->page_list != NULL);
- obj_priv->page_list = drm_calloc(page_count, sizeof(struct page *),
- DRM_MEM_DRIVER);
- if (obj_priv->page_list == NULL) {
+ BUG_ON(obj_priv->pages != NULL);
+ obj_priv->pages = drm_calloc(page_count, sizeof(struct page *),
+ DRM_MEM_DRIVER);
+ if (obj_priv->pages == NULL) {
DRM_ERROR("Faled to allocate page list\n");
+ obj_priv->pages_refcount--;
return -ENOMEM;
}
@@ -1440,10 +1908,10 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
if (IS_ERR(page)) {
ret = PTR_ERR(page);
DRM_ERROR("read_mapping_page failed: %d\n", ret);
- i915_gem_object_free_page_list(obj);
+ i915_gem_object_put_pages(obj);
return ret;
}
- obj_priv->page_list[i] = page;
+ obj_priv->pages[i] = page;
}
return 0;
}
@@ -1766,7 +2234,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
DRM_INFO("Binding object of size %d at 0x%08x\n",
obj->size, obj_priv->gtt_offset);
#endif
- ret = i915_gem_object_get_page_list(obj);
+ ret = i915_gem_object_get_pages(obj);
if (ret) {
drm_mm_put_block(obj_priv->gtt_space);
obj_priv->gtt_space = NULL;
@@ -1778,12 +2246,12 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
* into the GTT.
*/
obj_priv->agp_mem = drm_agp_bind_pages(dev,
- obj_priv->page_list,
+ obj_priv->pages,
page_count,
obj_priv->gtt_offset,
obj_priv->agp_type);
if (obj_priv->agp_mem == NULL) {
- i915_gem_object_free_page_list(obj);
+ i915_gem_object_put_pages(obj);
drm_mm_put_block(obj_priv->gtt_space);
obj_priv->gtt_space = NULL;
return -ENOMEM;
@@ -1810,10 +2278,10 @@ i915_gem_clflush_object(struct drm_gem_object *obj)
* to GPU, and we can ignore the cache flush because it'll happen
* again at bind time.
*/
- if (obj_priv->page_list == NULL)
+ if (obj_priv->pages == NULL)
return;
- drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE);
+ drm_clflush_pages(obj_priv->pages, obj->size / PAGE_SIZE);
}
/** Flushes any GPU write domain for the object if it's dirty. */
@@ -1913,7 +2381,6 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
static int
i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
{
- struct drm_device *dev = obj->dev;
int ret;
i915_gem_object_flush_gpu_write_domain(obj);
@@ -1932,7 +2399,6 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
/* Flush the CPU cache if it's still invalid. */
if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) {
i915_gem_clflush_object(obj);
- drm_agp_chipset_flush(dev);
obj->read_domains |= I915_GEM_DOMAIN_CPU;
}
@@ -2144,7 +2610,6 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
static void
i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj)
{
- struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
if (!obj_priv->page_cpu_valid)
@@ -2158,9 +2623,8 @@ i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj)
for (i = 0; i <= (obj->size - 1) / PAGE_SIZE; i++) {
if (obj_priv->page_cpu_valid[i])
continue;
- drm_clflush_pages(obj_priv->page_list + i, 1);
+ drm_clflush_pages(obj_priv->pages + i, 1);
}
- drm_agp_chipset_flush(dev);
}
/* Free the page_cpu_valid mappings which are now stale, whether
@@ -2224,7 +2688,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
if (obj_priv->page_cpu_valid[i])
continue;
- drm_clflush_pages(obj_priv->page_list + i, 1);
+ drm_clflush_pages(obj_priv->pages + i, 1);
obj_priv->page_cpu_valid[i] = 1;
}
@@ -2245,12 +2709,11 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
static int
i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
struct drm_file *file_priv,
- struct drm_i915_gem_exec_object *entry)
+ struct drm_i915_gem_exec_object *entry,
+ struct drm_i915_gem_relocation_entry *relocs)
{
struct drm_device *dev = obj->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_relocation_entry reloc;
- struct drm_i915_gem_relocation_entry __user *relocs;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
int i, ret;
void __iomem *reloc_page;
@@ -2262,25 +2725,18 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
entry->offset = obj_priv->gtt_offset;
- relocs = (struct drm_i915_gem_relocation_entry __user *)
- (uintptr_t) entry->relocs_ptr;
/* Apply the relocations, using the GTT aperture to avoid cache
* flushing requirements.
*/
for (i = 0; i < entry->relocation_count; i++) {
+ struct drm_i915_gem_relocation_entry *reloc= &relocs[i];
struct drm_gem_object *target_obj;
struct drm_i915_gem_object *target_obj_priv;
uint32_t reloc_val, reloc_offset;
uint32_t __iomem *reloc_entry;
- ret = copy_from_user(&reloc, relocs + i, sizeof(reloc));
- if (ret != 0) {
- i915_gem_object_unpin(obj);
- return ret;
- }
-
target_obj = drm_gem_object_lookup(obj->dev, file_priv,
- reloc.target_handle);
+ reloc->target_handle);
if (target_obj == NULL) {
i915_gem_object_unpin(obj);
return -EBADF;
@@ -2292,53 +2748,53 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
*/
if (target_obj_priv->gtt_space == NULL) {
DRM_ERROR("No GTT space found for object %d\n",
- reloc.target_handle);
+ reloc->target_handle);
drm_gem_object_unreference(target_obj);
i915_gem_object_unpin(obj);
return -EINVAL;
}
- if (reloc.offset > obj->size - 4) {
+ if (reloc->offset > obj->size - 4) {
DRM_ERROR("Relocation beyond object bounds: "
"obj %p target %d offset %d size %d.\n",
- obj, reloc.target_handle,
- (int) reloc.offset, (int) obj->size);
+ obj, reloc->target_handle,
+ (int) reloc->offset, (int) obj->size);
drm_gem_object_unreference(target_obj);
i915_gem_object_unpin(obj);
return -EINVAL;
}
- if (reloc.offset & 3) {
+ if (reloc->offset & 3) {
DRM_ERROR("Relocation not 4-byte aligned: "
"obj %p target %d offset %d.\n",
- obj, reloc.target_handle,
- (int) reloc.offset);
+ obj, reloc->target_handle,
+ (int) reloc->offset);
drm_gem_object_unreference(target_obj);
i915_gem_object_unpin(obj);
return -EINVAL;
}
- if (reloc.write_domain & I915_GEM_DOMAIN_CPU ||
- reloc.read_domains & I915_GEM_DOMAIN_CPU) {
+ if (reloc->write_domain & I915_GEM_DOMAIN_CPU ||
+ reloc->read_domains & I915_GEM_DOMAIN_CPU) {
DRM_ERROR("reloc with read/write CPU domains: "
"obj %p target %d offset %d "
"read %08x write %08x",
- obj, reloc.target_handle,
- (int) reloc.offset,
- reloc.read_domains,
- reloc.write_domain);
+ obj, reloc->target_handle,
+ (int) reloc->offset,
+ reloc->read_domains,
+ reloc->write_domain);
drm_gem_object_unreference(target_obj);
i915_gem_object_unpin(obj);
return -EINVAL;
}
- if (reloc.write_domain && target_obj->pending_write_domain &&
- reloc.write_domain != target_obj->pending_write_domain) {
+ if (reloc->write_domain && target_obj->pending_write_domain &&
+ reloc->write_domain != target_obj->pending_write_domain) {
DRM_ERROR("Write domain conflict: "
"obj %p target %d offset %d "
"new %08x old %08x\n",
- obj, reloc.target_handle,
- (int) reloc.offset,
- reloc.write_domain,
+ obj, reloc->target_handle,
+ (int) reloc->offset,
+ reloc->write_domain,
target_obj->pending_write_domain);
drm_gem_object_unreference(target_obj);
i915_gem_object_unpin(obj);
@@ -2351,22 +2807,22 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
"presumed %08x delta %08x\n",
__func__,
obj,
- (int) reloc.offset,
- (int) reloc.target_handle,
- (int) reloc.read_domains,
- (int) reloc.write_domain,
+ (int) reloc->offset,
+ (int) reloc->target_handle,
+ (int) reloc->read_domains,
+ (int) reloc->write_domain,
(int) target_obj_priv->gtt_offset,
- (int) reloc.presumed_offset,
- reloc.delta);
+ (int) reloc->presumed_offset,
+ reloc->delta);
#endif
- target_obj->pending_read_domains |= reloc.read_domains;
- target_obj->pending_write_domain |= reloc.write_domain;
+ target_obj->pending_read_domains |= reloc->read_domains;
+ target_obj->pending_write_domain |= reloc->write_domain;
/* If the relocation already has the right value in it, no
* more work needs to be done.
*/
- if (target_obj_priv->gtt_offset == reloc.presumed_offset) {
+ if (target_obj_priv->gtt_offset == reloc->presumed_offset) {
drm_gem_object_unreference(target_obj);
continue;
}
@@ -2381,32 +2837,26 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
/* Map the page containing the relocation we're going to
* perform.
*/
- reloc_offset = obj_priv->gtt_offset + reloc.offset;
+ reloc_offset = obj_priv->gtt_offset + reloc->offset;
reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
(reloc_offset &
~(PAGE_SIZE - 1)));
reloc_entry = (uint32_t __iomem *)(reloc_page +
(reloc_offset & (PAGE_SIZE - 1)));
- reloc_val = target_obj_priv->gtt_offset + reloc.delta;
+ reloc_val = target_obj_priv->gtt_offset + reloc->delta;
#if WATCH_BUF
DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n",
- obj, (unsigned int) reloc.offset,
+ obj, (unsigned int) reloc->offset,
readl(reloc_entry), reloc_val);
#endif
writel(reloc_val, reloc_entry);
io_mapping_unmap_atomic(reloc_page);
- /* Write the updated presumed offset for this entry back out
- * to the user.
+ /* The updated presumed offset for this entry will be
+ * copied back out to the user.
*/
- reloc.presumed_offset = target_obj_priv->gtt_offset;
- ret = copy_to_user(relocs + i, &reloc, sizeof(reloc));
- if (ret != 0) {
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return ret;
- }
+ reloc->presumed_offset = target_obj_priv->gtt_offset;
drm_gem_object_unreference(target_obj);
}
@@ -2423,11 +2873,10 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
static int
i915_dispatch_gem_execbuffer(struct drm_device *dev,
struct drm_i915_gem_execbuffer *exec,
+ struct drm_clip_rect *cliprects,
uint64_t exec_offset)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *)
- (uintptr_t) exec->cliprects_ptr;
int nbox = exec->num_cliprects;
int i = 0, count;
uint32_t exec_start, exec_len;
@@ -2448,7 +2897,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
for (i = 0; i < count; i++) {
if (i < nbox) {
- int ret = i915_emit_box(dev, boxes, i,
+ int ret = i915_emit_box(dev, cliprects, i,
exec->DR1, exec->DR4);
if (ret)
return ret;
@@ -2504,6 +2953,75 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
return ret;
}
+static int
+i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
+ uint32_t buffer_count,
+ struct drm_i915_gem_relocation_entry **relocs)
+{
+ uint32_t reloc_count = 0, reloc_index = 0, i;
+ int ret;
+
+ *relocs = NULL;
+ for (i = 0; i < buffer_count; i++) {
+ if (reloc_count + exec_list[i].relocation_count < reloc_count)
+ return -EINVAL;
+ reloc_count += exec_list[i].relocation_count;
+ }
+
+ *relocs = drm_calloc(reloc_count, sizeof(**relocs), DRM_MEM_DRIVER);
+ if (*relocs == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < buffer_count; i++) {
+ struct drm_i915_gem_relocation_entry __user *user_relocs;
+
+ user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
+
+ ret = copy_from_user(&(*relocs)[reloc_index],
+ user_relocs,
+ exec_list[i].relocation_count *
+ sizeof(**relocs));
+ if (ret != 0) {
+ drm_free(*relocs, reloc_count * sizeof(**relocs),
+ DRM_MEM_DRIVER);
+ *relocs = NULL;
+ return ret;
+ }
+
+ reloc_index += exec_list[i].relocation_count;
+ }
+
+ return ret;
+}
+
+static int
+i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
+ uint32_t buffer_count,
+ struct drm_i915_gem_relocation_entry *relocs)
+{
+ uint32_t reloc_count = 0, i;
+ int ret;
+
+ for (i = 0; i < buffer_count; i++) {
+ struct drm_i915_gem_relocation_entry __user *user_relocs;
+
+ user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
+
+ if (ret == 0) {
+ ret = copy_to_user(user_relocs,
+ &relocs[reloc_count],
+ exec_list[i].relocation_count *
+ sizeof(*relocs));
+ }
+
+ reloc_count += exec_list[i].relocation_count;
+ }
+
+ drm_free(relocs, reloc_count * sizeof(*relocs), DRM_MEM_DRIVER);
+
+ return ret;
+}
+
int
i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
@@ -2515,9 +3033,11 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_gem_object **object_list = NULL;
struct drm_gem_object *batch_obj;
struct drm_i915_gem_object *obj_priv;
- int ret, i, pinned = 0;
+ struct drm_clip_rect *cliprects = NULL;
+ struct drm_i915_gem_relocation_entry *relocs;
+ int ret, ret2, i, pinned = 0;
uint64_t exec_offset;
- uint32_t seqno, flush_domains;
+ uint32_t seqno, flush_domains, reloc_index;
int pin_tries;
#if WATCH_EXEC
@@ -2551,6 +3071,28 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
goto pre_mutex_err;
}
+ if (args->num_cliprects != 0) {
+ cliprects = drm_calloc(args->num_cliprects, sizeof(*cliprects),
+ DRM_MEM_DRIVER);
+ if (cliprects == NULL)
+ goto pre_mutex_err;
+
+ ret = copy_from_user(cliprects,
+ (struct drm_clip_rect __user *)
+ (uintptr_t) args->cliprects_ptr,
+ sizeof(*cliprects) * args->num_cliprects);
+ if (ret != 0) {
+ DRM_ERROR("copy %d cliprects failed: %d\n",
+ args->num_cliprects, ret);
+ goto pre_mutex_err;
+ }
+ }
+
+ ret = i915_gem_get_relocs_from_user(exec_list, args->buffer_count,
+ &relocs);
+ if (ret != 0)
+ goto pre_mutex_err;
+
mutex_lock(&dev->struct_mutex);
i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -2593,15 +3135,19 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
/* Pin and relocate */
for (pin_tries = 0; ; pin_tries++) {
ret = 0;
+ reloc_index = 0;
+
for (i = 0; i < args->buffer_count; i++) {
object_list[i]->pending_read_domains = 0;
object_list[i]->pending_write_domain = 0;
ret = i915_gem_object_pin_and_relocate(object_list[i],
file_priv,
- &exec_list[i]);
+ &exec_list[i],
+ &relocs[reloc_index]);
if (ret)
break;
pinned = i + 1;
+ reloc_index += exec_list[i].relocation_count;
}
/* success */
if (ret == 0)
@@ -2687,7 +3233,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
#endif
/* Exec the batchbuffer */
- ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset);
+ ret = i915_dispatch_gem_execbuffer(dev, args, cliprects, exec_offset);
if (ret) {
DRM_ERROR("dispatch failed %d\n", ret);
goto err;
@@ -2751,11 +3297,27 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
args->buffer_count, ret);
}
+ /* Copy the updated relocations out regardless of current error
+ * state. Failure to update the relocs would mean that the next
+ * time userland calls execbuf, it would do so with presumed offset
+ * state that didn't match the actual object state.
+ */
+ ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
+ relocs);
+ if (ret2 != 0) {
+ DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
+
+ if (ret == 0)
+ ret = ret2;
+ }
+
pre_mutex_err:
drm_free(object_list, sizeof(*object_list) * args->buffer_count,
DRM_MEM_DRIVER);
drm_free(exec_list, sizeof(*exec_list) * args->buffer_count,
DRM_MEM_DRIVER);
+ drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects,
+ DRM_MEM_DRIVER);
return ret;
}
@@ -3192,7 +3754,7 @@ i915_gem_init_hws(struct drm_device *dev)
dev_priv->status_gfx_addr = obj_priv->gtt_offset;
- dev_priv->hw_status_page = kmap(obj_priv->page_list[0]);
+ dev_priv->hw_status_page = kmap(obj_priv->pages[0]);
if (dev_priv->hw_status_page == NULL) {
DRM_ERROR("Failed to map status page.\n");
memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map));
@@ -3222,7 +3784,7 @@ i915_gem_cleanup_hws(struct drm_device *dev)
obj = dev_priv->hws_obj;
obj_priv = obj->driver_private;
- kunmap(obj_priv->page_list[0]);
+ kunmap(obj_priv->pages[0]);
i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
dev_priv->hws_obj = NULL;
@@ -3525,20 +4087,20 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
if (!obj_priv->phys_obj)
return;
- ret = i915_gem_object_get_page_list(obj);
+ ret = i915_gem_object_get_pages(obj);
if (ret)
goto out;
page_count = obj->size / PAGE_SIZE;
for (i = 0; i < page_count; i++) {
- char *dst = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *dst = kmap_atomic(obj_priv->pages[i], KM_USER0);
char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
memcpy(dst, src, PAGE_SIZE);
kunmap_atomic(dst, KM_USER0);
}
- drm_clflush_pages(obj_priv->page_list, page_count);
+ drm_clflush_pages(obj_priv->pages, page_count);
drm_agp_chipset_flush(dev);
out:
obj_priv->phys_obj->cur_obj = NULL;
@@ -3581,7 +4143,7 @@ i915_gem_attach_phys_object(struct drm_device *dev,
obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1];
obj_priv->phys_obj->cur_obj = obj;
- ret = i915_gem_object_get_page_list(obj);
+ ret = i915_gem_object_get_pages(obj);
if (ret) {
DRM_ERROR("failed to get page list\n");
goto out;
@@ -3590,7 +4152,7 @@ i915_gem_attach_phys_object(struct drm_device *dev,
page_count = obj->size / PAGE_SIZE;
for (i = 0; i < page_count; i++) {
- char *src = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *src = kmap_atomic(obj_priv->pages[i], KM_USER0);
char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
memcpy(dst, src, PAGE_SIZE);
diff --git a/trunk/drivers/gpu/drm/i915/i915_gem_debugfs.c b/trunk/drivers/gpu/drm/i915/i915_gem_debugfs.c
new file mode 100644
index 000000000000..455ec970b385
--- /dev/null
+++ b/trunk/drivers/gpu/drm/i915/i915_gem_debugfs.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright Ā© 2008 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt
+ * Keith Packard
+ *
+ */
+
+#include
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+#define DRM_I915_RING_DEBUG 1
+
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define ACTIVE_LIST 1
+#define FLUSHING_LIST 2
+#define INACTIVE_LIST 3
+
+static const char *get_pin_flag(struct drm_i915_gem_object *obj_priv)
+{
+ if (obj_priv->user_pin_count > 0)
+ return "P";
+ else if (obj_priv->pin_count > 0)
+ return "p";
+ else
+ return " ";
+}
+
+static const char *get_tiling_flag(struct drm_i915_gem_object *obj_priv)
+{
+ switch (obj_priv->tiling_mode) {
+ default:
+ case I915_TILING_NONE: return " ";
+ case I915_TILING_X: return "X";
+ case I915_TILING_Y: return "Y";
+ }
+}
+
+static int i915_gem_object_list_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ uintptr_t list = (uintptr_t) node->info_ent->data;
+ struct list_head *head;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv;
+
+ switch (list) {
+ case ACTIVE_LIST:
+ seq_printf(m, "Active:\n");
+ head = &dev_priv->mm.active_list;
+ break;
+ case INACTIVE_LIST:
+ seq_printf(m, "Inctive:\n");
+ head = &dev_priv->mm.inactive_list;
+ break;
+ case FLUSHING_LIST:
+ seq_printf(m, "Flushing:\n");
+ head = &dev_priv->mm.flushing_list;
+ break;
+ default:
+ DRM_INFO("Ooops, unexpected list\n");
+ return 0;
+ }
+
+ list_for_each_entry(obj_priv, head, list)
+ {
+ struct drm_gem_object *obj = obj_priv->obj;
+
+ seq_printf(m, " %p: %s %08x %08x %d",
+ obj,
+ get_pin_flag(obj_priv),
+ obj->read_domains, obj->write_domain,
+ obj_priv->last_rendering_seqno);
+
+ if (obj->name)
+ seq_printf(m, " (name: %d)", obj->name);
+ if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
+ seq_printf(m, " (fence: %d\n", obj_priv->fence_reg);
+ seq_printf(m, "\n");
+ }
+ return 0;
+}
+
+static int i915_gem_request_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_request *gem_request;
+
+ seq_printf(m, "Request:\n");
+ list_for_each_entry(gem_request, &dev_priv->mm.request_list, list) {
+ seq_printf(m, " %d @ %d\n",
+ gem_request->seqno,
+ (int) (jiffies - gem_request->emitted_jiffies));
+ }
+ return 0;
+}
+
+static int i915_gem_seqno_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ if (dev_priv->hw_status_page != NULL) {
+ seq_printf(m, "Current sequence: %d\n",
+ i915_get_gem_seqno(dev));
+ } else {
+ seq_printf(m, "Current sequence: hws uninitialized\n");
+ }
+ seq_printf(m, "Waiter sequence: %d\n",
+ dev_priv->mm.waiting_gem_seqno);
+ seq_printf(m, "IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno);
+ return 0;
+}
+
+
+static int i915_interrupt_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ seq_printf(m, "Interrupt enable: %08x\n",
+ I915_READ(IER));
+ seq_printf(m, "Interrupt identity: %08x\n",
+ I915_READ(IIR));
+ seq_printf(m, "Interrupt mask: %08x\n",
+ I915_READ(IMR));
+ seq_printf(m, "Pipe A stat: %08x\n",
+ I915_READ(PIPEASTAT));
+ seq_printf(m, "Pipe B stat: %08x\n",
+ I915_READ(PIPEBSTAT));
+ seq_printf(m, "Interrupts received: %d\n",
+ atomic_read(&dev_priv->irq_received));
+ if (dev_priv->hw_status_page != NULL) {
+ seq_printf(m, "Current sequence: %d\n",
+ i915_get_gem_seqno(dev));
+ } else {
+ seq_printf(m, "Current sequence: hws uninitialized\n");
+ }
+ seq_printf(m, "Waiter sequence: %d\n",
+ dev_priv->mm.waiting_gem_seqno);
+ seq_printf(m, "IRQ sequence: %d\n",
+ dev_priv->mm.irq_gem_seqno);
+ return 0;
+}
+
+static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int i;
+
+ seq_printf(m, "Reserved fences = %d\n", dev_priv->fence_reg_start);
+ seq_printf(m, "Total fences = %d\n", dev_priv->num_fence_regs);
+ for (i = 0; i < dev_priv->num_fence_regs; i++) {
+ struct drm_gem_object *obj = dev_priv->fence_regs[i].obj;
+
+ if (obj == NULL) {
+ seq_printf(m, "Fenced object[%2d] = unused\n", i);
+ } else {
+ struct drm_i915_gem_object *obj_priv;
+
+ obj_priv = obj->driver_private;
+ seq_printf(m, "Fenced object[%2d] = %p: %s "
+ "%08x %08zx %08x %s %08x %08x %d",
+ i, obj, get_pin_flag(obj_priv),
+ obj_priv->gtt_offset,
+ obj->size, obj_priv->stride,
+ get_tiling_flag(obj_priv),
+ obj->read_domains, obj->write_domain,
+ obj_priv->last_rendering_seqno);
+ if (obj->name)
+ seq_printf(m, " (name: %d)", obj->name);
+ seq_printf(m, "\n");
+ }
+ }
+
+ return 0;
+}
+
+static int i915_hws_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int i;
+ volatile u32 *hws;
+
+ hws = (volatile u32 *)dev_priv->hw_status_page;
+ if (hws == NULL)
+ return 0;
+
+ for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) {
+ seq_printf(m, "0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i * 4,
+ hws[i], hws[i + 1], hws[i + 2], hws[i + 3]);
+ }
+ return 0;
+}
+
+static struct drm_info_list i915_gem_debugfs_list[] = {
+ {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
+ {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
+ {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
+ {"i915_gem_request", i915_gem_request_info, 0},
+ {"i915_gem_seqno", i915_gem_seqno_info, 0},
+ {"i915_gem_fence_regs", i915_gem_fence_regs_info, 0},
+ {"i915_gem_interrupt", i915_interrupt_info, 0},
+ {"i915_gem_hws", i915_hws_info, 0},
+};
+#define I915_GEM_DEBUGFS_ENTRIES ARRAY_SIZE(i915_gem_debugfs_list)
+
+int i915_gem_debugfs_init(struct drm_minor *minor)
+{
+ return drm_debugfs_create_files(i915_gem_debugfs_list,
+ I915_GEM_DEBUGFS_ENTRIES,
+ minor->debugfs_root, minor);
+}
+
+void i915_gem_debugfs_cleanup(struct drm_minor *minor)
+{
+ drm_debugfs_remove_files(i915_gem_debugfs_list,
+ I915_GEM_DEBUGFS_ENTRIES, minor);
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/trunk/drivers/gpu/drm/i915/i915_gem_proc.c b/trunk/drivers/gpu/drm/i915/i915_gem_proc.c
deleted file mode 100644
index 4d1b9de0cd8b..000000000000
--- a/trunk/drivers/gpu/drm/i915/i915_gem_proc.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright Ā© 2008 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- *
- * Authors:
- * Eric Anholt
- * Keith Packard
- *
- */
-
-#include "drmP.h"
-#include "drm.h"
-#include "i915_drm.h"
-#include "i915_drv.h"
-
-static int i915_gem_active_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("Active:\n");
- list_for_each_entry(obj_priv, &dev_priv->mm.active_list,
- list)
- {
- struct drm_gem_object *obj = obj_priv->obj;
- if (obj->name) {
- DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
- obj, obj->name,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- } else {
- DRM_PROC_PRINT(" %p: %08x %08x %d\n",
- obj,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- }
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int i915_gem_flushing_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("Flushing:\n");
- list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list,
- list)
- {
- struct drm_gem_object *obj = obj_priv->obj;
- if (obj->name) {
- DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
- obj, obj->name,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- } else {
- DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- }
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int i915_gem_inactive_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("Inactive:\n");
- list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list,
- list)
- {
- struct drm_gem_object *obj = obj_priv->obj;
- if (obj->name) {
- DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
- obj, obj->name,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- } else {
- DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj,
- obj->read_domains, obj->write_domain,
- obj_priv->last_rendering_seqno);
- }
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int i915_gem_request_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_request *gem_request;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("Request:\n");
- list_for_each_entry(gem_request, &dev_priv->mm.request_list,
- list)
- {
- DRM_PROC_PRINT(" %d @ %d\n",
- gem_request->seqno,
- (int) (jiffies - gem_request->emitted_jiffies));
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int i915_gem_seqno_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- if (dev_priv->hw_status_page != NULL) {
- DRM_PROC_PRINT("Current sequence: %d\n",
- i915_get_gem_seqno(dev));
- } else {
- DRM_PROC_PRINT("Current sequence: hws uninitialized\n");
- }
- DRM_PROC_PRINT("Waiter sequence: %d\n",
- dev_priv->mm.waiting_gem_seqno);
- DRM_PROC_PRINT("IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno);
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-
-static int i915_interrupt_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int len = 0;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- DRM_PROC_PRINT("Interrupt enable: %08x\n",
- I915_READ(IER));
- DRM_PROC_PRINT("Interrupt identity: %08x\n",
- I915_READ(IIR));
- DRM_PROC_PRINT("Interrupt mask: %08x\n",
- I915_READ(IMR));
- DRM_PROC_PRINT("Pipe A stat: %08x\n",
- I915_READ(PIPEASTAT));
- DRM_PROC_PRINT("Pipe B stat: %08x\n",
- I915_READ(PIPEBSTAT));
- DRM_PROC_PRINT("Interrupts received: %d\n",
- atomic_read(&dev_priv->irq_received));
- if (dev_priv->hw_status_page != NULL) {
- DRM_PROC_PRINT("Current sequence: %d\n",
- i915_get_gem_seqno(dev));
- } else {
- DRM_PROC_PRINT("Current sequence: hws uninitialized\n");
- }
- DRM_PROC_PRINT("Waiter sequence: %d\n",
- dev_priv->mm.waiting_gem_seqno);
- DRM_PROC_PRINT("IRQ sequence: %d\n",
- dev_priv->mm.irq_gem_seqno);
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static int i915_hws_info(char *buf, char **start, off_t offset,
- int request, int *eof, void *data)
-{
- struct drm_minor *minor = (struct drm_minor *) data;
- struct drm_device *dev = minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int len = 0, i;
- volatile u32 *hws;
-
- if (offset > DRM_PROC_LIMIT) {
- *eof = 1;
- return 0;
- }
-
- hws = (volatile u32 *)dev_priv->hw_status_page;
- if (hws == NULL) {
- *eof = 1;
- return 0;
- }
-
- *start = &buf[offset];
- *eof = 0;
- for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) {
- DRM_PROC_PRINT("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
- i * 4,
- hws[i], hws[i + 1], hws[i + 2], hws[i + 3]);
- }
- if (len > request + offset)
- return request;
- *eof = 1;
- return len - offset;
-}
-
-static struct drm_proc_list {
- /** file name */
- const char *name;
- /** proc callback*/
- int (*f) (char *, char **, off_t, int, int *, void *);
-} i915_gem_proc_list[] = {
- {"i915_gem_active", i915_gem_active_info},
- {"i915_gem_flushing", i915_gem_flushing_info},
- {"i915_gem_inactive", i915_gem_inactive_info},
- {"i915_gem_request", i915_gem_request_info},
- {"i915_gem_seqno", i915_gem_seqno_info},
- {"i915_gem_interrupt", i915_interrupt_info},
- {"i915_gem_hws", i915_hws_info},
-};
-
-#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list)
-
-int i915_gem_proc_init(struct drm_minor *minor)
-{
- struct proc_dir_entry *ent;
- int i, j;
-
- for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) {
- ent = create_proc_entry(i915_gem_proc_list[i].name,
- S_IFREG | S_IRUGO, minor->dev_root);
- if (!ent) {
- DRM_ERROR("Cannot create /proc/dri/.../%s\n",
- i915_gem_proc_list[i].name);
- for (j = 0; j < i; j++)
- remove_proc_entry(i915_gem_proc_list[i].name,
- minor->dev_root);
- return -1;
- }
- ent->read_proc = i915_gem_proc_list[i].f;
- ent->data = minor;
- }
- return 0;
-}
-
-void i915_gem_proc_cleanup(struct drm_minor *minor)
-{
- int i;
-
- if (!minor->dev_root)
- return;
-
- for (i = 0; i < I915_GEM_PROC_ENTRIES; i++)
- remove_proc_entry(i915_gem_proc_list[i].name, minor->dev_root);
-}
diff --git a/trunk/drivers/gpu/drm/i915/i915_gem_tiling.c b/trunk/drivers/gpu/drm/i915/i915_gem_tiling.c
index 7fb4191ef934..4cce1aef438e 100644
--- a/trunk/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/trunk/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -96,16 +96,16 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
*/
swizzle_x = I915_BIT_6_SWIZZLE_NONE;
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
- } else if ((!IS_I965G(dev) && !IS_G33(dev)) || IS_I965GM(dev) ||
- IS_GM45(dev)) {
+ } else if (IS_MOBILE(dev)) {
uint32_t dcc;
- /* On 915-945 and GM965, channel interleave by the CPU is
- * determined by DCC. The CPU will alternate based on bit 6
- * in interleaved mode, and the GPU will then also alternate
- * on bit 6, 9, and 10 for X, but the CPU may also optionally
- * alternate based on bit 17 (XOR not disabled and XOR
- * bit == 17).
+ /* On mobile 9xx chipsets, channel interleave by the CPU is
+ * determined by DCC. For single-channel, neither the CPU
+ * nor the GPU do swizzling. For dual channel interleaved,
+ * the GPU's interleave is bit 9 and 10 for X tiled, and bit
+ * 9 for Y tiled. The CPU's interleave is independent, and
+ * can be based on either bit 11 (haven't seen this yet) or
+ * bit 17 (common).
*/
dcc = I915_READ(DCC);
switch (dcc & DCC_ADDRESSING_MODE_MASK) {
@@ -115,19 +115,18 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
break;
case DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED:
- if (IS_I915G(dev) || IS_I915GM(dev) ||
- dcc & DCC_CHANNEL_XOR_DISABLE) {
+ if (dcc & DCC_CHANNEL_XOR_DISABLE) {
+ /* This is the base swizzling by the GPU for
+ * tiled buffers.
+ */
swizzle_x = I915_BIT_6_SWIZZLE_9_10;
swizzle_y = I915_BIT_6_SWIZZLE_9;
- } else if ((IS_I965GM(dev) || IS_GM45(dev)) &&
- (dcc & DCC_CHANNEL_XOR_BIT_17) == 0) {
- /* GM965/GM45 does either bit 11 or bit 17
- * swizzling.
- */
+ } else if ((dcc & DCC_CHANNEL_XOR_BIT_17) == 0) {
+ /* Bit 11 swizzling by the CPU in addition. */
swizzle_x = I915_BIT_6_SWIZZLE_9_10_11;
swizzle_y = I915_BIT_6_SWIZZLE_9_11;
} else {
- /* Bit 17 or perhaps other swizzling */
+ /* Bit 17 swizzling by the CPU in addition. */
swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
}
diff --git a/trunk/drivers/gpu/drm/i915/i915_reg.h b/trunk/drivers/gpu/drm/i915/i915_reg.h
index 90600d899413..377cc588f5e9 100644
--- a/trunk/drivers/gpu/drm/i915/i915_reg.h
+++ b/trunk/drivers/gpu/drm/i915/i915_reg.h
@@ -359,6 +359,7 @@
#define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */
#define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */
#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */
+#define DPLL_FPA01_P1_POST_DIV_MASK_IGD 0x00ff8000 /* IGD */
#define I915_FIFO_UNDERRUN_STATUS (1UL<<31)
#define I915_CRC_ERROR_ENABLE (1UL<<29)
@@ -435,6 +436,7 @@
*/
#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000
#define DPLL_FPA01_P1_POST_DIV_SHIFT 16
+#define DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15
/* i830, required in DVO non-gang */
#define PLL_P2_DIVIDE_BY_4 (1 << 23)
#define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */
@@ -501,10 +503,12 @@
#define FPB0 0x06048
#define FPB1 0x0604c
#define FP_N_DIV_MASK 0x003f0000
+#define FP_N_IGD_DIV_MASK 0x00ff0000
#define FP_N_DIV_SHIFT 16
#define FP_M1_DIV_MASK 0x00003f00
#define FP_M1_DIV_SHIFT 8
#define FP_M2_DIV_MASK 0x0000003f
+#define FP_M2_IGD_DIV_MASK 0x000000ff
#define FP_M2_DIV_SHIFT 0
#define DPLL_TEST 0x606c
#define DPLLB_TEST_SDVO_DIV_1 (0 << 22)
@@ -629,6 +633,22 @@
#define TV_HOTPLUG_INT_EN (1 << 18)
#define CRT_HOTPLUG_INT_EN (1 << 9)
#define CRT_HOTPLUG_FORCE_DETECT (1 << 3)
+#define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8)
+/* must use period 64 on GM45 according to docs */
+#define CRT_HOTPLUG_ACTIVATION_PERIOD_64 (1 << 8)
+#define CRT_HOTPLUG_DAC_ON_TIME_2M (0 << 7)
+#define CRT_HOTPLUG_DAC_ON_TIME_4M (1 << 7)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_40 (0 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_50 (1 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_60 (2 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_70 (3 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK (3 << 5)
+#define CRT_HOTPLUG_DETECT_DELAY_1G (0 << 4)
+#define CRT_HOTPLUG_DETECT_DELAY_2G (1 << 4)
+#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2)
+#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
+#define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */
+
#define PORT_HOTPLUG_STAT 0x61114
#define HDMIB_HOTPLUG_INT_STATUS (1 << 29)
@@ -856,7 +876,7 @@
*/
# define TV_ENC_C0_FIX (1 << 10)
/** Bits that must be preserved by software */
-# define TV_CTL_SAVE ((3 << 8) | (3 << 6))
+# define TV_CTL_SAVE ((1 << 11) | (3 << 9) | (7 << 6) | 0xf)
# define TV_FUSE_STATE_MASK (3 << 4)
/** Read-only state that reports all features enabled */
# define TV_FUSE_STATE_ENABLED (0 << 4)
diff --git a/trunk/drivers/gpu/drm/i915/intel_bios.h b/trunk/drivers/gpu/drm/i915/intel_bios.h
index 5ea715ace3a0..de621aad85b5 100644
--- a/trunk/drivers/gpu/drm/i915/intel_bios.h
+++ b/trunk/drivers/gpu/drm/i915/intel_bios.h
@@ -162,13 +162,13 @@ struct bdb_lvds_options {
u8 panel_type;
u8 rsvd1;
/* LVDS capabilities, stored in a dword */
- u8 rsvd2:1;
- u8 lvds_edid:1;
- u8 pixel_dither:1;
- u8 pfit_ratio_auto:1;
- u8 pfit_gfx_mode_enhanced:1;
- u8 pfit_text_mode_enhanced:1;
u8 pfit_mode:2;
+ u8 pfit_text_mode_enhanced:1;
+ u8 pfit_gfx_mode_enhanced:1;
+ u8 pfit_ratio_auto:1;
+ u8 pixel_dither:1;
+ u8 lvds_edid:1;
+ u8 rsvd2:1;
u8 rsvd4;
} __attribute__((packed));
diff --git a/trunk/drivers/gpu/drm/i915/intel_crt.c b/trunk/drivers/gpu/drm/i915/intel_crt.c
index dcaed3466e83..2b6d44381c31 100644
--- a/trunk/drivers/gpu/drm/i915/intel_crt.c
+++ b/trunk/drivers/gpu/drm/i915/intel_crt.c
@@ -64,11 +64,21 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
static int intel_crt_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
+ struct drm_device *dev = connector->dev;
+
+ int max_clock = 0;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
- if (mode->clock > 400000 || mode->clock < 25000)
- return MODE_CLOCK_RANGE;
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+
+ if (!IS_I9XX(dev))
+ max_clock = 350000;
+ else
+ max_clock = 400000;
+ if (mode->clock > max_clock)
+ return MODE_CLOCK_HIGH;
return MODE_OK;
}
@@ -113,10 +123,13 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
- if (intel_crtc->pipe == 0)
+ if (intel_crtc->pipe == 0) {
adpa |= ADPA_PIPE_A_SELECT;
- else
+ I915_WRITE(BCLRPAT_A, 0);
+ } else {
adpa |= ADPA_PIPE_B_SELECT;
+ I915_WRITE(BCLRPAT_B, 0);
+ }
I915_WRITE(ADPA, adpa);
}
@@ -133,20 +146,39 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 temp;
-
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
- temp = I915_READ(PORT_HOTPLUG_EN);
-
- I915_WRITE(PORT_HOTPLUG_EN,
- temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+ u32 hotplug_en;
+ int i, tries = 0;
+ /*
+ * On 4 series desktop, CRT detect sequence need to be done twice
+ * to get a reliable result.
+ */
- do {
- if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
- break;
- msleep(1);
- } while (time_after(timeout, jiffies));
+ if (IS_G4X(dev) && !IS_GM45(dev))
+ tries = 2;
+ else
+ tries = 1;
+ hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+ hotplug_en &= ~(CRT_HOTPLUG_MASK);
+ hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
+
+ if (IS_GM45(dev))
+ hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
+
+ hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
+
+ for (i = 0; i < tries ; i++) {
+ unsigned long timeout;
+ /* turn on the FORCE_DETECT */
+ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
+ timeout = jiffies + msecs_to_jiffies(1000);
+ /* wait for FORCE_DETECT to go off */
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) &
+ CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+ }
if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
CRT_HOTPLUG_MONITOR_COLOR)
diff --git a/trunk/drivers/gpu/drm/i915/intel_display.c b/trunk/drivers/gpu/drm/i915/intel_display.c
index a2834276cb38..d9c50ff94d76 100644
--- a/trunk/drivers/gpu/drm/i915/intel_display.c
+++ b/trunk/drivers/gpu/drm/i915/intel_display.c
@@ -56,11 +56,13 @@ typedef struct {
} intel_p2_t;
#define INTEL_P2_NUM 2
-
-typedef struct {
+typedef struct intel_limit intel_limit_t;
+struct intel_limit {
intel_range_t dot, vco, n, m, m1, m2, p, p1;
intel_p2_t p2;
-} intel_limit_t;
+ bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
+ int, int, intel_clock_t *);
+};
#define I8XX_DOT_MIN 25000
#define I8XX_DOT_MAX 350000
@@ -90,18 +92,32 @@ typedef struct {
#define I9XX_DOT_MAX 400000
#define I9XX_VCO_MIN 1400000
#define I9XX_VCO_MAX 2800000
+#define IGD_VCO_MIN 1700000
+#define IGD_VCO_MAX 3500000
#define I9XX_N_MIN 1
#define I9XX_N_MAX 6
+/* IGD's Ncounter is a ring counter */
+#define IGD_N_MIN 3
+#define IGD_N_MAX 6
#define I9XX_M_MIN 70
#define I9XX_M_MAX 120
+#define IGD_M_MIN 2
+#define IGD_M_MAX 256
#define I9XX_M1_MIN 10
#define I9XX_M1_MAX 22
#define I9XX_M2_MIN 5
#define I9XX_M2_MAX 9
+/* IGD M1 is reserved, and must be 0 */
+#define IGD_M1_MIN 0
+#define IGD_M1_MAX 0
+#define IGD_M2_MIN 0
+#define IGD_M2_MAX 254
#define I9XX_P_SDVO_DAC_MIN 5
#define I9XX_P_SDVO_DAC_MAX 80
#define I9XX_P_LVDS_MIN 7
#define I9XX_P_LVDS_MAX 98
+#define IGD_P_LVDS_MIN 7
+#define IGD_P_LVDS_MAX 112
#define I9XX_P1_MIN 1
#define I9XX_P1_MAX 8
#define I9XX_P2_SDVO_DAC_SLOW 10
@@ -115,6 +131,97 @@ typedef struct {
#define INTEL_LIMIT_I8XX_LVDS 1
#define INTEL_LIMIT_I9XX_SDVO_DAC 2
#define INTEL_LIMIT_I9XX_LVDS 3
+#define INTEL_LIMIT_G4X_SDVO 4
+#define INTEL_LIMIT_G4X_HDMI_DAC 5
+#define INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS 6
+#define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS 7
+#define INTEL_LIMIT_IGD_SDVO_DAC 8
+#define INTEL_LIMIT_IGD_LVDS 9
+
+/*The parameter is for SDVO on G4x platform*/
+#define G4X_DOT_SDVO_MIN 25000
+#define G4X_DOT_SDVO_MAX 270000
+#define G4X_VCO_MIN 1750000
+#define G4X_VCO_MAX 3500000
+#define G4X_N_SDVO_MIN 1
+#define G4X_N_SDVO_MAX 4
+#define G4X_M_SDVO_MIN 104
+#define G4X_M_SDVO_MAX 138
+#define G4X_M1_SDVO_MIN 17
+#define G4X_M1_SDVO_MAX 23
+#define G4X_M2_SDVO_MIN 5
+#define G4X_M2_SDVO_MAX 11
+#define G4X_P_SDVO_MIN 10
+#define G4X_P_SDVO_MAX 30
+#define G4X_P1_SDVO_MIN 1
+#define G4X_P1_SDVO_MAX 3
+#define G4X_P2_SDVO_SLOW 10
+#define G4X_P2_SDVO_FAST 10
+#define G4X_P2_SDVO_LIMIT 270000
+
+/*The parameter is for HDMI_DAC on G4x platform*/
+#define G4X_DOT_HDMI_DAC_MIN 22000
+#define G4X_DOT_HDMI_DAC_MAX 400000
+#define G4X_N_HDMI_DAC_MIN 1
+#define G4X_N_HDMI_DAC_MAX 4
+#define G4X_M_HDMI_DAC_MIN 104
+#define G4X_M_HDMI_DAC_MAX 138
+#define G4X_M1_HDMI_DAC_MIN 16
+#define G4X_M1_HDMI_DAC_MAX 23
+#define G4X_M2_HDMI_DAC_MIN 5
+#define G4X_M2_HDMI_DAC_MAX 11
+#define G4X_P_HDMI_DAC_MIN 5
+#define G4X_P_HDMI_DAC_MAX 80
+#define G4X_P1_HDMI_DAC_MIN 1
+#define G4X_P1_HDMI_DAC_MAX 8
+#define G4X_P2_HDMI_DAC_SLOW 10
+#define G4X_P2_HDMI_DAC_FAST 5
+#define G4X_P2_HDMI_DAC_LIMIT 165000
+
+/*The parameter is for SINGLE_CHANNEL_LVDS on G4x platform*/
+#define G4X_DOT_SINGLE_CHANNEL_LVDS_MIN 20000
+#define G4X_DOT_SINGLE_CHANNEL_LVDS_MAX 115000
+#define G4X_N_SINGLE_CHANNEL_LVDS_MIN 1
+#define G4X_N_SINGLE_CHANNEL_LVDS_MAX 3
+#define G4X_M_SINGLE_CHANNEL_LVDS_MIN 104
+#define G4X_M_SINGLE_CHANNEL_LVDS_MAX 138
+#define G4X_M1_SINGLE_CHANNEL_LVDS_MIN 17
+#define G4X_M1_SINGLE_CHANNEL_LVDS_MAX 23
+#define G4X_M2_SINGLE_CHANNEL_LVDS_MIN 5
+#define G4X_M2_SINGLE_CHANNEL_LVDS_MAX 11
+#define G4X_P_SINGLE_CHANNEL_LVDS_MIN 28
+#define G4X_P_SINGLE_CHANNEL_LVDS_MAX 112
+#define G4X_P1_SINGLE_CHANNEL_LVDS_MIN 2
+#define G4X_P1_SINGLE_CHANNEL_LVDS_MAX 8
+#define G4X_P2_SINGLE_CHANNEL_LVDS_SLOW 14
+#define G4X_P2_SINGLE_CHANNEL_LVDS_FAST 14
+#define G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT 0
+
+/*The parameter is for DUAL_CHANNEL_LVDS on G4x platform*/
+#define G4X_DOT_DUAL_CHANNEL_LVDS_MIN 80000
+#define G4X_DOT_DUAL_CHANNEL_LVDS_MAX 224000
+#define G4X_N_DUAL_CHANNEL_LVDS_MIN 1
+#define G4X_N_DUAL_CHANNEL_LVDS_MAX 3
+#define G4X_M_DUAL_CHANNEL_LVDS_MIN 104
+#define G4X_M_DUAL_CHANNEL_LVDS_MAX 138
+#define G4X_M1_DUAL_CHANNEL_LVDS_MIN 17
+#define G4X_M1_DUAL_CHANNEL_LVDS_MAX 23
+#define G4X_M2_DUAL_CHANNEL_LVDS_MIN 5
+#define G4X_M2_DUAL_CHANNEL_LVDS_MAX 11
+#define G4X_P_DUAL_CHANNEL_LVDS_MIN 14
+#define G4X_P_DUAL_CHANNEL_LVDS_MAX 42
+#define G4X_P1_DUAL_CHANNEL_LVDS_MIN 2
+#define G4X_P1_DUAL_CHANNEL_LVDS_MAX 6
+#define G4X_P2_DUAL_CHANNEL_LVDS_SLOW 7
+#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
+#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
+
+static bool
+intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
static const intel_limit_t intel_limits[] = {
{ /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -128,6 +235,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
.p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ .find_pll = intel_find_best_PLL,
},
{ /* INTEL_LIMIT_I8XX_LVDS */
.dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
@@ -140,6 +248,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
.p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ .find_pll = intel_find_best_PLL,
},
{ /* INTEL_LIMIT_I9XX_SDVO_DAC */
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
@@ -152,6 +261,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
.p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
.p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ .find_pll = intel_find_best_PLL,
},
{ /* INTEL_LIMIT_I9XX_LVDS */
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
@@ -167,19 +277,157 @@ static const intel_limit_t intel_limits[] = {
*/
.p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
.p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ .find_pll = intel_find_best_PLL,
+ },
+ /* below parameter and function is for G4X Chipset Family*/
+ { /* INTEL_LIMIT_G4X_SDVO */
+ .dot = { .min = G4X_DOT_SDVO_MIN, .max = G4X_DOT_SDVO_MAX },
+ .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX},
+ .n = { .min = G4X_N_SDVO_MIN, .max = G4X_N_SDVO_MAX },
+ .m = { .min = G4X_M_SDVO_MIN, .max = G4X_M_SDVO_MAX },
+ .m1 = { .min = G4X_M1_SDVO_MIN, .max = G4X_M1_SDVO_MAX },
+ .m2 = { .min = G4X_M2_SDVO_MIN, .max = G4X_M2_SDVO_MAX },
+ .p = { .min = G4X_P_SDVO_MIN, .max = G4X_P_SDVO_MAX },
+ .p1 = { .min = G4X_P1_SDVO_MIN, .max = G4X_P1_SDVO_MAX},
+ .p2 = { .dot_limit = G4X_P2_SDVO_LIMIT,
+ .p2_slow = G4X_P2_SDVO_SLOW,
+ .p2_fast = G4X_P2_SDVO_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+ },
+ { /* INTEL_LIMIT_G4X_HDMI_DAC */
+ .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX },
+ .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX},
+ .n = { .min = G4X_N_HDMI_DAC_MIN, .max = G4X_N_HDMI_DAC_MAX },
+ .m = { .min = G4X_M_HDMI_DAC_MIN, .max = G4X_M_HDMI_DAC_MAX },
+ .m1 = { .min = G4X_M1_HDMI_DAC_MIN, .max = G4X_M1_HDMI_DAC_MAX },
+ .m2 = { .min = G4X_M2_HDMI_DAC_MIN, .max = G4X_M2_HDMI_DAC_MAX },
+ .p = { .min = G4X_P_HDMI_DAC_MIN, .max = G4X_P_HDMI_DAC_MAX },
+ .p1 = { .min = G4X_P1_HDMI_DAC_MIN, .max = G4X_P1_HDMI_DAC_MAX},
+ .p2 = { .dot_limit = G4X_P2_HDMI_DAC_LIMIT,
+ .p2_slow = G4X_P2_HDMI_DAC_SLOW,
+ .p2_fast = G4X_P2_HDMI_DAC_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+ },
+ { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */
+ .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_DOT_SINGLE_CHANNEL_LVDS_MAX },
+ .vco = { .min = G4X_VCO_MIN,
+ .max = G4X_VCO_MAX },
+ .n = { .min = G4X_N_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_N_SINGLE_CHANNEL_LVDS_MAX },
+ .m = { .min = G4X_M_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_M_SINGLE_CHANNEL_LVDS_MAX },
+ .m1 = { .min = G4X_M1_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_M1_SINGLE_CHANNEL_LVDS_MAX },
+ .m2 = { .min = G4X_M2_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_M2_SINGLE_CHANNEL_LVDS_MAX },
+ .p = { .min = G4X_P_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_P_SINGLE_CHANNEL_LVDS_MAX },
+ .p1 = { .min = G4X_P1_SINGLE_CHANNEL_LVDS_MIN,
+ .max = G4X_P1_SINGLE_CHANNEL_LVDS_MAX },
+ .p2 = { .dot_limit = G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT,
+ .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
+ .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+ },
+ { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */
+ .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_DOT_DUAL_CHANNEL_LVDS_MAX },
+ .vco = { .min = G4X_VCO_MIN,
+ .max = G4X_VCO_MAX },
+ .n = { .min = G4X_N_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_N_DUAL_CHANNEL_LVDS_MAX },
+ .m = { .min = G4X_M_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_M_DUAL_CHANNEL_LVDS_MAX },
+ .m1 = { .min = G4X_M1_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_M1_DUAL_CHANNEL_LVDS_MAX },
+ .m2 = { .min = G4X_M2_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_M2_DUAL_CHANNEL_LVDS_MAX },
+ .p = { .min = G4X_P_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_P_DUAL_CHANNEL_LVDS_MAX },
+ .p1 = { .min = G4X_P1_DUAL_CHANNEL_LVDS_MIN,
+ .max = G4X_P1_DUAL_CHANNEL_LVDS_MAX },
+ .p2 = { .dot_limit = G4X_P2_DUAL_CHANNEL_LVDS_LIMIT,
+ .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
+ .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+ },
+ { /* INTEL_LIMIT_IGD_SDVO */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX},
+ .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
+ .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
+ .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
+ .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
+ .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
},
+ { /* INTEL_LIMIT_IGD_LVDS */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
+ .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
+ .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
+ .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
+ .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
+ .p = { .min = IGD_P_LVDS_MIN, .max = IGD_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* IGD only supports single-channel mode. */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW },
+ },
+
};
+static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ const intel_limit_t *limit;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ /* LVDS with dual channel */
+ limit = &intel_limits
+ [INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS];
+ else
+ /* LVDS with dual channel */
+ limit = &intel_limits
+ [INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS];
+ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) ||
+ intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
+ limit = &intel_limits[INTEL_LIMIT_G4X_HDMI_DAC];
+ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+ limit = &intel_limits[INTEL_LIMIT_G4X_SDVO];
+ } else /* The option is for other outputs */
+ limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+
+ return limit;
+}
+
static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
const intel_limit_t *limit;
- if (IS_I9XX(dev)) {
+ if (IS_G4X(dev)) {
+ limit = intel_g4x_limit(crtc);
+ } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
else
limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+ } else if (IS_IGD(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_IGD_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_IGD_SDVO_DAC];
} else {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
@@ -189,8 +437,21 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
return limit;
}
-static void intel_clock(int refclk, intel_clock_t *clock)
+/* m1 is reserved as 0 in IGD, n is a ring counter */
+static void igd_clock(int refclk, intel_clock_t *clock)
{
+ clock->m = clock->m2 + 2;
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / clock->n;
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
+{
+ if (IS_IGD(dev)) {
+ igd_clock(refclk, clock);
+ return;
+ }
clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
clock->p = clock->p1 * clock->p2;
clock->vco = refclk * clock->m / (clock->n + 2);
@@ -226,6 +487,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
{
const intel_limit_t *limit = intel_limit (crtc);
+ struct drm_device *dev = crtc->dev;
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
INTELPllInvalid ("p1 out of range\n");
@@ -235,7 +497,7 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
INTELPllInvalid ("m2 out of range\n");
if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
INTELPllInvalid ("m1 out of range\n");
- if (clock->m1 <= clock->m2)
+ if (clock->m1 <= clock->m2 && !IS_IGD(dev))
INTELPllInvalid ("m1 <= m2\n");
if (clock->m < limit->m.min || limit->m.max < clock->m)
INTELPllInvalid ("m out of range\n");
@@ -252,18 +514,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
return true;
}
-/**
- * Returns a set of divisors for the desired target clock with the given
- * refclk, or FALSE. The returned values represent the clock equation:
- * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
- */
-static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
- int refclk, intel_clock_t *best_clock)
+static bool
+intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock)
+
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
intel_clock_t clock;
- const intel_limit_t *limit = intel_limit(crtc);
int err = target;
if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
@@ -289,15 +547,17 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
memset (best_clock, 0, sizeof (*best_clock));
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
- for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
- clock.m2 <= limit->m2.max; clock.m2++) {
+ for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
+ /* m1 is always 0 in IGD */
+ if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+ break;
for (clock.n = limit->n.min; clock.n <= limit->n.max;
clock.n++) {
for (clock.p1 = limit->p1.min;
clock.p1 <= limit->p1.max; clock.p1++) {
int this_err;
- intel_clock(refclk, &clock);
+ intel_clock(dev, refclk, &clock);
if (!intel_PLL_is_valid(crtc, &clock))
continue;
@@ -315,6 +575,63 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
return (err != target);
}
+static bool
+intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_clock_t clock;
+ int max_n;
+ bool found;
+ /* approximately equals target * 0.00488 */
+ int err_most = (target >> 8) + (target >> 10);
+ found = false;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+ clock.p2 = limit->p2.p2_slow;
+ } else {
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
+ }
+
+ memset(best_clock, 0, sizeof(*best_clock));
+ max_n = limit->n.max;
+ /* based on hardware requriment prefer smaller n to precision */
+ for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+ /* based on hardware requirment prefere larger m1,m2, p1 */
+ for (clock.m1 = limit->m1.max;
+ clock.m1 >= limit->m1.min; clock.m1--) {
+ for (clock.m2 = limit->m2.max;
+ clock.m2 >= limit->m2.min; clock.m2--) {
+ for (clock.p1 = limit->p1.max;
+ clock.p1 >= limit->p1.min; clock.p1--) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+ if (!intel_PLL_is_valid(crtc, &clock))
+ continue;
+ this_err = abs(clock.dot - target) ;
+ if (this_err < err_most) {
+ *best_clock = clock;
+ err_most = this_err;
+ max_n = clock.n;
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ return found;
+}
+
void
intel_wait_for_vblank(struct drm_device *dev)
{
@@ -634,7 +951,7 @@ static int intel_get_core_clock_speed(struct drm_device *dev)
return 400000;
else if (IS_I915G(dev))
return 333000;
- else if (IS_I945GM(dev) || IS_845G(dev))
+ else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev))
return 200000;
else if (IS_I915GM(dev)) {
u16 gcfgc = 0;
@@ -733,6 +1050,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
bool is_crt = false, is_lvds = false, is_tv = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
+ const intel_limit_t *limit;
int ret;
drm_vblank_pre_modeset(dev, pipe);
@@ -776,13 +1094,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
refclk = 48000;
}
- ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+ /*
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+ limit = intel_limit(crtc);
+ ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
return -EINVAL;
}
- fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+ if (IS_IGD(dev))
+ fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
+ else
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
dpll = DPLL_VGA_MODE_DIS;
if (IS_I9XX(dev)) {
@@ -799,7 +1126,10 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
/* compute bitmask from p1 value */
- dpll |= (1 << (clock.p1 - 1)) << 16;
+ if (IS_IGD(dev))
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
+ else
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
switch (clock.p2) {
case 5:
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
@@ -1279,10 +1609,20 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
- clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
- clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ if (IS_IGD(dev)) {
+ clock.n = ffs((fp & FP_N_IGD_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
+ clock.m2 = (fp & FP_M2_IGD_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ } else {
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ }
+
if (IS_I9XX(dev)) {
- clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ if (IS_IGD(dev))
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_IGD) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT_IGD);
+ else
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
DPLL_FPA01_P1_POST_DIV_SHIFT);
switch (dpll & DPLL_MODE_MASK) {
@@ -1301,7 +1641,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
}
/* XXX: Handle the 100Mhz refclk */
- intel_clock(96000, &clock);
+ intel_clock(dev, 96000, &clock);
} else {
bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
@@ -1313,9 +1653,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
if ((dpll & PLL_REF_INPUT_MASK) ==
PLLB_REF_INPUT_SPREADSPECTRUMIN) {
/* XXX: might not be 66MHz */
- intel_clock(66000, &clock);
+ intel_clock(dev, 66000, &clock);
} else
- intel_clock(48000, &clock);
+ intel_clock(dev, 48000, &clock);
} else {
if (dpll & PLL_P1_DIVIDE_BY_TWO)
clock.p1 = 2;
@@ -1328,7 +1668,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
else
clock.p2 = 2;
- intel_clock(48000, &clock);
+ intel_clock(dev, 48000, &clock);
}
}
@@ -1474,13 +1814,21 @@ static void intel_setup_outputs(struct drm_device *dev)
if (IS_I9XX(dev)) {
int found;
+ u32 reg;
if (I915_READ(SDVOB) & SDVO_DETECTED) {
found = intel_sdvo_init(dev, SDVOB);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOB);
}
- if (!IS_G4X(dev) || (I915_READ(SDVOB) & SDVO_DETECTED)) {
+
+ /* Before G4X SDVOC doesn't have its own detect register */
+ if (IS_G4X(dev))
+ reg = SDVOC;
+ else
+ reg = SDVOB;
+
+ if (I915_READ(reg) & SDVO_DETECTED) {
found = intel_sdvo_init(dev, SDVOC);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOC);
diff --git a/trunk/drivers/gpu/drm/i915/intel_lvds.c b/trunk/drivers/gpu/drm/i915/intel_lvds.c
index 0d211af98854..6619f26e46a5 100644
--- a/trunk/drivers/gpu/drm/i915/intel_lvds.c
+++ b/trunk/drivers/gpu/drm/i915/intel_lvds.c
@@ -265,7 +265,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
pfit_control = 0;
if (!IS_I965G(dev)) {
- if (dev_priv->panel_wants_dither)
+ if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
}
else
diff --git a/trunk/drivers/gpu/drm/i915/intel_tv.c b/trunk/drivers/gpu/drm/i915/intel_tv.c
index 56485d67369b..ceca9471a75a 100644
--- a/trunk/drivers/gpu/drm/i915/intel_tv.c
+++ b/trunk/drivers/gpu/drm/i915/intel_tv.c
@@ -217,8 +217,8 @@ static const u32 filter_table[] = {
*/
static const struct color_conversion ntsc_m_csc_composite = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
- .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
- .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
};
static const struct video_levels ntsc_m_levels_composite = {
@@ -226,9 +226,9 @@ static const struct video_levels ntsc_m_levels_composite = {
};
static const struct color_conversion ntsc_m_csc_svideo = {
- .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
- .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
- .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
};
static const struct video_levels ntsc_m_levels_svideo = {
@@ -237,8 +237,8 @@ static const struct video_levels ntsc_m_levels_svideo = {
static const struct color_conversion ntsc_j_csc_composite = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
- .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00,
- .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00,
+ .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
+ .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
};
static const struct video_levels ntsc_j_levels_composite = {
@@ -247,8 +247,8 @@ static const struct video_levels ntsc_j_levels_composite = {
static const struct color_conversion ntsc_j_csc_svideo = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
- .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00,
- .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00,
+ .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
+ .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
};
static const struct video_levels ntsc_j_levels_svideo = {
@@ -257,8 +257,8 @@ static const struct video_levels ntsc_j_levels_svideo = {
static const struct color_conversion pal_csc_composite = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
- .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00,
- .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00,
+ .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
+ .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
};
static const struct video_levels pal_levels_composite = {
@@ -267,8 +267,8 @@ static const struct video_levels pal_levels_composite = {
static const struct color_conversion pal_csc_svideo = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
- .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00,
- .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00,
+ .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
+ .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
};
static const struct video_levels pal_levels_svideo = {
@@ -277,8 +277,8 @@ static const struct video_levels pal_levels_svideo = {
static const struct color_conversion pal_m_csc_composite = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
- .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
- .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
};
static const struct video_levels pal_m_levels_composite = {
@@ -286,9 +286,9 @@ static const struct video_levels pal_m_levels_composite = {
};
static const struct color_conversion pal_m_csc_svideo = {
- .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
- .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
- .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
};
static const struct video_levels pal_m_levels_svideo = {
@@ -297,8 +297,8 @@ static const struct video_levels pal_m_levels_svideo = {
static const struct color_conversion pal_n_csc_composite = {
.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
- .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
- .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
};
static const struct video_levels pal_n_levels_composite = {
@@ -306,9 +306,9 @@ static const struct video_levels pal_n_levels_composite = {
};
static const struct color_conversion pal_n_csc_svideo = {
- .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
- .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
- .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
};
static const struct video_levels pal_n_levels_svideo = {
@@ -319,9 +319,9 @@ static const struct video_levels pal_n_levels_svideo = {
* Component connections
*/
static const struct color_conversion sdtv_csc_yprpb = {
- .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146,
- .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00,
- .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00,
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+ .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
+ .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
};
static const struct color_conversion sdtv_csc_rgb = {
@@ -331,9 +331,9 @@ static const struct color_conversion sdtv_csc_rgb = {
};
static const struct color_conversion hdtv_csc_yprpb = {
- .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146,
- .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00,
- .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00,
+ .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
+ .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
+ .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
};
static const struct color_conversion hdtv_csc_rgb = {
@@ -414,7 +414,7 @@ struct tv_mode {
static const struct tv_mode tv_modes[] = {
{
.name = "NTSC-M",
- .clock = 107520,
+ .clock = 108000,
.refresh = 29970,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
@@ -442,8 +442,8 @@ static const struct tv_mode tv_modes[] = {
.vburst_start_f4 = 10, .vburst_end_f4 = 240,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
- .dda1_inc = 136,
- .dda2_inc = 7624, .dda2_size = 20013,
+ .dda1_inc = 135,
+ .dda2_inc = 20800, .dda2_size = 27456,
.dda3_inc = 0, .dda3_size = 0,
.sc_reset = TV_SC_RESET_EVERY_4,
.pal_burst = false,
@@ -457,7 +457,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "NTSC-443",
- .clock = 107520,
+ .clock = 108000,
.refresh = 29970,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
@@ -485,10 +485,10 @@ static const struct tv_mode tv_modes[] = {
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
.dda1_inc = 168,
- .dda2_inc = 18557, .dda2_size = 20625,
- .dda3_inc = 0, .dda3_size = 0,
- .sc_reset = TV_SC_RESET_EVERY_8,
- .pal_burst = true,
+ .dda2_inc = 4093, .dda2_size = 27456,
+ .dda3_inc = 310, .dda3_size = 525,
+ .sc_reset = TV_SC_RESET_NEVER,
+ .pal_burst = false,
.composite_levels = &ntsc_m_levels_composite,
.composite_color = &ntsc_m_csc_composite,
@@ -499,7 +499,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "NTSC-J",
- .clock = 107520,
+ .clock = 108000,
.refresh = 29970,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
@@ -527,8 +527,8 @@ static const struct tv_mode tv_modes[] = {
.vburst_start_f4 = 10, .vburst_end_f4 = 240,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
- .dda1_inc = 136,
- .dda2_inc = 7624, .dda2_size = 20013,
+ .dda1_inc = 135,
+ .dda2_inc = 20800, .dda2_size = 27456,
.dda3_inc = 0, .dda3_size = 0,
.sc_reset = TV_SC_RESET_EVERY_4,
.pal_burst = false,
@@ -542,7 +542,7 @@ static const struct tv_mode tv_modes[] = {
},
{
.name = "PAL-M",
- .clock = 107520,
+ .clock = 108000,
.refresh = 29970,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
@@ -570,11 +570,11 @@ static const struct tv_mode tv_modes[] = {
.vburst_start_f4 = 10, .vburst_end_f4 = 240,
/* desired 3.5800000 actual 3.5800000 clock 107.52 */
- .dda1_inc = 136,
- .dda2_inc = 7624, .dda2_size = 20013,
+ .dda1_inc = 135,
+ .dda2_inc = 16704, .dda2_size = 27456,
.dda3_inc = 0, .dda3_size = 0,
- .sc_reset = TV_SC_RESET_EVERY_4,
- .pal_burst = false,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
.composite_levels = &pal_m_levels_composite,
.composite_color = &pal_m_csc_composite,
@@ -586,7 +586,7 @@ static const struct tv_mode tv_modes[] = {
{
/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
.name = "PAL-N",
- .clock = 107520,
+ .clock = 108000,
.refresh = 25000,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
@@ -615,9 +615,9 @@ static const struct tv_mode tv_modes[] = {
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
- .dda1_inc = 168,
- .dda2_inc = 18557, .dda2_size = 20625,
- .dda3_inc = 0, .dda3_size = 0,
+ .dda1_inc = 135,
+ .dda2_inc = 23578, .dda2_size = 27648,
+ .dda3_inc = 134, .dda3_size = 625,
.sc_reset = TV_SC_RESET_EVERY_8,
.pal_burst = true,
@@ -631,12 +631,12 @@ static const struct tv_mode tv_modes[] = {
{
/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
.name = "PAL",
- .clock = 107520,
+ .clock = 108000,
.refresh = 25000,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0,
- .hsync_end = 64, .hblank_end = 128,
+ .hsync_end = 64, .hblank_end = 142,
.hblank_start = 844, .htotal = 863,
.progressive = false, .trilevel_sync = false,
@@ -659,8 +659,8 @@ static const struct tv_mode tv_modes[] = {
/* desired 4.4336180 actual 4.4336180 clock 107.52 */
.dda1_inc = 168,
- .dda2_inc = 18557, .dda2_size = 20625,
- .dda3_inc = 0, .dda3_size = 0,
+ .dda2_inc = 4122, .dda2_size = 27648,
+ .dda3_inc = 67, .dda3_size = 625,
.sc_reset = TV_SC_RESET_EVERY_8,
.pal_burst = true,
@@ -689,7 +689,7 @@ static const struct tv_mode tv_modes[] = {
.veq_ena = false,
.vi_end_f1 = 44, .vi_end_f2 = 44,
- .nbr_end = 496,
+ .nbr_end = 479,
.burst_ena = false,
@@ -713,7 +713,7 @@ static const struct tv_mode tv_modes[] = {
.veq_ena = false,
.vi_end_f1 = 44, .vi_end_f2 = 44,
- .nbr_end = 496,
+ .nbr_end = 479,
.burst_ena = false,
@@ -876,7 +876,7 @@ static const struct tv_mode tv_modes[] = {
.component_only = 1,
.hsync_end = 88, .hblank_end = 235,
- .hblank_start = 2155, .htotal = 2200,
+ .hblank_start = 2155, .htotal = 2201,
.progressive = false, .trilevel_sync = true,
@@ -1082,7 +1082,7 @@ intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mo
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
/* Ensure TV refresh is close to desired refresh */
- if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1)
+ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 10)
return MODE_OK;
return MODE_CLOCK_RANGE;
}
@@ -1135,7 +1135,8 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (!tv_mode)
return; /* can't happen (mode_prepare prevents this) */
- tv_ctl = 0;
+ tv_ctl = I915_READ(TV_CTL);
+ tv_ctl &= TV_CTL_SAVE;
switch (tv_priv->type) {
default:
@@ -1215,7 +1216,6 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
/* dda1 implies valid video levels */
if (tv_mode->dda1_inc) {
scctl1 |= TV_SC_DDA1_EN;
- scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
}
if (tv_mode->dda2_inc)
@@ -1225,6 +1225,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
scctl1 |= TV_SC_DDA3_EN;
scctl1 |= tv_mode->sc_reset;
+ scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
@@ -1266,7 +1267,11 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
color_conversion->av);
}
- I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+ if (IS_I965G(dev))
+ I915_WRITE(TV_CLR_KNOBS, 0x00404000);
+ else
+ I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+
if (video_levels)
I915_WRITE(TV_CLR_LEVEL,
((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
@@ -1401,6 +1406,7 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
tv_dac = I915_READ(TV_DAC);
I915_WRITE(TV_DAC, save_tv_dac);
I915_WRITE(TV_CTL, save_tv_ctl);
+ intel_wait_for_vblank(dev);
}
/*
* A B C
@@ -1451,7 +1457,7 @@ intel_tv_detect(struct drm_connector *connector)
mode = reported_modes[0];
drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
- if (encoder->crtc) {
+ if (encoder->crtc && encoder->crtc->enabled) {
type = intel_tv_detect_type(encoder->crtc, intel_output);
} else {
crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode);
@@ -1462,6 +1468,8 @@ intel_tv_detect(struct drm_connector *connector)
type = -1;
}
+ tv_priv->type = type;
+
if (type < 0)
return connector_status_disconnected;
@@ -1495,7 +1503,8 @@ intel_tv_get_modes(struct drm_connector *connector)
struct drm_display_mode *mode_ptr;
struct intel_output *intel_output = to_intel_output(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
- int j;
+ int j, count = 0;
+ u64 tmp;
for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]);
j++) {
@@ -1510,8 +1519,9 @@ intel_tv_get_modes(struct drm_connector *connector)
&& !tv_mode->component_only))
continue;
- mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode),
- DRM_MEM_DRIVER);
+ mode_ptr = drm_mode_create(connector->dev);
+ if (!mode_ptr)
+ continue;
strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
mode_ptr->hdisplay = hactive_s;
@@ -1528,15 +1538,17 @@ intel_tv_get_modes(struct drm_connector *connector)
mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
mode_ptr->vtotal = vactive_s + 33;
- mode_ptr->clock = (int) (tv_mode->refresh *
- mode_ptr->vtotal *
- mode_ptr->htotal / 1000) / 1000;
+ tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
+ tmp *= mode_ptr->htotal;
+ tmp = div_u64(tmp, 1000000);
+ mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
drm_mode_probed_add(connector, mode_ptr);
+ count++;
}
- return 0;
+ return count;
}
static void
diff --git a/trunk/drivers/ieee1394/csr.c b/trunk/drivers/ieee1394/csr.c
index 31400c8ae051..d696f69ebce5 100644
--- a/trunk/drivers/ieee1394/csr.c
+++ b/trunk/drivers/ieee1394/csr.c
@@ -68,22 +68,22 @@ static struct hpsb_highlevel csr_highlevel = {
.host_reset = host_reset,
};
-const static struct hpsb_address_ops map_ops = {
+static const struct hpsb_address_ops map_ops = {
.read = read_maps,
};
-const static struct hpsb_address_ops fcp_ops = {
+static const struct hpsb_address_ops fcp_ops = {
.write = write_fcp,
};
-const static struct hpsb_address_ops reg_ops = {
+static const struct hpsb_address_ops reg_ops = {
.read = read_regs,
.write = write_regs,
.lock = lock_regs,
.lock64 = lock64_regs,
};
-const static struct hpsb_address_ops config_rom_ops = {
+static const struct hpsb_address_ops config_rom_ops = {
.read = read_config_rom,
};
diff --git a/trunk/drivers/ieee1394/dv1394.c b/trunk/drivers/ieee1394/dv1394.c
index cb15bfa38d70..823a6297a1af 100644
--- a/trunk/drivers/ieee1394/dv1394.c
+++ b/trunk/drivers/ieee1394/dv1394.c
@@ -2171,7 +2171,7 @@ static const struct file_operations dv1394_fops=
* Export information about protocols/devices supported by this driver.
*/
#ifdef MODULE
-static struct ieee1394_device_id dv1394_id_table[] = {
+static const struct ieee1394_device_id dv1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
diff --git a/trunk/drivers/ieee1394/eth1394.c b/trunk/drivers/ieee1394/eth1394.c
index 1a919df809f8..4ca103577c0a 100644
--- a/trunk/drivers/ieee1394/eth1394.c
+++ b/trunk/drivers/ieee1394/eth1394.c
@@ -181,7 +181,7 @@ static void ether1394_remove_host(struct hpsb_host *host);
static void ether1394_host_reset(struct hpsb_host *host);
/* Function for incoming 1394 packets */
-const static struct hpsb_address_ops addr_ops = {
+static const struct hpsb_address_ops addr_ops = {
.write = ether1394_write,
};
@@ -438,7 +438,7 @@ static int eth1394_update(struct unit_directory *ud)
return eth1394_new_node(hi, ud);
}
-static struct ieee1394_device_id eth1394_id_table[] = {
+static const struct ieee1394_device_id eth1394_id_table[] = {
{
.match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION),
diff --git a/trunk/drivers/ieee1394/highlevel.c b/trunk/drivers/ieee1394/highlevel.c
index 600e391c8fe7..4bc443546e04 100644
--- a/trunk/drivers/ieee1394/highlevel.c
+++ b/trunk/drivers/ieee1394/highlevel.c
@@ -478,7 +478,7 @@ int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
return retval;
}
-const static struct hpsb_address_ops dummy_ops;
+static const struct hpsb_address_ops dummy_ops;
/* dummy address spaces as lower and upper bounds of the host's a.s. list */
static void init_hpsb_highlevel(struct hpsb_host *host)
diff --git a/trunk/drivers/ieee1394/nodemgr.c b/trunk/drivers/ieee1394/nodemgr.c
index 53aada5bbe1e..a6d55bebe61a 100644
--- a/trunk/drivers/ieee1394/nodemgr.c
+++ b/trunk/drivers/ieee1394/nodemgr.c
@@ -484,7 +484,7 @@ static struct device_attribute *const fw_host_attrs[] = {
static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf)
{
struct hpsb_protocol_driver *driver;
- struct ieee1394_device_id *id;
+ const struct ieee1394_device_id *id;
int length = 0;
char *scratch = buf;
@@ -658,7 +658,7 @@ static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
{
struct hpsb_protocol_driver *driver;
struct unit_directory *ud;
- struct ieee1394_device_id *id;
+ const struct ieee1394_device_id *id;
/* We only match unit directories */
if (dev->platform_data != &nodemgr_ud_platform_data)
diff --git a/trunk/drivers/ieee1394/nodemgr.h b/trunk/drivers/ieee1394/nodemgr.h
index ee5acdbd114a..749b271d3107 100644
--- a/trunk/drivers/ieee1394/nodemgr.h
+++ b/trunk/drivers/ieee1394/nodemgr.h
@@ -125,7 +125,7 @@ struct hpsb_protocol_driver {
* probe function below can implement further protocol
* dependent or vendor dependent checking.
*/
- struct ieee1394_device_id *id_table;
+ const struct ieee1394_device_id *id_table;
/*
* The update function is called when the node has just
diff --git a/trunk/drivers/ieee1394/raw1394.c b/trunk/drivers/ieee1394/raw1394.c
index bad66c65b0d6..da5f8829b503 100644
--- a/trunk/drivers/ieee1394/raw1394.c
+++ b/trunk/drivers/ieee1394/raw1394.c
@@ -90,7 +90,7 @@ static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
u16 flags);
-const static struct hpsb_address_ops arm_ops = {
+static const struct hpsb_address_ops arm_ops = {
.read = arm_read,
.write = arm_write,
.lock = arm_lock,
@@ -369,6 +369,7 @@ static const char __user *raw1394_compat_write(const char __user *buf)
{
struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
struct raw1394_request __user *r;
+
r = compat_alloc_user_space(sizeof(struct raw1394_request));
#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x))
@@ -378,7 +379,8 @@ static const char __user *raw1394_compat_write(const char __user *buf)
C(tag) ||
C(sendb) ||
C(recvb))
- return ERR_PTR(-EFAULT);
+ return (__force const char __user *)ERR_PTR(-EFAULT);
+
return (const char __user *)r;
}
#undef C
@@ -389,6 +391,7 @@ static int
raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
{
struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
+
if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) ||
P(type) ||
P(error) ||
@@ -400,6 +403,7 @@ raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
P(sendb) ||
P(recvb))
return -EFAULT;
+
return sizeof(struct compat_raw1394_req);
}
#undef P
@@ -2249,8 +2253,8 @@ static ssize_t raw1394_write(struct file *file, const char __user * buffer,
sizeof(struct compat_raw1394_req) !=
sizeof(struct raw1394_request)) {
buffer = raw1394_compat_write(buffer);
- if (IS_ERR(buffer))
- return PTR_ERR(buffer);
+ if (IS_ERR((__force void *)buffer))
+ return PTR_ERR((__force void *)buffer);
} else
#endif
if (count != sizeof(struct raw1394_request)) {
@@ -2978,7 +2982,7 @@ static int raw1394_release(struct inode *inode, struct file *file)
* Export information about protocols/devices supported by this driver.
*/
#ifdef MODULE
-static struct ieee1394_device_id raw1394_id_table[] = {
+static const struct ieee1394_device_id raw1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
diff --git a/trunk/drivers/ieee1394/sbp2.c b/trunk/drivers/ieee1394/sbp2.c
index f3fd8657ce4b..a51ab233342d 100644
--- a/trunk/drivers/ieee1394/sbp2.c
+++ b/trunk/drivers/ieee1394/sbp2.c
@@ -265,7 +265,7 @@ static struct hpsb_highlevel sbp2_highlevel = {
.host_reset = sbp2_host_reset,
};
-const static struct hpsb_address_ops sbp2_ops = {
+static const struct hpsb_address_ops sbp2_ops = {
.write = sbp2_handle_status_write
};
@@ -275,7 +275,7 @@ static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *,
static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64,
size_t, u16);
-const static struct hpsb_address_ops sbp2_physdma_ops = {
+static const struct hpsb_address_ops sbp2_physdma_ops = {
.read = sbp2_handle_physdma_read,
.write = sbp2_handle_physdma_write,
};
@@ -285,7 +285,7 @@ const static struct hpsb_address_ops sbp2_physdma_ops = {
/*
* Interface to driver core and IEEE 1394 core
*/
-static struct ieee1394_device_id sbp2_id_table[] = {
+static const struct ieee1394_device_id sbp2_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff,
@@ -1413,8 +1413,7 @@ static void sbp2_parse_unit_directory(struct sbp2_lu *lu,
"(firmware_revision 0x%06x, vendor_id 0x%06x,"
" model_id 0x%06x)",
NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid),
- workarounds, firmware_revision,
- ud->vendor_id ? ud->vendor_id : ud->ne->vendor_id,
+ workarounds, firmware_revision, ud->vendor_id,
model);
/* We would need one SCSI host template for each target to adjust
diff --git a/trunk/drivers/ieee1394/video1394.c b/trunk/drivers/ieee1394/video1394.c
index 679a918a5cc7..d287ba79821d 100644
--- a/trunk/drivers/ieee1394/video1394.c
+++ b/trunk/drivers/ieee1394/video1394.c
@@ -1294,7 +1294,7 @@ static const struct file_operations video1394_fops=
* Export information about protocols/devices supported by this driver.
*/
#ifdef MODULE
-static struct ieee1394_device_id video1394_id_table[] = {
+static const struct ieee1394_device_id video1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
diff --git a/trunk/drivers/infiniband/hw/nes/nes_nic.c b/trunk/drivers/infiniband/hw/nes/nes_nic.c
index 8d3e4c6f237e..ecb1f6fd6276 100644
--- a/trunk/drivers/infiniband/hw/nes/nes_nic.c
+++ b/trunk/drivers/infiniband/hw/nes/nes_nic.c
@@ -1602,8 +1602,6 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
netif_napi_add(netdev, &nesvnic->napi, nes_netdev_poll, 128);
nes_debug(NES_DBG_INIT, "Enabling VLAN Insert/Delete.\n");
netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- netdev->vlan_rx_register = nes_netdev_vlan_rx_register;
- netdev->features |= NETIF_F_LLTX;
/* Fill in the port structure */
nesvnic->netdev = netdev;
diff --git a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
index 12876392516e..13d7674b293d 100644
--- a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -168,7 +168,7 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
{
int error = 0;
- debug_scsi("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
+ iser_dbg("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
error = iser_send_control(conn, task);
@@ -195,7 +195,7 @@ iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
/* Send data-out PDUs while there's still unsolicited data to send */
while (iscsi_task_has_unsol_data(task)) {
iscsi_prep_data_out_pdu(task, r2t, &hdr);
- debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
+ iser_dbg("Sending data-out: itt 0x%x, data count %d\n",
hdr.itt, r2t->data_count);
/* the buffer description has been passed with the command */
@@ -206,7 +206,7 @@ iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
goto iscsi_iser_task_xmit_unsol_data_exit;
}
r2t->sent += r2t->data_count;
- debug_scsi("Need to send %d more as data-out PDUs\n",
+ iser_dbg("Need to send %d more as data-out PDUs\n",
r2t->data_length - r2t->sent);
}
@@ -227,12 +227,12 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
BUG_ON(scsi_bufflen(task->sc) == 0);
- debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+ iser_dbg("cmd [itt %x total %d imm %d unsol_data %d\n",
task->itt, scsi_bufflen(task->sc),
task->imm_count, task->unsol_r2t.data_length);
}
- debug_scsi("task deq [cid %d itt 0x%x]\n",
+ iser_dbg("task deq [cid %d itt 0x%x]\n",
conn->id, task->itt);
/* Send the cmd PDU */
@@ -397,14 +397,14 @@ static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
static struct iscsi_cls_session *
iscsi_iser_session_create(struct iscsi_endpoint *ep,
uint16_t cmds_max, uint16_t qdepth,
- uint32_t initial_cmdsn, uint32_t *hostno)
+ uint32_t initial_cmdsn)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
struct iser_conn *ib_conn;
- shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
+ shost = iscsi_host_alloc(&iscsi_iser_sht, 0, 1);
if (!shost)
return NULL;
shost->transportt = iscsi_iser_scsi_transport;
@@ -423,7 +423,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
if (iscsi_host_add(shost,
ep ? ib_conn->device->ib_device->dma_device : NULL))
goto free_host;
- *hostno = shost->host_no;
/*
* we do not support setting can_queue cmd_per_lun from userspace yet
@@ -596,7 +595,7 @@ static struct scsi_host_template iscsi_iser_sht = {
.change_queue_depth = iscsi_change_queue_depth,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
.max_sectors = 1024,
- .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+ .cmd_per_lun = ISER_DEF_CMD_PER_LUN,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler= iscsi_eh_target_reset,
diff --git a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.h b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.h
index 861119593f2b..9d529cae1f0d 100644
--- a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -93,7 +93,7 @@
/* support upto 512KB in one RDMA */
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
-#define ISCSI_ISER_MAX_LUN 256
+#define ISER_DEF_CMD_PER_LUN 128
/* QP settings */
/* Maximal bounds on received asynchronous PDUs */
diff --git a/trunk/drivers/infiniband/ulp/iser/iser_initiator.c b/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
index e209cb8dd948..9de640200ad3 100644
--- a/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -661,7 +661,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
if (resume_tx) {
iser_dbg("%ld resuming tx\n",jiffies);
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
}
if (tx_desc->type == ISCSI_TX_CONTROL) {
diff --git a/trunk/drivers/media/dvb/firewire/firedtv-avc.c b/trunk/drivers/media/dvb/firewire/firedtv-avc.c
index b55d9ccaf33e..32526f103b59 100644
--- a/trunk/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/trunk/drivers/media/dvb/firewire/firedtv-avc.c
@@ -115,7 +115,7 @@ static const char *debug_fcp_ctype(unsigned int ctype)
}
static const char *debug_fcp_opcode(unsigned int opcode,
- const u8 *data, size_t length)
+ const u8 *data, int length)
{
switch (opcode) {
case AVC_OPCODE_VENDOR: break;
@@ -135,13 +135,14 @@ static const char *debug_fcp_opcode(unsigned int opcode,
case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: return "RegisterRC";
case SFE_VENDOR_OPCODE_LNB_CONTROL: return "LNBControl";
case SFE_VENDOR_OPCODE_TUNE_QPSK: return "TuneQPSK";
+ case SFE_VENDOR_OPCODE_TUNE_QPSK2: return "TuneQPSK2";
case SFE_VENDOR_OPCODE_HOST2CA: return "Host2CA";
case SFE_VENDOR_OPCODE_CA2HOST: return "CA2Host";
}
return "Vendor";
}
-static void debug_fcp(const u8 *data, size_t length)
+static void debug_fcp(const u8 *data, int length)
{
unsigned int subunit_type, subunit_id, op;
const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> ";
@@ -266,7 +267,10 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv,
c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;
c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;
c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;
- c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK;
+ if (fdtv->type == FIREDTV_DVB_S2)
+ c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK2;
+ else
+ c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK;
c->operand[4] = (params->frequency >> 24) & 0xff;
c->operand[5] = (params->frequency >> 16) & 0xff;
diff --git a/trunk/drivers/mtd/mtdsuper.c b/trunk/drivers/mtd/mtdsuper.c
index 00d46e137b2a..92285d0089c2 100644
--- a/trunk/drivers/mtd/mtdsuper.c
+++ b/trunk/drivers/mtd/mtdsuper.c
@@ -81,13 +81,16 @@ static int get_sb_mtd_aux(struct file_system_type *fs_type, int flags,
/* go */
sb->s_flags |= MS_ACTIVE;
- return simple_set_mnt(mnt, sb);
+ simple_set_mnt(mnt, sb);
+
+ return 0;
/* new mountpoint for an already mounted superblock */
already_mounted:
DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n",
mtd->index, mtd->name);
- ret = simple_set_mnt(mnt, sb);
+ simple_set_mnt(mnt, sb);
+ ret = 0;
goto out_put;
out_error:
diff --git a/trunk/drivers/net/3c503.c b/trunk/drivers/net/3c503.c
index 5b91a85fe107..4f08bd995836 100644
--- a/trunk/drivers/net/3c503.c
+++ b/trunk/drivers/net/3c503.c
@@ -353,9 +353,6 @@ el2_probe1(struct net_device *dev, int ioaddr)
dev->netdev_ops = &el2_netdev_ops;
dev->ethtool_ops = &netdev_ethtool_ops;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = eip_poll;
-#endif
retval = register_netdev(dev);
if (retval)
diff --git a/trunk/drivers/net/Kconfig b/trunk/drivers/net/Kconfig
index e5ffc1c606c1..f062b424704e 100644
--- a/trunk/drivers/net/Kconfig
+++ b/trunk/drivers/net/Kconfig
@@ -972,6 +972,14 @@ config ENC28J60_WRITEVERIFY
Enable the verify after the buffer write useful for debugging purpose.
If unsure, say N.
+config ETHOC
+ tristate "OpenCores 10/100 Mbps Ethernet MAC support"
+ depends on NET_ETHERNET
+ select MII
+ select PHYLIB
+ help
+ Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
+
config SMC911X
tristate "SMSC LAN911[5678] support"
select CRC32
diff --git a/trunk/drivers/net/Makefile b/trunk/drivers/net/Makefile
index 758ecdf4c820..98409c9dd445 100644
--- a/trunk/drivers/net/Makefile
+++ b/trunk/drivers/net/Makefile
@@ -230,6 +230,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
diff --git a/trunk/drivers/net/ac3200.c b/trunk/drivers/net/ac3200.c
index 071a851a2ea1..eac73382c087 100644
--- a/trunk/drivers/net/ac3200.c
+++ b/trunk/drivers/net/ac3200.c
@@ -143,6 +143,22 @@ struct net_device * __init ac3200_probe(int unit)
}
#endif
+static const struct net_device_ops ac_netdev_ops = {
+ .ndo_open = ac_open,
+ .ndo_stop = ac_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init ac_probe1(int ioaddr, struct net_device *dev)
{
int i, retval;
@@ -253,11 +269,7 @@ static int __init ac_probe1(int ioaddr, struct net_device *dev)
ei_status.block_output = &ac_block_output;
ei_status.get_8390_hdr = &ac_get_8390_hdr;
- dev->open = &ac_open;
- dev->stop = &ac_close_card;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ac_netdev_ops;
NS8390_init(dev, 0);
retval = register_netdev(dev);
diff --git a/trunk/drivers/net/appletalk/cops.c b/trunk/drivers/net/appletalk/cops.c
index 54819a34ba0a..7f8325419803 100644
--- a/trunk/drivers/net/appletalk/cops.c
+++ b/trunk/drivers/net/appletalk/cops.c
@@ -171,7 +171,6 @@ static unsigned int cops_debug = COPS_DEBUG;
struct cops_local
{
- struct net_device_stats stats;
int board; /* Holds what board type is. */
int nodeid; /* Set to 1 once have nodeid. */
unsigned char node_acquire; /* Node ID when acquired. */
@@ -197,7 +196,6 @@ static int cops_send_packet (struct sk_buff *skb, struct net_device *dev);
static void set_multicast_list (struct net_device *dev);
static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
static int cops_close (struct net_device *dev);
-static struct net_device_stats *cops_get_stats (struct net_device *dev);
static void cleanup_card(struct net_device *dev)
{
@@ -260,6 +258,15 @@ struct net_device * __init cops_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops cops_netdev_ops = {
+ .ndo_open = cops_open,
+ .ndo_stop = cops_close,
+ .ndo_start_xmit = cops_send_packet,
+ .ndo_tx_timeout = cops_timeout,
+ .ndo_do_ioctl = cops_ioctl,
+ .ndo_set_multicast_list = set_multicast_list,
+};
+
/*
* This is the real probe routine. Linux has a history of friendly device
* probes on the ISA bus. A good device probes avoids doing writes, and
@@ -333,16 +340,9 @@ static int __init cops_probe1(struct net_device *dev, int ioaddr)
/* Copy local board variable to lp struct. */
lp->board = board;
- dev->hard_start_xmit = cops_send_packet;
- dev->tx_timeout = cops_timeout;
+ dev->netdev_ops = &cops_netdev_ops;
dev->watchdog_timeo = HZ * 2;
- dev->get_stats = cops_get_stats;
- dev->open = cops_open;
- dev->stop = cops_close;
- dev->do_ioctl = cops_ioctl;
- dev->set_multicast_list = set_multicast_list;
- dev->mc_list = NULL;
/* Tell the user where the card is and what mode we're in. */
if(board==DAYNA)
@@ -797,7 +797,7 @@ static void cops_rx(struct net_device *dev)
{
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n",
dev->name);
- lp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
while(pkt_len--) /* Discard packet */
inb(ioaddr);
spin_unlock_irqrestore(&lp->lock, flags);
@@ -819,7 +819,7 @@ static void cops_rx(struct net_device *dev)
{
printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n",
dev->name, pkt_len);
- lp->stats.tx_errors++;
+ dev->stats.tx_errors++;
dev_kfree_skb_any(skb);
return;
}
@@ -836,7 +836,7 @@ static void cops_rx(struct net_device *dev)
if(rsp_type != LAP_RESPONSE)
{
printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type);
- lp->stats.tx_errors++;
+ dev->stats.tx_errors++;
dev_kfree_skb_any(skb);
return;
}
@@ -846,8 +846,8 @@ static void cops_rx(struct net_device *dev)
skb_reset_transport_header(skb); /* Point to data (Skip header). */
/* Update the counters. */
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
/* Send packet to a higher place. */
netif_rx(skb);
@@ -858,7 +858,7 @@ static void cops_timeout(struct net_device *dev)
struct cops_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
- lp->stats.tx_errors++;
+ dev->stats.tx_errors++;
if(lp->board==TANGENT)
{
if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
@@ -916,8 +916,8 @@ static int cops_send_packet(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */
/* Done sending packet, update counters and cleanup. */
- lp->stats.tx_packets++;
- lp->stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies;
dev_kfree_skb (skb);
return 0;
@@ -986,15 +986,6 @@ static int cops_close(struct net_device *dev)
return 0;
}
-/*
- * Get the current statistics.
- * This may be called with the card open or closed.
- */
-static struct net_device_stats *cops_get_stats(struct net_device *dev)
-{
- struct cops_local *lp = netdev_priv(dev);
- return &lp->stats;
-}
#ifdef MODULE
static struct net_device *cops_dev;
diff --git a/trunk/drivers/net/appletalk/ltpc.c b/trunk/drivers/net/appletalk/ltpc.c
index dc4d49605603..78cc71469136 100644
--- a/trunk/drivers/net/appletalk/ltpc.c
+++ b/trunk/drivers/net/appletalk/ltpc.c
@@ -261,7 +261,6 @@ static unsigned char *ltdmacbuf;
struct ltpc_private
{
- struct net_device_stats stats;
struct atalk_addr my_addr;
};
@@ -699,7 +698,6 @@ static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
static struct timer_list ltpc_timer;
static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev);
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev);
static int read_30 ( struct net_device *dev)
{
@@ -726,8 +724,6 @@ static int sendup_buffer (struct net_device *dev)
int dnode, snode, llaptype, len;
int sklen;
struct sk_buff *skb;
- struct ltpc_private *ltpc_priv = netdev_priv(dev);
- struct net_device_stats *stats = <pc_priv->stats;
struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
if (ltc->command != LT_RCVLAP) {
@@ -779,8 +775,8 @@ static int sendup_buffer (struct net_device *dev)
skb_reset_transport_header(skb);
- stats->rx_packets++;
- stats->rx_bytes+=skb->len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
/* toss it onwards */
netif_rx(skb);
@@ -904,10 +900,6 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
/* in kernel 1.3.xx, on entry skb->data points to ddp header,
* and skb->len is the length of the ddp data + ddp header
*/
-
- struct ltpc_private *ltpc_priv = netdev_priv(dev);
- struct net_device_stats *stats = <pc_priv->stats;
-
int i;
struct lt_sendlap cbuf;
unsigned char *hdr;
@@ -936,20 +928,13 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
printk("\n");
}
- stats->tx_packets++;
- stats->tx_bytes+=skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
dev_kfree_skb(skb);
return 0;
}
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev)
-{
- struct ltpc_private *ltpc_priv = netdev_priv(dev);
- struct net_device_stats *stats = <pc_priv->stats;
- return stats;
-}
-
/* initialization stuff */
static int __init ltpc_probe_dma(int base, int dma)
@@ -1027,6 +1012,12 @@ static int __init ltpc_probe_dma(int base, int dma)
return (want & 2) ? 3 : 1;
}
+static const struct net_device_ops ltpc_netdev = {
+ .ndo_start_xmit = ltpc_xmit,
+ .ndo_do_ioctl = ltpc_ioctl,
+ .ndo_set_multicast_list = set_multicast_list,
+};
+
struct net_device * __init ltpc_probe(void)
{
struct net_device *dev;
@@ -1133,14 +1124,7 @@ struct net_device * __init ltpc_probe(void)
else
printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma);
- /* Fill in the fields of the device structure with ethernet-generic values. */
- dev->hard_start_xmit = ltpc_xmit;
- dev->get_stats = ltpc_get_stats;
-
- /* add the ltpc-specific things */
- dev->do_ioctl = <pc_ioctl;
-
- dev->set_multicast_list = &set_multicast_list;
+ dev->netdev_ops = <pc_netdev;
dev->mc_list = NULL;
dev->base_addr = io;
dev->irq = irq;
diff --git a/trunk/drivers/net/at1700.c b/trunk/drivers/net/at1700.c
index ced70799b898..18b566ad4fd1 100644
--- a/trunk/drivers/net/at1700.c
+++ b/trunk/drivers/net/at1700.c
@@ -249,6 +249,17 @@ struct net_device * __init at1700_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops at1700_netdev_ops = {
+ .ndo_open = net_open,
+ .ndo_stop = net_close,
+ .ndo_start_xmit = net_send_packet,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_tx_timeout = net_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
"signature", the default bit pattern after a reset. This *doesn't* work --
there is no way to reset the bus interface without a complete power-cycle!
@@ -448,13 +459,7 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr)
if (net_debug)
printk(version);
- memset(lp, 0, sizeof(struct net_local));
-
- dev->open = net_open;
- dev->stop = net_close;
- dev->hard_start_xmit = net_send_packet;
- dev->set_multicast_list = &set_rx_mode;
- dev->tx_timeout = net_tx_timeout;
+ dev->netdev_ops = &at1700_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
spin_lock_init(&lp->lock);
diff --git a/trunk/drivers/net/benet/be_main.c b/trunk/drivers/net/benet/be_main.c
index f901fee79a20..9b75aa630062 100644
--- a/trunk/drivers/net/benet/be_main.c
+++ b/trunk/drivers/net/benet/be_main.c
@@ -16,6 +16,7 @@
*/
#include "be.h"
+#include
MODULE_VERSION(DRV_VER);
MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -290,6 +291,17 @@ static struct net_device_stats *be_get_stats(struct net_device *dev)
return &adapter->stats.net_stats;
}
+static u32 be_calc_rate(u64 bytes, unsigned long ticks)
+{
+ u64 rate = bytes;
+
+ do_div(rate, ticks / HZ);
+ rate <<= 3; /* bytes/sec -> bits/sec */
+ do_div(rate, 1000000ul); /* MB/Sec */
+
+ return rate;
+}
+
static void be_tx_rate_update(struct be_adapter *adapter)
{
struct be_drvr_stats *stats = drvr_stats(adapter);
@@ -303,11 +315,9 @@ static void be_tx_rate_update(struct be_adapter *adapter)
/* Update tx rate once in two seconds */
if ((now - stats->be_tx_jiffies) > 2 * HZ) {
- u32 r;
- r = (stats->be_tx_bytes - stats->be_tx_bytes_prev) /
- ((now - stats->be_tx_jiffies) / HZ);
- r = r / 1000000; /* M bytes/s */
- stats->be_tx_rate = r * 8; /* M bits/s */
+ stats->be_tx_rate = be_calc_rate(stats->be_tx_bytes
+ - stats->be_tx_bytes_prev,
+ now - stats->be_tx_jiffies);
stats->be_tx_jiffies = now;
stats->be_tx_bytes_prev = stats->be_tx_bytes;
}
@@ -599,7 +609,6 @@ static void be_rx_rate_update(struct be_adapter *adapter)
{
struct be_drvr_stats *stats = drvr_stats(adapter);
ulong now = jiffies;
- u32 rate;
/* Wrapped around */
if (time_before(now, stats->be_rx_jiffies)) {
@@ -611,10 +620,9 @@ static void be_rx_rate_update(struct be_adapter *adapter)
if ((now - stats->be_rx_jiffies) < 2 * HZ)
return;
- rate = (stats->be_rx_bytes - stats->be_rx_bytes_prev) /
- ((now - stats->be_rx_jiffies) / HZ);
- rate = rate / 1000000; /* MB/Sec */
- stats->be_rx_rate = rate * 8; /* Mega Bits/Sec */
+ stats->be_rx_rate = be_calc_rate(stats->be_rx_bytes
+ - stats->be_rx_bytes_prev,
+ now - stats->be_rx_jiffies);
stats->be_rx_jiffies = now;
stats->be_rx_bytes_prev = stats->be_rx_bytes;
}
diff --git a/trunk/drivers/net/cs89x0.c b/trunk/drivers/net/cs89x0.c
index ff6497658a45..7433b88eed7e 100644
--- a/trunk/drivers/net/cs89x0.c
+++ b/trunk/drivers/net/cs89x0.c
@@ -501,6 +501,21 @@ static void net_poll_controller(struct net_device *dev)
}
#endif
+static const struct net_device_ops net_ops = {
+ .ndo_open = net_open,
+ .ndo_stop = net_close,
+ .ndo_tx_timeout = net_timeout,
+ .ndo_start_xmit = net_send_packet,
+ .ndo_get_stats = net_get_stats,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_mac_address = set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = net_poll_controller,
+#endif
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probes avoids doing writes, and
verifies that the correct device exists and functions.
@@ -843,17 +858,8 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
/* print the ethernet address. */
printk(", MAC %pM", dev->dev_addr);
- dev->open = net_open;
- dev->stop = net_close;
- dev->tx_timeout = net_timeout;
- dev->watchdog_timeo = HZ;
- dev->hard_start_xmit = net_send_packet;
- dev->get_stats = net_get_stats;
- dev->set_multicast_list = set_multicast_list;
- dev->set_mac_address = set_mac_address;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = net_poll_controller;
-#endif
+ dev->netdev_ops = &net_ops;
+ dev->watchdog_timeo = HZ;
printk("\n");
if (net_debug)
diff --git a/trunk/drivers/net/cxgb3/adapter.h b/trunk/drivers/net/cxgb3/adapter.h
index 71eaa431371d..714df2b675e6 100644
--- a/trunk/drivers/net/cxgb3/adapter.h
+++ b/trunk/drivers/net/cxgb3/adapter.h
@@ -85,6 +85,8 @@ struct fl_pg_chunk {
struct page *page;
void *va;
unsigned int offset;
+ u64 *p_cnt;
+ DECLARE_PCI_UNMAP_ADDR(mapping);
};
struct rx_desc;
@@ -101,6 +103,7 @@ struct sge_fl { /* SGE per free-buffer list state */
struct fl_pg_chunk pg_chunk;/* page chunk cache */
unsigned int use_pages; /* whether FL uses pages or sk_buffs */
unsigned int order; /* order of page allocations */
+ unsigned int alloc_size; /* size of allocated buffer */
struct rx_desc *desc; /* address of HW Rx descriptor ring */
struct rx_sw_desc *sdesc; /* address of SW Rx descriptor ring */
dma_addr_t phys_addr; /* physical address of HW ring start */
@@ -291,6 +294,7 @@ void t3_os_link_fault_handler(struct adapter *adapter, int port_id);
void t3_sge_start(struct adapter *adap);
void t3_sge_stop(struct adapter *adap);
+void t3_start_sge_timers(struct adapter *adap);
void t3_stop_sge_timers(struct adapter *adap);
void t3_free_sge_resources(struct adapter *adap);
void t3_sge_err_intr_handler(struct adapter *adapter);
diff --git a/trunk/drivers/net/cxgb3/common.h b/trunk/drivers/net/cxgb3/common.h
index 9ee021e750c8..e508dc32f3ec 100644
--- a/trunk/drivers/net/cxgb3/common.h
+++ b/trunk/drivers/net/cxgb3/common.h
@@ -191,7 +191,8 @@ struct mdio_ops {
};
struct adapter_info {
- unsigned char nports; /* # of ports */
+ unsigned char nports0; /* # of ports on channel 0 */
+ unsigned char nports1; /* # of ports on channel 1 */
unsigned char phy_base_addr; /* MDIO PHY base address */
unsigned int gpio_out; /* GPIO output settings */
unsigned char gpio_intr[MAX_NPORTS]; /* GPIO PHY IRQ pins */
@@ -422,6 +423,7 @@ struct adapter_params {
unsigned short b_wnd[NCCTRL_WIN];
unsigned int nports; /* # of ethernet ports */
+ unsigned int chan_map; /* bitmap of in-use Tx channels */
unsigned int stats_update_period; /* MAC stats accumulation period */
unsigned int linkpoll_period; /* link poll period in 0.1s */
unsigned int rev; /* chip revision */
diff --git a/trunk/drivers/net/cxgb3/cxgb3_main.c b/trunk/drivers/net/cxgb3/cxgb3_main.c
index d8be89621bf7..2c2aaa741450 100644
--- a/trunk/drivers/net/cxgb3/cxgb3_main.c
+++ b/trunk/drivers/net/cxgb3/cxgb3_main.c
@@ -602,7 +602,6 @@ static int setup_sge_qsets(struct adapter *adap)
&adap->params.sge.qset[qset_idx], ntxq, dev,
netdev_get_tx_queue(dev, j));
if (err) {
- t3_stop_sge_timers(adap);
t3_free_sge_resources(adap);
return err;
}
@@ -1046,6 +1045,8 @@ static int cxgb_up(struct adapter *adap)
setup_rss(adap);
if (!(adap->flags & NAPI_INIT))
init_napi(adap);
+
+ t3_start_sge_timers(adap);
adap->flags |= FULL_INIT_DONE;
}
@@ -2870,6 +2871,9 @@ static void t3_io_resume(struct pci_dev *pdev)
{
struct adapter *adapter = pci_get_drvdata(pdev);
+ CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n",
+ t3_read_reg(adapter, A_PCIE_PEX_ERR));
+
t3_resume_ports(adapter);
}
@@ -3002,7 +3006,7 @@ static int __devinit init_one(struct pci_dev *pdev,
static int version_printed;
int i, err, pci_using_dac = 0;
- unsigned long mmio_start, mmio_len;
+ resource_size_t mmio_start, mmio_len;
const struct adapter_info *ai;
struct adapter *adapter = NULL;
struct port_info *pi;
@@ -3082,7 +3086,7 @@ static int __devinit init_one(struct pci_dev *pdev,
INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task);
INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task);
- for (i = 0; i < ai->nports; ++i) {
+ for (i = 0; i < ai->nports0 + ai->nports1; ++i) {
struct net_device *netdev;
netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS);
@@ -3172,7 +3176,7 @@ static int __devinit init_one(struct pci_dev *pdev,
out_free_dev:
iounmap(adapter->regs);
- for (i = ai->nports - 1; i >= 0; --i)
+ for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i)
if (adapter->port[i])
free_netdev(adapter->port[i]);
diff --git a/trunk/drivers/net/cxgb3/sge.c b/trunk/drivers/net/cxgb3/sge.c
index a7555cb3fa4a..26d3587f3399 100644
--- a/trunk/drivers/net/cxgb3/sge.c
+++ b/trunk/drivers/net/cxgb3/sge.c
@@ -50,6 +50,7 @@
#define SGE_RX_COPY_THRES 256
#define SGE_RX_PULL_LEN 128
+#define SGE_PG_RSVD SMP_CACHE_BYTES
/*
* Page chunk size for FL0 buffers if FL0 is to be populated with page chunks.
* It must be a divisor of PAGE_SIZE. If set to 0 FL0 will use sk_buffs
@@ -57,8 +58,10 @@
*/
#define FL0_PG_CHUNK_SIZE 2048
#define FL0_PG_ORDER 0
+#define FL0_PG_ALLOC_SIZE (PAGE_SIZE << FL0_PG_ORDER)
#define FL1_PG_CHUNK_SIZE (PAGE_SIZE > 8192 ? 16384 : 8192)
#define FL1_PG_ORDER (PAGE_SIZE > 8192 ? 0 : 1)
+#define FL1_PG_ALLOC_SIZE (PAGE_SIZE << FL1_PG_ORDER)
#define SGE_RX_DROP_THRES 16
#define RX_RECLAIM_PERIOD (HZ/4)
@@ -345,13 +348,21 @@ static inline int should_restart_tx(const struct sge_txq *q)
return q->in_use - r < (q->size >> 1);
}
-static void clear_rx_desc(const struct sge_fl *q, struct rx_sw_desc *d)
+static void clear_rx_desc(struct pci_dev *pdev, const struct sge_fl *q,
+ struct rx_sw_desc *d)
{
- if (q->use_pages) {
- if (d->pg_chunk.page)
- put_page(d->pg_chunk.page);
+ if (q->use_pages && d->pg_chunk.page) {
+ (*d->pg_chunk.p_cnt)--;
+ if (!*d->pg_chunk.p_cnt)
+ pci_unmap_page(pdev,
+ pci_unmap_addr(&d->pg_chunk, mapping),
+ q->alloc_size, PCI_DMA_FROMDEVICE);
+
+ put_page(d->pg_chunk.page);
d->pg_chunk.page = NULL;
} else {
+ pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr),
+ q->buf_size, PCI_DMA_FROMDEVICE);
kfree_skb(d->skb);
d->skb = NULL;
}
@@ -372,9 +383,8 @@ static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q)
while (q->credits--) {
struct rx_sw_desc *d = &q->sdesc[cidx];
- pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr),
- q->buf_size, PCI_DMA_FROMDEVICE);
- clear_rx_desc(q, d);
+
+ clear_rx_desc(pdev, q, d);
if (++cidx == q->size)
cidx = 0;
}
@@ -417,18 +427,39 @@ static inline int add_one_rx_buf(void *va, unsigned int len,
return 0;
}
-static int alloc_pg_chunk(struct sge_fl *q, struct rx_sw_desc *sd, gfp_t gfp,
+static inline int add_one_rx_chunk(dma_addr_t mapping, struct rx_desc *d,
+ unsigned int gen)
+{
+ d->addr_lo = cpu_to_be32(mapping);
+ d->addr_hi = cpu_to_be32((u64) mapping >> 32);
+ wmb();
+ d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
+ d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
+ return 0;
+}
+
+static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q,
+ struct rx_sw_desc *sd, gfp_t gfp,
unsigned int order)
{
if (!q->pg_chunk.page) {
+ dma_addr_t mapping;
+
q->pg_chunk.page = alloc_pages(gfp, order);
if (unlikely(!q->pg_chunk.page))
return -ENOMEM;
q->pg_chunk.va = page_address(q->pg_chunk.page);
+ q->pg_chunk.p_cnt = q->pg_chunk.va + (PAGE_SIZE << order) -
+ SGE_PG_RSVD;
q->pg_chunk.offset = 0;
+ mapping = pci_map_page(adapter->pdev, q->pg_chunk.page,
+ 0, q->alloc_size, PCI_DMA_FROMDEVICE);
+ pci_unmap_addr_set(&q->pg_chunk, mapping, mapping);
}
sd->pg_chunk = q->pg_chunk;
+ prefetch(sd->pg_chunk.p_cnt);
+
q->pg_chunk.offset += q->buf_size;
if (q->pg_chunk.offset == (PAGE_SIZE << order))
q->pg_chunk.page = NULL;
@@ -436,6 +467,12 @@ static int alloc_pg_chunk(struct sge_fl *q, struct rx_sw_desc *sd, gfp_t gfp,
q->pg_chunk.va += q->buf_size;
get_page(q->pg_chunk.page);
}
+
+ if (sd->pg_chunk.offset == 0)
+ *sd->pg_chunk.p_cnt = 1;
+ else
+ *sd->pg_chunk.p_cnt += 1;
+
return 0;
}
@@ -460,35 +497,43 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q)
*/
static int refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp)
{
- void *buf_start;
struct rx_sw_desc *sd = &q->sdesc[q->pidx];
struct rx_desc *d = &q->desc[q->pidx];
unsigned int count = 0;
while (n--) {
+ dma_addr_t mapping;
int err;
if (q->use_pages) {
- if (unlikely(alloc_pg_chunk(q, sd, gfp, q->order))) {
+ if (unlikely(alloc_pg_chunk(adap, q, sd, gfp,
+ q->order))) {
nomem: q->alloc_failed++;
break;
}
- buf_start = sd->pg_chunk.va;
+ mapping = pci_unmap_addr(&sd->pg_chunk, mapping) +
+ sd->pg_chunk.offset;
+ pci_unmap_addr_set(sd, dma_addr, mapping);
+
+ add_one_rx_chunk(mapping, d, q->gen);
+ pci_dma_sync_single_for_device(adap->pdev, mapping,
+ q->buf_size - SGE_PG_RSVD,
+ PCI_DMA_FROMDEVICE);
} else {
- struct sk_buff *skb = alloc_skb(q->buf_size, gfp);
+ void *buf_start;
+ struct sk_buff *skb = alloc_skb(q->buf_size, gfp);
if (!skb)
goto nomem;
sd->skb = skb;
buf_start = skb->data;
- }
-
- err = add_one_rx_buf(buf_start, q->buf_size, d, sd, q->gen,
- adap->pdev);
- if (unlikely(err)) {
- clear_rx_desc(q, sd);
- break;
+ err = add_one_rx_buf(buf_start, q->buf_size, d, sd,
+ q->gen, adap->pdev);
+ if (unlikely(err)) {
+ clear_rx_desc(adap->pdev, q, sd);
+ break;
+ }
}
d++;
@@ -795,19 +840,19 @@ static struct sk_buff *get_packet_pg(struct adapter *adap, struct sge_fl *fl,
struct sk_buff *newskb, *skb;
struct rx_sw_desc *sd = &fl->sdesc[fl->cidx];
- newskb = skb = q->pg_skb;
+ dma_addr_t dma_addr = pci_unmap_addr(sd, dma_addr);
+ newskb = skb = q->pg_skb;
if (!skb && (len <= SGE_RX_COPY_THRES)) {
newskb = alloc_skb(len, GFP_ATOMIC);
if (likely(newskb != NULL)) {
__skb_put(newskb, len);
- pci_dma_sync_single_for_cpu(adap->pdev,
- pci_unmap_addr(sd, dma_addr), len,
+ pci_dma_sync_single_for_cpu(adap->pdev, dma_addr, len,
PCI_DMA_FROMDEVICE);
memcpy(newskb->data, sd->pg_chunk.va, len);
- pci_dma_sync_single_for_device(adap->pdev,
- pci_unmap_addr(sd, dma_addr), len,
- PCI_DMA_FROMDEVICE);
+ pci_dma_sync_single_for_device(adap->pdev, dma_addr,
+ len,
+ PCI_DMA_FROMDEVICE);
} else if (!drop_thres)
return NULL;
recycle:
@@ -820,16 +865,25 @@ static struct sk_buff *get_packet_pg(struct adapter *adap, struct sge_fl *fl,
if (unlikely(q->rx_recycle_buf || (!skb && fl->credits <= drop_thres)))
goto recycle;
+ prefetch(sd->pg_chunk.p_cnt);
+
if (!skb)
newskb = alloc_skb(SGE_RX_PULL_LEN, GFP_ATOMIC);
+
if (unlikely(!newskb)) {
if (!drop_thres)
return NULL;
goto recycle;
}
- pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),
- fl->buf_size, PCI_DMA_FROMDEVICE);
+ pci_dma_sync_single_for_cpu(adap->pdev, dma_addr, len,
+ PCI_DMA_FROMDEVICE);
+ (*sd->pg_chunk.p_cnt)--;
+ if (!*sd->pg_chunk.p_cnt)
+ pci_unmap_page(adap->pdev,
+ pci_unmap_addr(&sd->pg_chunk, mapping),
+ fl->alloc_size,
+ PCI_DMA_FROMDEVICE);
if (!skb) {
__skb_put(newskb, SGE_RX_PULL_LEN);
memcpy(newskb->data, sd->pg_chunk.va, SGE_RX_PULL_LEN);
@@ -1089,7 +1143,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
struct tx_desc *d = &q->desc[pidx];
struct cpl_tx_pkt *cpl = (struct cpl_tx_pkt *)d;
- cpl->len = htonl(skb->len | 0x80000000);
+ cpl->len = htonl(skb->len);
cntrl = V_TXPKT_INTF(pi->port_id);
if (vlan_tx_tag_present(skb) && pi->vlan_grp)
@@ -1958,8 +2012,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
skb_pull(skb, sizeof(*p) + pad);
skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
pi = netdev_priv(skb->dev);
- if ((pi->rx_offload & T3_RX_CSUM) && p->csum_valid && p->csum == htons(0xffff) &&
- !p->fragment) {
+ if ((pi->rx_offload & T3_RX_CSUM) && p->csum_valid &&
+ p->csum == htons(0xffff) && !p->fragment) {
qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++;
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else
@@ -2034,10 +2088,19 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
fl->credits--;
len -= offset;
- pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),
- fl->buf_size, PCI_DMA_FROMDEVICE);
+ pci_dma_sync_single_for_cpu(adap->pdev,
+ pci_unmap_addr(sd, dma_addr),
+ fl->buf_size - SGE_PG_RSVD,
+ PCI_DMA_FROMDEVICE);
- prefetch(&qs->lro_frag_tbl);
+ (*sd->pg_chunk.p_cnt)--;
+ if (!*sd->pg_chunk.p_cnt)
+ pci_unmap_page(adap->pdev,
+ pci_unmap_addr(&sd->pg_chunk, mapping),
+ fl->alloc_size,
+ PCI_DMA_FROMDEVICE);
+
+ prefetch(qs->lro_va);
rx_frag += nr_frags;
rx_frag->page = sd->pg_chunk.page;
@@ -2047,6 +2110,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
qs->lro_frag_tbl.nr_frags++;
qs->lro_frag_tbl.len = frag_len;
+
if (!complete)
return;
@@ -2236,6 +2300,8 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs,
if (fl->use_pages) {
void *addr = fl->sdesc[fl->cidx].pg_chunk.va;
+ prefetch(&qs->lro_frag_tbl);
+
prefetch(addr);
#if L1_CACHE_BYTES < 128
prefetch(addr + L1_CACHE_BYTES);
@@ -2972,21 +3038,23 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
q->fl[1].use_pages = FL1_PG_CHUNK_SIZE > 0;
q->fl[0].order = FL0_PG_ORDER;
q->fl[1].order = FL1_PG_ORDER;
+ q->fl[0].alloc_size = FL0_PG_ALLOC_SIZE;
+ q->fl[1].alloc_size = FL1_PG_ALLOC_SIZE;
spin_lock_irq(&adapter->sge.reg_lock);
/* FL threshold comparison uses < */
ret = t3_sge_init_rspcntxt(adapter, q->rspq.cntxt_id, irq_vec_idx,
q->rspq.phys_addr, q->rspq.size,
- q->fl[0].buf_size, 1, 0);
+ q->fl[0].buf_size - SGE_PG_RSVD, 1, 0);
if (ret)
goto err_unlock;
for (i = 0; i < SGE_RXQ_PER_SET; ++i) {
ret = t3_sge_init_flcntxt(adapter, q->fl[i].cntxt_id, 0,
q->fl[i].phys_addr, q->fl[i].size,
- q->fl[i].buf_size, p->cong_thres, 1,
- 0);
+ q->fl[i].buf_size - SGE_PG_RSVD,
+ p->cong_thres, 1, 0);
if (ret)
goto err_unlock;
}
@@ -3044,9 +3112,6 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
t3_write_reg(adapter, A_SG_GTS, V_RSPQ(q->rspq.cntxt_id) |
V_NEWTIMER(q->rspq.holdoff_tmr));
- mod_timer(&q->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
- mod_timer(&q->rx_reclaim_timer, jiffies + RX_RECLAIM_PERIOD);
-
return 0;
err_unlock:
@@ -3056,6 +3121,27 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
return ret;
}
+/**
+ * t3_start_sge_timers - start SGE timer call backs
+ * @adap: the adapter
+ *
+ * Starts each SGE queue set's timer call back
+ */
+void t3_start_sge_timers(struct adapter *adap)
+{
+ int i;
+
+ for (i = 0; i < SGE_QSETS; ++i) {
+ struct sge_qset *q = &adap->sge.qs[i];
+
+ if (q->tx_reclaim_timer.function)
+ mod_timer(&q->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
+
+ if (q->rx_reclaim_timer.function)
+ mod_timer(&q->rx_reclaim_timer, jiffies + RX_RECLAIM_PERIOD);
+ }
+}
+
/**
* t3_stop_sge_timers - stop SGE timer call backs
* @adap: the adapter
diff --git a/trunk/drivers/net/cxgb3/t3_hw.c b/trunk/drivers/net/cxgb3/t3_hw.c
index ff262a04ded0..31ed31a3428b 100644
--- a/trunk/drivers/net/cxgb3/t3_hw.c
+++ b/trunk/drivers/net/cxgb3/t3_hw.c
@@ -493,20 +493,20 @@ int t3_phy_lasi_intr_handler(struct cphy *phy)
}
static const struct adapter_info t3_adap_info[] = {
- {2, 0,
+ {1, 1, 0,
F_GPIO2_OEN | F_GPIO4_OEN |
F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0,
&mi1_mdio_ops, "Chelsio PE9000"},
- {2, 0,
+ {1, 1, 0,
F_GPIO2_OEN | F_GPIO4_OEN |
F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0,
&mi1_mdio_ops, "Chelsio T302"},
- {1, 0,
+ {1, 0, 0,
F_GPIO1_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN |
F_GPIO11_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
{ 0 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI,
&mi1_mdio_ext_ops, "Chelsio T310"},
- {2, 0,
+ {1, 1, 0,
F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO5_OEN | F_GPIO6_OEN |
F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL |
F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
@@ -514,7 +514,7 @@ static const struct adapter_info t3_adap_info[] = {
&mi1_mdio_ext_ops, "Chelsio T320"},
{},
{},
- {1, 0,
+ {1, 0, 0,
F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO6_OEN | F_GPIO7_OEN |
F_GPIO10_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
{ S_GPIO9 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI,
@@ -2128,16 +2128,40 @@ void t3_port_intr_clear(struct adapter *adapter, int idx)
static int t3_sge_write_context(struct adapter *adapter, unsigned int id,
unsigned int type)
{
- t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
- t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
- t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff);
- t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+ if (type == F_RESPONSEQ) {
+ /*
+ * Can't write the Response Queue Context bits for
+ * Interrupt Armed or the Reserve bits after the chip
+ * has been initialized out of reset. Writing to these
+ * bits can confuse the hardware.
+ */
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0x17ffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+ } else {
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff);
+ t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+ }
t3_write_reg(adapter, A_SG_CONTEXT_CMD,
V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id));
return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
0, SG_CONTEXT_CMD_ATTEMPTS, 1);
}
+/**
+ * clear_sge_ctxt - completely clear an SGE context
+ * @adapter: the adapter
+ * @id: the context id
+ * @type: the context type
+ *
+ * Completely clear an SGE context. Used predominantly at post-reset
+ * initialization. Note in particular that we don't skip writing to any
+ * "sensitive bits" in the contexts the way that t3_sge_write_context()
+ * does ...
+ */
static int clear_sge_ctxt(struct adapter *adap, unsigned int id,
unsigned int type)
{
@@ -2145,7 +2169,14 @@ static int clear_sge_ctxt(struct adapter *adap, unsigned int id,
t3_write_reg(adap, A_SG_CONTEXT_DATA1, 0);
t3_write_reg(adap, A_SG_CONTEXT_DATA2, 0);
t3_write_reg(adap, A_SG_CONTEXT_DATA3, 0);
- return t3_sge_write_context(adap, id, type);
+ t3_write_reg(adap, A_SG_CONTEXT_MASK0, 0xffffffff);
+ t3_write_reg(adap, A_SG_CONTEXT_MASK1, 0xffffffff);
+ t3_write_reg(adap, A_SG_CONTEXT_MASK2, 0xffffffff);
+ t3_write_reg(adap, A_SG_CONTEXT_MASK3, 0xffffffff);
+ t3_write_reg(adap, A_SG_CONTEXT_CMD,
+ V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id));
+ return t3_wait_op_done(adap, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
+ 0, SG_CONTEXT_CMD_ATTEMPTS, 1);
}
/**
@@ -2729,10 +2760,10 @@ static void tp_config(struct adapter *adap, const struct tp_params *p)
F_TCPCHECKSUMOFFLOAD | V_IPTTL(64));
t3_write_reg(adap, A_TP_TCP_OPTIONS, V_MTUDEFAULT(576) |
F_MTUENABLE | V_WINDOWSCALEMODE(1) |
- V_TIMESTAMPSMODE(0) | V_SACKMODE(1) | V_SACKRX(1));
+ V_TIMESTAMPSMODE(1) | V_SACKMODE(1) | V_SACKRX(1));
t3_write_reg(adap, A_TP_DACK_CONFIG, V_AUTOSTATE3(1) |
V_AUTOSTATE2(1) | V_AUTOSTATE1(0) |
- V_BYTETHRESHOLD(16384) | V_MSSTHRESHOLD(2) |
+ V_BYTETHRESHOLD(26880) | V_MSSTHRESHOLD(2) |
F_AUTOCAREFUL | F_AUTOENABLE | V_DACK_MODE(1));
t3_set_reg_field(adap, A_TP_IN_CONFIG, F_RXFBARBPRIO | F_TXFBARBPRIO,
F_IPV6ENABLE | F_NICMODE);
@@ -3196,20 +3227,22 @@ int t3_mps_set_active_ports(struct adapter *adap, unsigned int port_mask)
}
/*
- * Perform the bits of HW initialization that are dependent on the number
- * of available ports.
+ * Perform the bits of HW initialization that are dependent on the Tx
+ * channels being used.
*/
-static void init_hw_for_avail_ports(struct adapter *adap, int nports)
+static void chan_init_hw(struct adapter *adap, unsigned int chan_map)
{
int i;
- if (nports == 1) {
+ if (chan_map != 3) { /* one channel */
t3_set_reg_field(adap, A_ULPRX_CTL, F_ROUND_ROBIN, 0);
t3_set_reg_field(adap, A_ULPTX_CONFIG, F_CFG_RR_ARB, 0);
- t3_write_reg(adap, A_MPS_CFG, F_TPRXPORTEN | F_TPTXPORT0EN |
- F_PORT0ACTIVE | F_ENFORCEPKT);
- t3_write_reg(adap, A_PM1_TX_CFG, 0xffffffff);
- } else {
+ t3_write_reg(adap, A_MPS_CFG, F_TPRXPORTEN | F_ENFORCEPKT |
+ (chan_map == 1 ? F_TPTXPORT0EN | F_PORT0ACTIVE :
+ F_TPTXPORT1EN | F_PORT1ACTIVE));
+ t3_write_reg(adap, A_PM1_TX_CFG,
+ chan_map == 1 ? 0xffffffff : 0);
+ } else { /* two channels */
t3_set_reg_field(adap, A_ULPRX_CTL, 0, F_ROUND_ROBIN);
t3_set_reg_field(adap, A_ULPTX_CONFIG, 0, F_CFG_RR_ARB);
t3_write_reg(adap, A_ULPTX_DMA_WEIGHT,
@@ -3517,7 +3550,7 @@ int t3_init_hw(struct adapter *adapter, u32 fw_params)
t3_write_reg(adapter, A_PM1_RX_CFG, 0xffffffff);
t3_write_reg(adapter, A_PM1_RX_MODE, 0);
t3_write_reg(adapter, A_PM1_TX_MODE, 0);
- init_hw_for_avail_ports(adapter, adapter->params.nports);
+ chan_init_hw(adapter, adapter->params.chan_map);
t3_sge_init(adapter, &adapter->params.sge);
t3_write_reg(adapter, A_T3DBG_GPIO_ACT_LOW, calc_gpio_intr(adapter));
@@ -3754,7 +3787,8 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
get_pci_mode(adapter, &adapter->params.pci);
adapter->params.info = ai;
- adapter->params.nports = ai->nports;
+ adapter->params.nports = ai->nports0 + ai->nports1;
+ adapter->params.chan_map = !!ai->nports0 | (!!ai->nports1 << 1);
adapter->params.rev = t3_read_reg(adapter, A_PL_REV);
/*
* We used to only run the "adapter check task" once a second if
@@ -3785,7 +3819,7 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
mc7_prep(adapter, &adapter->pmtx, MC7_PMTX_BASE_ADDR, "PMTX");
mc7_prep(adapter, &adapter->cm, MC7_CM_BASE_ADDR, "CM");
- p->nchan = ai->nports;
+ p->nchan = adapter->params.chan_map == 3 ? 2 : 1;
p->pmrx_size = t3_mc7_size(&adapter->pmrx);
p->pmtx_size = t3_mc7_size(&adapter->pmtx);
p->cm_size = t3_mc7_size(&adapter->cm);
diff --git a/trunk/drivers/net/depca.c b/trunk/drivers/net/depca.c
index 55625dbbae5a..357f565851ed 100644
--- a/trunk/drivers/net/depca.c
+++ b/trunk/drivers/net/depca.c
@@ -566,6 +566,18 @@ MODULE_LICENSE("GPL");
outw(CSR0, DEPCA_ADDR);\
outw(STOP, DEPCA_DATA)
+static const struct net_device_ops depca_netdev_ops = {
+ .ndo_open = depca_open,
+ .ndo_start_xmit = depca_start_xmit,
+ .ndo_stop = depca_close,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_do_ioctl = depca_ioctl,
+ .ndo_tx_timeout = depca_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init depca_hw_init (struct net_device *dev, struct device *device)
{
struct depca_private *lp;
@@ -793,12 +805,7 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
}
/* The DEPCA-specific entries in the device structure. */
- dev->open = &depca_open;
- dev->hard_start_xmit = &depca_start_xmit;
- dev->stop = &depca_close;
- dev->set_multicast_list = &set_multicast_list;
- dev->do_ioctl = &depca_ioctl;
- dev->tx_timeout = depca_tx_timeout;
+ dev->netdev_ops = &depca_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
dev->mem_start = 0;
diff --git a/trunk/drivers/net/eepro.c b/trunk/drivers/net/eepro.c
index e187c88ae145..cc2ab6412c73 100644
--- a/trunk/drivers/net/eepro.c
+++ b/trunk/drivers/net/eepro.c
@@ -739,6 +739,17 @@ static void __init eepro_print_info (struct net_device *dev)
static const struct ethtool_ops eepro_ethtool_ops;
+static const struct net_device_ops eepro_netdev_ops = {
+ .ndo_open = eepro_open,
+ .ndo_stop = eepro_close,
+ .ndo_start_xmit = eepro_send_packet,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_tx_timeout = eepro_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probe avoids doing writes, and
verifies that the correct device exists and functions. */
@@ -851,11 +862,7 @@ static int __init eepro_probe1(struct net_device *dev, int autoprobe)
}
}
- dev->open = eepro_open;
- dev->stop = eepro_close;
- dev->hard_start_xmit = eepro_send_packet;
- dev->set_multicast_list = &set_multicast_list;
- dev->tx_timeout = eepro_tx_timeout;
+ dev->netdev_ops = &eepro_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
dev->ethtool_ops = &eepro_ethtool_ops;
diff --git a/trunk/drivers/net/eexpress.c b/trunk/drivers/net/eexpress.c
index 9ff3f2f5e382..1686dca28748 100644
--- a/trunk/drivers/net/eexpress.c
+++ b/trunk/drivers/net/eexpress.c
@@ -1043,6 +1043,17 @@ static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf,
lp->last_tx = jiffies;
}
+static const struct net_device_ops eexp_netdev_ops = {
+ .ndo_open = eexp_open,
+ .ndo_stop = eexp_close,
+ .ndo_start_xmit = eexp_xmit,
+ .ndo_set_multicast_list = eexp_set_multicast,
+ .ndo_tx_timeout = eexp_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/*
* Sanity check the suspected EtherExpress card
* Read hardware address, reset card, size memory and initialize buffer
@@ -1163,11 +1174,7 @@ static int __init eexp_hw_probe(struct net_device *dev, unsigned short ioaddr)
lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE);
lp->width = buswidth;
- dev->open = eexp_open;
- dev->stop = eexp_close;
- dev->hard_start_xmit = eexp_xmit;
- dev->set_multicast_list = &eexp_set_multicast;
- dev->tx_timeout = eexp_timeout;
+ dev->netdev_ops = &eexp_netdev_ops;
dev->watchdog_timeo = 2*HZ;
return register_netdev(dev);
diff --git a/trunk/drivers/net/eth16i.c b/trunk/drivers/net/eth16i.c
index 5c048f2fd74f..0d8b6da046f2 100644
--- a/trunk/drivers/net/eth16i.c
+++ b/trunk/drivers/net/eth16i.c
@@ -475,6 +475,17 @@ struct net_device * __init eth16i_probe(int unit)
}
#endif
+static const struct net_device_ops eth16i_netdev_ops = {
+ .ndo_open = eth16i_open,
+ .ndo_stop = eth16i_close,
+ .ndo_start_xmit = eth16i_tx,
+ .ndo_set_multicast_list = eth16i_multicast,
+ .ndo_tx_timeout = eth16i_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
{
struct eth16i_local *lp = netdev_priv(dev);
@@ -549,12 +560,7 @@ static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
/* Initialize the device structure */
- memset(lp, 0, sizeof(struct eth16i_local));
- dev->open = eth16i_open;
- dev->stop = eth16i_close;
- dev->hard_start_xmit = eth16i_tx;
- dev->set_multicast_list = eth16i_multicast;
- dev->tx_timeout = eth16i_timeout;
+ dev->netdev_ops = ð16i_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
spin_lock_init(&lp->lock);
diff --git a/trunk/drivers/net/ethoc.c b/trunk/drivers/net/ethoc.c
new file mode 100644
index 000000000000..91a9b1a33764
--- /dev/null
+++ b/trunk/drivers/net/ethoc.c
@@ -0,0 +1,1112 @@
+/*
+ * linux/drivers/net/ethoc.c
+ *
+ * Copyright (C) 2007-2008 Avionic Design Development GmbH
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written by Thierry Reding
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* register offsets */
+#define MODER 0x00
+#define INT_SOURCE 0x04
+#define INT_MASK 0x08
+#define IPGT 0x0c
+#define IPGR1 0x10
+#define IPGR2 0x14
+#define PACKETLEN 0x18
+#define COLLCONF 0x1c
+#define TX_BD_NUM 0x20
+#define CTRLMODER 0x24
+#define MIIMODER 0x28
+#define MIICOMMAND 0x2c
+#define MIIADDRESS 0x30
+#define MIITX_DATA 0x34
+#define MIIRX_DATA 0x38
+#define MIISTATUS 0x3c
+#define MAC_ADDR0 0x40
+#define MAC_ADDR1 0x44
+#define ETH_HASH0 0x48
+#define ETH_HASH1 0x4c
+#define ETH_TXCTRL 0x50
+
+/* mode register */
+#define MODER_RXEN (1 << 0) /* receive enable */
+#define MODER_TXEN (1 << 1) /* transmit enable */
+#define MODER_NOPRE (1 << 2) /* no preamble */
+#define MODER_BRO (1 << 3) /* broadcast address */
+#define MODER_IAM (1 << 4) /* individual address mode */
+#define MODER_PRO (1 << 5) /* promiscuous mode */
+#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */
+#define MODER_LOOP (1 << 7) /* loopback */
+#define MODER_NBO (1 << 8) /* no back-off */
+#define MODER_EDE (1 << 9) /* excess defer enable */
+#define MODER_FULLD (1 << 10) /* full duplex */
+#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */
+#define MODER_DCRC (1 << 12) /* delayed CRC enable */
+#define MODER_CRC (1 << 13) /* CRC enable */
+#define MODER_HUGE (1 << 14) /* huge packets enable */
+#define MODER_PAD (1 << 15) /* padding enabled */
+#define MODER_RSM (1 << 16) /* receive small packets */
+
+/* interrupt source and mask registers */
+#define INT_MASK_TXF (1 << 0) /* transmit frame */
+#define INT_MASK_TXE (1 << 1) /* transmit error */
+#define INT_MASK_RXF (1 << 2) /* receive frame */
+#define INT_MASK_RXE (1 << 3) /* receive error */
+#define INT_MASK_BUSY (1 << 4)
+#define INT_MASK_TXC (1 << 5) /* transmit control frame */
+#define INT_MASK_RXC (1 << 6) /* receive control frame */
+
+#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE)
+#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE)
+
+#define INT_MASK_ALL ( \
+ INT_MASK_TXF | INT_MASK_TXE | \
+ INT_MASK_RXF | INT_MASK_RXE | \
+ INT_MASK_TXC | INT_MASK_RXC | \
+ INT_MASK_BUSY \
+ )
+
+/* packet length register */
+#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16)
+#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0)
+#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \
+ PACKETLEN_MAX(max))
+
+/* transmit buffer number register */
+#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80)
+
+/* control module mode register */
+#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */
+#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */
+#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */
+
+/* MII mode register */
+#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */
+#define MIIMODER_NOPRE (1 << 8) /* no preamble */
+
+/* MII command register */
+#define MIICOMMAND_SCAN (1 << 0) /* scan status */
+#define MIICOMMAND_READ (1 << 1) /* read status */
+#define MIICOMMAND_WRITE (1 << 2) /* write control data */
+
+/* MII address register */
+#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0)
+#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8)
+#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \
+ MIIADDRESS_RGAD(reg))
+
+/* MII transmit data register */
+#define MIITX_DATA_VAL(x) ((x) & 0xffff)
+
+/* MII receive data register */
+#define MIIRX_DATA_VAL(x) ((x) & 0xffff)
+
+/* MII status register */
+#define MIISTATUS_LINKFAIL (1 << 0)
+#define MIISTATUS_BUSY (1 << 1)
+#define MIISTATUS_INVALID (1 << 2)
+
+/* TX buffer descriptor */
+#define TX_BD_CS (1 << 0) /* carrier sense lost */
+#define TX_BD_DF (1 << 1) /* defer indication */
+#define TX_BD_LC (1 << 2) /* late collision */
+#define TX_BD_RL (1 << 3) /* retransmission limit */
+#define TX_BD_RETRY_MASK (0x00f0)
+#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4)
+#define TX_BD_UR (1 << 8) /* transmitter underrun */
+#define TX_BD_CRC (1 << 11) /* TX CRC enable */
+#define TX_BD_PAD (1 << 12) /* pad enable for short packets */
+#define TX_BD_WRAP (1 << 13)
+#define TX_BD_IRQ (1 << 14) /* interrupt request enable */
+#define TX_BD_READY (1 << 15) /* TX buffer ready */
+#define TX_BD_LEN(x) (((x) & 0xffff) << 16)
+#define TX_BD_LEN_MASK (0xffff << 16)
+
+#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \
+ TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR)
+
+/* RX buffer descriptor */
+#define RX_BD_LC (1 << 0) /* late collision */
+#define RX_BD_CRC (1 << 1) /* RX CRC error */
+#define RX_BD_SF (1 << 2) /* short frame */
+#define RX_BD_TL (1 << 3) /* too long */
+#define RX_BD_DN (1 << 4) /* dribble nibble */
+#define RX_BD_IS (1 << 5) /* invalid symbol */
+#define RX_BD_OR (1 << 6) /* receiver overrun */
+#define RX_BD_MISS (1 << 7)
+#define RX_BD_CF (1 << 8) /* control frame */
+#define RX_BD_WRAP (1 << 13)
+#define RX_BD_IRQ (1 << 14) /* interrupt request enable */
+#define RX_BD_EMPTY (1 << 15)
+#define RX_BD_LEN(x) (((x) & 0xffff) << 16)
+
+#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \
+ RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS)
+
+#define ETHOC_BUFSIZ 1536
+#define ETHOC_ZLEN 64
+#define ETHOC_BD_BASE 0x400
+#define ETHOC_TIMEOUT (HZ / 2)
+#define ETHOC_MII_TIMEOUT (1 + (HZ / 5))
+
+/**
+ * struct ethoc - driver-private device structure
+ * @iobase: pointer to I/O memory region
+ * @membase: pointer to buffer memory region
+ * @num_tx: number of send buffers
+ * @cur_tx: last send buffer written
+ * @dty_tx: last buffer actually sent
+ * @num_rx: number of receive buffers
+ * @cur_rx: current receive buffer
+ * @netdev: pointer to network device structure
+ * @napi: NAPI structure
+ * @stats: network device statistics
+ * @msg_enable: device state flags
+ * @rx_lock: receive lock
+ * @lock: device lock
+ * @phy: attached PHY
+ * @mdio: MDIO bus for PHY access
+ * @phy_id: address of attached PHY
+ */
+struct ethoc {
+ void __iomem *iobase;
+ void __iomem *membase;
+
+ unsigned int num_tx;
+ unsigned int cur_tx;
+ unsigned int dty_tx;
+
+ unsigned int num_rx;
+ unsigned int cur_rx;
+
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct net_device_stats stats;
+ u32 msg_enable;
+
+ spinlock_t rx_lock;
+ spinlock_t lock;
+
+ struct phy_device *phy;
+ struct mii_bus *mdio;
+ s8 phy_id;
+};
+
+/**
+ * struct ethoc_bd - buffer descriptor
+ * @stat: buffer statistics
+ * @addr: physical memory address
+ */
+struct ethoc_bd {
+ u32 stat;
+ u32 addr;
+};
+
+static u32 ethoc_read(struct ethoc *dev, loff_t offset)
+{
+ return ioread32(dev->iobase + offset);
+}
+
+static void ethoc_write(struct ethoc *dev, loff_t offset, u32 data)
+{
+ iowrite32(data, dev->iobase + offset);
+}
+
+static void ethoc_read_bd(struct ethoc *dev, int index, struct ethoc_bd *bd)
+{
+ loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
+ bd->stat = ethoc_read(dev, offset + 0);
+ bd->addr = ethoc_read(dev, offset + 4);
+}
+
+static void ethoc_write_bd(struct ethoc *dev, int index,
+ const struct ethoc_bd *bd)
+{
+ loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
+ ethoc_write(dev, offset + 0, bd->stat);
+ ethoc_write(dev, offset + 4, bd->addr);
+}
+
+static void ethoc_enable_irq(struct ethoc *dev, u32 mask)
+{
+ u32 imask = ethoc_read(dev, INT_MASK);
+ imask |= mask;
+ ethoc_write(dev, INT_MASK, imask);
+}
+
+static void ethoc_disable_irq(struct ethoc *dev, u32 mask)
+{
+ u32 imask = ethoc_read(dev, INT_MASK);
+ imask &= ~mask;
+ ethoc_write(dev, INT_MASK, imask);
+}
+
+static void ethoc_ack_irq(struct ethoc *dev, u32 mask)
+{
+ ethoc_write(dev, INT_SOURCE, mask);
+}
+
+static void ethoc_enable_rx_and_tx(struct ethoc *dev)
+{
+ u32 mode = ethoc_read(dev, MODER);
+ mode |= MODER_RXEN | MODER_TXEN;
+ ethoc_write(dev, MODER, mode);
+}
+
+static void ethoc_disable_rx_and_tx(struct ethoc *dev)
+{
+ u32 mode = ethoc_read(dev, MODER);
+ mode &= ~(MODER_RXEN | MODER_TXEN);
+ ethoc_write(dev, MODER, mode);
+}
+
+static int ethoc_init_ring(struct ethoc *dev)
+{
+ struct ethoc_bd bd;
+ int i;
+
+ dev->cur_tx = 0;
+ dev->dty_tx = 0;
+ dev->cur_rx = 0;
+
+ /* setup transmission buffers */
+ bd.addr = 0;
+ bd.stat = TX_BD_IRQ | TX_BD_CRC;
+
+ for (i = 0; i < dev->num_tx; i++) {
+ if (i == dev->num_tx - 1)
+ bd.stat |= TX_BD_WRAP;
+
+ ethoc_write_bd(dev, i, &bd);
+ bd.addr += ETHOC_BUFSIZ;
+ }
+
+ bd.addr = dev->num_tx * ETHOC_BUFSIZ;
+ bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
+
+ for (i = 0; i < dev->num_rx; i++) {
+ if (i == dev->num_rx - 1)
+ bd.stat |= RX_BD_WRAP;
+
+ ethoc_write_bd(dev, dev->num_tx + i, &bd);
+ bd.addr += ETHOC_BUFSIZ;
+ }
+
+ return 0;
+}
+
+static int ethoc_reset(struct ethoc *dev)
+{
+ u32 mode;
+
+ /* TODO: reset controller? */
+
+ ethoc_disable_rx_and_tx(dev);
+
+ /* TODO: setup registers */
+
+ /* enable FCS generation and automatic padding */
+ mode = ethoc_read(dev, MODER);
+ mode |= MODER_CRC | MODER_PAD;
+ ethoc_write(dev, MODER, mode);
+
+ /* set full-duplex mode */
+ mode = ethoc_read(dev, MODER);
+ mode |= MODER_FULLD;
+ ethoc_write(dev, MODER, mode);
+ ethoc_write(dev, IPGT, 0x15);
+
+ ethoc_ack_irq(dev, INT_MASK_ALL);
+ ethoc_enable_irq(dev, INT_MASK_ALL);
+ ethoc_enable_rx_and_tx(dev);
+ return 0;
+}
+
+static unsigned int ethoc_update_rx_stats(struct ethoc *dev,
+ struct ethoc_bd *bd)
+{
+ struct net_device *netdev = dev->netdev;
+ unsigned int ret = 0;
+
+ if (bd->stat & RX_BD_TL) {
+ dev_err(&netdev->dev, "RX: frame too long\n");
+ dev->stats.rx_length_errors++;
+ ret++;
+ }
+
+ if (bd->stat & RX_BD_SF) {
+ dev_err(&netdev->dev, "RX: frame too short\n");
+ dev->stats.rx_length_errors++;
+ ret++;
+ }
+
+ if (bd->stat & RX_BD_DN) {
+ dev_err(&netdev->dev, "RX: dribble nibble\n");
+ dev->stats.rx_frame_errors++;
+ }
+
+ if (bd->stat & RX_BD_CRC) {
+ dev_err(&netdev->dev, "RX: wrong CRC\n");
+ dev->stats.rx_crc_errors++;
+ ret++;
+ }
+
+ if (bd->stat & RX_BD_OR) {
+ dev_err(&netdev->dev, "RX: overrun\n");
+ dev->stats.rx_over_errors++;
+ ret++;
+ }
+
+ if (bd->stat & RX_BD_MISS)
+ dev->stats.rx_missed_errors++;
+
+ if (bd->stat & RX_BD_LC) {
+ dev_err(&netdev->dev, "RX: late collision\n");
+ dev->stats.collisions++;
+ ret++;
+ }
+
+ return ret;
+}
+
+static int ethoc_rx(struct net_device *dev, int limit)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ int count;
+
+ for (count = 0; count < limit; ++count) {
+ unsigned int entry;
+ struct ethoc_bd bd;
+
+ entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
+ ethoc_read_bd(priv, entry, &bd);
+ if (bd.stat & RX_BD_EMPTY)
+ break;
+
+ if (ethoc_update_rx_stats(priv, &bd) == 0) {
+ int size = bd.stat >> 16;
+ struct sk_buff *skb = netdev_alloc_skb(dev, size);
+ if (likely(skb)) {
+ void *src = priv->membase + bd.addr;
+ memcpy_fromio(skb_put(skb, size), src, size);
+ skb->protocol = eth_type_trans(skb, dev);
+ dev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += size;
+ netif_receive_skb(skb);
+ } else {
+ if (net_ratelimit())
+ dev_warn(&dev->dev, "low on memory - "
+ "packet dropped\n");
+
+ priv->stats.rx_dropped++;
+ break;
+ }
+ }
+
+ /* clear the buffer descriptor so it can be reused */
+ bd.stat &= ~RX_BD_STATS;
+ bd.stat |= RX_BD_EMPTY;
+ ethoc_write_bd(priv, entry, &bd);
+ priv->cur_rx++;
+ }
+
+ return count;
+}
+
+static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
+{
+ struct net_device *netdev = dev->netdev;
+
+ if (bd->stat & TX_BD_LC) {
+ dev_err(&netdev->dev, "TX: late collision\n");
+ dev->stats.tx_window_errors++;
+ }
+
+ if (bd->stat & TX_BD_RL) {
+ dev_err(&netdev->dev, "TX: retransmit limit\n");
+ dev->stats.tx_aborted_errors++;
+ }
+
+ if (bd->stat & TX_BD_UR) {
+ dev_err(&netdev->dev, "TX: underrun\n");
+ dev->stats.tx_fifo_errors++;
+ }
+
+ if (bd->stat & TX_BD_CS) {
+ dev_err(&netdev->dev, "TX: carrier sense lost\n");
+ dev->stats.tx_carrier_errors++;
+ }
+
+ if (bd->stat & TX_BD_STATS)
+ dev->stats.tx_errors++;
+
+ dev->stats.collisions += (bd->stat >> 4) & 0xf;
+ dev->stats.tx_bytes += bd->stat >> 16;
+ dev->stats.tx_packets++;
+ return 0;
+}
+
+static void ethoc_tx(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+
+ while (priv->dty_tx != priv->cur_tx) {
+ unsigned int entry = priv->dty_tx % priv->num_tx;
+ struct ethoc_bd bd;
+
+ ethoc_read_bd(priv, entry, &bd);
+ if (bd.stat & TX_BD_READY)
+ break;
+
+ entry = (++priv->dty_tx) % priv->num_tx;
+ (void)ethoc_update_tx_stats(priv, &bd);
+ }
+
+ if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2))
+ netif_wake_queue(dev);
+
+ ethoc_ack_irq(priv, INT_MASK_TX);
+ spin_unlock(&priv->lock);
+}
+
+static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct ethoc *priv = netdev_priv(dev);
+ u32 pending;
+
+ ethoc_disable_irq(priv, INT_MASK_ALL);
+ pending = ethoc_read(priv, INT_SOURCE);
+ if (unlikely(pending == 0)) {
+ ethoc_enable_irq(priv, INT_MASK_ALL);
+ return IRQ_NONE;
+ }
+
+ ethoc_ack_irq(priv, INT_MASK_ALL);
+
+ if (pending & INT_MASK_BUSY) {
+ dev_err(&dev->dev, "packet dropped\n");
+ priv->stats.rx_dropped++;
+ }
+
+ if (pending & INT_MASK_RX) {
+ if (napi_schedule_prep(&priv->napi))
+ __napi_schedule(&priv->napi);
+ } else {
+ ethoc_enable_irq(priv, INT_MASK_RX);
+ }
+
+ if (pending & INT_MASK_TX)
+ ethoc_tx(dev);
+
+ ethoc_enable_irq(priv, INT_MASK_ALL & ~INT_MASK_RX);
+ return IRQ_HANDLED;
+}
+
+static int ethoc_get_mac_address(struct net_device *dev, void *addr)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ u8 *mac = (u8 *)addr;
+ u32 reg;
+
+ reg = ethoc_read(priv, MAC_ADDR0);
+ mac[2] = (reg >> 24) & 0xff;
+ mac[3] = (reg >> 16) & 0xff;
+ mac[4] = (reg >> 8) & 0xff;
+ mac[5] = (reg >> 0) & 0xff;
+
+ reg = ethoc_read(priv, MAC_ADDR1);
+ mac[0] = (reg >> 8) & 0xff;
+ mac[1] = (reg >> 0) & 0xff;
+
+ return 0;
+}
+
+static int ethoc_poll(struct napi_struct *napi, int budget)
+{
+ struct ethoc *priv = container_of(napi, struct ethoc, napi);
+ int work_done = 0;
+
+ work_done = ethoc_rx(priv->netdev, budget);
+ if (work_done < budget) {
+ ethoc_enable_irq(priv, INT_MASK_RX);
+ napi_complete(napi);
+ }
+
+ return work_done;
+}
+
+static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
+{
+ unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
+ struct ethoc *priv = bus->priv;
+
+ ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
+ ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
+
+ while (time_before(jiffies, timeout)) {
+ u32 status = ethoc_read(priv, MIISTATUS);
+ if (!(status & MIISTATUS_BUSY)) {
+ u32 data = ethoc_read(priv, MIIRX_DATA);
+ /* reset MII command register */
+ ethoc_write(priv, MIICOMMAND, 0);
+ return data;
+ }
+
+ schedule();
+ }
+
+ return -EBUSY;
+}
+
+static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+ unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
+ struct ethoc *priv = bus->priv;
+
+ ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
+ ethoc_write(priv, MIITX_DATA, val);
+ ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
+
+ while (time_before(jiffies, timeout)) {
+ u32 stat = ethoc_read(priv, MIISTATUS);
+ if (!(stat & MIISTATUS_BUSY))
+ return 0;
+
+ schedule();
+ }
+
+ return -EBUSY;
+}
+
+static int ethoc_mdio_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+static void ethoc_mdio_poll(struct net_device *dev)
+{
+}
+
+static int ethoc_mdio_probe(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ struct phy_device *phy;
+ int i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ phy = priv->mdio->phy_map[i];
+ if (phy) {
+ if (priv->phy_id != -1) {
+ /* attach to specified PHY */
+ if (priv->phy_id == phy->addr)
+ break;
+ } else {
+ /* autoselect PHY if none was specified */
+ if (phy->addr != 0)
+ break;
+ }
+ }
+ }
+
+ if (!phy) {
+ dev_err(&dev->dev, "no PHY found\n");
+ return -ENXIO;
+ }
+
+ phy = phy_connect(dev, dev_name(&phy->dev), ðoc_mdio_poll, 0,
+ PHY_INTERFACE_MODE_GMII);
+ if (IS_ERR(phy)) {
+ dev_err(&dev->dev, "could not attach to PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ priv->phy = phy;
+ return 0;
+}
+
+static int ethoc_open(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ unsigned int min_tx = 2;
+ unsigned int num_bd;
+ int ret;
+
+ ret = request_irq(dev->irq, ethoc_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (ret)
+ return ret;
+
+ /* calculate the number of TX/RX buffers */
+ num_bd = (dev->mem_end - dev->mem_start + 1) / ETHOC_BUFSIZ;
+ priv->num_tx = min(min_tx, num_bd / 4);
+ priv->num_rx = num_bd - priv->num_tx;
+ ethoc_write(priv, TX_BD_NUM, priv->num_tx);
+
+ ethoc_init_ring(priv);
+ ethoc_reset(priv);
+
+ if (netif_queue_stopped(dev)) {
+ dev_dbg(&dev->dev, " resuming queue\n");
+ netif_wake_queue(dev);
+ } else {
+ dev_dbg(&dev->dev, " starting queue\n");
+ netif_start_queue(dev);
+ }
+
+ phy_start(priv->phy);
+ napi_enable(&priv->napi);
+
+ if (netif_msg_ifup(priv)) {
+ dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n",
+ dev->base_addr, dev->mem_start, dev->mem_end);
+ }
+
+ return 0;
+}
+
+static int ethoc_stop(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+
+ napi_disable(&priv->napi);
+
+ if (priv->phy)
+ phy_stop(priv->phy);
+
+ ethoc_disable_rx_and_tx(priv);
+ free_irq(dev->irq, dev);
+
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ struct mii_ioctl_data *mdio = if_mii(ifr);
+ struct phy_device *phy = NULL;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (cmd != SIOCGMIIPHY) {
+ if (mdio->phy_id >= PHY_MAX_ADDR)
+ return -ERANGE;
+
+ phy = priv->mdio->phy_map[mdio->phy_id];
+ if (!phy)
+ return -ENODEV;
+ } else {
+ phy = priv->phy;
+ }
+
+ return phy_mii_ioctl(phy, mdio, cmd);
+}
+
+static int ethoc_config(struct net_device *dev, struct ifmap *map)
+{
+ return -ENOSYS;
+}
+
+static int ethoc_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ u8 *mac = (u8 *)addr;
+
+ ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) |
+ (mac[4] << 8) | (mac[5] << 0));
+ ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0));
+
+ return 0;
+}
+
+static void ethoc_set_multicast_list(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ u32 mode = ethoc_read(priv, MODER);
+ struct dev_mc_list *mc = NULL;
+ u32 hash[2] = { 0, 0 };
+
+ /* set loopback mode if requested */
+ if (dev->flags & IFF_LOOPBACK)
+ mode |= MODER_LOOP;
+ else
+ mode &= ~MODER_LOOP;
+
+ /* receive broadcast frames if requested */
+ if (dev->flags & IFF_BROADCAST)
+ mode &= ~MODER_BRO;
+ else
+ mode |= MODER_BRO;
+
+ /* enable promiscuous mode if requested */
+ if (dev->flags & IFF_PROMISC)
+ mode |= MODER_PRO;
+ else
+ mode &= ~MODER_PRO;
+
+ ethoc_write(priv, MODER, mode);
+
+ /* receive multicast frames */
+ if (dev->flags & IFF_ALLMULTI) {
+ hash[0] = 0xffffffff;
+ hash[1] = 0xffffffff;
+ } else {
+ for (mc = dev->mc_list; mc; mc = mc->next) {
+ u32 crc = ether_crc(mc->dmi_addrlen, mc->dmi_addr);
+ int bit = (crc >> 26) & 0x3f;
+ hash[bit >> 5] |= 1 << (bit & 0x1f);
+ }
+ }
+
+ ethoc_write(priv, ETH_HASH0, hash[0]);
+ ethoc_write(priv, ETH_HASH1, hash[1]);
+}
+
+static int ethoc_change_mtu(struct net_device *dev, int new_mtu)
+{
+ return -ENOSYS;
+}
+
+static void ethoc_tx_timeout(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ u32 pending = ethoc_read(priv, INT_SOURCE);
+ if (likely(pending))
+ ethoc_interrupt(dev->irq, dev);
+}
+
+static struct net_device_stats *ethoc_stats(struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ return &priv->stats;
+}
+
+static int ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethoc *priv = netdev_priv(dev);
+ struct ethoc_bd bd;
+ unsigned int entry;
+ void *dest;
+
+ if (unlikely(skb->len > ETHOC_BUFSIZ)) {
+ priv->stats.tx_errors++;
+ return -EMSGSIZE;
+ }
+
+ entry = priv->cur_tx % priv->num_tx;
+ spin_lock_irq(&priv->lock);
+ priv->cur_tx++;
+
+ ethoc_read_bd(priv, entry, &bd);
+ if (unlikely(skb->len < ETHOC_ZLEN))
+ bd.stat |= TX_BD_PAD;
+ else
+ bd.stat &= ~TX_BD_PAD;
+
+ dest = priv->membase + bd.addr;
+ memcpy_toio(dest, skb->data, skb->len);
+
+ bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK);
+ bd.stat |= TX_BD_LEN(skb->len);
+ ethoc_write_bd(priv, entry, &bd);
+
+ bd.stat |= TX_BD_READY;
+ ethoc_write_bd(priv, entry, &bd);
+
+ if (priv->cur_tx == (priv->dty_tx + priv->num_tx)) {
+ dev_dbg(&dev->dev, "stopping queue\n");
+ netif_stop_queue(dev);
+ }
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+
+ spin_unlock_irq(&priv->lock);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ethoc_netdev_ops = {
+ .ndo_open = ethoc_open,
+ .ndo_stop = ethoc_stop,
+ .ndo_do_ioctl = ethoc_ioctl,
+ .ndo_set_config = ethoc_config,
+ .ndo_set_mac_address = ethoc_set_mac_address,
+ .ndo_set_multicast_list = ethoc_set_multicast_list,
+ .ndo_change_mtu = ethoc_change_mtu,
+ .ndo_tx_timeout = ethoc_tx_timeout,
+ .ndo_get_stats = ethoc_stats,
+ .ndo_start_xmit = ethoc_start_xmit,
+};
+
+/**
+ * ethoc_probe() - initialize OpenCores ethernet MAC
+ * pdev: platform device
+ */
+static int ethoc_probe(struct platform_device *pdev)
+{
+ struct net_device *netdev = NULL;
+ struct resource *res = NULL;
+ struct resource *mmio = NULL;
+ struct resource *mem = NULL;
+ struct ethoc *priv = NULL;
+ unsigned int phy;
+ int ret = 0;
+
+ /* allocate networking device */
+ netdev = alloc_etherdev(sizeof(struct ethoc));
+ if (!netdev) {
+ dev_err(&pdev->dev, "cannot allocate network device\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ platform_set_drvdata(pdev, netdev);
+
+ /* obtain I/O memory space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory space\n");
+ ret = -ENXIO;
+ goto free;
+ }
+
+ mmio = devm_request_mem_region(&pdev->dev, res->start,
+ res->end - res->start + 1, res->name);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot request I/O memory space\n");
+ ret = -ENXIO;
+ goto free;
+ }
+
+ netdev->base_addr = mmio->start;
+
+ /* obtain buffer memory space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain memory space\n");
+ ret = -ENXIO;
+ goto free;
+ }
+
+ mem = devm_request_mem_region(&pdev->dev, res->start,
+ res->end - res->start + 1, res->name);
+ if (!mem) {
+ dev_err(&pdev->dev, "cannot request memory space\n");
+ ret = -ENXIO;
+ goto free;
+ }
+
+ netdev->mem_start = mem->start;
+ netdev->mem_end = mem->end;
+
+ /* obtain device IRQ number */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain IRQ\n");
+ ret = -ENXIO;
+ goto free;
+ }
+
+ netdev->irq = res->start;
+
+ /* setup driver-private data */
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+
+ priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr,
+ mmio->end - mmio->start + 1);
+ if (!priv->iobase) {
+ dev_err(&pdev->dev, "cannot remap I/O memory space\n");
+ ret = -ENXIO;
+ goto error;
+ }
+
+ priv->membase = devm_ioremap_nocache(&pdev->dev, netdev->mem_start,
+ mem->end - mem->start + 1);
+ if (!priv->membase) {
+ dev_err(&pdev->dev, "cannot remap memory space\n");
+ ret = -ENXIO;
+ goto error;
+ }
+
+ /* Allow the platform setup code to pass in a MAC address. */
+ if (pdev->dev.platform_data) {
+ struct ethoc_platform_data *pdata =
+ (struct ethoc_platform_data *)pdev->dev.platform_data;
+ memcpy(netdev->dev_addr, pdata->hwaddr, IFHWADDRLEN);
+ priv->phy_id = pdata->phy_id;
+ }
+
+ /* Check that the given MAC address is valid. If it isn't, read the
+ * current MAC from the controller. */
+ if (!is_valid_ether_addr(netdev->dev_addr))
+ ethoc_get_mac_address(netdev, netdev->dev_addr);
+
+ /* Check the MAC again for validity, if it still isn't choose and
+ * program a random one. */
+ if (!is_valid_ether_addr(netdev->dev_addr))
+ random_ether_addr(netdev->dev_addr);
+
+ ethoc_set_mac_address(netdev, netdev->dev_addr);
+
+ /* register MII bus */
+ priv->mdio = mdiobus_alloc();
+ if (!priv->mdio) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ priv->mdio->name = "ethoc-mdio";
+ snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%d",
+ priv->mdio->name, pdev->id);
+ priv->mdio->read = ethoc_mdio_read;
+ priv->mdio->write = ethoc_mdio_write;
+ priv->mdio->reset = ethoc_mdio_reset;
+ priv->mdio->priv = priv;
+
+ priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!priv->mdio->irq) {
+ ret = -ENOMEM;
+ goto free_mdio;
+ }
+
+ for (phy = 0; phy < PHY_MAX_ADDR; phy++)
+ priv->mdio->irq[phy] = PHY_POLL;
+
+ ret = mdiobus_register(priv->mdio);
+ if (ret) {
+ dev_err(&netdev->dev, "failed to register MDIO bus\n");
+ goto free_mdio;
+ }
+
+ ret = ethoc_mdio_probe(netdev);
+ if (ret) {
+ dev_err(&netdev->dev, "failed to probe MDIO bus\n");
+ goto error;
+ }
+
+ ether_setup(netdev);
+
+ /* setup the net_device structure */
+ netdev->netdev_ops = ðoc_netdev_ops;
+ netdev->watchdog_timeo = ETHOC_TIMEOUT;
+ netdev->features |= 0;
+
+ /* setup NAPI */
+ memset(&priv->napi, 0, sizeof(priv->napi));
+ netif_napi_add(netdev, &priv->napi, ethoc_poll, 64);
+
+ spin_lock_init(&priv->rx_lock);
+ spin_lock_init(&priv->lock);
+
+ ret = register_netdev(netdev);
+ if (ret < 0) {
+ dev_err(&netdev->dev, "failed to register interface\n");
+ goto error;
+ }
+
+ goto out;
+
+error:
+ mdiobus_unregister(priv->mdio);
+free_mdio:
+ kfree(priv->mdio->irq);
+ mdiobus_free(priv->mdio);
+free:
+ free_netdev(netdev);
+out:
+ return ret;
+}
+
+/**
+ * ethoc_remove() - shutdown OpenCores ethernet MAC
+ * @pdev: platform device
+ */
+static int ethoc_remove(struct platform_device *pdev)
+{
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct ethoc *priv = netdev_priv(netdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (netdev) {
+ phy_disconnect(priv->phy);
+ priv->phy = NULL;
+
+ if (priv->mdio) {
+ mdiobus_unregister(priv->mdio);
+ kfree(priv->mdio->irq);
+ mdiobus_free(priv->mdio);
+ }
+
+ unregister_netdev(netdev);
+ free_netdev(netdev);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ethoc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+static int ethoc_resume(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+#else
+# define ethoc_suspend NULL
+# define ethoc_resume NULL
+#endif
+
+static struct platform_driver ethoc_driver = {
+ .probe = ethoc_probe,
+ .remove = ethoc_remove,
+ .suspend = ethoc_suspend,
+ .resume = ethoc_resume,
+ .driver = {
+ .name = "ethoc",
+ },
+};
+
+static int __init ethoc_init(void)
+{
+ return platform_driver_register(ðoc_driver);
+}
+
+static void __exit ethoc_exit(void)
+{
+ platform_driver_unregister(ðoc_driver);
+}
+
+module_init(ethoc_init);
+module_exit(ethoc_exit);
+
+MODULE_AUTHOR("Thierry Reding ");
+MODULE_DESCRIPTION("OpenCores Ethernet MAC driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/trunk/drivers/net/ewrk3.c b/trunk/drivers/net/ewrk3.c
index b852303c9362..1a685a04d4b2 100644
--- a/trunk/drivers/net/ewrk3.c
+++ b/trunk/drivers/net/ewrk3.c
@@ -388,6 +388,18 @@ static int __init ewrk3_probe1(struct net_device *dev, u_long iobase, int irq)
return err;
}
+static const struct net_device_ops ewrk3_netdev_ops = {
+ .ndo_open = ewrk3_open,
+ .ndo_start_xmit = ewrk3_queue_pkt,
+ .ndo_stop = ewrk3_close,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_do_ioctl = ewrk3_ioctl,
+ .ndo_tx_timeout = ewrk3_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init
ewrk3_hw_init(struct net_device *dev, u_long iobase)
{
@@ -603,16 +615,11 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase)
printk(version);
}
/* The EWRK3-specific entries in the device structure. */
- dev->open = ewrk3_open;
- dev->hard_start_xmit = ewrk3_queue_pkt;
- dev->stop = ewrk3_close;
- dev->set_multicast_list = set_multicast_list;
- dev->do_ioctl = ewrk3_ioctl;
+ dev->netdev_ops = &ewrk3_netdev_ops;
if (lp->adapter_name[4] == '3')
SET_ETHTOOL_OPS(dev, ðtool_ops_203);
else
SET_ETHTOOL_OPS(dev, ðtool_ops);
- dev->tx_timeout = ewrk3_timeout;
dev->watchdog_timeo = QUEUE_PKT_TIMEOUT;
dev->mem_start = 0;
diff --git a/trunk/drivers/net/gianfar.c b/trunk/drivers/net/gianfar.c
index 9d81e7a48dba..6a38800be3f1 100644
--- a/trunk/drivers/net/gianfar.c
+++ b/trunk/drivers/net/gianfar.c
@@ -1239,19 +1239,9 @@ static int gfar_enet_open(struct net_device *dev)
return err;
}
-static inline struct txfcb *gfar_add_fcb(struct sk_buff **skbp)
+static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
{
- struct txfcb *fcb;
- struct sk_buff *skb = *skbp;
-
- if (unlikely(skb_headroom(skb) < GMAC_FCB_LEN)) {
- struct sk_buff *old_skb = skb;
- skb = skb_realloc_headroom(old_skb, GMAC_FCB_LEN);
- if (!skb)
- return NULL;
- dev_kfree_skb_any(old_skb);
- }
- fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN);
+ struct txfcb *fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN);
cacheable_memzero(fcb, GMAC_FCB_LEN);
return fcb;
@@ -1320,6 +1310,22 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
base = priv->tx_bd_base;
+ /* make space for additional header when fcb is needed */
+ if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
+ (priv->vlgrp && vlan_tx_tag_present(skb))) &&
+ (skb_headroom(skb) < GMAC_FCB_LEN)) {
+ struct sk_buff *skb_new;
+
+ skb_new = skb_realloc_headroom(skb, GMAC_FCB_LEN);
+ if (!skb_new) {
+ dev->stats.tx_errors++;
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ kfree_skb(skb);
+ skb = skb_new;
+ }
+
/* total number of fragments in the SKB */
nr_frags = skb_shinfo(skb)->nr_frags;
@@ -1372,20 +1378,18 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Set up checksumming */
if (CHECKSUM_PARTIAL == skb->ip_summed) {
- fcb = gfar_add_fcb(&skb);
- if (likely(fcb != NULL)) {
- lstatus |= BD_LFLAG(TXBD_TOE);
- gfar_tx_checksum(skb, fcb);
- }
+ fcb = gfar_add_fcb(skb);
+ lstatus |= BD_LFLAG(TXBD_TOE);
+ gfar_tx_checksum(skb, fcb);
}
if (priv->vlgrp && vlan_tx_tag_present(skb)) {
- if (unlikely(NULL == fcb))
- fcb = gfar_add_fcb(&skb);
- if (likely(fcb != NULL)) {
+ if (unlikely(NULL == fcb)) {
+ fcb = gfar_add_fcb(skb);
lstatus |= BD_LFLAG(TXBD_TOE);
- gfar_tx_vlan(skb, fcb);
}
+
+ gfar_tx_vlan(skb, fcb);
}
/* setup the TxBD length and buffer pointer for the first BD */
@@ -1433,7 +1437,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Unlock priv */
spin_unlock_irqrestore(&priv->txlock, flags);
- return 0;
+ return NETDEV_TX_OK;
}
/* Stops the kernel queue, and halts the controller */
diff --git a/trunk/drivers/net/ibmlana.c b/trunk/drivers/net/ibmlana.c
index 5b5bf9f9861a..c25bc0bc0b25 100644
--- a/trunk/drivers/net/ibmlana.c
+++ b/trunk/drivers/net/ibmlana.c
@@ -905,6 +905,17 @@ static char *ibmlana_adapter_names[] __devinitdata = {
NULL
};
+
+static const struct net_device_ops ibmlana_netdev_ops = {
+ .ndo_open = ibmlana_open,
+ .ndo_stop = ibmlana_close,
+ .ndo_start_xmit = ibmlana_tx,
+ .ndo_set_multicast_list = ibmlana_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __devinit ibmlana_init_one(struct device *kdev)
{
struct mca_device *mdev = to_mca_device(kdev);
@@ -973,11 +984,7 @@ static int __devinit ibmlana_init_one(struct device *kdev)
mca_device_set_claim(mdev, 1);
/* set methods */
-
- dev->open = ibmlana_open;
- dev->stop = ibmlana_close;
- dev->hard_start_xmit = ibmlana_tx;
- dev->set_multicast_list = ibmlana_set_multicast_list;
+ dev->netdev_ops = &ibmlana_netdev_ops;
dev->flags |= IFF_MULTICAST;
/* copy out MAC address */
diff --git a/trunk/drivers/net/irda/donauboe.c b/trunk/drivers/net/irda/donauboe.c
index 6f3e7f71658d..6b6548b9fda0 100644
--- a/trunk/drivers/net/irda/donauboe.c
+++ b/trunk/drivers/net/irda/donauboe.c
@@ -1524,6 +1524,13 @@ toshoboe_close (struct pci_dev *pci_dev)
free_netdev(self->netdev);
}
+static const struct net_device_ops toshoboe_netdev_ops = {
+ .ndo_open = toshoboe_net_open,
+ .ndo_stop = toshoboe_net_close,
+ .ndo_start_xmit = toshoboe_hard_xmit,
+ .ndo_do_ioctl = toshoboe_net_ioctl,
+};
+
static int
toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
{
@@ -1657,10 +1664,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
#endif
SET_NETDEV_DEV(dev, &pci_dev->dev);
- dev->hard_start_xmit = toshoboe_hard_xmit;
- dev->open = toshoboe_net_open;
- dev->stop = toshoboe_net_close;
- dev->do_ioctl = toshoboe_net_ioctl;
+ dev->netdev_ops = &toshoboe_netdev_ops;
err = register_netdev(dev);
if (err)
diff --git a/trunk/drivers/net/lance.c b/trunk/drivers/net/lance.c
index d83d4010656d..633808d447be 100644
--- a/trunk/drivers/net/lance.c
+++ b/trunk/drivers/net/lance.c
@@ -454,6 +454,18 @@ struct net_device * __init lance_probe(int unit)
}
#endif
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_stop = lance_close,
+ .ndo_get_stats = lance_get_stats,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
{
struct lance_private *lp;
@@ -714,12 +726,7 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
printk(version);
/* The LANCE-specific entries in the device structure. */
- dev->open = lance_open;
- dev->hard_start_xmit = lance_start_xmit;
- dev->stop = lance_close;
- dev->get_stats = lance_get_stats;
- dev->set_multicast_list = set_multicast_list;
- dev->tx_timeout = lance_tx_timeout;
+ dev->netdev_ops = &lance_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
err = register_netdev(dev);
diff --git a/trunk/drivers/net/lp486e.c b/trunk/drivers/net/lp486e.c
index 4d1a059921c6..d44bddbee373 100644
--- a/trunk/drivers/net/lp486e.c
+++ b/trunk/drivers/net/lp486e.c
@@ -952,6 +952,17 @@ static void print_eth(char *add)
(unsigned char) add[12], (unsigned char) add[13]);
}
+static const struct net_device_ops i596_netdev_ops = {
+ .ndo_open = i596_open,
+ .ndo_stop = i596_close,
+ .ndo_start_xmit = i596_start_xmit,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_tx_timeout = i596_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init lp486e_probe(struct net_device *dev) {
struct i596_private *lp;
unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
@@ -1014,12 +1025,8 @@ static int __init lp486e_probe(struct net_device *dev) {
printk("\n");
/* The LP486E-specific entries in the device structure. */
- dev->open = &i596_open;
- dev->stop = &i596_close;
- dev->hard_start_xmit = &i596_start_xmit;
- dev->set_multicast_list = &set_multicast_list;
+ dev->netdev_ops = &i596_netdev_ops;
dev->watchdog_timeo = 5*HZ;
- dev->tx_timeout = i596_tx_timeout;
#if 0
/* selftest reports 0x320925ae - don't know what that means */
diff --git a/trunk/drivers/net/ni52.c b/trunk/drivers/net/ni52.c
index a8bcc00c3302..77d44a061703 100644
--- a/trunk/drivers/net/ni52.c
+++ b/trunk/drivers/net/ni52.c
@@ -441,6 +441,18 @@ struct net_device * __init ni52_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops ni52_netdev_ops = {
+ .ndo_open = ni52_open,
+ .ndo_stop = ni52_close,
+ .ndo_get_stats = ni52_get_stats,
+ .ndo_tx_timeout = ni52_timeout,
+ .ndo_start_xmit = ni52_send_packet,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static int __init ni52_probe1(struct net_device *dev, int ioaddr)
{
int i, size, retval;
@@ -561,15 +573,8 @@ static int __init ni52_probe1(struct net_device *dev, int ioaddr)
printk("IRQ %d (assigned and not checked!).\n", dev->irq);
}
- dev->open = ni52_open;
- dev->stop = ni52_close;
- dev->get_stats = ni52_get_stats;
- dev->tx_timeout = ni52_timeout;
+ dev->netdev_ops = &ni52_netdev_ops;
dev->watchdog_timeo = HZ/20;
- dev->hard_start_xmit = ni52_send_packet;
- dev->set_multicast_list = set_multicast_list;
-
- dev->if_port = 0;
return 0;
out:
diff --git a/trunk/drivers/net/ni65.c b/trunk/drivers/net/ni65.c
index df5f869e8d8f..6474f02bf783 100644
--- a/trunk/drivers/net/ni65.c
+++ b/trunk/drivers/net/ni65.c
@@ -237,7 +237,7 @@ struct priv
void *tmdbounce[TMDNUM];
int tmdbouncenum;
int lock,xmit_queued;
- struct net_device_stats stats;
+
void *self;
int cmdr_addr;
int cardno;
@@ -257,7 +257,6 @@ static void ni65_timeout(struct net_device *dev);
static int ni65_close(struct net_device *dev);
static int ni65_alloc_buffer(struct net_device *dev);
static void ni65_free_buffer(struct priv *p);
-static struct net_device_stats *ni65_get_stats(struct net_device *);
static void set_multicast_list(struct net_device *dev);
static int irqtab[] __initdata = { 9,12,15,5 }; /* irq config-translate */
@@ -401,6 +400,17 @@ struct net_device * __init ni65_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops ni65_netdev_ops = {
+ .ndo_open = ni65_open,
+ .ndo_stop = ni65_close,
+ .ndo_start_xmit = ni65_send_packet,
+ .ndo_tx_timeout = ni65_timeout,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/*
* this is the real card probe ..
*/
@@ -549,13 +559,9 @@ static int __init ni65_probe1(struct net_device *dev,int ioaddr)
}
dev->base_addr = ioaddr;
- dev->open = ni65_open;
- dev->stop = ni65_close;
- dev->hard_start_xmit = ni65_send_packet;
- dev->tx_timeout = ni65_timeout;
+ dev->netdev_ops = &ni65_netdev_ops;
dev->watchdog_timeo = HZ/2;
- dev->get_stats = ni65_get_stats;
- dev->set_multicast_list = set_multicast_list;
+
return 0; /* everything is OK */
}
@@ -901,13 +907,13 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
if(debuglevel > 1)
printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0);
if(csr0 & CSR0_BABL)
- p->stats.tx_errors++;
+ dev->stats.tx_errors++;
if(csr0 & CSR0_MISS) {
int i;
for(i=0;irmdhead[i].u.s.status);
printk("\n");
- p->stats.rx_errors++;
+ dev->stats.rx_errors++;
}
if(csr0 & CSR0_MERR) {
if(debuglevel > 1)
@@ -997,12 +1003,12 @@ static void ni65_xmit_intr(struct net_device *dev,int csr0)
#endif
/* checking some errors */
if(tmdp->status2 & XMIT_RTRY)
- p->stats.tx_aborted_errors++;
+ dev->stats.tx_aborted_errors++;
if(tmdp->status2 & XMIT_LCAR)
- p->stats.tx_carrier_errors++;
+ dev->stats.tx_carrier_errors++;
if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
/* this stops the xmitter */
- p->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
if(debuglevel > 0)
printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
if(p->features & INIT_RING_BEFORE_START) {
@@ -1016,12 +1022,12 @@ static void ni65_xmit_intr(struct net_device *dev,int csr0)
if(debuglevel > 2)
printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
if(!(csr0 & CSR0_BABL)) /* don't count errors twice */
- p->stats.tx_errors++;
+ dev->stats.tx_errors++;
tmdp->status2 = 0;
}
else {
- p->stats.tx_bytes -= (short)(tmdp->blen);
- p->stats.tx_packets++;
+ dev->stats.tx_bytes -= (short)(tmdp->blen);
+ dev->stats.tx_packets++;
}
#ifdef XMT_VIA_SKB
@@ -1057,7 +1063,7 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
if(!(rmdstat & RCV_ERR)) {
if(rmdstat & RCV_START)
{
- p->stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
}
}
@@ -1066,16 +1072,16 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
if(rmdstat & RCV_FRAM)
- p->stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
if(rmdstat & RCV_OFLO)
- p->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
if(rmdstat & RCV_CRC)
- p->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
if(rmdstat & RCV_BUF_ERR)
- p->stats.rx_fifo_errors++;
+ dev->stats.rx_fifo_errors++;
}
if(!(csr0 & CSR0_MISS)) /* don't count errors twice */
- p->stats.rx_errors++;
+ dev->stats.rx_errors++;
}
else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
{
@@ -1106,20 +1112,20 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
skb_put(skb,len);
skb_copy_to_linear_data(skb, (unsigned char *) p->recvbounce[p->rmdnum],len);
#endif
- p->stats.rx_packets++;
- p->stats.rx_bytes += len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
}
else
{
printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
- p->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
}
}
else {
printk(KERN_INFO "%s: received runt packet\n",dev->name);
- p->stats.rx_errors++;
+ dev->stats.rx_errors++;
}
rmdp->blen = -(R_BUF_SIZE-8);
rmdp->mlen = 0;
@@ -1213,23 +1219,6 @@ static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static struct net_device_stats *ni65_get_stats(struct net_device *dev)
-{
-
-#if 0
- int i;
- struct priv *p = dev->ml_priv;
- for(i=0;irmdhead + ((p->rmdnum + i) & (RMDNUM-1));
- printk("%02x ",rmdp->u.s.status);
- }
- printk("\n");
-#endif
-
- return &((struct priv *)dev->ml_priv)->stats;
-}
-
static void set_multicast_list(struct net_device *dev)
{
if(!ni65_lance_reinit(dev))
diff --git a/trunk/drivers/net/seeq8005.c b/trunk/drivers/net/seeq8005.c
index 12a8ffffeb03..ebbbe09725fe 100644
--- a/trunk/drivers/net/seeq8005.c
+++ b/trunk/drivers/net/seeq8005.c
@@ -143,6 +143,17 @@ struct net_device * __init seeq8005_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops seeq8005_netdev_ops = {
+ .ndo_open = seeq8005_open,
+ .ndo_stop = seeq8005_close,
+ .ndo_start_xmit = seeq8005_send_packet,
+ .ndo_tx_timeout = seeq8005_timeout,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probes avoids doing writes, and
verifies that the correct device exists and functions. */
@@ -332,12 +343,8 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
}
}
#endif
- dev->open = seeq8005_open;
- dev->stop = seeq8005_close;
- dev->hard_start_xmit = seeq8005_send_packet;
- dev->tx_timeout = seeq8005_timeout;
+ dev->netdev_ops = &seeq8005_netdev_ops;
dev->watchdog_timeo = HZ/20;
- dev->set_multicast_list = set_multicast_list;
dev->flags &= ~IFF_MULTICAST;
return 0;
diff --git a/trunk/drivers/net/smc-ultra.c b/trunk/drivers/net/smc-ultra.c
index 2033fee3143a..0291ea098a06 100644
--- a/trunk/drivers/net/smc-ultra.c
+++ b/trunk/drivers/net/smc-ultra.c
@@ -142,9 +142,6 @@ static int __init do_ultra_probe(struct net_device *dev)
int base_addr = dev->base_addr;
int irq = dev->irq;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = &ultra_poll;
-#endif
if (base_addr > 0x1ff) /* Check a single specified location. */
return ultra_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
@@ -199,7 +196,7 @@ static const struct net_device_ops ultra_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = ei_poll,
+ .ndo_poll_controller = ultra_poll,
#endif
};
diff --git a/trunk/drivers/net/smc-ultra32.c b/trunk/drivers/net/smc-ultra32.c
index cb6c097a2e0a..7a554adc70fb 100644
--- a/trunk/drivers/net/smc-ultra32.c
+++ b/trunk/drivers/net/smc-ultra32.c
@@ -153,6 +153,22 @@ struct net_device * __init ultra32_probe(int unit)
return ERR_PTR(err);
}
+
+static const struct net_device_ops ultra32_netdev_ops = {
+ .ndo_open = ultra32_open,
+ .ndo_stop = ultra32_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
{
int i, edge, media, retval;
@@ -273,11 +289,8 @@ static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
ei_status.block_output = &ultra32_block_output;
ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
ei_status.reset_8390 = &ultra32_reset_8390;
- dev->open = &ultra32_open;
- dev->stop = &ultra32_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+
+ dev->netdev_ops = &ultra32_netdev_ops;
NS8390_init(dev, 0);
return 0;
diff --git a/trunk/drivers/net/smc9194.c b/trunk/drivers/net/smc9194.c
index 18d653bbd4e0..9a7973a54116 100644
--- a/trunk/drivers/net/smc9194.c
+++ b/trunk/drivers/net/smc9194.c
@@ -831,6 +831,17 @@ static int __init smc_findirq(int ioaddr)
#endif
}
+static const struct net_device_ops smc_netdev_ops = {
+ .ndo_open = smc_open,
+ .ndo_stop = smc_close,
+ .ndo_start_xmit = smc_wait_to_send_packet,
+ .ndo_tx_timeout = smc_timeout,
+ .ndo_set_multicast_list = smc_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/*----------------------------------------------------------------------
. Function: smc_probe( int ioaddr )
.
@@ -1044,12 +1055,8 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
goto err_out;
}
- dev->open = smc_open;
- dev->stop = smc_close;
- dev->hard_start_xmit = smc_wait_to_send_packet;
- dev->tx_timeout = smc_timeout;
+ dev->netdev_ops = &smc_netdev_ops;
dev->watchdog_timeo = HZ/20;
- dev->set_multicast_list = smc_set_multicast_list;
return 0;
diff --git a/trunk/drivers/net/smsc911x.c b/trunk/drivers/net/smsc911x.c
index ad3cbc91a8fa..af8f60ca0f57 100644
--- a/trunk/drivers/net/smsc911x.c
+++ b/trunk/drivers/net/smsc911x.c
@@ -1680,6 +1680,7 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
u8 address, u8 data)
{
u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ u32 temp;
int ret;
SMSC_TRACE(DRV, "address 0x%x, data 0x%x", address, data);
@@ -1688,6 +1689,10 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
if (!ret) {
op = E2P_CMD_EPC_CMD_WRITE_ | address;
smsc911x_reg_write(pdata, E2P_DATA, (u32)data);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
ret = smsc911x_eeprom_send_cmd(pdata, op);
}
diff --git a/trunk/drivers/net/tokenring/madgemc.c b/trunk/drivers/net/tokenring/madgemc.c
index 193308118f95..456f8bff40be 100644
--- a/trunk/drivers/net/tokenring/madgemc.c
+++ b/trunk/drivers/net/tokenring/madgemc.c
@@ -142,7 +142,7 @@ static void madgemc_sifwritew(struct net_device *dev, unsigned short val, unsign
return;
}
-
+static struct net_device_ops madgemc_netdev_ops __read_mostly;
static int __devinit madgemc_probe(struct device *device)
{
@@ -168,7 +168,7 @@ static int __devinit madgemc_probe(struct device *device)
goto getout;
}
- dev->dma = 0;
+ dev->netdev_ops = &madgemc_netdev_ops;
card = kmalloc(sizeof(struct card_info), GFP_KERNEL);
if (card==NULL) {
@@ -348,9 +348,6 @@ static int __devinit madgemc_probe(struct device *device)
memcpy(tp->ProductID, "Madge MCA 16/4 ", PROD_ID_SIZE + 1);
- dev->open = madgemc_open;
- dev->stop = madgemc_close;
-
tp->tmspriv = card;
dev_set_drvdata(device, dev);
@@ -758,6 +755,10 @@ static struct mca_driver madgemc_driver = {
static int __init madgemc_init (void)
{
+ madgemc_netdev_ops = tms380tr_netdev_ops;
+ madgemc_netdev_ops.ndo_open = madgemc_open;
+ madgemc_netdev_ops.ndo_stop = madgemc_close;
+
return mca_register_driver (&madgemc_driver);
}
diff --git a/trunk/drivers/net/tokenring/proteon.c b/trunk/drivers/net/tokenring/proteon.c
index b8c955f6d31a..16e8783ee9cd 100644
--- a/trunk/drivers/net/tokenring/proteon.c
+++ b/trunk/drivers/net/tokenring/proteon.c
@@ -116,6 +116,8 @@ static int __init proteon_probe1(struct net_device *dev, int ioaddr)
return -ENODEV;
}
+static struct net_device_ops proteon_netdev_ops __read_mostly;
+
static int __init setup_card(struct net_device *dev, struct device *pdev)
{
struct net_local *tp;
@@ -167,8 +169,7 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
tp->tmspriv = NULL;
- dev->open = proteon_open;
- dev->stop = tms380tr_close;
+ dev->netdev_ops = &proteon_netdev_ops;
if (dev->irq == 0)
{
@@ -352,6 +353,10 @@ static int __init proteon_init(void)
struct platform_device *pdev;
int i, num = 0, err = 0;
+ proteon_netdev_ops = tms380tr_netdev_ops;
+ proteon_netdev_ops.ndo_open = proteon_open;
+ proteon_netdev_ops.ndo_stop = tms380tr_close;
+
err = platform_driver_register(&proteon_driver);
if (err)
return err;
diff --git a/trunk/drivers/net/tokenring/skisa.c b/trunk/drivers/net/tokenring/skisa.c
index c0f58f08782c..46db5c5395b2 100644
--- a/trunk/drivers/net/tokenring/skisa.c
+++ b/trunk/drivers/net/tokenring/skisa.c
@@ -133,6 +133,8 @@ static int __init sk_isa_probe1(struct net_device *dev, int ioaddr)
return 0;
}
+static struct net_device_ops sk_isa_netdev_ops __read_mostly;
+
static int __init setup_card(struct net_device *dev, struct device *pdev)
{
struct net_local *tp;
@@ -184,8 +186,7 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
tp->tmspriv = NULL;
- dev->open = sk_isa_open;
- dev->stop = tms380tr_close;
+ dev->netdev_ops = &sk_isa_netdev_ops;
if (dev->irq == 0)
{
@@ -362,6 +363,10 @@ static int __init sk_isa_init(void)
struct platform_device *pdev;
int i, num = 0, err = 0;
+ sk_isa_netdev_ops = tms380tr_netdev_ops;
+ sk_isa_netdev_ops.ndo_open = sk_isa_open;
+ sk_isa_netdev_ops.ndo_stop = tms380tr_close;
+
err = platform_driver_register(&sk_isa_driver);
if (err)
return err;
diff --git a/trunk/drivers/net/tokenring/smctr.c b/trunk/drivers/net/tokenring/smctr.c
index 9d7db2c8d661..a91d9c55d78e 100644
--- a/trunk/drivers/net/tokenring/smctr.c
+++ b/trunk/drivers/net/tokenring/smctr.c
@@ -124,7 +124,6 @@ static unsigned int smctr_get_num_rx_bdbs(struct net_device *dev);
static int smctr_get_physical_drop_number(struct net_device *dev);
static __u8 *smctr_get_rx_pointer(struct net_device *dev, short queue);
static int smctr_get_station_id(struct net_device *dev);
-static struct net_device_stats *smctr_get_stats(struct net_device *dev);
static FCBlock *smctr_get_tx_fcb(struct net_device *dev, __u16 queue,
__u16 bytes_count);
static int smctr_get_upstream_neighbor_addr(struct net_device *dev);
@@ -3633,6 +3632,14 @@ struct net_device __init *smctr_probe(int unit)
return ERR_PTR(err);
}
+static const struct net_device_ops smctr_netdev_ops = {
+ .ndo_open = smctr_open,
+ .ndo_stop = smctr_close,
+ .ndo_start_xmit = smctr_send_packet,
+ .ndo_tx_timeout = smctr_timeout,
+ .ndo_get_stats = smctr_get_stats,
+ .ndo_set_multicast_list = smctr_set_multicast_list,
+};
static int __init smctr_probe1(struct net_device *dev, int ioaddr)
{
@@ -3683,13 +3690,8 @@ static int __init smctr_probe1(struct net_device *dev, int ioaddr)
(unsigned int)dev->base_addr,
dev->irq, tp->rom_base, tp->ram_base);
- dev->open = smctr_open;
- dev->stop = smctr_close;
- dev->hard_start_xmit = smctr_send_packet;
- dev->tx_timeout = smctr_timeout;
+ dev->netdev_ops = &smctr_netdev_ops;
dev->watchdog_timeo = HZ;
- dev->get_stats = smctr_get_stats;
- dev->set_multicast_list = &smctr_set_multicast_list;
return (0);
out:
diff --git a/trunk/drivers/net/ucc_geth.c b/trunk/drivers/net/ucc_geth.c
index a110326dce6f..86a479f61c0c 100644
--- a/trunk/drivers/net/ucc_geth.c
+++ b/trunk/drivers/net/ucc_geth.c
@@ -2009,6 +2009,9 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth)
/* Disable Rx and Tx */
clrbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX);
+ phy_disconnect(ugeth->phydev);
+ ugeth->phydev = NULL;
+
ucc_geth_memclean(ugeth);
}
@@ -3345,6 +3348,14 @@ static int ucc_geth_open(struct net_device *dev)
return -EINVAL;
}
+ err = init_phy(dev);
+ if (err) {
+ if (netif_msg_ifup(ugeth))
+ ugeth_err("%s: Cannot initialize PHY, aborting.",
+ dev->name);
+ return err;
+ }
+
err = ucc_struct_init(ugeth);
if (err) {
if (netif_msg_ifup(ugeth))
@@ -3381,13 +3392,6 @@ static int ucc_geth_open(struct net_device *dev)
&ugeth->ug_regs->macstnaddr1,
&ugeth->ug_regs->macstnaddr2);
- err = init_phy(dev);
- if (err) {
- if (netif_msg_ifup(ugeth))
- ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name);
- goto out_err;
- }
-
phy_start(ugeth->phydev);
err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
@@ -3430,9 +3434,6 @@ static int ucc_geth_close(struct net_device *dev)
free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
- phy_disconnect(ugeth->phydev);
- ugeth->phydev = NULL;
-
netif_stop_queue(dev);
return 0;
diff --git a/trunk/drivers/net/wan/sdla.c b/trunk/drivers/net/wan/sdla.c
index 6a07ba9371db..1d637f407a0c 100644
--- a/trunk/drivers/net/wan/sdla.c
+++ b/trunk/drivers/net/wan/sdla.c
@@ -714,19 +714,19 @@ static int sdla_transmit(struct sk_buff *skb, struct net_device *dev)
switch (ret)
{
case SDLA_RET_OK:
- flp->stats.tx_packets++;
+ dev->stats.tx_packets++;
ret = DLCI_RET_OK;
break;
case SDLA_RET_CIR_OVERFLOW:
case SDLA_RET_BUF_OVERSIZE:
case SDLA_RET_NO_BUFS:
- flp->stats.tx_dropped++;
+ dev->stats.tx_dropped++;
ret = DLCI_RET_DROP;
break;
default:
- flp->stats.tx_errors++;
+ dev->stats.tx_errors++;
ret = DLCI_RET_ERR;
break;
}
@@ -807,7 +807,7 @@ static void sdla_receive(struct net_device *dev)
if (i == CONFIG_DLCI_MAX)
{
printk(KERN_NOTICE "%s: Received packet from invalid DLCI %i, ignoring.", dev->name, dlci);
- flp->stats.rx_errors++;
+ dev->stats.rx_errors++;
success = 0;
}
}
@@ -819,7 +819,7 @@ static void sdla_receive(struct net_device *dev)
if (skb == NULL)
{
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
- flp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
success = 0;
}
else
@@ -859,7 +859,7 @@ static void sdla_receive(struct net_device *dev)
if (success)
{
- flp->stats.rx_packets++;
+ dev->stats.rx_packets++;
dlp = netdev_priv(master);
(*dlp->receive)(skb, master);
}
@@ -1590,13 +1590,14 @@ static int sdla_set_config(struct net_device *dev, struct ifmap *map)
return err;
}
-static struct net_device_stats *sdla_stats(struct net_device *dev)
-{
- struct frad_local *flp;
- flp = netdev_priv(dev);
-
- return(&flp->stats);
-}
+static const struct net_device_ops sdla_netdev_ops = {
+ .ndo_open = sdla_open,
+ .ndo_stop = sdla_close,
+ .ndo_do_ioctl = sdla_ioctl,
+ .ndo_set_config = sdla_set_config,
+ .ndo_start_xmit = sdla_transmit,
+ .ndo_change_mtu = sdla_change_mtu,
+};
static void setup_sdla(struct net_device *dev)
{
@@ -1604,20 +1605,13 @@ static void setup_sdla(struct net_device *dev)
netdev_boot_setup_check(dev);
+ dev->netdev_ops = &sdla_netdev_ops;
dev->flags = 0;
dev->type = 0xFFFF;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->mtu = SDLA_MAX_MTU;
- dev->open = sdla_open;
- dev->stop = sdla_close;
- dev->do_ioctl = sdla_ioctl;
- dev->set_config = sdla_set_config;
- dev->get_stats = sdla_stats;
- dev->hard_start_xmit = sdla_transmit;
- dev->change_mtu = sdla_change_mtu;
-
flp->activate = sdla_activate;
flp->deactivate = sdla_deactivate;
flp->assoc = sdla_assoc;
diff --git a/trunk/drivers/net/wireless/Kconfig b/trunk/drivers/net/wireless/Kconfig
index 612fffe100a6..8a0823588c51 100644
--- a/trunk/drivers/net/wireless/Kconfig
+++ b/trunk/drivers/net/wireless/Kconfig
@@ -485,6 +485,7 @@ config MWL8K
source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/ath5k/Kconfig"
source "drivers/net/wireless/ath9k/Kconfig"
+source "drivers/net/wireless/ar9170/Kconfig"
source "drivers/net/wireless/ipw2x00/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
source "drivers/net/wireless/hostap/Kconfig"
diff --git a/trunk/drivers/net/wireless/Makefile b/trunk/drivers/net/wireless/Makefile
index d780487c420f..50e7fba7f0ea 100644
--- a/trunk/drivers/net/wireless/Makefile
+++ b/trunk/drivers/net/wireless/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_P54_COMMON) += p54/
obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K) += ath9k/
+obj-$(CONFIG_AR9170_USB) += ar9170/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
diff --git a/trunk/drivers/net/wireless/ar9170/Kconfig b/trunk/drivers/net/wireless/ar9170/Kconfig
new file mode 100644
index 000000000000..de4281fda129
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/Kconfig
@@ -0,0 +1,17 @@
+config AR9170_USB
+ tristate "Atheros AR9170 802.11n USB support"
+ depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
+ select FW_LOADER
+ help
+ This is a driver for the Atheros "otus" 802.11n USB devices.
+
+ These devices require additional firmware (2 files).
+ For now, these files can be downloaded from here:
+ http://wireless.kernel.org/en/users/Drivers/ar9170
+
+ If you choose to build a module, it'll be called ar9170usb.
+
+config AR9170_LEDS
+ bool
+ depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB)
+ default y
diff --git a/trunk/drivers/net/wireless/ar9170/Makefile b/trunk/drivers/net/wireless/ar9170/Makefile
new file mode 100644
index 000000000000..8d91c7ee3215
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/Makefile
@@ -0,0 +1,3 @@
+ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o
+
+obj-$(CONFIG_AR9170_USB) += ar9170usb.o
diff --git a/trunk/drivers/net/wireless/ar9170/ar9170.h b/trunk/drivers/net/wireless/ar9170/ar9170.h
new file mode 100644
index 000000000000..f4fb2e94aea0
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/ar9170.h
@@ -0,0 +1,209 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_H
+#define __AR9170_H
+
+#include
+#include
+#include
+#include
+#ifdef CONFIG_AR9170_LEDS
+#include
+#endif /* CONFIG_AR9170_LEDS */
+#include "eeprom.h"
+#include "hw.h"
+
+#define PAYLOAD_MAX (AR9170_MAX_CMD_LEN/4 - 1)
+
+enum ar9170_bw {
+ AR9170_BW_20,
+ AR9170_BW_40_BELOW,
+ AR9170_BW_40_ABOVE,
+
+ __AR9170_NUM_BW,
+};
+
+enum ar9170_rf_init_mode {
+ AR9170_RFI_NONE,
+ AR9170_RFI_WARM,
+ AR9170_RFI_COLD,
+};
+
+#define AR9170_MAX_RX_BUFFER_SIZE 8192
+
+#ifdef CONFIG_AR9170_LEDS
+struct ar9170;
+
+struct ar9170_led {
+ struct ar9170 *ar;
+ struct led_classdev l;
+ char name[32];
+ unsigned int toggled;
+ bool registered;
+};
+
+#endif /* CONFIG_AR9170_LEDS */
+
+enum ar9170_device_state {
+ AR9170_UNKNOWN_STATE,
+ AR9170_STOPPED,
+ AR9170_IDLE,
+ AR9170_STARTED,
+ AR9170_ASSOCIATED,
+};
+
+struct ar9170 {
+ struct ieee80211_hw *hw;
+ struct mutex mutex;
+ enum ar9170_device_state state;
+
+ int (*open)(struct ar9170 *);
+ void (*stop)(struct ar9170 *);
+ int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int);
+ int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
+ void *, u32 , void *);
+ void (*callback_cmd)(struct ar9170 *, u32 , void *);
+
+ /* interface mode settings */
+ struct ieee80211_vif *vif;
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ /* beaconing */
+ struct sk_buff *beacon;
+ struct work_struct beacon_work;
+
+ /* cryptographic engine */
+ u64 usedkeys;
+ bool rx_software_decryption;
+ bool disable_offload;
+
+ /* filter settings */
+ struct work_struct filter_config_work;
+ u64 cur_mc_hash, want_mc_hash;
+ u32 cur_filter, want_filter;
+ unsigned int filter_changed;
+ bool sniffer_enabled;
+
+ /* PHY */
+ struct ieee80211_channel *channel;
+ int noise[4];
+
+ /* power calibration data */
+ u8 power_5G_leg[4];
+ u8 power_2G_cck[4];
+ u8 power_2G_ofdm[4];
+ u8 power_5G_ht20[8];
+ u8 power_5G_ht40[8];
+ u8 power_2G_ht20[8];
+ u8 power_2G_ht40[8];
+
+#ifdef CONFIG_AR9170_LEDS
+ struct delayed_work led_work;
+ struct ar9170_led leds[AR9170_NUM_LEDS];
+#endif /* CONFIG_AR9170_LEDS */
+
+ /* qos queue settings */
+ spinlock_t tx_stats_lock;
+ struct ieee80211_tx_queue_stats tx_stats[5];
+ struct ieee80211_tx_queue_params edcf[5];
+
+ spinlock_t cmdlock;
+ __le32 cmdbuf[PAYLOAD_MAX + 1];
+
+ /* MAC statistics */
+ struct ieee80211_low_level_stats stats;
+
+ /* EEPROM */
+ struct ar9170_eeprom eeprom;
+
+ /* global tx status for unregistered Stations. */
+ struct sk_buff_head global_tx_status;
+ struct sk_buff_head global_tx_status_waste;
+ struct delayed_work tx_status_janitor;
+};
+
+struct ar9170_sta_info {
+ struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+};
+
+#define IS_STARTED(a) (a->state >= AR9170_STARTED)
+#define IS_ACCEPTING_CMD(a) (a->state >= AR9170_IDLE)
+
+#define AR9170_FILTER_CHANGED_PROMISC BIT(0)
+#define AR9170_FILTER_CHANGED_MULTICAST BIT(1)
+#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2)
+
+/* exported interface */
+void *ar9170_alloc(size_t priv_size);
+int ar9170_register(struct ar9170 *ar, struct device *pdev);
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb);
+void ar9170_unregister(struct ar9170 *ar);
+void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+ bool update_statistics, u16 tx_status);
+
+/* MAC */
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+int ar9170_init_mac(struct ar9170 *ar);
+int ar9170_set_qos(struct ar9170 *ar);
+int ar9170_update_multicast(struct ar9170 *ar);
+int ar9170_update_frame_filter(struct ar9170 *ar);
+int ar9170_set_operating_mode(struct ar9170 *ar);
+int ar9170_set_beacon_timers(struct ar9170 *ar);
+int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry);
+int ar9170_update_beacon(struct ar9170 *ar);
+void ar9170_new_beacon(struct work_struct *work);
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+ u8 keyidx, u8 *keydata, int keylen);
+int ar9170_disable_key(struct ar9170 *ar, u8 id);
+
+/* LEDs */
+#ifdef CONFIG_AR9170_LEDS
+int ar9170_register_leds(struct ar9170 *ar);
+void ar9170_unregister_leds(struct ar9170 *ar);
+#endif /* CONFIG_AR9170_LEDS */
+int ar9170_init_leds(struct ar9170 *ar);
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state);
+
+/* PHY / RF */
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band);
+int ar9170_init_rf(struct ar9170 *ar);
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+ enum ar9170_rf_init_mode rfi, enum ar9170_bw bw);
+
+#endif /* __AR9170_H */
diff --git a/trunk/drivers/net/wireless/ar9170/cmd.c b/trunk/drivers/net/wireless/ar9170/cmd.c
new file mode 100644
index 000000000000..f57a6200167b
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/cmd.c
@@ -0,0 +1,129 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len)
+{
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL);
+ if (err)
+ printk(KERN_DEBUG "%s: writing memory failed\n",
+ wiphy_name(ar->hw->wiphy));
+ return err;
+}
+
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
+{
+ __le32 buf[2] = {
+ cpu_to_le32(reg),
+ cpu_to_le32(val),
+ };
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf),
+ (u8 *) buf, 0, NULL);
+ if (err)
+ printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n",
+ wiphy_name(ar->hw->wiphy), reg, val);
+ return err;
+}
+
+static int ar9170_read_mreg(struct ar9170 *ar, int nregs,
+ const u32 *regs, u32 *out)
+{
+ int i, err;
+ __le32 *offs, *res;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ /* abuse "out" for the register offsets, must be same length */
+ offs = (__le32 *)out;
+ for (i = 0; i < nregs; i++)
+ offs[i] = cpu_to_le32(regs[i]);
+
+ /* also use the same buffer for the input */
+ res = (__le32 *)out;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+ 4 * nregs, (u8 *)offs,
+ 4 * nregs, (u8 *)res);
+ if (err)
+ return err;
+
+ /* convert result to cpu endian */
+ for (i = 0; i < nregs; i++)
+ out[i] = le32_to_cpu(res[i]);
+
+ return 0;
+}
+
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
+{
+ return ar9170_read_mreg(ar, 1, ®, val);
+}
+
+int ar9170_echo_test(struct ar9170 *ar, u32 v)
+{
+ __le32 echobuf = cpu_to_le32(v);
+ __le32 echores;
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return -ENODEV;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_ECHO,
+ 4, (u8 *)&echobuf,
+ 4, (u8 *)&echores);
+ if (err)
+ return err;
+
+ if (echobuf != echores)
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/trunk/drivers/net/wireless/ar9170/cmd.h b/trunk/drivers/net/wireless/ar9170/cmd.h
new file mode 100644
index 000000000000..a4f0e50e52b4
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/cmd.h
@@ -0,0 +1,91 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CMD_H
+#define __CMD_H
+
+#include "ar9170.h"
+
+/* basic HW access */
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len);
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val);
+int ar9170_echo_test(struct ar9170 *ar, u32 v);
+
+/*
+ * Macros to facilitate writing multiple registers in a single
+ * write-combining USB command. Note that when the first group
+ * fails the whole thing will fail without any others attempted,
+ * but you won't know which write in the group failed.
+ */
+#define ar9170_regwrite_begin(ar) \
+do { \
+ int __nreg = 0, __err = 0; \
+ struct ar9170 *__ar = ar;
+
+#define ar9170_regwrite(r, v) do { \
+ __ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r); \
+ __ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v); \
+ __nreg++; \
+ if ((__nreg >= PAYLOAD_MAX/2)) { \
+ if (IS_ACCEPTING_CMD(__ar)) \
+ __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
+ 8 * __nreg, \
+ (u8 *) &__ar->cmdbuf[1], \
+ 0, NULL); \
+ __nreg = 0; \
+ if (__err) \
+ goto __regwrite_out; \
+ } \
+} while (0)
+
+#define ar9170_regwrite_finish() \
+__regwrite_out : \
+ if (__nreg) { \
+ if (IS_ACCEPTING_CMD(__ar)) \
+ __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
+ 8 * __nreg, \
+ (u8 *) &__ar->cmdbuf[1], \
+ 0, NULL); \
+ __nreg = 0; \
+ }
+
+#define ar9170_regwrite_result() \
+ __err; \
+} while (0);
+
+#endif /* __CMD_H */
diff --git a/trunk/drivers/net/wireless/ar9170/eeprom.h b/trunk/drivers/net/wireless/ar9170/eeprom.h
new file mode 100644
index 000000000000..d2c8cc83f1dd
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/eeprom.h
@@ -0,0 +1,179 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * EEPROM layout
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_EEPROM_H
+#define __AR9170_EEPROM_H
+
+#define AR5416_MAX_CHAINS 2
+#define AR5416_MODAL_SPURS 5
+
+struct ar9170_eeprom_modal {
+ __le32 antCtrlChain[AR5416_MAX_CHAINS];
+ __le32 antCtrlCommon;
+ s8 antennaGainCh[AR5416_MAX_CHAINS];
+ u8 switchSettling;
+ u8 txRxAttenCh[AR5416_MAX_CHAINS];
+ u8 rxTxMarginCh[AR5416_MAX_CHAINS];
+ s8 adcDesiredSize;
+ s8 pgaDesiredSize;
+ u8 xlnaGainCh[AR5416_MAX_CHAINS];
+ u8 txEndToXpaOff;
+ u8 txEndToRxOn;
+ u8 txFrameToXpaOn;
+ u8 thresh62;
+ s8 noiseFloorThreshCh[AR5416_MAX_CHAINS];
+ u8 xpdGain;
+ u8 xpd;
+ s8 iqCalICh[AR5416_MAX_CHAINS];
+ s8 iqCalQCh[AR5416_MAX_CHAINS];
+ u8 pdGainOverlap;
+ u8 ob;
+ u8 db;
+ u8 xpaBiasLvl;
+ u8 pwrDecreaseFor2Chain;
+ u8 pwrDecreaseFor3Chain;
+ u8 txFrameToDataStart;
+ u8 txFrameToPaOn;
+ u8 ht40PowerIncForPdadc;
+ u8 bswAtten[AR5416_MAX_CHAINS];
+ u8 bswMargin[AR5416_MAX_CHAINS];
+ u8 swSettleHt40;
+ u8 reserved[22];
+ struct spur_channel {
+ __le16 spurChan;
+ u8 spurRangeLow;
+ u8 spurRangeHigh;
+ } __packed spur_channels[AR5416_MODAL_SPURS];
+} __packed;
+
+#define AR5416_NUM_PD_GAINS 4
+#define AR5416_PD_GAIN_ICEPTS 5
+
+struct ar9170_calibration_data_per_freq {
+ u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+ u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+} __packed;
+
+#define AR5416_NUM_5G_CAL_PIERS 8
+#define AR5416_NUM_2G_CAL_PIERS 4
+
+#define AR5416_NUM_5G_TARGET_PWRS 8
+#define AR5416_NUM_2G_CCK_TARGET_PWRS 3
+#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4
+#define AR5416_MAX_NUM_TGT_PWRS 8
+
+struct ar9170_calibration_target_power_legacy {
+ u8 freq;
+ u8 power[4];
+} __packed;
+
+struct ar9170_calibration_target_power_ht {
+ u8 freq;
+ u8 power[8];
+} __packed;
+
+#define AR5416_NUM_CTLS 24
+
+struct ar9170_calctl_edges {
+ u8 channel;
+#define AR9170_CALCTL_EDGE_FLAGS 0xC0
+ u8 power_flags;
+} __packed;
+
+#define AR5416_NUM_BAND_EDGES 8
+
+struct ar9170_calctl_data {
+ struct ar9170_calctl_edges
+ control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+} __packed;
+
+
+struct ar9170_eeprom {
+ __le16 length;
+ __le16 checksum;
+ __le16 version;
+ u8 operating_flags;
+#define AR9170_OPFLAG_5GHZ 1
+#define AR9170_OPFLAG_2GHZ 2
+ u8 misc;
+ __le16 reg_domain[2];
+ u8 mac_address[6];
+ u8 rx_mask;
+ u8 tx_mask;
+ __le16 rf_silent;
+ __le16 bluetooth_options;
+ __le16 device_capabilities;
+ __le32 build_number;
+ u8 deviceType;
+ u8 reserved[33];
+
+ u8 customer_data[64];
+
+ struct ar9170_eeprom_modal
+ modal_header[2];
+
+ u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
+ u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
+
+ struct ar9170_calibration_data_per_freq
+ cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
+ cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
+
+ /* power calibration data */
+ struct ar9170_calibration_target_power_legacy
+ cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
+ struct ar9170_calibration_target_power_ht
+ cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
+ cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
+
+ struct ar9170_calibration_target_power_legacy
+ cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
+ cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+ struct ar9170_calibration_target_power_ht
+ cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
+ cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+
+ /* conformance testing limits */
+ u8 ctl_index[AR5416_NUM_CTLS];
+ struct ar9170_calctl_data
+ ctl_data[AR5416_NUM_CTLS];
+
+ u8 pad;
+ __le16 subsystem_id;
+} __packed;
+
+#endif /* __AR9170_EEPROM_H */
diff --git a/trunk/drivers/net/wireless/ar9170/hw.h b/trunk/drivers/net/wireless/ar9170/hw.h
new file mode 100644
index 000000000000..13091bd9d815
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/hw.h
@@ -0,0 +1,417 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Hardware-specific definitions
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_HW_H
+#define __AR9170_HW_H
+
+#define AR9170_MAX_CMD_LEN 64
+
+enum ar9170_cmd {
+ AR9170_CMD_RREG = 0x00,
+ AR9170_CMD_WREG = 0x01,
+ AR9170_CMD_RMEM = 0x02,
+ AR9170_CMD_WMEM = 0x03,
+ AR9170_CMD_BITAND = 0x04,
+ AR9170_CMD_BITOR = 0x05,
+ AR9170_CMD_EKEY = 0x28,
+ AR9170_CMD_DKEY = 0x29,
+ AR9170_CMD_FREQUENCY = 0x30,
+ AR9170_CMD_RF_INIT = 0x31,
+ AR9170_CMD_SYNTH = 0x32,
+ AR9170_CMD_FREQ_START = 0x33,
+ AR9170_CMD_ECHO = 0x80,
+ AR9170_CMD_TALLY = 0x81,
+ AR9170_CMD_TALLY_APD = 0x82,
+ AR9170_CMD_CONFIG = 0x83,
+ AR9170_CMD_RESET = 0x90,
+ AR9170_CMD_DKRESET = 0x91,
+ AR9170_CMD_DKTX_STATUS = 0x92,
+ AR9170_CMD_FDC = 0xA0,
+ AR9170_CMD_WREEPROM = 0xB0,
+ AR9170_CMD_WFLASH = 0xB0,
+ AR9170_CMD_FLASH_ERASE = 0xB1,
+ AR9170_CMD_FLASH_PROG = 0xB2,
+ AR9170_CMD_FLASH_CHKSUM = 0xB3,
+ AR9170_CMD_FLASH_READ = 0xB4,
+ AR9170_CMD_FW_DL_INIT = 0xB5,
+ AR9170_CMD_MEM_WREEPROM = 0xBB,
+};
+
+/* endpoints */
+#define AR9170_EP_TX 1
+#define AR9170_EP_RX 2
+#define AR9170_EP_IRQ 3
+#define AR9170_EP_CMD 4
+
+#define AR9170_EEPROM_START 0x1600
+
+#define AR9170_GPIO_REG_BASE 0x1d0100
+#define AR9170_GPIO_REG_PORT_TYPE AR9170_GPIO_REG_BASE
+#define AR9170_GPIO_REG_DATA (AR9170_GPIO_REG_BASE + 4)
+#define AR9170_NUM_LEDS 2
+
+
+#define AR9170_USB_REG_BASE 0x1e1000
+#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108)
+#define AR9170_DMA_CTL_ENABLE_TO_DEVICE 0x1
+#define AR9170_DMA_CTL_ENABLE_FROM_DEVICE 0x2
+#define AR9170_DMA_CTL_HIGH_SPEED 0x4
+#define AR9170_DMA_CTL_PACKET_MODE 0x8
+
+#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110)
+#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114)
+
+
+
+#define AR9170_MAC_REG_BASE 0x1c3000
+
+#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514)
+#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518)
+
+#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51C)
+#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520)
+#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524)
+
+#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610)
+#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614)
+#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618)
+#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c)
+
+#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624)
+#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628)
+
+#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62C)
+
+#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630)
+#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634)
+#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638)
+#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c)
+#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640)
+#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64C)
+
+#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658)
+#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674)
+#define AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC BIT(0)
+#define AR9170_MAC_REG_SNIFFER_DEFAULTS 0x02000000
+#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678)
+#define AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE BIT(3)
+#define AR9170_MAC_REG_ENCRYPTION_DEFAULTS 0x70
+
+#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680)
+#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688)
+
+#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c)
+#define AR9170_MAC_REG_FTF_ASSOC_REQ BIT(0)
+#define AR9170_MAC_REG_FTF_ASSOC_RESP BIT(1)
+#define AR9170_MAC_REG_FTF_REASSOC_REQ BIT(2)
+#define AR9170_MAC_REG_FTF_REASSOC_RESP BIT(3)
+#define AR9170_MAC_REG_FTF_PRB_REQ BIT(4)
+#define AR9170_MAC_REG_FTF_PRB_RESP BIT(5)
+#define AR9170_MAC_REG_FTF_BIT6 BIT(6)
+#define AR9170_MAC_REG_FTF_BIT7 BIT(7)
+#define AR9170_MAC_REG_FTF_BEACON BIT(8)
+#define AR9170_MAC_REG_FTF_ATIM BIT(9)
+#define AR9170_MAC_REG_FTF_DEASSOC BIT(10)
+#define AR9170_MAC_REG_FTF_AUTH BIT(11)
+#define AR9170_MAC_REG_FTF_DEAUTH BIT(12)
+#define AR9170_MAC_REG_FTF_BIT13 BIT(13)
+#define AR9170_MAC_REG_FTF_BIT14 BIT(14)
+#define AR9170_MAC_REG_FTF_BIT15 BIT(15)
+#define AR9170_MAC_REG_FTF_BAR BIT(24)
+#define AR9170_MAC_REG_FTF_BIT25 BIT(25)
+#define AR9170_MAC_REG_FTF_PSPOLL BIT(26)
+#define AR9170_MAC_REG_FTF_RTS BIT(27)
+#define AR9170_MAC_REG_FTF_CTS BIT(28)
+#define AR9170_MAC_REG_FTF_ACK BIT(29)
+#define AR9170_MAC_REG_FTF_CFE BIT(30)
+#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31)
+#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff
+#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff
+
+#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0)
+#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6A4)
+#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6A8)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6AC)
+#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6B0)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6BC)
+#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6CC)
+#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6F4)
+
+
+#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690)
+#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698)
+
+#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6F0)
+
+#define AR9170_MAC_REG_POWERMANAGEMENT (AR9170_MAC_REG_BASE + 0x700)
+#define AR9170_MAC_REG_POWERMGT_IBSS 0xe0
+#define AR9170_MAC_REG_POWERMGT_AP 0xa1
+#define AR9170_MAC_REG_POWERMGT_STA 0x2
+#define AR9170_MAC_REG_POWERMGT_AP_WDS 0x3
+#define AR9170_MAC_REG_POWERMGT_DEFAULTS (0xf << 24)
+
+#define AR9170_MAC_REG_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704)
+#define AR9170_MAC_REG_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708)
+
+#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xB00)
+#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xB04)
+#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xB08)
+#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xB0C)
+#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xB10)
+#define AR9170_MAC_REG_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xB14)
+#define AR9170_MAC_REG_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xB18)
+
+#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xB28)
+
+#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xBB0)
+#define AR9170_MAC_FCS_SWFCS 0x1
+#define AR9170_MAC_FCS_FIFO_PROT 0x4
+
+
+#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xB30)
+
+#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xB44)
+#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xB48)
+
+#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xC00)
+#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xC50)
+
+#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xD7C)
+#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f
+#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0
+#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000
+#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000
+
+#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xD84)
+#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xD88)
+#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xD90)
+#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xD94)
+#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xDA0)
+#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xDA4)
+
+
+#define AR9170_PWR_REG_BASE 0x1D4000
+
+#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008)
+#define AR9170_PWR_CLK_AHB_40MHZ 0
+#define AR9170_PWR_CLK_AHB_20_22MHZ 1
+#define AR9170_PWR_CLK_AHB_40_44MHZ 2
+#define AR9170_PWR_CLK_AHB_80_88MHZ 3
+#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70
+
+
+/* put beacon here in memory */
+#define AR9170_BEACON_BUFFER_ADDRESS 0x117900
+
+
+struct ar9170_tx_control {
+ __le16 length;
+ __le16 mac_control;
+ __le32 phy_control;
+ u8 frame_data[0];
+} __packed;
+
+/* these are either-or */
+#define AR9170_TX_MAC_PROT_RTS 0x0001
+#define AR9170_TX_MAC_PROT_CTS 0x0002
+
+#define AR9170_TX_MAC_NO_ACK 0x0004
+/* if unset, MAC will only do SIFS space before frame */
+#define AR9170_TX_MAC_BACKOFF 0x0008
+#define AR9170_TX_MAC_BURST 0x0010
+#define AR9170_TX_MAC_AGGR 0x0020
+
+/* encryption is a two-bit field */
+#define AR9170_TX_MAC_ENCR_NONE 0x0000
+#define AR9170_TX_MAC_ENCR_RC4 0x0040
+#define AR9170_TX_MAC_ENCR_CENC 0x0080
+#define AR9170_TX_MAC_ENCR_AES 0x00c0
+
+#define AR9170_TX_MAC_MMIC 0x0100
+#define AR9170_TX_MAC_HW_DURATION 0x0200
+#define AR9170_TX_MAC_QOS_SHIFT 10
+#define AR9170_TX_MAC_QOS_MASK (3 << AR9170_TX_MAC_QOS_SHIFT)
+#define AR9170_TX_MAC_AGGR_QOS_BIT1 0x0400
+#define AR9170_TX_MAC_AGGR_QOS_BIT2 0x0800
+#define AR9170_TX_MAC_DISABLE_TXOP 0x1000
+#define AR9170_TX_MAC_TXOP_RIFS 0x2000
+#define AR9170_TX_MAC_IMM_AMPDU 0x4000
+#define AR9170_TX_MAC_RATE_PROBE 0x8000
+
+/* either-or */
+#define AR9170_TX_PHY_MOD_CCK 0x00000000
+#define AR9170_TX_PHY_MOD_OFDM 0x00000001
+#define AR9170_TX_PHY_MOD_HT 0x00000002
+
+/* depends on modulation */
+#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004
+#define AR9170_TX_PHY_GREENFIELD 0x00000004
+
+#define AR9170_TX_PHY_BW_SHIFT 3
+#define AR9170_TX_PHY_BW_MASK (3 << AR9170_TX_PHY_BW_SHIFT)
+#define AR9170_TX_PHY_BW_20MHZ 0
+#define AR9170_TX_PHY_BW_40MHZ 2
+#define AR9170_TX_PHY_BW_40MHZ_DUP 3
+
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT 6
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT)
+
+#define AR9170_TX_PHY_TX_PWR_SHIFT 9
+#define AR9170_TX_PHY_TX_PWR_MASK (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT)
+
+/* not part of the hw-spec */
+#define AR9170_TX_PHY_QOS_SHIFT 25
+#define AR9170_TX_PHY_QOS_MASK (3 << AR9170_TX_PHY_QOS_SHIFT)
+
+#define AR9170_TX_PHY_TXCHAIN_SHIFT 15
+#define AR9170_TX_PHY_TXCHAIN_MASK (7 << AR9170_TX_PHY_TXCHAIN_SHIFT)
+#define AR9170_TX_PHY_TXCHAIN_1 1
+/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
+#define AR9170_TX_PHY_TXCHAIN_2 5
+
+#define AR9170_TX_PHY_MCS_SHIFT 18
+#define AR9170_TX_PHY_MCS_MASK (0x7f << AR9170_TX_PHY_MCS_SHIFT)
+
+#define AR9170_TX_PHY_SHORT_GI 0x80000000
+
+struct ar9170_rx_head {
+ u8 plcp[12];
+};
+
+struct ar9170_rx_tail {
+ union {
+ struct {
+ u8 rssi_ant0, rssi_ant1, rssi_ant2,
+ rssi_ant0x, rssi_ant1x, rssi_ant2x,
+ rssi_combined;
+ };
+ u8 rssi[7];
+ };
+
+ u8 evm_stream0[6], evm_stream1[6];
+ u8 phy_err;
+ u8 SAidx, DAidx;
+ u8 error;
+ u8 status;
+};
+
+#define AR9170_ENC_ALG_NONE 0x0
+#define AR9170_ENC_ALG_WEP64 0x1
+#define AR9170_ENC_ALG_TKIP 0x2
+#define AR9170_ENC_ALG_AESCCMP 0x4
+#define AR9170_ENC_ALG_WEP128 0x5
+#define AR9170_ENC_ALG_WEP256 0x6
+#define AR9170_ENC_ALG_CENC 0x7
+
+#define AR9170_RX_ENC_SOFTWARE 0x8
+
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_tail *t)
+{
+ return (t->SAidx & 0xc0) >> 4 |
+ (t->DAidx & 0xc0) >> 6;
+}
+
+#define AR9170_RX_STATUS_MODULATION_MASK 0x03
+#define AR9170_RX_STATUS_MODULATION_CCK 0x00
+#define AR9170_RX_STATUS_MODULATION_OFDM 0x01
+#define AR9170_RX_STATUS_MODULATION_HT 0x02
+#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03
+
+/* depends on modulation */
+#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08
+#define AR9170_RX_STATUS_GREENFIELD 0x08
+
+#define AR9170_RX_STATUS_MPDU_MASK 0x30
+#define AR9170_RX_STATUS_MPDU_SINGLE 0x00
+#define AR9170_RX_STATUS_MPDU_FIRST 0x10
+#define AR9170_RX_STATUS_MPDU_MIDDLE 0x20
+#define AR9170_RX_STATUS_MPDU_LAST 0x30
+
+
+#define AR9170_RX_ERROR_RXTO 0x01
+#define AR9170_RX_ERROR_OVERRUN 0x02
+#define AR9170_RX_ERROR_DECRYPT 0x04
+#define AR9170_RX_ERROR_FCS 0x08
+#define AR9170_RX_ERROR_WRONG_RA 0x10
+#define AR9170_RX_ERROR_PLCP 0x20
+#define AR9170_RX_ERROR_MMIC 0x40
+
+struct ar9170_cmd_tx_status {
+ __le16 unkn;
+ u8 dst[ETH_ALEN];
+ __le32 rate;
+ __le16 status;
+} __packed;
+
+#define AR9170_TX_STATUS_COMPLETE 0x00
+#define AR9170_TX_STATUS_RETRY 0x01
+#define AR9170_TX_STATUS_FAILED 0x02
+
+struct ar9170_cmd_ba_failed_count {
+ __le16 failed;
+ __le16 rate;
+} __packed;
+
+struct ar9170_cmd_response {
+ u8 flag;
+ u8 type;
+
+ union {
+ struct ar9170_cmd_tx_status tx_status;
+ struct ar9170_cmd_ba_failed_count ba_fail_cnt;
+ u8 data[0];
+ };
+} __packed;
+
+/* QoS */
+
+/* mac80211 queue to HW/FW map */
+static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 };
+
+/* HW/FW queue to mac80211 map */
+static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 };
+
+enum ar9170_txq {
+ AR9170_TXQ_BE,
+ AR9170_TXQ_BK,
+ AR9170_TXQ_VI,
+ AR9170_TXQ_VO,
+
+ __AR9170_NUM_TXQ,
+};
+
+#endif /* __AR9170_HW_H */
diff --git a/trunk/drivers/net/wireless/ar9170/led.c b/trunk/drivers/net/wireless/ar9170/led.c
new file mode 100644
index 000000000000..341cead7f606
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/led.c
@@ -0,0 +1,171 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * LED handling
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state)
+{
+ return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state);
+}
+
+int ar9170_init_leds(struct ar9170 *ar)
+{
+ int err;
+
+ /* disable LEDs */
+ /* GPIO [0/1 mode: output, 2/3: input] */
+ err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
+ if (err)
+ goto out;
+
+ /* GPIO 0/1 value: off */
+ err = ar9170_set_leds_state(ar, 0);
+
+out:
+ return err;
+}
+
+#ifdef CONFIG_AR9170_LEDS
+static void ar9170_update_leds(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
+ int i, tmp, blink_delay = 1000;
+ u32 led_val = 0;
+ bool rerun = false;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+ for (i = 0; i < AR9170_NUM_LEDS; i++)
+ if (ar->leds[i].toggled) {
+ led_val |= 1 << i;
+
+ tmp = 70 + 200 / (ar->leds[i].toggled);
+ if (tmp < blink_delay)
+ blink_delay = tmp;
+
+ if (ar->leds[i].toggled > 1)
+ ar->leds[i].toggled = 0;
+
+ rerun = true;
+ }
+
+ ar9170_set_leds_state(ar, led_val);
+ mutex_unlock(&ar->mutex);
+
+ if (rerun)
+ queue_delayed_work(ar->hw->workqueue, &ar->led_work,
+ msecs_to_jiffies(blink_delay));
+}
+
+static void ar9170_led_brightness_set(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct ar9170_led *arl = container_of(led, struct ar9170_led, l);
+ struct ar9170 *ar = arl->ar;
+
+ arl->toggled++;
+
+ if (likely(IS_ACCEPTING_CMD(ar) && brightness))
+ queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10);
+}
+
+static int ar9170_register_led(struct ar9170 *ar, int i, char *name,
+ char *trigger)
+{
+ int err;
+
+ snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
+ "ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
+
+ ar->leds[i].ar = ar;
+ ar->leds[i].l.name = ar->leds[i].name;
+ ar->leds[i].l.brightness_set = ar9170_led_brightness_set;
+ ar->leds[i].l.brightness = 0;
+ ar->leds[i].l.default_trigger = trigger;
+
+ err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
+ &ar->leds[i].l);
+ if (err)
+ printk(KERN_ERR "%s: failed to register %s LED (%d).\n",
+ wiphy_name(ar->hw->wiphy), ar->leds[i].name, err);
+ else
+ ar->leds[i].registered = true;
+
+ return err;
+}
+
+void ar9170_unregister_leds(struct ar9170 *ar)
+{
+ int i;
+
+ cancel_delayed_work_sync(&ar->led_work);
+
+ for (i = 0; i < AR9170_NUM_LEDS; i++)
+ if (ar->leds[i].registered) {
+ led_classdev_unregister(&ar->leds[i].l);
+ ar->leds[i].registered = false;
+ }
+}
+
+int ar9170_register_leds(struct ar9170 *ar)
+{
+ int err;
+
+ INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds);
+
+ err = ar9170_register_led(ar, 0, "tx",
+ ieee80211_get_tx_led_name(ar->hw));
+ if (err)
+ goto fail;
+
+ err = ar9170_register_led(ar, 1, "assoc",
+ ieee80211_get_assoc_led_name(ar->hw));
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ ar9170_unregister_leds(ar);
+ return err;
+}
+
+#endif /* CONFIG_AR9170_LEDS */
diff --git a/trunk/drivers/net/wireless/ar9170/mac.c b/trunk/drivers/net/wireless/ar9170/mac.c
new file mode 100644
index 000000000000..c8fa3073169f
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/mac.c
@@ -0,0 +1,452 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * MAC programming
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_qos(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
+ (ar->edcf[0].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
+ (ar->edcf[1].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
+ (ar->edcf[2].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
+ (ar->edcf[3].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
+ (ar->edcf[4].cw_max << 16));
+
+ ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS,
+ ((ar->edcf[0].aifs * 9 + 10)) |
+ ((ar->edcf[1].aifs * 9 + 10) << 12) |
+ ((ar->edcf[2].aifs * 9 + 10) << 24));
+ ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS,
+ ((ar->edcf[2].aifs * 9 + 10) >> 8) |
+ ((ar->edcf[3].aifs * 9 + 10) << 4) |
+ ((ar->edcf[4].aifs * 9 + 10) << 16));
+
+ ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
+ ar->edcf[0].txop | ar->edcf[1].txop << 16);
+ ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
+ ar->edcf[1].txop | ar->edcf[3].txop << 16);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_init_mac(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
+
+ ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0);
+
+ /* enable MMIC */
+ ar9170_regwrite(AR9170_MAC_REG_SNIFFER,
+ AR9170_MAC_REG_SNIFFER_DEFAULTS);
+
+ ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
+
+ ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
+ ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
+ ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
+
+ /* CF-END mode */
+ ar9170_regwrite(0x1c3b2c, 0x19000000);
+
+ /* NAV protects ACK only (in TXOP) */
+ ar9170_regwrite(0x1c3b38, 0x201);
+
+ /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
+ /* OTUS set AM to 0x1 */
+ ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
+
+ ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
+
+ /* AGG test code*/
+ /* Aggregation MAX number and timeout */
+ ar9170_regwrite(0x1c3b9c, 0x10000a);
+
+ ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+ AR9170_MAC_REG_FTF_DEFAULTS);
+
+ /* Enable deaggregator, response in sniffer mode */
+ ar9170_regwrite(0x1c3c40, 0x1 | 1<<30);
+
+ /* rate sets */
+ ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
+ ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
+ ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
+
+ /* MIMO response control */
+ ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28 otus-AM */
+
+ /* switch MAC to OTUS interface */
+ ar9170_regwrite(0x1c3600, 0x3);
+
+ ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
+
+ /* set PHY register read timeout (??) */
+ ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
+
+ /* Disable Rx TimeOut, workaround for BB. */
+ ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
+
+ /* Set CPU clock frequency to 88/80MHz */
+ ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL,
+ AR9170_PWR_CLK_AHB_80_88MHZ |
+ AR9170_PWR_CLK_DAC_160_INV_DLY);
+
+ /* Set WLAN DMA interrupt mode: generate int per packet */
+ ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
+
+ ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
+ AR9170_MAC_FCS_FIFO_PROT);
+
+ /* Disables the CF_END frame, undocumented register */
+ ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
+ 0x141E0F48);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac)
+{
+ static const u8 zero[ETH_ALEN] = { 0 };
+
+ if (!mac)
+ mac = zero;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(reg,
+ (mac[3] << 24) | (mac[2] << 16) |
+ (mac[1] << 8) | mac[0]);
+
+ ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_update_multicast(struct ar9170 *ar)
+{
+ int err;
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H,
+ ar->want_mc_hash >> 32);
+ ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L,
+ ar->want_mc_hash);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+
+ if (err)
+ return err;
+
+ ar->cur_mc_hash = ar->want_mc_hash;
+
+ return 0;
+}
+
+int ar9170_update_frame_filter(struct ar9170 *ar)
+{
+ int err;
+
+ err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER,
+ ar->want_filter);
+
+ if (err)
+ return err;
+
+ ar->cur_filter = ar->want_filter;
+
+ return 0;
+}
+
+static int ar9170_set_promiscouous(struct ar9170 *ar)
+{
+ u32 encr_mode, sniffer;
+ int err;
+
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer);
+ if (err)
+ return err;
+
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode);
+ if (err)
+ return err;
+
+ if (ar->sniffer_enabled) {
+ sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+ /*
+ * Rx decryption works in place.
+ *
+ * If we don't disable it, the hardware will render all
+ * encrypted frames which are encrypted with an unknown
+ * key useless.
+ */
+
+ encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ ar->sniffer_enabled = true;
+ } else {
+ sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+ if (ar->rx_software_decryption)
+ encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ else
+ encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ }
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode);
+ ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_set_operating_mode(struct ar9170 *ar)
+{
+ u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS;
+ u8 *mac_addr, *bssid;
+ int err;
+
+ if (ar->vif) {
+ mac_addr = ar->mac_addr;
+ bssid = ar->bssid;
+
+ switch (ar->vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS;
+ break;
+/* case NL80211_IFTYPE_AP:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_AP;
+ break;*/
+ case NL80211_IFTYPE_WDS:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ ar->sniffer_enabled = true;
+ ar->rx_software_decryption = true;
+ break;
+ default:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_STA;
+ break;
+ }
+ } else {
+ mac_addr = NULL;
+ bssid = NULL;
+ }
+
+ err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
+ if (err)
+ return err;
+
+ err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
+ if (err)
+ return err;
+
+ err = ar9170_set_promiscouous(ar);
+ if (err)
+ return err;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode);
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry)
+{
+ u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
+
+ return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
+}
+
+int ar9170_set_beacon_timers(struct ar9170 *ar)
+{
+ u32 v = 0;
+ u32 pretbtt = 0;
+
+ v |= ar->hw->conf.beacon_int;
+
+ if (ar->vif) {
+ switch (ar->vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ v |= BIT(25);
+ break;
+ case NL80211_IFTYPE_AP:
+ v |= BIT(24);
+ pretbtt = (ar->hw->conf.beacon_int - 6) << 16;
+ break;
+ default:
+ break;
+ }
+
+ v |= ar->vif->bss_conf.dtim_period << 16;
+ }
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+int ar9170_update_beacon(struct ar9170 *ar)
+{
+ struct sk_buff *skb;
+ __le32 *data, *old = NULL;
+ u32 word;
+ int i;
+
+ skb = ieee80211_beacon_get(ar->hw, ar->vif);
+ if (!skb)
+ return -ENOMEM;
+
+ data = (__le32 *)skb->data;
+ if (ar->beacon)
+ old = (__le32 *)ar->beacon->data;
+
+ ar9170_regwrite_begin(ar);
+ for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+ /*
+ * XXX: This accesses beyond skb data for up
+ * to the last 3 bytes!!
+ */
+
+ if (old && (data[i] == old[i]))
+ continue;
+
+ word = le32_to_cpu(data[i]);
+ ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word);
+ }
+
+ /* XXX: use skb->cb info */
+ if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+ ((skb->len + 4) << (3+16)) + 0x0400);
+ else
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+ ((skb->len + 4) << (3+16)) + 0x0400);
+
+ ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1);
+
+ ar9170_regwrite_finish();
+
+ dev_kfree_skb(ar->beacon);
+ ar->beacon = skb;
+
+ return ar9170_regwrite_result();
+}
+
+void ar9170_new_beacon(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ beacon_work);
+ struct sk_buff *skb;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+
+ if (!ar->vif)
+ goto out;
+
+ ar9170_update_beacon(ar);
+
+ rcu_read_lock();
+ while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif)))
+ ar9170_op_tx(ar->hw, skb);
+
+ rcu_read_unlock();
+
+ out:
+ mutex_unlock(&ar->mutex);
+}
+
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+ u8 keyidx, u8 *keydata, int keylen)
+{
+ __le32 vals[7];
+ static const u8 bcast[ETH_ALEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ u8 dummy;
+
+ mac = mac ? : bcast;
+
+ vals[0] = cpu_to_le32((keyidx << 16) + id);
+ vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype);
+ vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 |
+ mac[3] << 8 | mac[2]);
+ memset(&vals[3], 0, 16);
+ if (keydata)
+ memcpy(&vals[3], keydata, keylen);
+
+ return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+ sizeof(vals), (u8 *)vals,
+ 1, &dummy);
+}
+
+int ar9170_disable_key(struct ar9170 *ar, u8 id)
+{
+ __le32 val = cpu_to_le32(id);
+ u8 dummy;
+
+ return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+ sizeof(val), (u8 *)&val,
+ 1, &dummy);
+}
diff --git a/trunk/drivers/net/wireless/ar9170/main.c b/trunk/drivers/net/wireless/ar9170/main.c
new file mode 100644
index 000000000000..5996ff9f7f47
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/main.c
@@ -0,0 +1,1671 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * mac80211 interaction code
+ *
+ * Copyright 2008, Johannes Berg
+ * Copyright 2009, Christian Lamparter
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include
+#include "ar9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate) | (_txpidx) << 4, \
+}
+
+static struct ieee80211_rate __ar9170_ratetable[] = {
+ RATE(10, 0, 0, 0),
+ RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0xb, 0, 0),
+ RATE(90, 0xf, 0, 0),
+ RATE(120, 0xa, 0, 0),
+ RATE(180, 0xe, 0, 0),
+ RATE(240, 0x9, 0, 0),
+ RATE(360, 0xd, 1, 0),
+ RATE(480, 0x8, 2, 0),
+ RATE(540, 0xc, 3, 0),
+};
+#undef RATE
+
+#define ar9170_g_ratetable (__ar9170_ratetable + 0)
+#define ar9170_g_ratetable_size 12
+#define ar9170_a_ratetable (__ar9170_ratetable + 4)
+#define ar9170_a_ratetable_size 8
+
+/*
+ * NB: The hw_value is used as an index into the ar9170_phy_freq_params
+ * array in phy.c so that we don't have to do frequency lookups!
+ */
+#define CHAN(_freq, _idx) { \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 18, /* XXX */ \
+}
+
+static struct ieee80211_channel ar9170_2ghz_chantable[] = {
+ CHAN(2412, 0),
+ CHAN(2417, 1),
+ CHAN(2422, 2),
+ CHAN(2427, 3),
+ CHAN(2432, 4),
+ CHAN(2437, 5),
+ CHAN(2442, 6),
+ CHAN(2447, 7),
+ CHAN(2452, 8),
+ CHAN(2457, 9),
+ CHAN(2462, 10),
+ CHAN(2467, 11),
+ CHAN(2472, 12),
+ CHAN(2484, 13),
+};
+
+static struct ieee80211_channel ar9170_5ghz_chantable[] = {
+ CHAN(4920, 14),
+ CHAN(4940, 15),
+ CHAN(4960, 16),
+ CHAN(4980, 17),
+ CHAN(5040, 18),
+ CHAN(5060, 19),
+ CHAN(5080, 20),
+ CHAN(5180, 21),
+ CHAN(5200, 22),
+ CHAN(5220, 23),
+ CHAN(5240, 24),
+ CHAN(5260, 25),
+ CHAN(5280, 26),
+ CHAN(5300, 27),
+ CHAN(5320, 28),
+ CHAN(5500, 29),
+ CHAN(5520, 30),
+ CHAN(5540, 31),
+ CHAN(5560, 32),
+ CHAN(5580, 33),
+ CHAN(5600, 34),
+ CHAN(5620, 35),
+ CHAN(5640, 36),
+ CHAN(5660, 37),
+ CHAN(5680, 38),
+ CHAN(5700, 39),
+ CHAN(5745, 40),
+ CHAN(5765, 41),
+ CHAN(5785, 42),
+ CHAN(5805, 43),
+ CHAN(5825, 44),
+ CHAN(5170, 45),
+ CHAN(5190, 46),
+ CHAN(5210, 47),
+ CHAN(5230, 48),
+};
+#undef CHAN
+
+static struct ieee80211_supported_band ar9170_band_2GHz = {
+ .channels = ar9170_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(ar9170_2ghz_chantable),
+ .bitrates = ar9170_g_ratetable,
+ .n_bitrates = ar9170_g_ratetable_size,
+};
+
+#ifdef AR9170_QUEUE_DEBUG
+/*
+ * In case some wants works with AR9170's crazy tx_status queueing techniques.
+ * He might need this rather useful probing function.
+ *
+ * NOTE: caller must hold the queue's spinlock!
+ */
+
+static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+ printk(KERN_DEBUG "%s: => FRAME [skb:%p, queue:%d, DA:[%pM] "
+ "mac_control:%04x, phy_control:%08x]\n",
+ wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
+ ieee80211_get_DA(hdr), le16_to_cpu(txc->mac_control),
+ le32_to_cpu(txc->phy_control));
+}
+
+static void ar9170_dump_station_tx_status_queue(struct ar9170 *ar,
+ struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ int i = 0;
+
+ printk(KERN_DEBUG "---[ cut here ]---\n");
+ printk(KERN_DEBUG "%s: %d entries in tx_status queue.\n",
+ wiphy_name(ar->hw->wiphy), skb_queue_len(queue));
+
+ skb_queue_walk(queue, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+ printk(KERN_DEBUG "index:%d => \n", i);
+ ar9170_print_txheader(ar, skb);
+ }
+ printk(KERN_DEBUG "---[ end ]---\n");
+}
+#endif /* AR9170_QUEUE_DEBUG */
+
+static struct ieee80211_supported_band ar9170_band_5GHz = {
+ .channels = ar9170_5ghz_chantable,
+ .n_channels = ARRAY_SIZE(ar9170_5ghz_chantable),
+ .bitrates = ar9170_a_ratetable,
+ .n_bitrates = ar9170_a_ratetable_size,
+};
+
+void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+ bool valid_status, u16 tx_status)
+{
+ struct ieee80211_tx_info *txinfo;
+ unsigned int retries = 0, queue = skb_get_queue_mapping(skb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ ar->tx_stats[queue].len--;
+ if (ieee80211_queue_stopped(ar->hw, queue))
+ ieee80211_wake_queue(ar->hw, queue);
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+ txinfo = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(txinfo);
+
+ switch (tx_status) {
+ case AR9170_TX_STATUS_RETRY:
+ retries = 2;
+ case AR9170_TX_STATUS_COMPLETE:
+ txinfo->flags |= IEEE80211_TX_STAT_ACK;
+ break;
+
+ case AR9170_TX_STATUS_FAILED:
+ retries = ar->hw->conf.long_frame_max_tx_count;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: invalid tx_status response (%x).\n",
+ wiphy_name(ar->hw->wiphy), tx_status);
+ break;
+ }
+
+ if (valid_status)
+ txinfo->status.rates[0].count = retries + 1;
+
+ skb_pull(skb, sizeof(struct ar9170_tx_control));
+ ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+
+static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar,
+ const u8 *mac,
+ const u32 queue,
+ struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk(q, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+ u32 txc_queue = (le32_to_cpu(txc->phy_control) &
+ AR9170_TX_PHY_QOS_MASK) >>
+ AR9170_TX_PHY_QOS_SHIFT;
+
+ if ((queue != txc_queue) ||
+ (compare_ether_addr(ieee80211_get_DA(hdr), mac)))
+ continue;
+
+ __skb_unlink(skb, q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return skb;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ return NULL;
+}
+
+static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac,
+ const u32 queue)
+{
+ struct ieee80211_sta *sta;
+ struct sk_buff *skb;
+
+ /*
+ * Unfortunately, the firmware does not tell to which (queued) frame
+ * this transmission status report belongs to.
+ *
+ * So we have to make risky guesses - with the scarce information
+ * the firmware provided (-> destination MAC, and phy_control) -
+ * and hope that we picked the right one...
+ */
+ rcu_read_lock();
+ sta = ieee80211_find_sta(ar->hw, mac);
+
+ if (likely(sta)) {
+ struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv;
+ skb = skb_dequeue(&sta_priv->tx_status[queue]);
+ rcu_read_unlock();
+ if (likely(skb))
+ return skb;
+ } else
+ rcu_read_unlock();
+
+ /* scan the waste queue for candidates */
+ skb = ar9170_find_skb_in_queue(ar, mac, queue,
+ &ar->global_tx_status_waste);
+ if (!skb) {
+ /* so it still _must_ be in the global list. */
+ skb = ar9170_find_skb_in_queue(ar, mac, queue,
+ &ar->global_tx_status);
+ }
+
+#ifdef AR9170_QUEUE_DEBUG
+ if (unlikely((!skb) && net_ratelimit())) {
+ printk(KERN_ERR "%s: ESS:[%pM] does not have any "
+ "outstanding frames in this queue (%d).\n",
+ wiphy_name(ar->hw->wiphy), mac, queue);
+ }
+#endif /* AR9170_QUEUE_DEBUG */
+ return skb;
+}
+
+/*
+ * This worker tries to keep the global tx_status queue empty.
+ * So we can guarantee that incoming tx_status reports for
+ * unregistered stations are always synced with the actual
+ * frame - which we think - belongs to.
+ */
+
+static void ar9170_tx_status_janitor(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ tx_status_janitor.work);
+ struct sk_buff *skb;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+ /* recycle the garbage back to mac80211... one by one. */
+ while ((skb = skb_dequeue(&ar->global_tx_status_waste))) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: dispose queued frame =>\n",
+ wiphy_name(ar->hw->wiphy));
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ ar9170_handle_tx_status(ar, skb, false,
+ AR9170_TX_STATUS_FAILED);
+ }
+
+ while ((skb = skb_dequeue(&ar->global_tx_status))) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: moving frame into waste queue =>\n",
+ wiphy_name(ar->hw->wiphy));
+
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ skb_queue_tail(&ar->global_tx_status_waste, skb);
+ }
+
+ /* recall the janitor in 100ms - if there's garbage in the can. */
+ if (skb_queue_len(&ar->global_tx_status_waste) > 0)
+ queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
+ msecs_to_jiffies(100));
+
+ mutex_unlock(&ar->mutex);
+}
+
+static void ar9170_handle_command_response(struct ar9170 *ar,
+ void *buf, u32 len)
+{
+ struct ar9170_cmd_response *cmd = (void *) buf;
+
+ if ((cmd->type & 0xc0) != 0xc0) {
+ ar->callback_cmd(ar, len, buf);
+ return;
+ }
+
+ /* hardware event handlers */
+ switch (cmd->type) {
+ case 0xc1: {
+ /*
+ * TX status notification:
+ * bytes: 0c c1 XX YY M1 M2 M3 M4 M5 M6 R4 R3 R2 R1 S2 S1
+ *
+ * XX always 81
+ * YY always 00
+ * M1-M6 is the MAC address
+ * R1-R4 is the transmit rate
+ * S1-S2 is the transmit status
+ */
+
+ struct sk_buff *skb;
+ u32 queue = (le32_to_cpu(cmd->tx_status.rate) &
+ AR9170_TX_PHY_QOS_MASK) >> AR9170_TX_PHY_QOS_SHIFT;
+
+ skb = ar9170_find_queued_skb(ar, cmd->tx_status.dst, queue);
+ if (unlikely(!skb))
+ return ;
+
+ ar9170_handle_tx_status(ar, skb, true,
+ le16_to_cpu(cmd->tx_status.status));
+ break;
+ }
+
+ case 0xc0:
+ /*
+ * pre-TBTT event
+ */
+ if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP)
+ queue_work(ar->hw->workqueue, &ar->beacon_work);
+ break;
+
+ case 0xc2:
+ /*
+ * (IBSS) beacon send notification
+ * bytes: 04 c2 XX YY B4 B3 B2 B1
+ *
+ * XX always 80
+ * YY always 00
+ * B1-B4 "should" be the number of send out beacons.
+ */
+ break;
+
+ case 0xc3:
+ /* End of Atim Window */
+ break;
+
+ case 0xc4:
+ case 0xc5:
+ /* BlockACK events */
+ break;
+
+ case 0xc6:
+ /* Watchdog Interrupt */
+ break;
+
+ case 0xc9:
+ /* retransmission issue / SIFS/EIFS collision ?! */
+ break;
+
+ default:
+ printk(KERN_INFO "received unhandled event %x\n", cmd->type);
+ print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
+ break;
+ }
+}
+
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we can
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
+static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+ struct sk_buff *skb;
+ struct ar9170_rx_head *head = (void *)buf;
+ struct ar9170_rx_tail *tail;
+ struct ieee80211_rx_status status;
+ int mpdu_len, i;
+ u8 error, antennas = 0, decrypt;
+ __le16 fc;
+ int reserved;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ /* Received MPDU */
+ mpdu_len = len;
+ mpdu_len -= sizeof(struct ar9170_rx_head);
+ mpdu_len -= sizeof(struct ar9170_rx_tail);
+ BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+ BUILD_BUG_ON(sizeof(struct ar9170_rx_tail) != 24);
+
+ if (mpdu_len <= FCS_LEN)
+ return;
+
+ tail = (void *)(buf + sizeof(struct ar9170_rx_head) + mpdu_len);
+
+ for (i = 0; i < 3; i++)
+ if (tail->rssi[i] != 0x80)
+ antennas |= BIT(i);
+
+ /* post-process RSSI */
+ for (i = 0; i < 7; i++)
+ if (tail->rssi[i] & 0x80)
+ tail->rssi[i] = ((tail->rssi[i] & 0x7f) + 1) & 0x7f;
+
+ memset(&status, 0, sizeof(status));
+
+ status.band = ar->channel->band;
+ status.freq = ar->channel->center_freq;
+ status.signal = ar->noise[0] + tail->rssi_combined;
+ status.noise = ar->noise[0];
+ status.antenna = antennas;
+
+ switch (tail->status & AR9170_RX_STATUS_MODULATION_MASK) {
+ case AR9170_RX_STATUS_MODULATION_CCK:
+ if (tail->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+ status.flag |= RX_FLAG_SHORTPRE;
+ switch (head->plcp[0]) {
+ case 0x0a:
+ status.rate_idx = 0;
+ break;
+ case 0x14:
+ status.rate_idx = 1;
+ break;
+ case 0x37:
+ status.rate_idx = 2;
+ break;
+ case 0x6e:
+ status.rate_idx = 3;
+ break;
+ default:
+ if ((!ar->sniffer_enabled) && (net_ratelimit()))
+ printk(KERN_ERR "%s: invalid plcp cck rate "
+ "(%x).\n", wiphy_name(ar->hw->wiphy),
+ head->plcp[0]);
+ return;
+ }
+ break;
+ case AR9170_RX_STATUS_MODULATION_OFDM:
+ switch (head->plcp[0] & 0xF) {
+ case 0xB:
+ status.rate_idx = 0;
+ break;
+ case 0xF:
+ status.rate_idx = 1;
+ break;
+ case 0xA:
+ status.rate_idx = 2;
+ break;
+ case 0xE:
+ status.rate_idx = 3;
+ break;
+ case 0x9:
+ status.rate_idx = 4;
+ break;
+ case 0xD:
+ status.rate_idx = 5;
+ break;
+ case 0x8:
+ status.rate_idx = 6;
+ break;
+ case 0xC:
+ status.rate_idx = 7;
+ break;
+ default:
+ if ((!ar->sniffer_enabled) && (net_ratelimit()))
+ printk(KERN_ERR "%s: invalid plcp ofdm rate "
+ "(%x).\n", wiphy_name(ar->hw->wiphy),
+ head->plcp[0]);
+ return;
+ }
+ if (status.band == IEEE80211_BAND_2GHZ)
+ status.rate_idx += 4;
+ break;
+ case AR9170_RX_STATUS_MODULATION_HT:
+ case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+ /* XXX */
+
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: invalid modulation\n",
+ wiphy_name(ar->hw->wiphy));
+ return;
+ }
+
+ error = tail->error;
+
+ if (error & AR9170_RX_ERROR_MMIC) {
+ status.flag |= RX_FLAG_MMIC_ERROR;
+ error &= ~AR9170_RX_ERROR_MMIC;
+ }
+
+ if (error & AR9170_RX_ERROR_PLCP) {
+ status.flag |= RX_FLAG_FAILED_PLCP_CRC;
+ error &= ~AR9170_RX_ERROR_PLCP;
+ }
+
+ if (error & AR9170_RX_ERROR_FCS) {
+ status.flag |= RX_FLAG_FAILED_FCS_CRC;
+ error &= ~AR9170_RX_ERROR_FCS;
+ }
+
+ decrypt = ar9170_get_decrypt_type(tail);
+ if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+ decrypt != AR9170_ENC_ALG_NONE)
+ status.flag |= RX_FLAG_DECRYPTED;
+
+ /* ignore wrong RA errors */
+ error &= ~AR9170_RX_ERROR_WRONG_RA;
+
+ if (error & AR9170_RX_ERROR_DECRYPT) {
+ error &= ~AR9170_RX_ERROR_DECRYPT;
+
+ /*
+ * Rx decryption is done in place,
+ * the original data is lost anyway.
+ */
+ return ;
+ }
+
+ /* drop any other error frames */
+ if ((error) && (net_ratelimit())) {
+ printk(KERN_DEBUG "%s: errors: %#x\n",
+ wiphy_name(ar->hw->wiphy), error);
+ return;
+ }
+
+ buf += sizeof(struct ar9170_rx_head);
+ fc = *(__le16 *)buf;
+
+ if (ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc))
+ reserved = 32 + 2;
+ else
+ reserved = 32;
+
+ skb = dev_alloc_skb(mpdu_len + reserved);
+ if (!skb)
+ return;
+
+ skb_reserve(skb, reserved);
+ memcpy(skb_put(skb, mpdu_len), buf, mpdu_len);
+ ieee80211_rx_irqsafe(ar->hw, skb, &status);
+}
+
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb)
+{
+ unsigned int i, tlen, resplen;
+ u8 *tbuf, *respbuf;
+
+ tbuf = skb->data;
+ tlen = skb->len;
+
+ while (tlen >= 4) {
+ int clen = tbuf[1] << 8 | tbuf[0];
+ int wlen = (clen + 3) & ~3;
+
+ /*
+ * parse stream (if any)
+ */
+ if (tbuf[2] != 0 || tbuf[3] != 0x4e) {
+ printk(KERN_ERR "%s: missing tag!\n",
+ wiphy_name(ar->hw->wiphy));
+ return ;
+ }
+ if (wlen > tlen - 4) {
+ printk(KERN_ERR "%s: invalid RX (%d, %d, %d)\n",
+ wiphy_name(ar->hw->wiphy), clen, wlen, tlen);
+ print_hex_dump(KERN_DEBUG, "data: ",
+ DUMP_PREFIX_OFFSET,
+ 16, 1, tbuf, tlen, true);
+ return ;
+ }
+ resplen = clen;
+ respbuf = tbuf + 4;
+ tbuf += wlen + 4;
+ tlen -= wlen + 4;
+
+ i = 0;
+
+ /* weird thing, but this is the same in the original driver */
+ while (resplen > 2 && i < 12 &&
+ respbuf[0] == 0xff && respbuf[1] == 0xff) {
+ i += 2;
+ resplen -= 2;
+ respbuf += 2;
+ }
+
+ if (resplen < 4)
+ continue;
+
+ /* found the 6 * 0xffff marker? */
+ if (i == 12)
+ ar9170_handle_command_response(ar, respbuf, resplen);
+ else
+ ar9170_handle_mpdu(ar, respbuf, resplen);
+ }
+
+ if (tlen)
+ printk(KERN_ERR "%s: buffer remains!\n",
+ wiphy_name(ar->hw->wiphy));
+}
+
+#define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \
+do { \
+ queue.aifs = ai_fs; \
+ queue.cw_min = cwmin; \
+ queue.cw_max = cwmax; \
+ queue.txop = _txop; \
+} while (0)
+
+static int ar9170_op_start(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+ int err, i;
+
+ mutex_lock(&ar->mutex);
+
+ /* reinitialize queues statistics */
+ memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
+ for (i = 0; i < ARRAY_SIZE(ar->tx_stats); i++)
+ ar->tx_stats[i].limit = 8;
+
+ /* reset QoS defaults */
+ AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT*/
+ AR9170_FILL_QUEUE(ar->edcf[1], 7, 15, 1023, 0); /* BACKGROUND */
+ AR9170_FILL_QUEUE(ar->edcf[2], 2, 7, 15, 94); /* VIDEO */
+ AR9170_FILL_QUEUE(ar->edcf[3], 2, 3, 7, 47); /* VOICE */
+ AR9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */
+
+ err = ar->open(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_init_mac(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_set_qos(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_init_phy(ar, IEEE80211_BAND_2GHZ);
+ if (err)
+ goto out;
+
+ err = ar9170_init_rf(ar);
+ if (err)
+ goto out;
+
+ /* start DMA */
+ err = ar9170_write_reg(ar, 0x1c3d30, 0x100);
+ if (err)
+ goto out;
+
+ ar->state = AR9170_STARTED;
+
+out:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_op_stop(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+
+ if (IS_STARTED(ar))
+ ar->state = AR9170_IDLE;
+
+ mutex_lock(&ar->mutex);
+
+ cancel_delayed_work_sync(&ar->tx_status_janitor);
+ cancel_work_sync(&ar->filter_config_work);
+ cancel_work_sync(&ar->beacon_work);
+ skb_queue_purge(&ar->global_tx_status_waste);
+ skb_queue_purge(&ar->global_tx_status);
+
+ if (IS_ACCEPTING_CMD(ar)) {
+ ar9170_set_leds_state(ar, 0);
+
+ /* stop DMA */
+ ar9170_write_reg(ar, 0x1c3d30, 0);
+ ar->stop(ar);
+ }
+
+ mutex_unlock(&ar->mutex);
+}
+
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct ar9170 *ar = hw->priv;
+ struct ieee80211_hdr *hdr;
+ struct ar9170_tx_control *txc;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_rate *rate = NULL;
+ struct ieee80211_tx_rate *txrate;
+ unsigned int queue = skb_get_queue_mapping(skb);
+ unsigned long flags = 0;
+ struct ar9170_sta_info *sta_info = NULL;
+ u32 power, chains;
+ u16 keytype = 0;
+ u16 len, icv = 0;
+ int err;
+ bool tx_status;
+
+ if (unlikely(!IS_STARTED(ar)))
+ goto err_free;
+
+ hdr = (void *)skb->data;
+ info = IEEE80211_SKB_CB(skb);
+ len = skb->len;
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ if (ar->tx_stats[queue].limit < ar->tx_stats[queue].len) {
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+ return NETDEV_TX_OK;
+ }
+
+ ar->tx_stats[queue].len++;
+ ar->tx_stats[queue].count++;
+ if (ar->tx_stats[queue].limit == ar->tx_stats[queue].len)
+ ieee80211_stop_queue(hw, queue);
+
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+ txc = (void *)skb_push(skb, sizeof(*txc));
+
+ tx_status = (((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) != 0) ||
+ ((info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) != 0));
+
+ if (info->control.hw_key) {
+ icv = info->control.hw_key->icv_len;
+
+ switch (info->control.hw_key->alg) {
+ case ALG_WEP:
+ keytype = AR9170_TX_MAC_ENCR_RC4;
+ break;
+ case ALG_TKIP:
+ keytype = AR9170_TX_MAC_ENCR_RC4;
+ break;
+ case ALG_CCMP:
+ keytype = AR9170_TX_MAC_ENCR_AES;
+ break;
+ default:
+ WARN_ON(1);
+ goto err_dequeue;
+ }
+ }
+
+ /* Length */
+ txc->length = cpu_to_le16(len + icv + 4);
+
+ txc->mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION |
+ AR9170_TX_MAC_BACKOFF);
+ txc->mac_control |= cpu_to_le16(ar9170_qos_hwmap[queue] <<
+ AR9170_TX_MAC_QOS_SHIFT);
+ txc->mac_control |= cpu_to_le16(keytype);
+ txc->phy_control = cpu_to_le32(0);
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
+
+ if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+
+ txrate = &info->control.rates[0];
+
+ if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+ else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+
+ if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
+
+ if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
+
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ);
+ /* this works because 40 MHz is 2 and dup is 3 */
+ if (txrate->flags & IEEE80211_TX_RC_DUP_DATA)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP);
+
+ if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
+
+ if (txrate->flags & IEEE80211_TX_RC_MCS) {
+ u32 r = txrate->idx;
+ u8 *txpower;
+
+ r <<= AR9170_TX_PHY_MCS_SHIFT;
+ if (WARN_ON(r & ~AR9170_TX_PHY_MCS_MASK))
+ goto err_dequeue;
+ txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK);
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
+
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ if (info->band == IEEE80211_BAND_5GHZ)
+ txpower = ar->power_5G_ht40;
+ else
+ txpower = ar->power_2G_ht40;
+ } else {
+ if (info->band == IEEE80211_BAND_5GHZ)
+ txpower = ar->power_5G_ht20;
+ else
+ txpower = ar->power_2G_ht20;
+ }
+
+ power = txpower[(txrate->idx) & 7];
+ } else {
+ u8 *txpower;
+ u32 mod;
+ u32 phyrate;
+ u8 idx = txrate->idx;
+
+ if (info->band != IEEE80211_BAND_2GHZ) {
+ idx += 4;
+ txpower = ar->power_5G_leg;
+ mod = AR9170_TX_PHY_MOD_OFDM;
+ } else {
+ if (idx < 4) {
+ txpower = ar->power_2G_cck;
+ mod = AR9170_TX_PHY_MOD_CCK;
+ } else {
+ mod = AR9170_TX_PHY_MOD_OFDM;
+ txpower = ar->power_2G_ofdm;
+ }
+ }
+
+ rate = &__ar9170_ratetable[idx];
+
+ phyrate = rate->hw_value & 0xF;
+ power = txpower[(rate->hw_value & 0x30) >> 4];
+ phyrate <<= AR9170_TX_PHY_MCS_SHIFT;
+
+ txc->phy_control |= cpu_to_le32(mod);
+ txc->phy_control |= cpu_to_le32(phyrate);
+ }
+
+ power <<= AR9170_TX_PHY_TX_PWR_SHIFT;
+ power &= AR9170_TX_PHY_TX_PWR_MASK;
+ txc->phy_control |= cpu_to_le32(power);
+
+ /* set TX chains */
+ if (ar->eeprom.tx_mask == 1) {
+ chains = AR9170_TX_PHY_TXCHAIN_1;
+ } else {
+ chains = AR9170_TX_PHY_TXCHAIN_2;
+
+ /* >= 36M legacy OFDM - use only one chain */
+ if (rate && rate->bitrate >= 360)
+ chains = AR9170_TX_PHY_TXCHAIN_1;
+ }
+ txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
+
+ if (tx_status) {
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
+ /*
+ * WARNING:
+ * Putting the QoS queue bits into an unexplored territory is
+ * certainly not elegant.
+ *
+ * In my defense: This idea provides a reasonable way to
+ * smuggle valuable information to the tx_status callback.
+ * Also, the idea behind this bit-abuse came straight from
+ * the original driver code.
+ */
+
+ txc->phy_control |=
+ cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
+
+ if (info->control.sta) {
+ sta_info = (void *) info->control.sta->drv_priv;
+ skb_queue_tail(&sta_info->tx_status[queue], skb);
+ } else {
+ skb_queue_tail(&ar->global_tx_status, skb);
+
+ queue_delayed_work(ar->hw->workqueue,
+ &ar->tx_status_janitor,
+ msecs_to_jiffies(100));
+ }
+ }
+
+ err = ar->tx(ar, skb, tx_status, 0);
+ if (unlikely(tx_status && err)) {
+ if (info->control.sta)
+ skb_unlink(skb, &sta_info->tx_status[queue]);
+ else
+ skb_unlink(skb, &ar->global_tx_status);
+ }
+
+ return NETDEV_TX_OK;
+
+err_dequeue:
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ ar->tx_stats[queue].len--;
+ ar->tx_stats[queue].count--;
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+err_free:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int ar9170_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (ar->vif) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ ar->vif = conf->vif;
+ memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN);
+
+ if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) {
+ ar->rx_software_decryption = true;
+ ar->disable_offload = true;
+ }
+
+ ar->cur_filter = 0;
+ ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS;
+ err = ar9170_update_frame_filter(ar);
+ if (err)
+ goto unlock;
+
+ err = ar9170_set_operating_mode(ar);
+
+unlock:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct ar9170 *ar = hw->priv;
+
+ mutex_lock(&ar->mutex);
+ ar->vif = NULL;
+ ar->want_filter = 0;
+ ar9170_update_frame_filter(ar);
+ ar9170_set_beacon_timers(ar);
+ dev_kfree_skb(ar->beacon);
+ ar->beacon = NULL;
+ ar->sniffer_enabled = false;
+ ar->rx_software_decryption = false;
+ ar9170_set_operating_mode(ar);
+ mutex_unlock(&ar->mutex);
+}
+
+static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ /*
+ * is it long_frame_max_tx_count or short_frame_max_tx_count?
+ */
+
+ err = ar9170_set_hwretry_limit(ar,
+ ar->hw->conf.long_frame_max_tx_count);
+ if (err)
+ goto out;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) {
+ err = ar9170_set_beacon_timers(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ err = ar9170_set_channel(ar, hw->conf.channel,
+ AR9170_RFI_NONE, AR9170_BW_20);
+ if (err)
+ goto out;
+ /* adjust slot time for 5 GHz */
+ if (hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+ err = ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME,
+ 9 << 10);
+ }
+
+out:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static int ar9170_op_config_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (conf->changed & IEEE80211_IFCC_BSSID) {
+ memcpy(ar->bssid, conf->bssid, ETH_ALEN);
+ err = ar9170_set_operating_mode(ar);
+ }
+
+ if (conf->changed & IEEE80211_IFCC_BEACON) {
+ err = ar9170_update_beacon(ar);
+
+ if (err)
+ goto out;
+ err = ar9170_set_beacon_timers(ar);
+ }
+
+out:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_set_filters(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ filter_config_work);
+ int err;
+
+ mutex_lock(&ar->mutex);
+ if (unlikely(!IS_STARTED(ar)))
+ goto unlock;
+
+ if (ar->filter_changed & AR9170_FILTER_CHANGED_PROMISC) {
+ err = ar9170_set_operating_mode(ar);
+ if (err)
+ goto unlock;
+ }
+
+ if (ar->filter_changed & AR9170_FILTER_CHANGED_MULTICAST) {
+ err = ar9170_update_multicast(ar);
+ if (err)
+ goto unlock;
+ }
+
+ if (ar->filter_changed & AR9170_FILTER_CHANGED_FRAMEFILTER)
+ err = ar9170_update_frame_filter(ar);
+
+unlock:
+ mutex_unlock(&ar->mutex);
+}
+
+static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ int mc_count, struct dev_mc_list *mclist)
+{
+ struct ar9170 *ar = hw->priv;
+
+ /* mask supported flags */
+ *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROMISC_IN_BSS;
+
+ /*
+ * We can support more by setting the sniffer bit and
+ * then checking the error flags, later.
+ */
+
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*new_flags & FIF_ALLMULTI) {
+ ar->want_mc_hash = ~0ULL;
+ } else {
+ u64 mchash;
+ int i;
+
+ /* always get broadcast frames */
+ mchash = 1ULL << (0xff>>2);
+
+ for (i = 0; i < mc_count; i++) {
+ if (WARN_ON(!mclist))
+ break;
+ mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
+ mclist = mclist->next;
+ }
+ ar->want_mc_hash = mchash;
+ }
+ ar->filter_changed |= AR9170_FILTER_CHANGED_MULTICAST;
+ }
+
+ if (changed_flags & FIF_CONTROL) {
+ u32 filter = AR9170_MAC_REG_FTF_PSPOLL |
+ AR9170_MAC_REG_FTF_RTS |
+ AR9170_MAC_REG_FTF_CTS |
+ AR9170_MAC_REG_FTF_ACK |
+ AR9170_MAC_REG_FTF_CFE |
+ AR9170_MAC_REG_FTF_CFE_ACK;
+
+ if (*new_flags & FIF_CONTROL)
+ ar->want_filter = ar->cur_filter | filter;
+ else
+ ar->want_filter = ar->cur_filter & ~filter;
+
+ ar->filter_changed |= AR9170_FILTER_CHANGED_FRAMEFILTER;
+ }
+
+ if (changed_flags & FIF_PROMISC_IN_BSS) {
+ ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0;
+ ar->filter_changed |= AR9170_FILTER_CHANGED_PROMISC;
+ }
+
+ if (likely(IS_STARTED(ar)))
+ queue_work(ar->hw->workqueue, &ar->filter_config_work);
+}
+
+static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changed)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ ar9170_regwrite_begin(ar);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ ar->state = bss_conf->assoc ? AR9170_ASSOCIATED : ar->state;
+
+#ifndef CONFIG_AR9170_LEDS
+ /* enable assoc LED. */
+ err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0);
+#endif /* CONFIG_AR9170_LEDS */
+ }
+
+ if (changed & BSS_CHANGED_HT) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ u32 slottime = 20;
+
+ if (bss_conf->use_short_slot)
+ slottime = 9;
+
+ ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, slottime << 10);
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ u32 cck, ofdm;
+
+ if (hw->conf.channel->band == IEEE80211_BAND_5GHZ) {
+ ofdm = bss_conf->basic_rates;
+ cck = 0;
+ } else {
+ /* four cck rates */
+ cck = bss_conf->basic_rates & 0xf;
+ ofdm = bss_conf->basic_rates >> 4;
+ }
+ ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE,
+ ofdm << 8 | cck);
+ }
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ mutex_unlock(&ar->mutex);
+}
+
+static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+ int err;
+ u32 tsf_low;
+ u32 tsf_high;
+ u64 tsf;
+
+ mutex_lock(&ar->mutex);
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low);
+ if (!err)
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high);
+ mutex_unlock(&ar->mutex);
+
+ if (WARN_ON(err))
+ return 0;
+
+ tsf = tsf_high;
+ tsf = (tsf << 32) | tsf_low;
+ return tsf;
+}
+
+static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0, i;
+ u8 ktype;
+
+ if ((!ar->vif) || (ar->disable_offload))
+ return -EOPNOTSUPP;
+
+ switch (key->alg) {
+ case ALG_WEP:
+ if (key->keylen == LEN_WEP40)
+ ktype = AR9170_ENC_ALG_WEP64;
+ else
+ ktype = AR9170_ENC_ALG_WEP128;
+ break;
+ case ALG_TKIP:
+ ktype = AR9170_ENC_ALG_TKIP;
+ break;
+ case ALG_CCMP:
+ ktype = AR9170_ENC_ALG_AESCCMP;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&ar->mutex);
+ if (cmd == SET_KEY) {
+ if (unlikely(!IS_STARTED(ar))) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* group keys need all-zeroes address */
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ sta = NULL;
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ for (i = 0; i < 64; i++)
+ if (!(ar->usedkeys & BIT(i)))
+ break;
+ if (i == 64) {
+ ar->rx_software_decryption = true;
+ ar9170_set_operating_mode(ar);
+ err = -ENOSPC;
+ goto out;
+ }
+ } else {
+ i = 64 + key->keyidx;
+ }
+
+ key->hw_key_idx = i;
+
+ err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0,
+ key->key, min_t(u8, 16, key->keylen));
+ if (err)
+ goto out;
+
+ if (key->alg == ALG_TKIP) {
+ err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL,
+ ktype, 1, key->key + 16, 16);
+ if (err)
+ goto out;
+
+ /*
+ * hardware is not capable generating the MMIC
+ * for fragmented frames!
+ */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ }
+
+ if (i < 64)
+ ar->usedkeys |= BIT(i);
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ } else {
+ if (unlikely(!IS_STARTED(ar))) {
+ /* The device is gone... together with the key ;-) */
+ err = 0;
+ goto out;
+ }
+
+ err = ar9170_disable_key(ar, key->hw_key_idx);
+ if (err)
+ goto out;
+
+ if (key->hw_key_idx < 64) {
+ ar->usedkeys &= ~BIT(key->hw_key_idx);
+ } else {
+ err = ar9170_upload_key(ar, key->hw_key_idx, NULL,
+ AR9170_ENC_ALG_NONE, 0,
+ NULL, 0);
+ if (err)
+ goto out;
+
+ if (key->alg == ALG_TKIP) {
+ err = ar9170_upload_key(ar, key->hw_key_idx,
+ NULL,
+ AR9170_ENC_ALG_NONE, 1,
+ NULL, 0);
+ if (err)
+ goto out;
+ }
+
+ }
+ }
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_L, ar->usedkeys);
+ ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_H, ar->usedkeys >> 32);
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+
+out:
+ mutex_unlock(&ar->mutex);
+
+ return err;
+}
+
+static void ar9170_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+ struct ar9170 *ar = hw->priv;
+ struct ar9170_sta_info *info = (void *) sta->drv_priv;
+ struct sk_buff *skb;
+ unsigned int i;
+
+ switch (cmd) {
+ case STA_NOTIFY_ADD:
+ for (i = 0; i < ar->hw->queues; i++)
+ skb_queue_head_init(&info->tx_status[i]);
+ break;
+
+ case STA_NOTIFY_REMOVE:
+
+ /*
+ * transfer all outstanding frames that need a tx_status
+ * reports to the global tx_status queue
+ */
+
+ for (i = 0; i < ar->hw->queues; i++) {
+ while ((skb = skb_dequeue(&info->tx_status[i]))) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: queueing frame in "
+ "global tx_status queue =>\n",
+ wiphy_name(ar->hw->wiphy));
+
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ skb_queue_tail(&ar->global_tx_status, skb);
+ }
+ }
+ queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
+ msecs_to_jiffies(100));
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int ar9170_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct ar9170 *ar = hw->priv;
+ u32 val;
+ int err;
+
+ mutex_lock(&ar->mutex);
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TX_RETRY, &val);
+ ar->stats.dot11ACKFailureCount += val;
+
+ memcpy(stats, &ar->stats, sizeof(*stats));
+ mutex_unlock(&ar->mutex);
+
+ return 0;
+}
+
+static int ar9170_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *tx_stats)
+{
+ struct ar9170 *ar = hw->priv;
+
+ spin_lock_bh(&ar->tx_stats_lock);
+ memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues);
+ spin_unlock_bh(&ar->tx_stats_lock);
+
+ return 0;
+}
+
+static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ const struct ieee80211_tx_queue_params *param)
+{
+ struct ar9170 *ar = hw->priv;
+ int ret;
+
+ mutex_lock(&ar->mutex);
+ if ((param) && !(queue > ar->hw->queues)) {
+ memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
+ param, sizeof(*param));
+
+ ret = ar9170_set_qos(ar);
+ } else
+ ret = -EINVAL;
+
+ mutex_unlock(&ar->mutex);
+ return ret;
+}
+
+static const struct ieee80211_ops ar9170_ops = {
+ .start = ar9170_op_start,
+ .stop = ar9170_op_stop,
+ .tx = ar9170_op_tx,
+ .add_interface = ar9170_op_add_interface,
+ .remove_interface = ar9170_op_remove_interface,
+ .config = ar9170_op_config,
+ .config_interface = ar9170_op_config_interface,
+ .configure_filter = ar9170_op_configure_filter,
+ .conf_tx = ar9170_conf_tx,
+ .bss_info_changed = ar9170_op_bss_info_changed,
+ .get_tsf = ar9170_op_get_tsf,
+ .set_key = ar9170_set_key,
+ .sta_notify = ar9170_sta_notify,
+ .get_stats = ar9170_get_stats,
+ .get_tx_stats = ar9170_get_tx_stats,
+};
+
+void *ar9170_alloc(size_t priv_size)
+{
+ struct ieee80211_hw *hw;
+ struct ar9170 *ar;
+ int i;
+
+ hw = ieee80211_alloc_hw(priv_size, &ar9170_ops);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ ar = hw->priv;
+ ar->hw = hw;
+
+ mutex_init(&ar->mutex);
+ spin_lock_init(&ar->cmdlock);
+ spin_lock_init(&ar->tx_stats_lock);
+ skb_queue_head_init(&ar->global_tx_status);
+ skb_queue_head_init(&ar->global_tx_status_waste);
+ INIT_WORK(&ar->filter_config_work, ar9170_set_filters);
+ INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
+ INIT_DELAYED_WORK(&ar->tx_status_janitor, ar9170_tx_status_janitor);
+
+ /* all hw supports 2.4 GHz, so set channel to 1 by default */
+ ar->channel = &ar9170_2ghz_chantable[0];
+
+ /* first part of wiphy init */
+ ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_WDS) |
+ BIT(NL80211_IFTYPE_ADHOC);
+ ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_NOISE_DBM;
+
+ ar->hw->queues = __AR9170_NUM_TXQ;
+ ar->hw->extra_tx_headroom = 8;
+ ar->hw->sta_data_size = sizeof(struct ar9170_sta_info);
+
+ ar->hw->max_rates = 1;
+ ar->hw->max_rate_tries = 3;
+
+ for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
+ ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
+
+ return ar;
+}
+
+static int ar9170_read_eeprom(struct ar9170 *ar)
+{
+#define RW 8 /* number of words to read at once */
+#define RB (sizeof(u32) * RW)
+ DECLARE_MAC_BUF(mbuf);
+ u8 *eeprom = (void *)&ar->eeprom;
+ u8 *addr = ar->eeprom.mac_address;
+ __le32 offsets[RW];
+ int i, j, err, bands = 0;
+
+ BUILD_BUG_ON(sizeof(ar->eeprom) & 3);
+
+ BUILD_BUG_ON(RB > AR9170_MAX_CMD_LEN - 4);
+#ifndef __CHECKER__
+ /* don't want to handle trailing remains */
+ BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
+#endif
+
+ for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+ for (j = 0; j < RW; j++)
+ offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
+ RB * i + 4 * j);
+
+ err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+ RB, (u8 *) &offsets,
+ RB, eeprom + RB * i);
+ if (err)
+ return err;
+ }
+
+#undef RW
+#undef RB
+
+ if (ar->eeprom.length == cpu_to_le16(0xFFFF))
+ return -ENODATA;
+
+ if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
+ ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar9170_band_2GHz;
+ bands++;
+ }
+ if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
+ ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz;
+ bands++;
+ }
+ /*
+ * I measured this, a bandswitch takes roughly
+ * 135 ms and a frequency switch about 80.
+ *
+ * FIXME: measure these values again once EEPROM settings
+ * are used, that will influence them!
+ */
+ if (bands == 2)
+ ar->hw->channel_change_time = 135 * 1000;
+ else
+ ar->hw->channel_change_time = 80 * 1000;
+
+ /* second part of wiphy init */
+ SET_IEEE80211_PERM_ADDR(ar->hw, addr);
+
+ return bands ? 0 : -EINVAL;
+}
+
+int ar9170_register(struct ar9170 *ar, struct device *pdev)
+{
+ int err;
+
+ /* try to read EEPROM, init MAC addr */
+ err = ar9170_read_eeprom(ar);
+ if (err)
+ goto err_out;
+
+ err = ieee80211_register_hw(ar->hw);
+ if (err)
+ goto err_out;
+
+ err = ar9170_init_leds(ar);
+ if (err)
+ goto err_unreg;
+
+#ifdef CONFIG_AR9170_LEDS
+ err = ar9170_register_leds(ar);
+ if (err)
+ goto err_unreg;
+#endif /* CONFIG_AR9170_LEDS */
+
+ dev_info(pdev, "Atheros AR9170 is registered as '%s'\n",
+ wiphy_name(ar->hw->wiphy));
+
+ return err;
+
+err_unreg:
+ ieee80211_unregister_hw(ar->hw);
+
+err_out:
+ return err;
+}
+
+void ar9170_unregister(struct ar9170 *ar)
+{
+#ifdef CONFIG_AR9170_LEDS
+ ar9170_unregister_leds(ar);
+#endif /* CONFIG_AR9170_LEDS */
+
+ ieee80211_unregister_hw(ar->hw);
+ mutex_destroy(&ar->mutex);
+}
diff --git a/trunk/drivers/net/wireless/ar9170/phy.c b/trunk/drivers/net/wireless/ar9170/phy.c
new file mode 100644
index 000000000000..6ce20754b8e7
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/phy.c
@@ -0,0 +1,1240 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * PHY and RF code
+ *
+ * Copyright 2008, Johannes Berg
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+#include "ar9170.h"
+#include "cmd.h"
+
+static int ar9170_init_power_cal(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(0x1bc000 + 0x993c, 0x7f);
+ ar9170_regwrite(0x1bc000 + 0x9934, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0x9938, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa234, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa238, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa38c, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa390, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3cc, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3d0, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3d4, 0x3f3f3f3f);
+
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+struct ar9170_phy_init {
+ u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20;
+};
+
+static struct ar9170_phy_init ar5416_phy_init[] = {
+ { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, },
+ { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, },
+ { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, },
+ { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, },
+ { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, },
+ { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, },
+ { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+ { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, },
+ { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+ { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+ { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, },
+ { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, },
+ { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, },
+ { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, },
+ { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, },
+ { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, },
+ { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, },
+ { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, },
+ { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, },
+ { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, },
+ { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, },
+ { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, },
+ { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, },
+ { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, },
+ { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, },
+ { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, },
+ { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, },
+ { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+ { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, },
+ { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, },
+ { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, },
+ { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, },
+ { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, },
+ { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, },
+ { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, },
+ { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, },
+ { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, },
+ { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, },
+ { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, },
+ { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, },
+ { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, },
+ { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, },
+ { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, },
+ { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, },
+ { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, },
+ { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, },
+ { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, },
+ { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, },
+ { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, },
+ { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, },
+ { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, },
+ { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, },
+ { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, },
+ { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, },
+ { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, },
+ { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, },
+ { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, },
+ { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, },
+ { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, },
+ { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+ { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, },
+ { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, },
+ { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, },
+ { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, },
+ { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, },
+ { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, },
+ { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, },
+ { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, },
+ { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, },
+ { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, },
+ { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, },
+ { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, },
+ { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, },
+ { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, },
+ { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, },
+ { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, },
+ { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, },
+ { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, },
+ { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, },
+ { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, },
+ { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, },
+ { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, },
+ { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, },
+ { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, },
+ { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, },
+ { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, },
+ { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, },
+ { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, },
+ { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, },
+ { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, },
+ { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, },
+ { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+ { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, },
+ { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, },
+ { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, },
+ { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, },
+ { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, },
+ { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, },
+ { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, },
+ { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+ { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, },
+ { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, },
+ { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, },
+ { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, },
+ { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, },
+ { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, },
+ { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, },
+ { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+ { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, },
+ { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, },
+ { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, },
+ { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, },
+ { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, },
+ { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, },
+ { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, },
+ { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, },
+ { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, },
+ { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+ { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, },
+ { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, },
+ { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, },
+ { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, },
+ { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, },
+ { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, },
+ { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, },
+ { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, },
+ { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, },
+ { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, },
+ { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+ { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+ { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, },
+ { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, },
+ { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, },
+ { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, },
+ { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, },
+ { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, },
+ { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, },
+ { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, },
+ { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, },
+ { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, },
+ { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, },
+ { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, },
+ { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, },
+ { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, },
+ { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, },
+ { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+ { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, },
+ { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, },
+ { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, },
+ { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, },
+ { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+ { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, },
+ { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, },
+ { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, },
+ { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, },
+ { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, },
+ { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, },
+ { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, },
+ { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, },
+ { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, },
+ { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, },
+ { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, },
+ { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, },
+ { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, },
+ { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, },
+ { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, },
+ { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, },
+ { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, },
+ { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, },
+ { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+ { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+ { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+ { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+ { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+ { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+/* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */
+ { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+ { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, },
+ { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, },
+ { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+ { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, },
+ { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, },
+ { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, },
+ { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, },
+ { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, },
+ { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, },
+ { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, },
+ { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, },
+ { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, },
+ { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, },
+ { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, },
+ { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, }
+};
+
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
+{
+ int i, err;
+ u32 val;
+ bool is_2ghz = band == IEEE80211_BAND_2GHZ;
+ bool is_40mhz = false; /* XXX: for now */
+
+ ar9170_regwrite_begin(ar);
+
+ for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+ if (is_40mhz) {
+ if (is_2ghz)
+ val = ar5416_phy_init[i]._2ghz_40;
+ else
+ val = ar5416_phy_init[i]._5ghz_40;
+ } else {
+ if (is_2ghz)
+ val = ar5416_phy_init[i]._2ghz_20;
+ else
+ val = ar5416_phy_init[i]._5ghz_20;
+ }
+
+ ar9170_regwrite(ar5416_phy_init[i].reg, val);
+ }
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ return err;
+
+ /* XXX: use EEPROM data here! */
+
+ err = ar9170_init_power_cal(ar);
+ if (err)
+ return err;
+
+ /* XXX: remove magic! */
+ if (is_2ghz)
+ err = ar9170_write_reg(ar, 0x1d4014, 0x5163);
+ else
+ err = ar9170_write_reg(ar, 0x1d4014, 0x5143);
+
+ return err;
+}
+
+struct ar9170_rf_init {
+ u32 reg, _5ghz, _2ghz;
+};
+
+static struct ar9170_rf_init ar9170_rf_init[] = {
+ /* bank 0 */
+ { 0x1c58b0, 0x1e5795e5, 0x1e5795e5},
+ { 0x1c58e0, 0x02008020, 0x02008020},
+ /* bank 1 */
+ { 0x1c58b0, 0x02108421, 0x02108421},
+ { 0x1c58ec, 0x00000008, 0x00000008},
+ /* bank 2 */
+ { 0x1c58b0, 0x0e73ff17, 0x0e73ff17},
+ { 0x1c58e0, 0x00000420, 0x00000420},
+ /* bank 3 */
+ { 0x1c58f0, 0x01400018, 0x01c00018},
+ /* bank 4 */
+ { 0x1c58b0, 0x000001a1, 0x000001a1},
+ { 0x1c58e8, 0x00000001, 0x00000001},
+ /* bank 5 */
+ { 0x1c58b0, 0x00000013, 0x00000013},
+ { 0x1c58e4, 0x00000002, 0x00000002},
+ /* bank 6 */
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00002c00, 0x00002c00},
+ { 0x1c58b0, 0x00004800, 0x00004800},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00006000, 0x00006000},
+ { 0x1c58b0, 0x00001000, 0x00001000},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00087c00, 0x00087c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00005400, 0x00005400},
+ { 0x1c58b0, 0x00000c00, 0x00000c00},
+ { 0x1c58b0, 0x00001800, 0x00001800},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00002c00, 0x00002c00},
+ { 0x1c58b0, 0x00003c00, 0x00003c00},
+ { 0x1c58b0, 0x00003800, 0x00003800},
+ { 0x1c58b0, 0x00001c00, 0x00001c00},
+ { 0x1c58b0, 0x00000800, 0x00000800},
+ { 0x1c58b0, 0x00000408, 0x00000408},
+ { 0x1c58b0, 0x00004c15, 0x00004c15},
+ { 0x1c58b0, 0x00004188, 0x00004188},
+ { 0x1c58b0, 0x0000201e, 0x0000201e},
+ { 0x1c58b0, 0x00010408, 0x00010408},
+ { 0x1c58b0, 0x00000801, 0x00000801},
+ { 0x1c58b0, 0x00000c08, 0x00000c08},
+ { 0x1c58b0, 0x0000181e, 0x0000181e},
+ { 0x1c58b0, 0x00001016, 0x00001016},
+ { 0x1c58b0, 0x00002800, 0x00002800},
+ { 0x1c58b0, 0x00004010, 0x00004010},
+ { 0x1c58b0, 0x0000081c, 0x0000081c},
+ { 0x1c58b0, 0x00000115, 0x00000115},
+ { 0x1c58b0, 0x00000015, 0x00000015},
+ { 0x1c58b0, 0x00000066, 0x00000066},
+ { 0x1c58b0, 0x0000001c, 0x0000001c},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000004, 0x00000004},
+ { 0x1c58b0, 0x00000015, 0x00000015},
+ { 0x1c58b0, 0x0000001f, 0x0000001f},
+ { 0x1c58e0, 0x00000000, 0x00000400},
+ /* bank 7 */
+ { 0x1c58b0, 0x000000a0, 0x000000a0},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000040, 0x00000040},
+ { 0x1c58f0, 0x0000001c, 0x0000001c},
+};
+
+static int ar9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz)
+{
+ int err, i;
+
+ ar9170_regwrite_begin(ar);
+
+ for (i = 0; i < ARRAY_SIZE(ar9170_rf_init); i++)
+ ar9170_regwrite(ar9170_rf_init[i].reg,
+ band5ghz ? ar9170_rf_init[i]._5ghz
+ : ar9170_rf_init[i]._2ghz);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ printk(KERN_ERR "%s: rf init failed\n",
+ wiphy_name(ar->hw->wiphy));
+ return err;
+}
+
+static int ar9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
+ u32 freq, enum ar9170_bw bw)
+{
+ int err;
+ u32 d0, d1, td0, td1, fd0, fd1;
+ u8 chansel;
+ u8 refsel0 = 1, refsel1 = 0;
+ u8 lf_synth = 0;
+
+ switch (bw) {
+ case AR9170_BW_40_ABOVE:
+ freq += 10;
+ break;
+ case AR9170_BW_40_BELOW:
+ freq -= 10;
+ break;
+ case AR9170_BW_20:
+ break;
+ case __AR9170_NUM_BW:
+ BUG();
+ }
+
+ if (band5ghz) {
+ if (freq % 10) {
+ chansel = (freq - 4800) / 5;
+ } else {
+ chansel = ((freq - 4800) / 10) * 2;
+ refsel0 = 0;
+ refsel1 = 1;
+ }
+ chansel = byte_rev_table[chansel];
+ } else {
+ if (freq == 2484) {
+ chansel = 10 + (freq - 2274) / 5;
+ lf_synth = 1;
+ } else
+ chansel = 16 + (freq - 2272) / 5;
+ chansel *= 4;
+ chansel = byte_rev_table[chansel];
+ }
+
+ d1 = chansel;
+ d0 = 0x21 |
+ refsel0 << 3 |
+ refsel1 << 2 |
+ lf_synth << 1;
+ td0 = d0 & 0x1f;
+ td1 = d1 & 0x1f;
+ fd0 = td1 << 5 | td0;
+
+ td0 = (d0 >> 5) & 0x7;
+ td1 = (d1 >> 5) & 0x7;
+ fd1 = td1 << 5 | td0;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(0x1c58b0, fd0);
+ ar9170_regwrite(0x1c58e8, fd1);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ return err;
+
+ msleep(10);
+
+ return 0;
+}
+
+struct ar9170_phy_freq_params {
+ u8 coeff_exp;
+ u16 coeff_man;
+ u8 coeff_exp_shgi;
+ u16 coeff_man_shgi;
+};
+
+struct ar9170_phy_freq_entry {
+ u16 freq;
+ struct ar9170_phy_freq_params params[__AR9170_NUM_BW];
+};
+
+/* NB: must be in sync with channel tables in main! */
+static const struct ar9170_phy_freq_entry ar9170_phy_freq_params[] = {
+/*
+ * freq,
+ * 20MHz,
+ * 40MHz (below),
+ * 40Mhz (above),
+ */
+ { 2412, {
+ { 3, 21737, 3, 19563, },
+ { 3, 21827, 3, 19644, },
+ { 3, 21647, 3, 19482, },
+ } },
+ { 2417, {
+ { 3, 21692, 3, 19523, },
+ { 3, 21782, 3, 19604, },
+ { 3, 21602, 3, 19442, },
+ } },
+ { 2422, {
+ { 3, 21647, 3, 19482, },
+ { 3, 21737, 3, 19563, },
+ { 3, 21558, 3, 19402, },
+ } },
+ { 2427, {
+ { 3, 21602, 3, 19442, },
+ { 3, 21692, 3, 19523, },
+ { 3, 21514, 3, 19362, },
+ } },
+ { 2432, {
+ { 3, 21558, 3, 19402, },
+ { 3, 21647, 3, 19482, },
+ { 3, 21470, 3, 19323, },
+ } },
+ { 2437, {
+ { 3, 21514, 3, 19362, },
+ { 3, 21602, 3, 19442, },
+ { 3, 21426, 3, 19283, },
+ } },
+ { 2442, {
+ { 3, 21470, 3, 19323, },
+ { 3, 21558, 3, 19402, },
+ { 3, 21382, 3, 19244, },
+ } },
+ { 2447, {
+ { 3, 21426, 3, 19283, },
+ { 3, 21514, 3, 19362, },
+ { 3, 21339, 3, 19205, },
+ } },
+ { 2452, {
+ { 3, 21382, 3, 19244, },
+ { 3, 21470, 3, 19323, },
+ { 3, 21295, 3, 19166, },
+ } },
+ { 2457, {
+ { 3, 21339, 3, 19205, },
+ { 3, 21426, 3, 19283, },
+ { 3, 21252, 3, 19127, },
+ } },
+ { 2462, {
+ { 3, 21295, 3, 19166, },
+ { 3, 21382, 3, 19244, },
+ { 3, 21209, 3, 19088, },
+ } },
+ { 2467, {
+ { 3, 21252, 3, 19127, },
+ { 3, 21339, 3, 19205, },
+ { 3, 21166, 3, 19050, },
+ } },
+ { 2472, {
+ { 3, 21209, 3, 19088, },
+ { 3, 21295, 3, 19166, },
+ { 3, 21124, 3, 19011, },
+ } },
+ { 2484, {
+ { 3, 21107, 3, 18996, },
+ { 3, 21192, 3, 19073, },
+ { 3, 21022, 3, 18920, },
+ } },
+ { 4920, {
+ { 4, 21313, 4, 19181, },
+ { 4, 21356, 4, 19220, },
+ { 4, 21269, 4, 19142, },
+ } },
+ { 4940, {
+ { 4, 21226, 4, 19104, },
+ { 4, 21269, 4, 19142, },
+ { 4, 21183, 4, 19065, },
+ } },
+ { 4960, {
+ { 4, 21141, 4, 19027, },
+ { 4, 21183, 4, 19065, },
+ { 4, 21098, 4, 18988, },
+ } },
+ { 4980, {
+ { 4, 21056, 4, 18950, },
+ { 4, 21098, 4, 18988, },
+ { 4, 21014, 4, 18912, },
+ } },
+ { 5040, {
+ { 4, 20805, 4, 18725, },
+ { 4, 20846, 4, 18762, },
+ { 4, 20764, 4, 18687, },
+ } },
+ { 5060, {
+ { 4, 20723, 4, 18651, },
+ { 4, 20764, 4, 18687, },
+ { 4, 20682, 4, 18614, },
+ } },
+ { 5080, {
+ { 4, 20641, 4, 18577, },
+ { 4, 20682, 4, 18614, },
+ { 4, 20601, 4, 18541, },
+ } },
+ { 5180, {
+ { 4, 20243, 4, 18219, },
+ { 4, 20282, 4, 18254, },
+ { 4, 20204, 4, 18183, },
+ } },
+ { 5200, {
+ { 4, 20165, 4, 18148, },
+ { 4, 20204, 4, 18183, },
+ { 4, 20126, 4, 18114, },
+ } },
+ { 5220, {
+ { 4, 20088, 4, 18079, },
+ { 4, 20126, 4, 18114, },
+ { 4, 20049, 4, 18044, },
+ } },
+ { 5240, {
+ { 4, 20011, 4, 18010, },
+ { 4, 20049, 4, 18044, },
+ { 4, 19973, 4, 17976, },
+ } },
+ { 5260, {
+ { 4, 19935, 4, 17941, },
+ { 4, 19973, 4, 17976, },
+ { 4, 19897, 4, 17907, },
+ } },
+ { 5280, {
+ { 4, 19859, 4, 17873, },
+ { 4, 19897, 4, 17907, },
+ { 4, 19822, 4, 17840, },
+ } },
+ { 5300, {
+ { 4, 19784, 4, 17806, },
+ { 4, 19822, 4, 17840, },
+ { 4, 19747, 4, 17772, },
+ } },
+ { 5320, {
+ { 4, 19710, 4, 17739, },
+ { 4, 19747, 4, 17772, },
+ { 4, 19673, 4, 17706, },
+ } },
+ { 5500, {
+ { 4, 19065, 4, 17159, },
+ { 4, 19100, 4, 17190, },
+ { 4, 19030, 4, 17127, },
+ } },
+ { 5520, {
+ { 4, 18996, 4, 17096, },
+ { 4, 19030, 4, 17127, },
+ { 4, 18962, 4, 17065, },
+ } },
+ { 5540, {
+ { 4, 18927, 4, 17035, },
+ { 4, 18962, 4, 17065, },
+ { 4, 18893, 4, 17004, },
+ } },
+ { 5560, {
+ { 4, 18859, 4, 16973, },
+ { 4, 18893, 4, 17004, },
+ { 4, 18825, 4, 16943, },
+ } },
+ { 5580, {
+ { 4, 18792, 4, 16913, },
+ { 4, 18825, 4, 16943, },
+ { 4, 18758, 4, 16882, },
+ } },
+ { 5600, {
+ { 4, 18725, 4, 16852, },
+ { 4, 18758, 4, 16882, },
+ { 4, 18691, 4, 16822, },
+ } },
+ { 5620, {
+ { 4, 18658, 4, 16792, },
+ { 4, 18691, 4, 16822, },
+ { 4, 18625, 4, 16762, },
+ } },
+ { 5640, {
+ { 4, 18592, 4, 16733, },
+ { 4, 18625, 4, 16762, },
+ { 4, 18559, 4, 16703, },
+ } },
+ { 5660, {
+ { 4, 18526, 4, 16673, },
+ { 4, 18559, 4, 16703, },
+ { 4, 18493, 4, 16644, },
+ } },
+ { 5680, {
+ { 4, 18461, 4, 16615, },
+ { 4, 18493, 4, 16644, },
+ { 4, 18428, 4, 16586, },
+ } },
+ { 5700, {
+ { 4, 18396, 4, 16556, },
+ { 4, 18428, 4, 16586, },
+ { 4, 18364, 4, 16527, },
+ } },
+ { 5745, {
+ { 4, 18252, 4, 16427, },
+ { 4, 18284, 4, 16455, },
+ { 4, 18220, 4, 16398, },
+ } },
+ { 5765, {
+ { 4, 18189, 5, 32740, },
+ { 4, 18220, 4, 16398, },
+ { 4, 18157, 5, 32683, },
+ } },
+ { 5785, {
+ { 4, 18126, 5, 32626, },
+ { 4, 18157, 5, 32683, },
+ { 4, 18094, 5, 32570, },
+ } },
+ { 5805, {
+ { 4, 18063, 5, 32514, },
+ { 4, 18094, 5, 32570, },
+ { 4, 18032, 5, 32458, },
+ } },
+ { 5825, {
+ { 4, 18001, 5, 32402, },
+ { 4, 18032, 5, 32458, },
+ { 4, 17970, 5, 32347, },
+ } },
+ { 5170, {
+ { 4, 20282, 4, 18254, },
+ { 4, 20321, 4, 18289, },
+ { 4, 20243, 4, 18219, },
+ } },
+ { 5190, {
+ { 4, 20204, 4, 18183, },
+ { 4, 20243, 4, 18219, },
+ { 4, 20165, 4, 18148, },
+ } },
+ { 5210, {
+ { 4, 20126, 4, 18114, },
+ { 4, 20165, 4, 18148, },
+ { 4, 20088, 4, 18079, },
+ } },
+ { 5230, {
+ { 4, 20049, 4, 18044, },
+ { 4, 20088, 4, 18079, },
+ { 4, 20011, 4, 18010, },
+ } },
+};
+
+static const struct ar9170_phy_freq_params *
+ar9170_get_hw_dyn_params(struct ieee80211_channel *channel,
+ enum ar9170_bw bw)
+{
+ unsigned int chanidx = 0;
+ u16 freq = 2412;
+
+ if (channel) {
+ chanidx = channel->hw_value;
+ freq = channel->center_freq;
+ }
+
+ BUG_ON(chanidx >= ARRAY_SIZE(ar9170_phy_freq_params));
+
+ BUILD_BUG_ON(__AR9170_NUM_BW != 3);
+
+ WARN_ON(ar9170_phy_freq_params[chanidx].freq != freq);
+
+ return &ar9170_phy_freq_params[chanidx].params[bw];
+}
+
+
+int ar9170_init_rf(struct ar9170 *ar)
+{
+ const struct ar9170_phy_freq_params *freqpar;
+ __le32 cmd[7];
+ int err;
+
+ err = ar9170_init_rf_banks_0_7(ar, false);
+ if (err)
+ return err;
+
+ err = ar9170_init_rf_bank4_pwr(ar, false, 2412, AR9170_BW_20);
+ if (err)
+ return err;
+
+ freqpar = ar9170_get_hw_dyn_params(NULL, AR9170_BW_20);
+
+ cmd[0] = cpu_to_le32(2412 * 1000);
+ cmd[1] = cpu_to_le32(0);
+ cmd[2] = cpu_to_le32(1);
+ cmd[3] = cpu_to_le32(freqpar->coeff_exp);
+ cmd[4] = cpu_to_le32(freqpar->coeff_man);
+ cmd[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+ cmd[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+
+ /* RF_INIT echoes the command back to us */
+ err = ar->exec_cmd(ar, AR9170_CMD_RF_INIT,
+ sizeof(cmd), (u8 *)cmd,
+ sizeof(cmd), (u8 *)cmd);
+ if (err)
+ return err;
+
+ msleep(1000);
+
+ return ar9170_echo_test(ar, 0xaabbccdd);
+}
+
+static int ar9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f)
+{
+ int idx = nfreqs - 2;
+
+ while (idx >= 0) {
+ if (f >= freqs[idx])
+ return idx;
+ idx--;
+ }
+
+ return 0;
+}
+
+static s32 ar9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+ /* nothing to interpolate, it's horizontal */
+ if (y2 == y1)
+ return y1;
+
+ /* check if we hit one of the edges */
+ if (x == x1)
+ return y1;
+ if (x == x2)
+ return y2;
+
+ /* x1 == x2 is bad, hopefully == x */
+ if (x2 == x1)
+ return y1;
+
+ return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1));
+}
+
+static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
+{
+#define SHIFT 8
+ s32 y;
+
+ y = ar9170_interpolate_s32(x << SHIFT,
+ x1 << SHIFT, y1 << SHIFT,
+ x2 << SHIFT, y2 << SHIFT);
+
+ /*
+ * XXX: unwrap this expression
+ * Isn't it just DIV_ROUND_UP(y, 1<> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+#undef SHIFT
+}
+
+static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
+{
+ struct ar9170_calibration_target_power_legacy *ctpl;
+ struct ar9170_calibration_target_power_ht *ctph;
+ u8 *ctpres;
+ int ntargets;
+ int idx, i, n;
+ u8 ackpower, ackchains, f;
+ u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+ if (freq < 3000)
+ f = freq - 2300;
+ else
+ f = (freq - 4800)/5;
+
+ /*
+ * cycle through the various modes
+ *
+ * legacy modes first: 5G, 2G CCK, 2G OFDM
+ */
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0: /* 5 GHz legacy */
+ ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_leg;
+ break;
+ case 1: /* 2.4 GHz CCK */
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+ ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+ ctpres = ar->power_2G_cck;
+ break;
+ case 2: /* 2.4 GHz OFDM */
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ofdm;
+ break;
+ default:
+ BUG();
+ }
+
+ for (n = 0; n < ntargets; n++) {
+ if (ctpl[n].freq == 0xff)
+ break;
+ pwr_freqs[n] = ctpl[n].freq;
+ }
+ ntargets = n;
+ idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+ for (n = 0; n < 4; n++)
+ ctpres[n] = ar9170_interpolate_u8(
+ f,
+ ctpl[idx + 0].freq,
+ ctpl[idx + 0].power[n],
+ ctpl[idx + 1].freq,
+ ctpl[idx + 1].power[n]);
+ }
+
+ /*
+ * HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40
+ */
+ for (i = 0; i < 4; i++) {
+ switch (i) {
+ case 0: /* 5 GHz HT 20 */
+ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_ht20;
+ break;
+ case 1: /* 5 GHz HT 40 */
+ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_ht40;
+ break;
+ case 2: /* 2.4 GHz HT 20 */
+ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ht20;
+ break;
+ case 3: /* 2.4 GHz HT 40 */
+ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ht40;
+ break;
+ default:
+ BUG();
+ }
+
+ for (n = 0; n < ntargets; n++) {
+ if (ctph[n].freq == 0xff)
+ break;
+ pwr_freqs[n] = ctph[n].freq;
+ }
+ ntargets = n;
+ idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+ for (n = 0; n < 8; n++)
+ ctpres[n] = ar9170_interpolate_u8(
+ f,
+ ctph[idx + 0].freq,
+ ctph[idx + 0].power[n],
+ ctph[idx + 1].freq,
+ ctph[idx + 1].power[n]);
+ }
+
+ /* set ACK/CTS TX power */
+ ar9170_regwrite_begin(ar);
+
+ if (ar->eeprom.tx_mask != 1)
+ ackchains = AR9170_TX_PHY_TXCHAIN_2;
+ else
+ ackchains = AR9170_TX_PHY_TXCHAIN_1;
+
+ if (freq < 3000)
+ ackpower = ar->power_2G_ofdm[0] & 0x3f;
+ else
+ ackpower = ar->power_5G_leg[0] & 0x3f;
+
+ ar9170_regwrite(0x1c3694, ackpower << 20 | ackchains << 26);
+ ar9170_regwrite(0x1c3bb4, ackpower << 5 | ackchains << 11 |
+ ackpower << 21 | ackchains << 27);
+
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+static int ar9170_calc_noise_dbm(u32 raw_noise)
+{
+ if (raw_noise & 0x100)
+ return ~((raw_noise & 0x0ff) >> 1);
+ else
+ return (raw_noise & 0xff) >> 1;
+}
+
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+ enum ar9170_rf_init_mode rfi, enum ar9170_bw bw)
+{
+ const struct ar9170_phy_freq_params *freqpar;
+ u32 cmd, tmp, offs;
+ __le32 vals[8];
+ int i, err;
+ bool bandswitch;
+
+ /* clear BB heavy clip enable */
+ err = ar9170_write_reg(ar, 0x1c59e0, 0x200);
+ if (err)
+ return err;
+
+ /* may be NULL at first setup */
+ if (ar->channel)
+ bandswitch = ar->channel->band != channel->band;
+ else
+ bandswitch = true;
+
+ /* HW workaround */
+ if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
+ channel->center_freq <= 2417)
+ bandswitch = true;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_FREQ_START, 0, NULL, 0, NULL);
+ if (err)
+ return err;
+
+ if (rfi != AR9170_RFI_NONE || bandswitch) {
+ u32 val = 0x400;
+
+ if (rfi == AR9170_RFI_COLD)
+ val = 0x800;
+
+ /* warm/cold reset BB/ADDA */
+ err = ar9170_write_reg(ar, 0x1d4004, val);
+ if (err)
+ return err;
+
+ err = ar9170_write_reg(ar, 0x1d4004, 0x0);
+ if (err)
+ return err;
+
+ err = ar9170_init_phy(ar, channel->band);
+ if (err)
+ return err;
+
+ err = ar9170_init_rf_banks_0_7(ar,
+ channel->band == IEEE80211_BAND_5GHZ);
+ if (err)
+ return err;
+
+ cmd = AR9170_CMD_RF_INIT;
+ } else {
+ cmd = AR9170_CMD_FREQUENCY;
+ }
+
+ err = ar9170_init_rf_bank4_pwr(ar,
+ channel->band == IEEE80211_BAND_5GHZ,
+ channel->center_freq, bw);
+ if (err)
+ return err;
+
+ switch (bw) {
+ case AR9170_BW_20:
+ tmp = 0x240;
+ offs = 0;
+ break;
+ case AR9170_BW_40_BELOW:
+ tmp = 0x2c4;
+ offs = 3;
+ break;
+ case AR9170_BW_40_ABOVE:
+ tmp = 0x2d4;
+ offs = 1;
+ break;
+ default:
+ BUG();
+ return -ENOSYS;
+ }
+
+ if (0 /* 2 streams capable */)
+ tmp |= 0x100;
+
+ err = ar9170_write_reg(ar, 0x1c5804, tmp);
+ if (err)
+ return err;
+
+ err = ar9170_set_power_cal(ar, channel->center_freq, bw);
+ if (err)
+ return err;
+
+ freqpar = ar9170_get_hw_dyn_params(channel, bw);
+
+ vals[0] = cpu_to_le32(channel->center_freq * 1000);
+ vals[1] = cpu_to_le32(bw == AR9170_BW_20 ? 0 : 1);
+ vals[2] = cpu_to_le32(offs << 2 | 1);
+ vals[3] = cpu_to_le32(freqpar->coeff_exp);
+ vals[4] = cpu_to_le32(freqpar->coeff_man);
+ vals[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+ vals[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+ vals[7] = cpu_to_le32(1000);
+
+ err = ar->exec_cmd(ar, cmd, sizeof(vals), (u8 *)vals,
+ sizeof(vals), (u8 *)vals);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2; i++) {
+ ar->noise[i] = ar9170_calc_noise_dbm(
+ (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff);
+
+ ar->noise[i + 2] = ar9170_calc_noise_dbm(
+ (le32_to_cpu(vals[5 + i]) >> 23) & 0x1ff);
+ }
+
+ ar->channel = channel;
+ return 0;
+}
diff --git a/trunk/drivers/net/wireless/ar9170/usb.c b/trunk/drivers/net/wireless/ar9170/usb.c
new file mode 100644
index 000000000000..ad296840893e
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/usb.c
@@ -0,0 +1,748 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * USB - frontend
+ *
+ * Copyright 2008, Johannes Berg
+ * Copyright 2009, Christian Lamparter
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "ar9170.h"
+#include "cmd.h"
+#include "hw.h"
+#include "usb.h"
+
+MODULE_AUTHOR("Johannes Berg ");
+MODULE_AUTHOR("Christian Lamparter ");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE("ar9170-1.fw");
+MODULE_FIRMWARE("ar9170-2.fw");
+
+static struct usb_device_id ar9170_usb_ids[] = {
+ /* Atheros 9170 */
+ { USB_DEVICE(0x0cf3, 0x9170) },
+ /* Atheros TG121N */
+ { USB_DEVICE(0x0cf3, 0x1001) },
+ /* D-Link DWA 160A */
+ { USB_DEVICE(0x07d1, 0x3c10) },
+ /* Netgear WNDA3100 */
+ { USB_DEVICE(0x0846, 0x9010) },
+ /* Netgear WN111 v2 */
+ { USB_DEVICE(0x0846, 0x9001) },
+ /* Zydas ZD1221 */
+ { USB_DEVICE(0x0ace, 0x1221) },
+ /* Z-Com UB81 BG */
+ { USB_DEVICE(0x0cde, 0x0023) },
+ /* Z-Com UB82 ABG */
+ { USB_DEVICE(0x0cde, 0x0026) },
+ /* Arcadyan WN7512 */
+ { USB_DEVICE(0x083a, 0xf522) },
+ /* Planex GWUS300 */
+ { USB_DEVICE(0x2019, 0x5304) },
+ /* IO-Data WNGDNUS2 */
+ { USB_DEVICE(0x04bb, 0x093f) },
+
+ /* terminate */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, ar9170_usb_ids);
+
+static void ar9170_usb_tx_urb_complete_free(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct ar9170_usb *aru = (struct ar9170_usb *)
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+ if (!aru) {
+ dev_kfree_skb_irq(skb);
+ return ;
+ }
+
+ ar9170_handle_tx_status(&aru->common, skb, false,
+ AR9170_TX_STATUS_COMPLETE);
+}
+
+static void ar9170_usb_tx_urb_complete(struct urb *urb)
+{
+}
+
+static void ar9170_usb_irq_completed(struct urb *urb)
+{
+ struct ar9170_usb *aru = urb->context;
+
+ switch (urb->status) {
+ /* everything is fine */
+ case 0:
+ break;
+
+ /* disconnect */
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ goto free;
+
+ default:
+ goto resubmit;
+ }
+
+ print_hex_dump_bytes("ar9170 irq: ", DUMP_PREFIX_OFFSET,
+ urb->transfer_buffer, urb->actual_length);
+
+resubmit:
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ usb_unanchor_urb(urb);
+ goto free;
+ }
+
+ return;
+
+free:
+ usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma);
+}
+
+static void ar9170_usb_rx_completed(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct ar9170_usb *aru = (struct ar9170_usb *)
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+ int err;
+
+ if (!aru)
+ goto free;
+
+ switch (urb->status) {
+ /* everything is fine */
+ case 0:
+ break;
+
+ /* disconnect */
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ goto free;
+
+ default:
+ goto resubmit;
+ }
+
+ skb_put(skb, urb->actual_length);
+ ar9170_rx(&aru->common, skb);
+
+resubmit:
+ skb_reset_tail_pointer(skb);
+ skb_trim(skb, 0);
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(urb);
+ dev_kfree_skb_irq(skb);
+ }
+
+ return ;
+
+free:
+ dev_kfree_skb_irq(skb);
+ return;
+}
+
+static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru,
+ struct urb *urb, gfp_t gfp)
+{
+ struct sk_buff *skb;
+
+ skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp);
+ if (!skb)
+ return -ENOMEM;
+
+ /* reserve some space for mac80211's radiotap */
+ skb_reserve(skb, 32);
+
+ usb_fill_bulk_urb(urb, aru->udev,
+ usb_rcvbulkpipe(aru->udev, AR9170_EP_RX),
+ skb->data, min(skb_tailroom(skb),
+ AR9170_MAX_RX_BUFFER_SIZE),
+ ar9170_usb_rx_completed, skb);
+
+ return 0;
+}
+
+static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru)
+{
+ struct urb *urb = NULL;
+ void *ibuf;
+ int err = -ENOMEM;
+
+ /* initialize interrupt endpoint */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto out;
+
+ ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma);
+ if (!ibuf)
+ goto out;
+
+ usb_fill_int_urb(urb, aru->udev,
+ usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf,
+ 64, ar9170_usb_irq_completed, aru, 1);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_buffer_free(aru->udev, 64, urb->transfer_buffer,
+ urb->transfer_dma);
+ }
+
+out:
+ usb_free_urb(urb);
+ return err;
+}
+
+static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru)
+{
+ struct urb *urb;
+ int i;
+ int err = -EINVAL;
+
+ for (i = 0; i < AR9170_NUM_RX_URBS; i++) {
+ err = -ENOMEM;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto err_out;
+
+ err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL);
+ if (err) {
+ usb_free_urb(urb);
+ goto err_out;
+ }
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ dev_kfree_skb_any((void *) urb->transfer_buffer);
+ usb_free_urb(urb);
+ goto err_out;
+ }
+ usb_free_urb(urb);
+ }
+
+ /* the device now waiting for a firmware. */
+ aru->common.state = AR9170_IDLE;
+ return 0;
+
+err_out:
+
+ usb_kill_anchored_urbs(&aru->rx_submitted);
+ return err;
+}
+
+static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
+{
+ int ret;
+
+ aru->common.state = AR9170_UNKNOWN_STATE;
+
+ usb_unlink_anchored_urbs(&aru->tx_submitted);
+
+ /* give the LED OFF command and the deauth frame a chance to air. */
+ ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+
+ usb_poison_anchored_urbs(&aru->rx_submitted);
+}
+
+static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd,
+ unsigned int plen, void *payload,
+ unsigned int outlen, void *out)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ struct urb *urb = NULL;
+ unsigned long flags;
+ int err = -ENOMEM;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return -EPERM;
+
+ if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4))
+ return -EINVAL;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (unlikely(!urb))
+ goto err_free;
+
+ ar->cmdbuf[0] = cpu_to_le32(plen);
+ ar->cmdbuf[0] |= cpu_to_le32(cmd << 8);
+ /* writing multiple regs fills this buffer already */
+ if (plen && payload != (u8 *)(&ar->cmdbuf[1]))
+ memcpy(&ar->cmdbuf[1], payload, plen);
+
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ aru->readbuf = (u8 *)out;
+ aru->readlen = outlen;
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+ usb_fill_int_urb(urb, aru->udev,
+ usb_sndbulkpipe(aru->udev, AR9170_EP_CMD),
+ aru->common.cmdbuf, plen + 4,
+ ar9170_usb_tx_urb_complete, NULL, 1);
+
+ usb_anchor_urb(urb, &aru->tx_submitted);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ goto err_unbuf;
+ }
+ usb_free_urb(urb);
+
+ err = wait_for_completion_timeout(&aru->cmd_wait, HZ);
+ if (err == 0) {
+ err = -ETIMEDOUT;
+ goto err_unbuf;
+ }
+
+ if (outlen >= 0 && aru->readlen != outlen) {
+ err = -EMSGSIZE;
+ goto err_unbuf;
+ }
+
+ return 0;
+
+err_unbuf:
+ /* Maybe the device was removed in the second we were waiting? */
+ if (IS_STARTED(ar)) {
+ dev_err(&aru->udev->dev, "no command feedback "
+ "received (%d).\n", err);
+
+ /* provide some maybe useful debug information */
+ print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE,
+ aru->common.cmdbuf, plen + 4);
+ dump_stack();
+ }
+
+ /* invalidate to avoid completing the next prematurely */
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ aru->readbuf = NULL;
+ aru->readlen = 0;
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+err_free:
+
+ return err;
+}
+
+static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb,
+ bool txstatus_needed, unsigned int extra_len)
+{
+ struct ar9170_usb *aru = (struct ar9170_usb *) ar;
+ struct urb *urb;
+ int err;
+
+ if (unlikely(!IS_STARTED(ar))) {
+ /* Seriously, what were you drink... err... thinking!? */
+ return -EPERM;
+ }
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (unlikely(!urb))
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(urb, aru->udev,
+ usb_sndbulkpipe(aru->udev, AR9170_EP_TX),
+ skb->data, skb->len + extra_len, (txstatus_needed ?
+ ar9170_usb_tx_urb_complete :
+ ar9170_usb_tx_urb_complete_free), skb);
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
+ usb_anchor_urb(urb, &aru->tx_submitted);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err))
+ usb_unanchor_urb(urb);
+
+ usb_free_urb(urb);
+ return err;
+}
+
+static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ unsigned long flags;
+ u32 in, out;
+
+ if (!buffer)
+ return ;
+
+ in = le32_to_cpup((__le32 *)buffer);
+ out = le32_to_cpu(ar->cmdbuf[0]);
+
+ /* mask off length byte */
+ out &= ~0xFF;
+
+ if (aru->readlen >= 0) {
+ /* add expected length */
+ out |= aru->readlen;
+ } else {
+ /* add obtained length */
+ out |= in & 0xFF;
+ }
+
+ /*
+ * Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response
+ * length and we cannot predict the correct length in advance.
+ * So we only check if we provided enough space for the data.
+ */
+ if (unlikely(out < in)) {
+ dev_warn(&aru->udev->dev, "received invalid command response "
+ "got %d bytes, instead of %d bytes "
+ "and the resp length is %d bytes\n",
+ in, out, len);
+ print_hex_dump_bytes("ar9170 invalid resp: ",
+ DUMP_PREFIX_OFFSET, buffer, len);
+ /*
+ * Do not complete, then the command times out,
+ * and we get a stack trace from there.
+ */
+ return ;
+ }
+
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ if (aru->readbuf && len > 0) {
+ memcpy(aru->readbuf, buffer + 4, len - 4);
+ aru->readbuf = NULL;
+ }
+ complete(&aru->cmd_wait);
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+}
+
+static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data,
+ size_t len, u32 addr, bool complete)
+{
+ int transfer, err;
+ u8 *buf = kmalloc(4096, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (len) {
+ transfer = min_t(int, len, 4096);
+ memcpy(buf, data, transfer);
+
+ err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+ 0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
+ addr >> 8, 0, buf, transfer, 1000);
+
+ if (err < 0) {
+ kfree(buf);
+ return err;
+ }
+
+ len -= transfer;
+ data += transfer;
+ addr += transfer;
+ }
+ kfree(buf);
+
+ if (complete) {
+ err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+ 0x31 /* FW DL COMPLETE */,
+ 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000);
+ }
+
+ return 0;
+}
+
+static int ar9170_usb_request_firmware(struct ar9170_usb *aru)
+{
+ int err = 0;
+
+ err = request_firmware(&aru->init_values, "ar9170-1.fw",
+ &aru->udev->dev);
+ if (err) {
+ dev_err(&aru->udev->dev, "file with init values not found.\n");
+ return err;
+ }
+
+ err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev);
+ if (err) {
+ release_firmware(aru->init_values);
+ dev_err(&aru->udev->dev, "firmware file not found.\n");
+ return err;
+ }
+
+ return err;
+}
+
+static int ar9170_usb_reset(struct ar9170_usb *aru)
+{
+ int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING);
+
+ if (lock) {
+ ret = usb_lock_device_for_reset(aru->udev, aru->intf);
+ if (ret < 0) {
+ dev_err(&aru->udev->dev, "unable to lock device "
+ "for reset (%d).\n", ret);
+ return ret;
+ }
+ }
+
+ ret = usb_reset_device(aru->udev);
+ if (lock)
+ usb_unlock_device(aru->udev);
+
+ /* let it rest - for a second - */
+ msleep(1000);
+
+ return ret;
+}
+
+static int ar9170_usb_upload_firmware(struct ar9170_usb *aru)
+{
+ int err;
+
+ /* First, upload initial values to device RAM */
+ err = ar9170_usb_upload(aru, aru->init_values->data,
+ aru->init_values->size, 0x102800, false);
+ if (err) {
+ dev_err(&aru->udev->dev, "firmware part 1 "
+ "upload failed (%d).\n", err);
+ return err;
+ }
+
+ /* Then, upload the firmware itself and start it */
+ return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size,
+ 0x200000, true);
+}
+
+static int ar9170_usb_init_transport(struct ar9170_usb *aru)
+{
+ struct ar9170 *ar = (void *) &aru->common;
+ int err;
+
+ ar9170_regwrite_begin(ar);
+
+ /* Set USB Rx stream mode MAX packet number to 2 */
+ ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4);
+
+ /* Set USB Rx stream mode timeout to 10us */
+ ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
+
+ ar9170_regwrite_finish();
+
+ err = ar9170_regwrite_result();
+ if (err)
+ dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err);
+
+ return err;
+}
+
+static void ar9170_usb_stop(struct ar9170 *ar)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ int ret;
+
+ if (IS_ACCEPTING_CMD(ar))
+ aru->common.state = AR9170_STOPPED;
+
+ /* lets wait a while until the tx - queues are dried out */
+ ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
+
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+
+ /*
+ * Note:
+ * So far we freed all tx urbs, but we won't dare to touch any rx urbs.
+ * Else we would end up with a unresponsive device...
+ */
+}
+
+static int ar9170_usb_open(struct ar9170 *ar)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ int err;
+
+ usb_unpoison_anchored_urbs(&aru->tx_submitted);
+ err = ar9170_usb_init_transport(aru);
+ if (err) {
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+ return err;
+ }
+
+ aru->common.state = AR9170_IDLE;
+ return 0;
+}
+
+static int ar9170_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct ar9170_usb *aru;
+ struct ar9170 *ar;
+ struct usb_device *udev;
+ int err;
+
+ aru = ar9170_alloc(sizeof(*aru));
+ if (IS_ERR(aru)) {
+ err = PTR_ERR(aru);
+ goto out;
+ }
+
+ udev = interface_to_usbdev(intf);
+ usb_get_dev(udev);
+ aru->udev = udev;
+ aru->intf = intf;
+ ar = &aru->common;
+
+ usb_set_intfdata(intf, aru);
+ SET_IEEE80211_DEV(ar->hw, &udev->dev);
+
+ init_usb_anchor(&aru->rx_submitted);
+ init_usb_anchor(&aru->tx_submitted);
+ init_completion(&aru->cmd_wait);
+
+ aru->common.stop = ar9170_usb_stop;
+ aru->common.open = ar9170_usb_open;
+ aru->common.tx = ar9170_usb_tx;
+ aru->common.exec_cmd = ar9170_usb_exec_cmd;
+ aru->common.callback_cmd = ar9170_usb_callback_cmd;
+
+ err = ar9170_usb_reset(aru);
+ if (err)
+ goto err_unlock;
+
+ err = ar9170_usb_request_firmware(aru);
+ if (err)
+ goto err_unlock;
+
+ err = ar9170_usb_alloc_rx_irq_urb(aru);
+ if (err)
+ goto err_freefw;
+
+ err = ar9170_usb_alloc_rx_bulk_urbs(aru);
+ if (err)
+ goto err_unrx;
+
+ err = ar9170_usb_upload_firmware(aru);
+ if (err) {
+ err = ar9170_echo_test(&aru->common, 0x60d43110);
+ if (err) {
+ /* force user invention, by disabling the device */
+ err = usb_driver_set_configuration(aru->udev, -1);
+ dev_err(&aru->udev->dev, "device is in a bad state. "
+ "please reconnect it!\n");
+ goto err_unrx;
+ }
+ }
+
+ err = ar9170_usb_open(ar);
+ if (err)
+ goto err_unrx;
+
+ err = ar9170_register(ar, &udev->dev);
+
+ ar9170_usb_stop(ar);
+ if (err)
+ goto err_unrx;
+
+ return 0;
+
+err_unrx:
+ ar9170_usb_cancel_urbs(aru);
+
+err_freefw:
+ release_firmware(aru->init_values);
+ release_firmware(aru->firmware);
+
+err_unlock:
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(udev);
+ ieee80211_free_hw(ar->hw);
+out:
+ return err;
+}
+
+static void ar9170_usb_disconnect(struct usb_interface *intf)
+{
+ struct ar9170_usb *aru = usb_get_intfdata(intf);
+
+ if (!aru)
+ return;
+
+ aru->common.state = AR9170_IDLE;
+ ar9170_unregister(&aru->common);
+ ar9170_usb_cancel_urbs(aru);
+
+ release_firmware(aru->init_values);
+ release_firmware(aru->firmware);
+
+ usb_put_dev(aru->udev);
+ usb_set_intfdata(intf, NULL);
+ ieee80211_free_hw(aru->common.hw);
+}
+
+static struct usb_driver ar9170_driver = {
+ .name = "ar9170usb",
+ .probe = ar9170_usb_probe,
+ .disconnect = ar9170_usb_disconnect,
+ .id_table = ar9170_usb_ids,
+ .soft_unbind = 1,
+};
+
+static int __init ar9170_init(void)
+{
+ return usb_register(&ar9170_driver);
+}
+
+static void __exit ar9170_exit(void)
+{
+ usb_deregister(&ar9170_driver);
+}
+
+module_init(ar9170_init);
+module_exit(ar9170_exit);
diff --git a/trunk/drivers/net/wireless/ar9170/usb.h b/trunk/drivers/net/wireless/ar9170/usb.h
new file mode 100644
index 000000000000..f5852924cd64
--- /dev/null
+++ b/trunk/drivers/net/wireless/ar9170/usb.h
@@ -0,0 +1,74 @@
+/*
+ * Atheros AR9170 USB driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg
+ * Copyright 2009, Christian Lamparter
+ *
+ * This program is free software; you can redistribute 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; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __USB_H
+#define __USB_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "eeprom.h"
+#include "hw.h"
+#include "ar9170.h"
+
+#define AR9170_NUM_RX_URBS 16
+
+struct firmware;
+
+struct ar9170_usb {
+ struct ar9170 common;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+
+ struct usb_anchor rx_submitted;
+ struct usb_anchor tx_submitted;
+
+ spinlock_t cmdlock;
+ struct completion cmd_wait;
+ int readlen;
+ u8 *readbuf;
+
+ const struct firmware *init_values;
+ const struct firmware *firmware;
+};
+
+#endif /* __USB_H */
diff --git a/trunk/drivers/net/wireless/arlan-main.c b/trunk/drivers/net/wireless/arlan-main.c
index bfca15da6f0f..5b9f1e06ebf6 100644
--- a/trunk/drivers/net/wireless/arlan-main.c
+++ b/trunk/drivers/net/wireless/arlan-main.c
@@ -1030,7 +1030,17 @@ static int arlan_mac_addr(struct net_device *dev, void *p)
return 0;
}
-
+static const struct net_device_ops arlan_netdev_ops = {
+ .ndo_open = arlan_open,
+ .ndo_stop = arlan_close,
+ .ndo_start_xmit = arlan_tx,
+ .ndo_get_stats = arlan_statistics,
+ .ndo_set_multicast_list = arlan_set_multicast,
+ .ndo_change_mtu = arlan_change_mtu,
+ .ndo_set_mac_address = arlan_mac_addr,
+ .ndo_tx_timeout = arlan_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+};
static int __init arlan_setup_device(struct net_device *dev, int num)
{
@@ -1042,14 +1052,7 @@ static int __init arlan_setup_device(struct net_device *dev, int num)
ap->conf = (struct arlan_shmem *)(ap+1);
dev->tx_queue_len = tx_queue_len;
- dev->open = arlan_open;
- dev->stop = arlan_close;
- dev->hard_start_xmit = arlan_tx;
- dev->get_stats = arlan_statistics;
- dev->set_multicast_list = arlan_set_multicast;
- dev->change_mtu = arlan_change_mtu;
- dev->set_mac_address = arlan_mac_addr;
- dev->tx_timeout = arlan_tx_timeout;
+ dev->netdev_ops = &arlan_netdev_ops;
dev->watchdog_timeo = 3*HZ;
ap->irq_test_done = 0;
diff --git a/trunk/drivers/net/wireless/ath5k/ath5k.h b/trunk/drivers/net/wireless/ath5k/ath5k.h
index 0dc2c7321c8b..0b616e72fe05 100644
--- a/trunk/drivers/net/wireless/ath5k/ath5k.h
+++ b/trunk/drivers/net/wireless/ath5k/ath5k.h
@@ -204,9 +204,9 @@
#define AR5K_TUNE_CWMAX_11B 1023
#define AR5K_TUNE_CWMAX_XR 7
#define AR5K_TUNE_NOISE_FLOOR -72
-#define AR5K_TUNE_MAX_TXPOWER 60
-#define AR5K_TUNE_DEFAULT_TXPOWER 30
-#define AR5K_TUNE_TPC_TXPOWER true
+#define AR5K_TUNE_MAX_TXPOWER 63
+#define AR5K_TUNE_DEFAULT_TXPOWER 25
+#define AR5K_TUNE_TPC_TXPOWER false
#define AR5K_TUNE_ANT_DIVERSITY true
#define AR5K_TUNE_HWTXTRIES 4
@@ -551,11 +551,11 @@ enum ath5k_pkt_type {
*/
#define AR5K_TXPOWER_OFDM(_r, _v) ( \
((0 & 1) << ((_v) + 6)) | \
- (((ah->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v)) \
+ (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \
)
#define AR5K_TXPOWER_CCK(_r, _v) ( \
- (ah->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v) \
+ (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \
)
/*
@@ -1085,13 +1085,25 @@ struct ath5k_hw {
struct ath5k_gain ah_gain;
u8 ah_offset[AR5K_MAX_RF_BANKS];
+
struct {
- u16 txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE];
- u16 txp_rates[AR5K_MAX_RATES];
- s16 txp_min;
- s16 txp_max;
+ /* Temporary tables used for interpolation */
+ u8 tmpL[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_POWER_TABLE_SIZE];
+ u8 tmpR[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_POWER_TABLE_SIZE];
+ u8 txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
+ u16 txp_rates_power_table[AR5K_MAX_RATES];
+ u8 txp_min_idx;
bool txp_tpc;
+ /* Values in 0.25dB units */
+ s16 txp_min_pwr;
+ s16 txp_max_pwr;
+ s16 txp_offset;
s16 txp_ofdm;
+ /* Values in dB units */
+ s16 txp_cck_ofdm_pwr_delta;
+ s16 txp_cck_ofdm_gainf_delta;
} ah_txpower;
struct {
@@ -1161,6 +1173,7 @@ extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ieee80211_l
/* EEPROM access functions */
extern int ath5k_eeprom_init(struct ath5k_hw *ah);
+extern void ath5k_eeprom_detach(struct ath5k_hw *ah);
extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac);
extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah);
@@ -1256,8 +1269,8 @@ extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant);
extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah);
extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);
/* TX power setup */
-extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int txpower);
-extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power);
+extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 txpower);
+extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 ee_mode, u8 txpower);
/*
* Functions used internaly
diff --git a/trunk/drivers/net/wireless/ath5k/attach.c b/trunk/drivers/net/wireless/ath5k/attach.c
index 656cb9dc833b..70d376c63aac 100644
--- a/trunk/drivers/net/wireless/ath5k/attach.c
+++ b/trunk/drivers/net/wireless/ath5k/attach.c
@@ -341,6 +341,8 @@ void ath5k_hw_detach(struct ath5k_hw *ah)
if (ah->ah_rf_banks != NULL)
kfree(ah->ah_rf_banks);
+ ath5k_eeprom_detach(ah);
+
/* assume interrupts are down */
kfree(ah);
}
diff --git a/trunk/drivers/net/wireless/ath5k/base.c b/trunk/drivers/net/wireless/ath5k/base.c
index cad3ccf61b00..5d57d774e466 100644
--- a/trunk/drivers/net/wireless/ath5k/base.c
+++ b/trunk/drivers/net/wireless/ath5k/base.c
@@ -685,13 +685,6 @@ ath5k_pci_resume(struct pci_dev *pdev)
if (err)
return err;
- /*
- * Suspend/Resume resets the PCI configuration space, so we have to
- * re-disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state
- */
- pci_write_config_byte(pdev, 0x41, 0);
-
err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
if (err) {
ATH5K_ERR(sc, "request_irq failed\n");
@@ -1095,9 +1088,18 @@ ath5k_mode_setup(struct ath5k_softc *sc)
static inline int
ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
{
- WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
- "hw_rix out of bounds: %x\n", hw_rix);
- return sc->rate_idx[sc->curband->band][hw_rix];
+ int rix;
+
+ /* return base rate on errors */
+ if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
+ "hw_rix out of bounds: %x\n", hw_rix))
+ return 0;
+
+ rix = sc->rate_idx[sc->curband->band][hw_rix];
+ if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
+ rix = 0;
+
+ return rix;
}
/***************\
@@ -1216,6 +1218,9 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
pktlen = skb->len;
+ /* FIXME: If we are in g mode and rate is a CCK rate
+ * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+ * from tx power (value is in dB units already) */
if (info->control.hw_key) {
keyidx = info->control.hw_key->hw_key_idx;
pktlen += info->control.hw_key->icv_len;
@@ -2044,6 +2049,9 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
antenna = sc->bsent & 4 ? 2 : 1;
}
+ /* FIXME: If we are in g mode and rate is a CCK rate
+ * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+ * from tx power (value is in dB units already) */
ds->ds_data = bf->skbaddr;
ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
ieee80211_get_hdrlen_from_skb(skb),
@@ -2305,7 +2313,7 @@ ath5k_init(struct ath5k_softc *sc)
sc->curband = &sc->sbands[sc->curchan->band];
sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
- AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+ AR5K_INT_FATAL | AR5K_INT_GLOBAL;
ret = ath5k_reset(sc, false, false);
if (ret)
goto done;
@@ -2554,7 +2562,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (skb_headroom(skb) < padsize) {
ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
" headroom to pad %d\n", hdrlen, padsize);
- return NETDEV_TX_BUSY;
+ goto drop_packet;
}
skb_push(skb, padsize);
memmove(skb->data, skb->data+padsize, hdrlen);
@@ -2565,7 +2573,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
spin_unlock_irqrestore(&sc->txbuflock, flags);
ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
- return NETDEV_TX_BUSY;
+ goto drop_packet;
}
bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
list_del(&bf->list);
@@ -2582,10 +2590,12 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
list_add_tail(&bf->list, &sc->txbuf);
sc->txbuf_len++;
spin_unlock_irqrestore(&sc->txbuflock, flags);
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
+ goto drop_packet;
}
+ return NETDEV_TX_OK;
+drop_packet:
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -2608,12 +2618,6 @@ ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel)
goto err;
}
- /*
- * This is needed only to setup initial state
- * but it's best done after a reset.
- */
- ath5k_hw_set_txpower_limit(sc->ah, 0);
-
ret = ath5k_rx_start(sc);
if (ret) {
ATH5K_ERR(sc, "can't start recv logic\n");
diff --git a/trunk/drivers/net/wireless/ath5k/base.h b/trunk/drivers/net/wireless/ath5k/base.h
index 20e0d14b41ec..822956114cd7 100644
--- a/trunk/drivers/net/wireless/ath5k/base.h
+++ b/trunk/drivers/net/wireless/ath5k/base.h
@@ -112,7 +112,7 @@ struct ath5k_softc {
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ieee80211_channel channels[ATH_CHAN_MAX];
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
- u8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+ s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
enum nl80211_iftype opmode;
struct ath5k_hw *ah; /* Atheros HW */
diff --git a/trunk/drivers/net/wireless/ath5k/desc.c b/trunk/drivers/net/wireless/ath5k/desc.c
index b40a9287a39a..dc30a2b70a6b 100644
--- a/trunk/drivers/net/wireless/ath5k/desc.c
+++ b/trunk/drivers/net/wireless/ath5k/desc.c
@@ -194,6 +194,10 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
return -EINVAL;
}
+ tx_power += ah->ah_txpower.txp_offset;
+ if (tx_power > AR5K_TUNE_MAX_TXPOWER)
+ tx_power = AR5K_TUNE_MAX_TXPOWER;
+
/* Clear descriptor */
memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
diff --git a/trunk/drivers/net/wireless/ath5k/eeprom.c b/trunk/drivers/net/wireless/ath5k/eeprom.c
index ac45ca47ca87..c0fb3b09ba45 100644
--- a/trunk/drivers/net/wireless/ath5k/eeprom.c
+++ b/trunk/drivers/net/wireless/ath5k/eeprom.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter
- * Copyright (c) 2006-2008 Nick Kossifidis
- * Copyright (c) 2008 Felix Fietkau
+ * Copyright (c) 2006-2009 Nick Kossifidis
+ * Copyright (c) 2008-2009 Felix Fietkau
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
int ret;
u16 val;
- /* Initial TX thermal adjustment values */
- ee->ee_tx_clip = 4;
- ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
- ee->ee_gain_select = 1;
-
/*
* Read values from EEPROM and store them in the capability structure
*/
@@ -241,22 +236,22 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
switch(mode) {
case AR5K_EEPROM_MODE_11A:
- ee->ee_ob[mode][3] = (val >> 5) & 0x7;
- ee->ee_db[mode][3] = (val >> 2) & 0x7;
- ee->ee_ob[mode][2] = (val << 1) & 0x7;
+ ee->ee_ob[mode][3] = (val >> 5) & 0x7;
+ ee->ee_db[mode][3] = (val >> 2) & 0x7;
+ ee->ee_ob[mode][2] = (val << 1) & 0x7;
AR5K_EEPROM_READ(o++, val);
- ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
- ee->ee_db[mode][2] = (val >> 12) & 0x7;
- ee->ee_ob[mode][1] = (val >> 9) & 0x7;
- ee->ee_db[mode][1] = (val >> 6) & 0x7;
- ee->ee_ob[mode][0] = (val >> 3) & 0x7;
- ee->ee_db[mode][0] = val & 0x7;
+ ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
+ ee->ee_db[mode][2] = (val >> 12) & 0x7;
+ ee->ee_ob[mode][1] = (val >> 9) & 0x7;
+ ee->ee_db[mode][1] = (val >> 6) & 0x7;
+ ee->ee_ob[mode][0] = (val >> 3) & 0x7;
+ ee->ee_db[mode][0] = val & 0x7;
break;
case AR5K_EEPROM_MODE_11G:
case AR5K_EEPROM_MODE_11B:
- ee->ee_ob[mode][1] = (val >> 4) & 0x7;
- ee->ee_db[mode][1] = val & 0x7;
+ ee->ee_ob[mode][1] = (val >> 4) & 0x7;
+ ee->ee_db[mode][1] = val & 0x7;
break;
}
@@ -504,35 +499,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah)
return 0;
}
-/* Used to match PCDAC steps with power values on RF5111 chips
- * (eeprom versions < 4). For RF5111 we have 10 pre-defined PCDAC
- * steps that match with the power values we read from eeprom. On
- * older eeprom versions (< 3.2) these steps are equaly spaced at
- * 10% of the pcdac curve -until the curve reaches it's maximum-
- * (10 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
- * these 10 steps are spaced in a different way. This function returns
- * the pcdac steps based on eeprom version and curve min/max so that we
- * can have pcdac/pwr points.
- */
-static inline void
-ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
-{
- static const u16 intercepts3[] =
- { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
- static const u16 intercepts3_2[] =
- { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
- const u16 *ip;
- int i;
-
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
- ip = intercepts3_2;
- else
- ip = intercepts3;
-
- for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
- *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100;
-}
-
/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
* frequency mask) */
static inline int
@@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
int ret;
u16 val;
+ ee->ee_n_piers[mode] = 0;
while(i < max) {
AR5K_EEPROM_READ(o++, val);
- freq1 = (val >> 8) & 0xff;
- freq2 = val & 0xff;
-
- if (freq1) {
- pc[i++].freq = ath5k_eeprom_bin2freq(ee,
- freq1, mode);
- ee->ee_n_piers[mode]++;
- }
+ freq1 = val & 0xff;
+ if (!freq1)
+ break;
- if (freq2) {
- pc[i++].freq = ath5k_eeprom_bin2freq(ee,
- freq2, mode);
- ee->ee_n_piers[mode]++;
- }
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq1, mode);
+ ee->ee_n_piers[mode]++;
- if (!freq1 || !freq2)
+ freq2 = (val >> 8) & 0xff;
+ if (!freq2)
break;
+
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq2, mode);
+ ee->ee_n_piers[mode]++;
}
/* return new offset */
@@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
return 0;
}
-/* Read power calibration for RF5111 chips
+/*
+ * Read power calibration for RF5111 chips
+ *
* For RF5111 we have an XPD -eXternal Power Detector- curve
- * for each calibrated channel. Each curve has PCDAC steps on
- * x axis and power on y axis and looks like a logarithmic
- * function. To recreate the curve and pass the power values
- * on the pcdac table, we read 10 points here and interpolate later.
+ * for each calibrated channel. Each curve has 0,5dB Power steps
+ * on x axis and PCDAC steps (offsets) on y axis and looks like an
+ * exponential function. To recreate the curve we read 11 points
+ * here and interpolate later.
*/
+
+/* Used to match PCDAC steps with power values on RF5111 chips
+ * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
+ * steps that match with the power values we read from eeprom. On
+ * older eeprom versions (< 3.2) these steps are equaly spaced at
+ * 10% of the pcdac curve -until the curve reaches it's maximum-
+ * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
+ * these 11 steps are spaced in a different way. This function returns
+ * the pcdac steps based on eeprom version and curve min/max so that we
+ * can have pcdac/pwr points.
+ */
+static inline void
+ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
+{
+ const static u16 intercepts3[] =
+ { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
+ const static u16 intercepts3_2[] =
+ { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
+ const u16 *ip;
+ int i;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
+ ip = intercepts3_2;
+ else
+ ip = intercepts3;
+
+ for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
+ vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100;
+}
+
+/* Convert RF5111 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5111 *pcinfo;
+ struct ath5k_pdgain_info *pd;
+ u8 pier, point, idx;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf5111_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ return -ENOMEM;
+
+ /* Only one curve for RF5111
+ * find out which one and place
+ * in in pd_curves.
+ * Note: ee_x_gain is reversed here */
+ for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) {
+
+ if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) {
+ pdgain_idx[0] = idx;
+ break;
+ }
+ }
+
+ ee->ee_pd_gains[mode] = 1;
+
+ pd = &chinfo[pier].pd_curves[idx];
+
+ pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+ sizeof(u8), GFP_KERNEL);
+ if (!pd->pd_step)
+ return -ENOMEM;
+
+ pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+ sizeof(s16), GFP_KERNEL);
+ if (!pd->pd_pwr)
+ return -ENOMEM;
+
+ /* Fill raw dataset
+ * (convert power to 0.25dB units
+ * for RF5112 combatibility) */
+ for (point = 0; point < pd->pd_points; point++) {
+
+ /* Absolute values */
+ pd->pd_pwr[point] = 2 * pcinfo->pwr[point];
+
+ /* Already sorted */
+ pd->pd_step[point] = pcinfo->pcdac[point];
+ }
+
+ /* Set min/max pwr */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+ chinfo[pier].max_pwr = pd->pd_pwr[10];
+
+ }
+
+ return 0;
+}
+
+/* Parse EEPROM data */
static int
ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
{
@@ -747,30 +821,165 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
cdata->pcdac_max, cdata->pcdac);
}
- return 0;
+ return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal);
}
-/* Read power calibration for RF5112 chips
+
+/*
+ * Read power calibration for RF5112 chips
+ *
* For RF5112 we have 4 XPD -eXternal Power Detector- curves
* for each calibrated channel on 0, -6, -12 and -18dbm but we only
- * use the higher (3) and the lower (0) curves. Each curve has PCDAC
- * steps on x axis and power on y axis and looks like a linear
- * function. To recreate the curve and pass the power values
- * on the pcdac table, we read 4 points for xpd 0 and 3 points
- * for xpd 3 here and interpolate later.
+ * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
+ * power steps on x axis and PCDAC steps on y axis and looks like a
+ * linear function. To recreate the curve and pass the power values
+ * on hw, we read 4 points for xpd 0 (lower gain -> max power)
+ * and 3 points for xpd 3 (higher gain -> lower power) here and
+ * interpolate later.
*
* Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
*/
+
+/* Convert RF5112 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5112 *pcinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ unsigned int pier, pdg, point;
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf5112_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ return -ENOMEM;
+
+ /* Fill pd_curves */
+ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+ u8 idx = pdgain_idx[pdg];
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[idx];
+
+ /* Lowest gain curve (max power) */
+ if (pdg == 0) {
+ /* One more point for better accuracy */
+ pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ return -ENOMEM;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ return -ENOMEM;
+
+
+ /* Fill raw dataset
+ * (all power levels are in 0.25dB units) */
+ pd->pd_step[0] = pcinfo->pcdac_x0[0];
+ pd->pd_pwr[0] = pcinfo->pwr_x0[0];
+
+ for (point = 1; point < pd->pd_points;
+ point++) {
+ /* Absolute values */
+ pd->pd_pwr[point] =
+ pcinfo->pwr_x0[point];
+
+ /* Deltas */
+ pd->pd_step[point] =
+ pd->pd_step[point - 1] +
+ pcinfo->pcdac_x0[point];
+ }
+
+ /* Set min power for this frequency */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+ /* Highest gain curve (min power) */
+ } else if (pdg == 1) {
+
+ pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ return -ENOMEM;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ return -ENOMEM;
+
+ /* Fill raw dataset
+ * (all power levels are in 0.25dB units) */
+ for (point = 0; point < pd->pd_points;
+ point++) {
+ /* Absolute values */
+ pd->pd_pwr[point] =
+ pcinfo->pwr_x3[point];
+
+ /* Fixed points */
+ pd->pd_step[point] =
+ pcinfo->pcdac_x3[point];
+ }
+
+ /* Since we have a higher gain curve
+ * override min power */
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Parse EEPROM data */
static int
ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
struct ath5k_chan_pcal_info *gen_chan_info;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
u32 offset;
- unsigned int i, c;
+ u8 i, c;
u16 val;
int ret;
+ u8 pd_gains = 0;
+
+ /* Count how many curves we have and
+ * identify them (which one of the 4
+ * available curves we have on each count).
+ * Curves are stored from lower (x0) to
+ * higher (x3) gain */
+ for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) {
+ /* ee_x_gain[mode] is x gain mask */
+ if ((ee->ee_x_gain[mode] >> i) & 0x1)
+ pdgain_idx[pd_gains++] = i;
+ }
+ ee->ee_pd_gains[mode] = pd_gains;
+
+ if (pd_gains == 0 || pd_gains > 2)
+ return -EINVAL;
switch (mode) {
case AR5K_EEPROM_MODE_11A:
@@ -808,13 +1017,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
for (i = 0; i < ee->ee_n_piers[mode]; i++) {
chan_pcal_info = &gen_chan_info[i].rf5112_info;
- /* Power values in dBm * 4
+ /* Power values in quarter dB
* for the lower xpd gain curve
* (0 dBm -> higher output power) */
for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_x0[c] = (val & 0xff);
- chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff);
+ chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
+ chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
}
/* PCDAC steps
@@ -825,12 +1034,12 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
- /* Power values in dBm * 4
+ /* Power values in quarter dB
* for the higher xpd gain curve
* (18 dBm -> lower output power) */
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_x3[0] = (val & 0xff);
- chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff);
+ chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
+ chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr_x3[2] = (val & 0xff);
@@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
chan_pcal_info->pcdac_x3[2] = 63;
if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
- chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff);
+ chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f);
/* Last xpd0 power level is also channel maximum */
gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
} else {
chan_pcal_info->pcdac_x0[0] = 1;
- gen_chan_info[i].max_pwr = ((val >> 8) & 0xff);
+ gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff);
}
- /* Recreate pcdac_x0 table for this channel using pcdac steps */
- chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0];
- chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1];
- chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2];
}
- return 0;
+ return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info);
}
+
+/*
+ * Read power calibration for RF2413 chips
+ *
+ * For RF2413 we have a Power to PDDAC table (Power Detector)
+ * instead of a PCDAC and 4 pd gain curves for each calibrated channel.
+ * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y
+ * axis and looks like an exponential function like the RF5111 curve.
+ *
+ * To recreate the curves we read here the points and interpolate
+ * later. Note that in most cases only 2 (higher and lower) curves are
+ * used (like RF5112) but vendors have the oportunity to include all
+ * 4 curves on eeprom. The final curve (higher power) has an extra
+ * point for better accuracy like RF5112.
+ */
+
/* For RF2413 power calibration data doesn't start on a fixed location and
* if a mode is not supported, it's section is missing -not zeroed-.
* So we need to calculate the starting offset for each section by using
@@ -890,13 +1111,15 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
switch(mode) {
case AR5K_EEPROM_MODE_11G:
if (AR5K_EEPROM_HDR_11B(ee->ee_header))
- offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) +
- AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ offset += ath5k_pdgains_size_2413(ee,
+ AR5K_EEPROM_MODE_11B) +
+ AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
/* fall through */
case AR5K_EEPROM_MODE_11B:
if (AR5K_EEPROM_HDR_11A(ee->ee_header))
- offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) +
- AR5K_EEPROM_N_5GHZ_CHAN / 2;
+ offset += ath5k_pdgains_size_2413(ee,
+ AR5K_EEPROM_MODE_11A) +
+ AR5K_EEPROM_N_5GHZ_CHAN / 2;
/* fall through */
case AR5K_EEPROM_MODE_11A:
break;
@@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
return offset;
}
-/* Read power calibration for RF2413 chips
- * For RF2413 we have a PDDAC table (Power Detector) instead
- * of a PCDAC and 4 pd gain curves for each calibrated channel.
- * Each curve has PDDAC steps on x axis and power on y axis and
- * looks like an exponential function. To recreate the curves
- * we read here the points and interpolate later. Note that
- * in most cases only higher and lower curves are used (like
- * RF5112) but vendors have the oportunity to include all 4
- * curves on eeprom. The final curve (higher power) has an extra
- * point for better accuracy like RF5112.
- */
+/* Convert RF2413 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+ unsigned int pier, pdg, point;
+
+ /* Fill raw data for each calibration pier */
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+ pcinfo = &chinfo[pier].rf2413_info;
+
+ /* Allocate pd_curves for this cal pier */
+ chinfo[pier].pd_curves =
+ kcalloc(AR5K_EEPROM_N_PD_CURVES,
+ sizeof(struct ath5k_pdgain_info),
+ GFP_KERNEL);
+
+ if (!chinfo[pier].pd_curves)
+ return -ENOMEM;
+
+ /* Fill pd_curves */
+ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+ u8 idx = pdgain_idx[pdg];
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[idx];
+
+ /* One more point for the highest power
+ * curve (lowest gain) */
+ if (pdg == ee->ee_pd_gains[mode] - 1)
+ pd->pd_points = AR5K_EEPROM_N_PD_POINTS;
+ else
+ pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1;
+
+ /* Allocate pd points for this curve */
+ pd->pd_step = kcalloc(pd->pd_points,
+ sizeof(u8), GFP_KERNEL);
+
+ if (!pd->pd_step)
+ return -ENOMEM;
+
+ pd->pd_pwr = kcalloc(pd->pd_points,
+ sizeof(s16), GFP_KERNEL);
+
+ if (!pd->pd_pwr)
+ return -ENOMEM;
+
+ /* Fill raw dataset
+ * convert all pwr levels to
+ * quarter dB for RF5112 combatibility */
+ pd->pd_step[0] = pcinfo->pddac_i[pdg];
+ pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
+
+ for (point = 1; point < pd->pd_points; point++) {
+
+ pd->pd_pwr[point] = pd->pd_pwr[point - 1] +
+ 2 * pcinfo->pwr[pdg][point - 1];
+
+ pd->pd_step[point] = pd->pd_step[point - 1] +
+ pcinfo->pddac[pdg][point - 1];
+
+ }
+
+ /* Highest gain curve -> min power */
+ if (pdg == 0)
+ chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+ /* Lowest gain curve -> max power */
+ if (pdg == ee->ee_pd_gains[mode] - 1)
+ chinfo[pier].max_pwr =
+ pd->pd_pwr[pd->pd_points - 1];
+ }
+ }
+
+ return 0;
+}
+
+/* Parse EEPROM data */
static int
ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
- struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info;
- struct ath5k_chan_pcal_info *gen_chan_info;
- unsigned int i, c;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ struct ath5k_chan_pcal_info *chinfo;
+ u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
u32 offset;
- int ret;
+ int idx, i, ret;
u16 val;
u8 pd_gains = 0;
- if (ee->ee_x_gain[mode] & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++;
+ /* Count how many curves we have and
+ * identify them (which one of the 4
+ * available curves we have on each count).
+ * Curves are stored from higher to
+ * lower gain so we go backwards */
+ for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) {
+ /* ee_x_gain[mode] is x gain mask */
+ if ((ee->ee_x_gain[mode] >> idx) & 0x1)
+ pdgain_idx[pd_gains++] = idx;
+
+ }
ee->ee_pd_gains[mode] = pd_gains;
+ if (pd_gains == 0)
+ return -EINVAL;
+
offset = ath5k_cal_data_offset_2413(ee, mode);
- ee->ee_n_piers[mode] = 0;
switch (mode) {
case AR5K_EEPROM_MODE_11A:
if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
@@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11a_pcal_freq(ah, offset);
offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
- gen_chan_info = ee->ee_pwr_cal_a;
+ chinfo = ee->ee_pwr_cal_a;
break;
case AR5K_EEPROM_MODE_11B:
if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
@@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11bg_2413(ah, mode, offset);
offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
- gen_chan_info = ee->ee_pwr_cal_b;
+ chinfo = ee->ee_pwr_cal_b;
break;
case AR5K_EEPROM_MODE_11G:
if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
@@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11bg_2413(ah, mode, offset);
offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
- gen_chan_info = ee->ee_pwr_cal_g;
+ chinfo = ee->ee_pwr_cal_g;
break;
default:
return -EINVAL;
}
- if (pd_gains == 0)
- return 0;
-
for (i = 0; i < ee->ee_n_piers[mode]; i++) {
- chan_pcal_info = &gen_chan_info[i].rf2413_info;
+ pcinfo = &chinfo[i].rf2413_info;
/*
* Read pwr_i, pddac_i and the first
* 2 pd points (pwr, pddac)
*/
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_i[0] = val & 0x1f;
- chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f;
- chan_pcal_info->pwr[0][0] =
- (val >> 12) & 0xf;
+ pcinfo->pwr_i[0] = val & 0x1f;
+ pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
+ pcinfo->pwr[0][0] = (val >> 12) & 0xf;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[0][0] = val & 0x3f;
- chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf;
- chan_pcal_info->pddac[0][1] =
- (val >> 10) & 0x3f;
+ pcinfo->pddac[0][0] = val & 0x3f;
+ pcinfo->pwr[0][1] = (val >> 6) & 0xf;
+ pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[0][2] = val & 0xf;
- chan_pcal_info->pddac[0][2] =
- (val >> 4) & 0x3f;
+ pcinfo->pwr[0][2] = val & 0xf;
+ pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
- chan_pcal_info->pwr[0][3] = 0;
- chan_pcal_info->pddac[0][3] = 0;
+ pcinfo->pwr[0][3] = 0;
+ pcinfo->pddac[0][3] = 0;
if (pd_gains > 1) {
/*
@@ -1003,44 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
* so it only has 2 pd points.
* Continue wih pd gain 1.
*/
- chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f;
+ pcinfo->pwr_i[1] = (val >> 10) & 0x1f;
- chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1;
+ pcinfo->pddac_i[1] = (val >> 15) & 0x1;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1;
+ pcinfo->pddac_i[1] |= (val & 0x3F) << 1;
- chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf;
- chan_pcal_info->pddac[1][0] =
- (val >> 10) & 0x3f;
+ pcinfo->pwr[1][0] = (val >> 6) & 0xf;
+ pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[1][1] = val & 0xf;
- chan_pcal_info->pddac[1][1] =
- (val >> 4) & 0x3f;
- chan_pcal_info->pwr[1][2] =
- (val >> 10) & 0xf;
-
- chan_pcal_info->pddac[1][2] =
- (val >> 14) & 0x3;
+ pcinfo->pwr[1][1] = val & 0xf;
+ pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+ pcinfo->pwr[1][2] = (val >> 10) & 0xf;
+
+ pcinfo->pddac[1][2] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[1][2] |=
- (val & 0xF) << 2;
+ pcinfo->pddac[1][2] |= (val & 0xF) << 2;
- chan_pcal_info->pwr[1][3] = 0;
- chan_pcal_info->pddac[1][3] = 0;
+ pcinfo->pwr[1][3] = 0;
+ pcinfo->pddac[1][3] = 0;
} else if (pd_gains == 1) {
/*
* Pd gain 0 is the last one so
* read the extra point.
*/
- chan_pcal_info->pwr[0][3] =
- (val >> 10) & 0xf;
+ pcinfo->pwr[0][3] = (val >> 10) & 0xf;
- chan_pcal_info->pddac[0][3] =
- (val >> 14) & 0x3;
+ pcinfo->pddac[0][3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[0][3] |=
- (val & 0xF) << 2;
+ pcinfo->pddac[0][3] |= (val & 0xF) << 2;
}
/*
@@ -1048,105 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
* as above.
*/
if (pd_gains > 2) {
- chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f;
- chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f;
+ pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
+ pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[2][0] =
- (val >> 0) & 0xf;
- chan_pcal_info->pddac[2][0] =
- (val >> 4) & 0x3f;
- chan_pcal_info->pwr[2][1] =
- (val >> 10) & 0xf;
-
- chan_pcal_info->pddac[2][1] =
- (val >> 14) & 0x3;
+ pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+ pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+ pcinfo->pwr[2][1] = (val >> 10) & 0xf;
+
+ pcinfo->pddac[2][1] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[2][1] |=
- (val & 0xF) << 2;
+ pcinfo->pddac[2][1] |= (val & 0xF) << 2;
- chan_pcal_info->pwr[2][2] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[2][2] =
- (val >> 8) & 0x3f;
+ pcinfo->pwr[2][2] = (val >> 4) & 0xf;
+ pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
- chan_pcal_info->pwr[2][3] = 0;
- chan_pcal_info->pddac[2][3] = 0;
+ pcinfo->pwr[2][3] = 0;
+ pcinfo->pddac[2][3] = 0;
} else if (pd_gains == 2) {
- chan_pcal_info->pwr[1][3] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[1][3] =
- (val >> 8) & 0x3f;
+ pcinfo->pwr[1][3] = (val >> 4) & 0xf;
+ pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
}
if (pd_gains > 3) {
- chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3;
+ pcinfo->pwr_i[3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+ pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
- chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f;
- chan_pcal_info->pwr[3][0] =
- (val >> 10) & 0xf;
- chan_pcal_info->pddac[3][0] =
- (val >> 14) & 0x3;
+ pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
+ pcinfo->pwr[3][0] = (val >> 10) & 0xf;
+ pcinfo->pddac[3][0] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[3][0] |=
- (val & 0xF) << 2;
- chan_pcal_info->pwr[3][1] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[3][1] =
- (val >> 8) & 0x3f;
-
- chan_pcal_info->pwr[3][2] =
- (val >> 14) & 0x3;
+ pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+ pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+ pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
+
+ pcinfo->pwr[3][2] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[3][2] |=
- ((val >> 0) & 0x3) << 2;
+ pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
- chan_pcal_info->pddac[3][2] =
- (val >> 2) & 0x3f;
- chan_pcal_info->pwr[3][3] =
- (val >> 8) & 0xf;
+ pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
+ pcinfo->pwr[3][3] = (val >> 8) & 0xf;
- chan_pcal_info->pddac[3][3] =
- (val >> 12) & 0xF;
+ pcinfo->pddac[3][3] = (val >> 12) & 0xF;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[3][3] |=
- ((val >> 0) & 0x3) << 4;
+ pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
} else if (pd_gains == 3) {
- chan_pcal_info->pwr[2][3] =
- (val >> 14) & 0x3;
+ pcinfo->pwr[2][3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[2][3] |=
- ((val >> 0) & 0x3) << 2;
-
- chan_pcal_info->pddac[2][3] =
- (val >> 2) & 0x3f;
- }
+ pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
- for (c = 0; c < pd_gains; c++) {
- /* Recreate pwr table for this channel using pwr steps */
- chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2;
- chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0];
- chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1];
- chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2];
- if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2])
- chan_pcal_info->pwr[c][3] = 0;
-
- /* Recreate pddac table for this channel using pddac steps */
- chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c];
- chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0];
- chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1];
- chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2];
- if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2])
- chan_pcal_info->pddac[c][3] = 0;
+ pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
}
}
- return 0;
+ return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
}
+
/*
* Read per rate target power (this is the maximum tx power
* supported by the card). This info is used when setting
@@ -1154,11 +1404,12 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
*
* This also works for v5 EEPROMs.
*/
-static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
+static int
+ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_rate_pcal_info *rate_pcal_info;
- u16 *rate_target_pwr_num;
+ u8 *rate_target_pwr_num;
u32 offset;
u16 val;
int ret, i;
@@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
else
read_pcal = ath5k_eeprom_read_pcal_info_5111;
- for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G;
+ mode++) {
err = read_pcal(ah, mode);
if (err)
return err;
@@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
return 0;
}
+static int
+ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *chinfo;
+ u8 pier, pdg;
+
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+ chinfo = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+ if (!chinfo[pier].pd_curves)
+ continue;
+
+ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+ struct ath5k_pdgain_info *pd =
+ &chinfo[pier].pd_curves[pdg];
+
+ if (pd != NULL) {
+ kfree(pd->pd_step);
+ kfree(pd->pd_pwr);
+ }
+ }
+
+ kfree(chinfo[pier].pd_curves);
+ }
+
+ return 0;
+}
+
+void
+ath5k_eeprom_detach(struct ath5k_hw *ah)
+{
+ u8 mode;
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++)
+ ath5k_eeprom_free_pcal_info(ah, mode);
+}
+
/* Read conformance test limits used for regulatory control */
static int
ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
@@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah)
else
return false;
}
+
diff --git a/trunk/drivers/net/wireless/ath5k/eeprom.h b/trunk/drivers/net/wireless/ath5k/eeprom.h
index 1deebc0257d4..b0c0606dea0b 100644
--- a/trunk/drivers/net/wireless/ath5k/eeprom.h
+++ b/trunk/drivers/net/wireless/ath5k/eeprom.h
@@ -173,6 +173,7 @@
#define AR5K_EEPROM_N_5GHZ_CHAN 10
#define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
+#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
#define AR5K_EEPROM_MAX_CHAN 10
#define AR5K_EEPROM_N_PWR_POINTS_5111 11
#define AR5K_EEPROM_N_PCDAC 11
@@ -193,7 +194,7 @@
#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
#define AR5K_EEPROM_MAX_CTLS 32
-#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4
+#define AR5K_EEPROM_N_PD_CURVES 4
#define AR5K_EEPROM_N_XPD0_POINTS 4
#define AR5K_EEPROM_N_XPD3_POINTS 3
#define AR5K_EEPROM_N_PD_GAINS 4
@@ -232,7 +233,7 @@ enum ath5k_ctl_mode {
AR5K_CTL_11B = 1,
AR5K_CTL_11G = 2,
AR5K_CTL_TURBO = 3,
- AR5K_CTL_108G = 4,
+ AR5K_CTL_TURBOG = 4,
AR5K_CTL_2GHT20 = 5,
AR5K_CTL_5GHT20 = 6,
AR5K_CTL_2GHT40 = 7,
@@ -240,65 +241,114 @@ enum ath5k_ctl_mode {
AR5K_CTL_MODE_M = 15,
};
+/* Default CTL ids for the 3 main reg domains.
+ * Atheros only uses these by default but vendors
+ * can have up to 32 different CTLs for different
+ * scenarios. Note that theese values are ORed with
+ * the mode id (above) so we can have up to 24 CTL
+ * datasets out of these 3 main regdomains. That leaves
+ * 8 ids that can be used by vendors and since 0x20 is
+ * missing from HAL sources i guess this is the set of
+ * custom CTLs vendors can use. */
+#define AR5K_CTL_FCC 0x10
+#define AR5K_CTL_CUSTOM 0x20
+#define AR5K_CTL_ETSI 0x30
+#define AR5K_CTL_MKK 0x40
+
+/* Indicates a CTL with only mode set and
+ * no reg domain mapping, such CTLs are used
+ * for world roaming domains or simply when
+ * a reg domain is not set */
+#define AR5K_CTL_NO_REGDOMAIN 0xf0
+
+/* Indicates an empty (invalid) CTL */
+#define AR5K_CTL_NO_CTL 0xff
+
/* Per channel calibration data, used for power table setup */
struct ath5k_chan_pcal_info_rf5111 {
/* Power levels in half dbm units
* for one power curve. */
- u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+ u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
/* PCDAC table steps
* for the above values */
- u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+ u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
/* Starting PCDAC step */
- u8 pcdac_min;
+ u8 pcdac_min;
/* Final PCDAC step */
- u8 pcdac_max;
+ u8 pcdac_max;
};
struct ath5k_chan_pcal_info_rf5112 {
/* Power levels in quarter dBm units
* for lower (0) and higher (3)
- * level curves */
- s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
- s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+ * level curves in 0.25dB units */
+ s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
/* PCDAC table steps
* for the above values */
- u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
- u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+ u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
};
struct ath5k_chan_pcal_info_rf2413 {
/* Starting pwr/pddac values */
- s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
- u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
- /* (pwr,pddac) points */
- s8 pwr[AR5K_EEPROM_N_PD_GAINS]
- [AR5K_EEPROM_N_PD_POINTS];
- u8 pddac[AR5K_EEPROM_N_PD_GAINS]
- [AR5K_EEPROM_N_PD_POINTS];
+ s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+ u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+ /* (pwr,pddac) points
+ * power levels in 0.5dB units */
+ s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+ u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+};
+
+enum ath5k_powertable_type {
+ AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
+ AR5K_PWRTABLE_LINEAR_PCDAC = 1,
+ AR5K_PWRTABLE_PWR_TO_PDADC = 2,
+};
+
+struct ath5k_pdgain_info {
+ u8 pd_points;
+ u8 *pd_step;
+ /* Power values are in
+ * 0.25dB units */
+ s16 *pd_pwr;
};
struct ath5k_chan_pcal_info {
/* Frequency */
u16 freq;
- /* Max available power */
- s8 max_pwr;
+ /* Tx power boundaries */
+ s16 max_pwr;
+ s16 min_pwr;
union {
struct ath5k_chan_pcal_info_rf5111 rf5111_info;
struct ath5k_chan_pcal_info_rf5112 rf5112_info;
struct ath5k_chan_pcal_info_rf2413 rf2413_info;
};
+ /* Raw values used by phy code
+ * Curves are stored in order from lower
+ * gain to higher gain (max txpower -> min txpower) */
+ struct ath5k_pdgain_info *pd_curves;
};
-/* Per rate calibration data for each mode, used for power table setup */
+/* Per rate calibration data for each mode,
+ * used for rate power table setup.
+ * Note: Values in 0.5dB units */
struct ath5k_rate_pcal_info {
u16 freq; /* Frequency */
- /* Power level for 6-24Mbit/s rates */
+ /* Power level for 6-24Mbit/s rates or
+ * 1Mb rate */
u16 target_power_6to24;
- /* Power level for 36Mbit rate */
+ /* Power level for 36Mbit rate or
+ * 2Mb rate */
u16 target_power_36;
- /* Power level for 48Mbit rate */
+ /* Power level for 48Mbit rate or
+ * 5.5Mbit rate */
u16 target_power_48;
- /* Power level for 54Mbit rate */
+ /* Power level for 54Mbit rate or
+ * 11Mbit rate */
u16 target_power_54;
};
@@ -330,12 +380,6 @@ struct ath5k_eeprom_info {
u16 ee_cck_ofdm_power_delta;
u16 ee_scaled_cck_delta;
- /* Used for tx thermal adjustment (eeprom_init, rfregs) */
- u16 ee_tx_clip;
- u16 ee_pwd_84;
- u16 ee_pwd_90;
- u16 ee_gain_select;
-
/* RF Calibration settings (reset, rfregs) */
u16 ee_i_cal[AR5K_EEPROM_N_MODES];
u16 ee_q_cal[AR5K_EEPROM_N_MODES];
@@ -363,23 +407,25 @@ struct ath5k_eeprom_info {
/* Power calibration data */
u16 ee_false_detect[AR5K_EEPROM_N_MODES];
- /* Number of pd gain curves per mode (RF2413) */
- u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+ /* Number of pd gain curves per mode */
+ u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+ /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
+ u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
- u8 ee_n_piers[AR5K_EEPROM_N_MODES];
+ u8 ee_n_piers[AR5K_EEPROM_N_MODES];
struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
- struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN];
- struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN];
+ struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+ struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Per rate target power levels */
- u16 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+ u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
- struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN];
- struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Conformance test limits (Unused) */
- u16 ee_ctls;
- u16 ee_ctl[AR5K_EEPROM_MAX_CTLS];
+ u8 ee_ctls;
+ u8 ee_ctl[AR5K_EEPROM_MAX_CTLS];
struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
/* Noise Floor Calibration settings */
diff --git a/trunk/drivers/net/wireless/ath5k/initvals.c b/trunk/drivers/net/wireless/ath5k/initvals.c
index 44886434187b..61fb621ed20d 100644
--- a/trunk/drivers/net/wireless/ath5k/initvals.c
+++ b/trunk/drivers/net/wireless/ath5k/initvals.c
@@ -1510,8 +1510,8 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
rf2425_ini_mode_end, mode);
ath5k_hw_ini_registers(ah,
- ARRAY_SIZE(rf2413_ini_common_end),
- rf2413_ini_common_end, change_channel);
+ ARRAY_SIZE(rf2425_ini_common_end),
+ rf2425_ini_common_end, change_channel);
ath5k_hw_ini_registers(ah,
ARRAY_SIZE(rf5112_ini_bbgain),
diff --git a/trunk/drivers/net/wireless/ath5k/led.c b/trunk/drivers/net/wireless/ath5k/led.c
index 0686e12738b3..19555fb79c9b 100644
--- a/trunk/drivers/net/wireless/ath5k/led.c
+++ b/trunk/drivers/net/wireless/ath5k/led.c
@@ -65,6 +65,8 @@ static const struct pci_device_id ath5k_led_devices[] = {
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
/* E-machines E510 (tuliom@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) },
+ /* Acer Extensa 5620z (nekoreeve@gmail.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) },
{ }
};
diff --git a/trunk/drivers/net/wireless/ath5k/phy.c b/trunk/drivers/net/wireless/ath5k/phy.c
index 81f5bebc48b1..9e2faae5ae94 100644
--- a/trunk/drivers/net/wireless/ath5k/phy.c
+++ b/trunk/drivers/net/wireless/ath5k/phy.c
@@ -4,6 +4,7 @@
* Copyright (c) 2004-2007 Reyk Floeter
* Copyright (c) 2006-2009 Nick Kossifidis
* Copyright (c) 2007-2008 Jiri Slaby
+ * Copyright (c) 2008-2009 Felix Fietkau
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -183,7 +184,9 @@ static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
return;
- ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max,
+ /* Send the packet with 2dB below max power as
+ * patent doc suggest */
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max_pwr - 4,
AR5K_PHY_PAPD_PROBE_TXPOWER) |
AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
@@ -1433,93 +1436,1120 @@ unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah)
return false; /*XXX: What do we return for 5210 ?*/
}
+
+/****************\
+* TX power setup *
+\****************/
+
+/*
+ * Helper functions
+ */
+
+/*
+ * Do linear interpolation between two given (x, y) points
+ */
+static s16
+ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
+ s16 y_left, s16 y_right)
+{
+ s16 ratio, result;
+
+ /* Avoid divide by zero and skip interpolation
+ * if we have the same point */
+ if ((x_left == x_right) || (y_left == y_right))
+ return y_left;
+
+ /*
+ * Since we use ints and not fps, we need to scale up in
+ * order to get a sane ratio value (or else we 'll eg. get
+ * always 1 instead of 1.25, 1.75 etc). We scale up by 100
+ * to have some accuracy both for 0.5 and 0.25 steps.
+ */
+ ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left));
+
+ /* Now scale down to be in range */
+ result = y_left + (ratio * (target - x_left) / 100);
+
+ return result;
+}
+
+/*
+ * Find vertical boundary (min pwr) for the linear PCDAC curve.
+ *
+ * Since we have the top of the curve and we draw the line below
+ * until we reach 1 (1 pcdac step) we need to know which point
+ * (x value) that is so that we don't go below y axis and have negative
+ * pcdac values when creating the curve, or fill the table with zeroes.
+ */
+static s16
+ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
+ const s16 *pwrL, const s16 *pwrR)
+{
+ s8 tmp;
+ s16 min_pwrL, min_pwrR;
+ s16 pwr_i = pwrL[0];
+
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrL[0], pwrL[1],
+ stepL[0], stepL[1]);
+
+ } while (tmp > 1);
+
+ min_pwrL = pwr_i;
+
+ pwr_i = pwrR[0];
+ do {
+ pwr_i--;
+ tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+ pwrR[0], pwrR[1],
+ stepR[0], stepR[1]);
+
+ } while (tmp > 1);
+
+ min_pwrR = pwr_i;
+
+ /* Keep the right boundary so that it works for both curves */
+ return max(min_pwrL, min_pwrR);
+}
+
+/*
+ * Interpolate (pwr,vpd) points to create a Power to PDADC or a
+ * Power to PCDAC curve.
+ *
+ * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC
+ * steps (offsets) on y axis. Power can go up to 31.5dB and max
+ * PCDAC/PDADC step for each curve is 64 but we can write more than
+ * one curves on hw so we can go up to 128 (which is the max step we
+ * can write on the final table).
+ *
+ * We write y values (PCDAC/PDADC steps) on hw.
+ */
+static void
+ath5k_create_power_curve(s16 pmin, s16 pmax,
+ const s16 *pwr, const u8 *vpd,
+ u8 num_points,
+ u8 *vpd_table, u8 type)
+{
+ u8 idx[2] = { 0, 1 };
+ s16 pwr_i = 2*pmin;
+ int i;
+
+ if (num_points < 2)
+ return;
+
+ /* We want the whole line, so adjust boundaries
+ * to cover the entire power range. Note that
+ * power values are already 0.25dB so no need
+ * to multiply pwr_i by 2 */
+ if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
+ pwr_i = pmin;
+ pmin = 0;
+ pmax = 63;
+ }
+
+ /* Find surrounding turning points (TPs)
+ * and interpolate between them */
+ for (i = 0; (i <= (u16) (pmax - pmin)) &&
+ (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+
+ /* We passed the right TP, move to the next set of TPs
+ * if we pass the last TP, extrapolate above using the last
+ * two TPs for ratio */
+ if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
+ idx[0]++;
+ idx[1]++;
+ }
+
+ vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
+ pwr[idx[0]], pwr[idx[1]],
+ vpd[idx[0]], vpd[idx[1]]);
+
+ /* Increase by 0.5dB
+ * (0.25 dB units) */
+ pwr_i += 2;
+ }
+}
+
+/*
+ * Get the surrounding per-channel power calibration piers
+ * for a given frequency so that we can interpolate between
+ * them and come up with an apropriate dataset for our current
+ * channel.
+ */
+static void
+ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ struct ath5k_chan_pcal_info **pcinfo_l,
+ struct ath5k_chan_pcal_info **pcinfo_r)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcinfo;
+ u8 idx_l, idx_r;
+ u8 mode, max, i;
+ u32 target = channel->center_freq;
+
+ idx_l = 0;
+ idx_r = 0;
+
+ if (!(channel->hw_value & CHANNEL_OFDM)) {
+ pcinfo = ee->ee_pwr_cal_b;
+ mode = AR5K_EEPROM_MODE_11B;
+ } else if (channel->hw_value & CHANNEL_2GHZ) {
+ pcinfo = ee->ee_pwr_cal_g;
+ mode = AR5K_EEPROM_MODE_11G;
+ } else {
+ pcinfo = ee->ee_pwr_cal_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ }
+ max = ee->ee_n_piers[mode] - 1;
+
+ /* Frequency is below our calibrated
+ * range. Use the lowest power curve
+ * we have */
+ if (target < pcinfo[0].freq) {
+ idx_l = idx_r = 0;
+ goto done;
+ }
+
+ /* Frequency is above our calibrated
+ * range. Use the highest power curve
+ * we have */
+ if (target > pcinfo[max].freq) {
+ idx_l = idx_r = max;
+ goto done;
+ }
+
+ /* Frequency is inside our calibrated
+ * channel range. Pick the surrounding
+ * calibration piers so that we can
+ * interpolate */
+ for (i = 0; i <= max; i++) {
+
+ /* Frequency matches one of our calibration
+ * piers, no need to interpolate, just use
+ * that calibration pier */
+ if (pcinfo[i].freq == target) {
+ idx_l = idx_r = i;
+ goto done;
+ }
+
+ /* We found a calibration pier that's above
+ * frequency, use this pier and the previous
+ * one to interpolate */
+ if (target < pcinfo[i].freq) {
+ idx_r = i;
+ idx_l = idx_r - 1;
+ goto done;
+ }
+ }
+
+done:
+ *pcinfo_l = &pcinfo[idx_l];
+ *pcinfo_r = &pcinfo[idx_r];
+
+ return;
+}
+
+/*
+ * Get the surrounding per-rate power calibration data
+ * for a given frequency and interpolate between power
+ * values to set max target power supported by hw for
+ * each rate.
+ */
+static void
+ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ struct ath5k_rate_pcal_info *rates)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_rate_pcal_info *rpinfo;
+ u8 idx_l, idx_r;
+ u8 mode, max, i;
+ u32 target = channel->center_freq;
+
+ idx_l = 0;
+ idx_r = 0;
+
+ if (!(channel->hw_value & CHANNEL_OFDM)) {
+ rpinfo = ee->ee_rate_tpwr_b;
+ mode = AR5K_EEPROM_MODE_11B;
+ } else if (channel->hw_value & CHANNEL_2GHZ) {
+ rpinfo = ee->ee_rate_tpwr_g;
+ mode = AR5K_EEPROM_MODE_11G;
+ } else {
+ rpinfo = ee->ee_rate_tpwr_a;
+ mode = AR5K_EEPROM_MODE_11A;
+ }
+ max = ee->ee_rate_target_pwr_num[mode] - 1;
+
+ /* Get the surrounding calibration
+ * piers - same as above */
+ if (target < rpinfo[0].freq) {
+ idx_l = idx_r = 0;
+ goto done;
+ }
+
+ if (target > rpinfo[max].freq) {
+ idx_l = idx_r = max;
+ goto done;
+ }
+
+ for (i = 0; i <= max; i++) {
+
+ if (rpinfo[i].freq == target) {
+ idx_l = idx_r = i;
+ goto done;
+ }
+
+ if (target < rpinfo[i].freq) {
+ idx_r = i;
+ idx_l = idx_r - 1;
+ goto done;
+ }
+ }
+
+done:
+ /* Now interpolate power value, based on the frequency */
+ rates->freq = target;
+
+ rates->target_power_6to24 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_6to24,
+ rpinfo[idx_r].target_power_6to24);
+
+ rates->target_power_36 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_36,
+ rpinfo[idx_r].target_power_36);
+
+ rates->target_power_48 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_48,
+ rpinfo[idx_r].target_power_48);
+
+ rates->target_power_54 =
+ ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+ rpinfo[idx_r].freq,
+ rpinfo[idx_l].target_power_54,
+ rpinfo[idx_r].target_power_54);
+}
+
+/*
+ * Get the max edge power for this channel if
+ * we have such data from EEPROM's Conformance Test
+ * Limits (CTL), and limit max power if needed.
+ *
+ * FIXME: Only works for world regulatory domains
+ */
+static void
+ath5k_get_max_ctl_power(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
+ u8 *ctl_val = ee->ee_ctl;
+ s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
+ s16 edge_pwr = 0;
+ u8 rep_idx;
+ u8 i, ctl_mode;
+ u8 ctl_idx = 0xFF;
+ u32 target = channel->center_freq;
+
+ /* Find out a CTL for our mode that's not mapped
+ * on a specific reg domain.
+ *
+ * TODO: Map our current reg domain to one of the 3 available
+ * reg domain ids so that we can support more CTLs. */
+ switch (channel->hw_value & CHANNEL_MODES) {
+ case CHANNEL_A:
+ ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN;
+ break;
+ case CHANNEL_G:
+ ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN;
+ break;
+ case CHANNEL_B:
+ ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN;
+ break;
+ case CHANNEL_T:
+ ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN;
+ break;
+ case CHANNEL_TG:
+ ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN;
+ break;
+ case CHANNEL_XR:
+ /* Fall through */
+ default:
+ return;
+ }
+
+ for (i = 0; i < ee->ee_ctls; i++) {
+ if (ctl_val[i] == ctl_mode) {
+ ctl_idx = i;
+ break;
+ }
+ }
+
+ /* If we have a CTL dataset available grab it and find the
+ * edge power for our frequency */
+ if (ctl_idx == 0xFF)
+ return;
+
+ /* Edge powers are sorted by frequency from lower
+ * to higher. Each CTL corresponds to 8 edge power
+ * measurements. */
+ rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
+
+ /* Don't do boundaries check because we
+ * might have more that one bands defined
+ * for this mode */
+
+ /* Get the edge power that's closer to our
+ * frequency */
+ for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
+ rep_idx += i;
+ if (target <= rep[rep_idx].freq)
+ edge_pwr = (s16) rep[rep_idx].edge;
+ }
+
+ if (edge_pwr)
+ ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr);
+}
+
+
+/*
+ * Power to PCDAC table functions
+ */
+
/*
- * TX power setup
+ * Fill Power to PCDAC table on RF5111
+ *
+ * No further processing is needed for RF5111, the only thing we have to
+ * do is fill the values below and above calibration range since eeprom data
+ * may not cover the entire PCDAC table.
*/
+static void
+ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
+ s16 *table_max)
+{
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_tmp = ah->ah_txpower.tmpL[0];
+ u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
+ s16 min_pwr, max_pwr;
+
+ /* Get table boundaries */
+ min_pwr = table_min[0];
+ pcdac_0 = pcdac_tmp[0];
+
+ max_pwr = table_max[0];
+ pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];
+
+ /* Extrapolate below minimum using pcdac_0 */
+ pcdac_i = 0;
+ for (i = 0; i < min_pwr; i++)
+ pcdac_out[pcdac_i++] = pcdac_0;
+
+ /* Copy values from pcdac_tmp */
+ pwr_idx = min_pwr;
+ for (i = 0 ; pwr_idx <= max_pwr &&
+ pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
+ pcdac_out[pcdac_i++] = pcdac_tmp[i];
+ pwr_idx++;
+ }
+
+ /* Extrapolate above maximum */
+ while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
+ pcdac_out[pcdac_i++] = pcdac_n;
+
+}
/*
- * Initialize the tx power table (not fully implemented)
+ * Combine available XPD Curves and fill Linear Power to PCDAC table
+ * on RF5112
+ *
+ * RFX112 can have up to 2 curves (one for low txpower range and one for
+ * higher txpower range). We need to put them both on pcdac_out and place
+ * them in the correct location. In case we only have one curve available
+ * just fit it on pcdac_out (it's supposed to cover the entire range of
+ * available pwr levels since it's always the higher power curve). Extrapolate
+ * below and above final table if needed.
*/
-static void ath5k_txpower_table(struct ath5k_hw *ah,
- struct ieee80211_channel *channel, s16 max_power)
+static void
+ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
+ s16 *table_max, u8 pdcurves)
{
- unsigned int i, min, max, n;
- u16 txpower, *rates;
-
- rates = ah->ah_txpower.txp_rates;
-
- txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2;
- if (max_power > txpower)
- txpower = max_power > AR5K_TUNE_MAX_TXPOWER ?
- AR5K_TUNE_MAX_TXPOWER : max_power;
-
- for (i = 0; i < AR5K_MAX_RATES; i++)
- rates[i] = txpower;
-
- /* XXX setup target powers by rate */
-
- ah->ah_txpower.txp_min = rates[7];
- ah->ah_txpower.txp_max = rates[0];
- ah->ah_txpower.txp_ofdm = rates[0];
-
- /* Calculate the power table */
- n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac);
- min = AR5K_EEPROM_PCDAC_START;
- max = AR5K_EEPROM_PCDAC_STOP;
- for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP)
- ah->ah_txpower.txp_pcdac[i] =
-#ifdef notyet
- min + ((i * (max - min)) / n);
-#else
- min;
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ u8 *pcdac_low_pwr;
+ u8 *pcdac_high_pwr;
+ u8 *pcdac_tmp;
+ u8 pwr;
+ s16 max_pwr_idx;
+ s16 min_pwr_idx;
+ s16 mid_pwr_idx = 0;
+ /* Edge flag turs on the 7nth bit on the PCDAC
+ * to delcare the higher power curve (force values
+ * to be greater than 64). If we only have one curve
+ * we don't need to set this, if we have 2 curves and
+ * fill the table backwards this can also be used to
+ * switch from higher power curve to lower power curve */
+ u8 edge_flag;
+ int i;
+
+ /* When we have only one curve available
+ * that's the higher power curve. If we have
+ * two curves the first is the high power curve
+ * and the next is the low power curve. */
+ if (pdcurves > 1) {
+ pcdac_low_pwr = ah->ah_txpower.tmpL[1];
+ pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+ mid_pwr_idx = table_max[1] - table_min[1] - 1;
+ max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+
+ /* If table size goes beyond 31.5dB, keep the
+ * upper 31.5dB range when setting tx power.
+ * Note: 126 = 31.5 dB in quarter dB steps */
+ if (table_max[0] - table_min[1] > 126)
+ min_pwr_idx = table_max[0] - 126;
+ else
+ min_pwr_idx = table_min[1];
+
+ /* Since we fill table backwards
+ * start from high power curve */
+ pcdac_tmp = pcdac_high_pwr;
+
+ edge_flag = 0x40;
+#if 0
+ /* If both min and max power limits are in lower
+ * power curve's range, only use the low power curve.
+ * TODO: min/max levels are related to target
+ * power values requested from driver/user
+ * XXX: Is this really needed ? */
+ if (min_pwr < table_max[1] &&
+ max_pwr < table_max[1]) {
+ edge_flag = 0;
+ pcdac_tmp = pcdac_low_pwr;
+ max_pwr_idx = (table_max[1] - table_min[1])/2;
+ }
#endif
+ } else {
+ pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
+ pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+ min_pwr_idx = table_min[0];
+ max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+ pcdac_tmp = pcdac_high_pwr;
+ edge_flag = 0;
+ }
+
+ /* This is used when setting tx power*/
+ ah->ah_txpower.txp_min_idx = min_pwr_idx/2;
+
+ /* Fill Power to PCDAC table backwards */
+ pwr = max_pwr_idx;
+ for (i = 63; i >= 0; i--) {
+ /* Entering lower power range, reset
+ * edge flag and set pcdac_tmp to lower
+ * power curve.*/
+ if (edge_flag == 0x40 &&
+ (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
+ edge_flag = 0x00;
+ pcdac_tmp = pcdac_low_pwr;
+ pwr = mid_pwr_idx/2;
+ }
+
+ /* Don't go below 1, extrapolate below if we have
+ * already swithced to the lower power curve -or
+ * we only have one curve and edge_flag is zero
+ * anyway */
+ if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
+ while (i >= 0) {
+ pcdac_out[i] = pcdac_out[i + 1];
+ i--;
+ }
+ break;
+ }
+
+ pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
+
+ /* Extrapolate above if pcdac is greater than
+ * 126 -this can happen because we OR pcdac_out
+ * value with edge_flag on high power curve */
+ if (pcdac_out[i] > 126)
+ pcdac_out[i] = 126;
+
+ /* Decrease by a 0.5dB step */
+ pwr--;
+ }
}
+/* Write PCDAC values on hw */
+static void
+ath5k_setup_pcdac_table(struct ath5k_hw *ah)
+{
+ u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
+ int i;
+
+ /*
+ * Write TX power values
+ */
+ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+ ath5k_hw_reg_write(ah,
+ (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) |
+ (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16),
+ AR5K_PHY_PCDAC_TXPOWER(i));
+ }
+}
+
+
/*
- * Set transmition power
+ * Power to PDADC table functions
*/
-int /*O.K. - txpower_table is unimplemented so this doesn't work*/
-ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
- unsigned int txpower)
+
+/*
+ * Set the gain boundaries and create final Power to PDADC table
+ *
+ * We can have up to 4 pd curves, we need to do a simmilar process
+ * as we do for RF5112. This time we don't have an edge_flag but we
+ * set the gain boundaries on a separate register.
+ */
+static void
+ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
+ s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
{
- bool tpc = ah->ah_txpower.txp_tpc;
- unsigned int i;
+ u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
+ u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+ u8 *pdadc_tmp;
+ s16 pdadc_0;
+ u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
+ u8 pd_gain_overlap;
+
+ /* Note: Register value is initialized on initvals
+ * there is no feedback from hw.
+ * XXX: What about pd_gain_overlap from EEPROM ? */
+ pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
+
+ /* Create final PDADC table */
+ for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
+ pdadc_tmp = ah->ah_txpower.tmpL[pdg];
+
+ if (pdg == pdcurves - 1)
+ /* 2 dB boundary stretch for last
+ * (higher power) curve */
+ gain_boundaries[pdg] = pwr_max[pdg] + 4;
+ else
+ /* Set gain boundary in the middle
+ * between this curve and the next one */
+ gain_boundaries[pdg] =
+ (pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
+
+ /* Sanity check in case our 2 db stretch got out of
+ * range. */
+ if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
+ gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
+
+ /* For the first curve (lower power)
+ * start from 0 dB */
+ if (pdg == 0)
+ pdadc_0 = 0;
+ else
+ /* For the other curves use the gain overlap */
+ pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
+ pd_gain_overlap;
- ATH5K_TRACE(ah->ah_sc);
- if (txpower > AR5K_TUNE_MAX_TXPOWER) {
- ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
- return -EINVAL;
+ /* Force each power step to be at least 0.5 dB */
+ if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
+ pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
+ else
+ pwr_step = 1;
+
+ /* If pdadc_0 is negative, we need to extrapolate
+ * below this pdgain by a number of pwr_steps */
+ while ((pdadc_0 < 0) && (pdadc_i < 128)) {
+ s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
+ pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
+ pdadc_0++;
+ }
+
+ /* Set last pwr level, using gain boundaries */
+ pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
+ /* Limit it to be inside pwr range */
+ table_size = pwr_max[pdg] - pwr_min[pdg];
+ max_idx = (pdadc_n < table_size) ? pdadc_n : table_size;
+
+ /* Fill pdadc_out table */
+ while (pdadc_0 < max_idx)
+ pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
+
+ /* Need to extrapolate above this pdgain? */
+ if (pdadc_n <= max_idx)
+ continue;
+
+ /* Force each power step to be at least 0.5 dB */
+ if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
+ pwr_step = pdadc_tmp[table_size - 1] -
+ pdadc_tmp[table_size - 2];
+ else
+ pwr_step = 1;
+
+ /* Extrapolate above */
+ while ((pdadc_0 < (s16) pdadc_n) &&
+ (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
+ s16 tmp = pdadc_tmp[table_size - 1] +
+ (pdadc_0 - max_idx) * pwr_step;
+ pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
+ pdadc_0++;
+ }
}
+ while (pdg < AR5K_EEPROM_N_PD_GAINS) {
+ gain_boundaries[pdg] = gain_boundaries[pdg - 1];
+ pdg++;
+ }
+
+ while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
+ pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
+ pdadc_i++;
+ }
+
+ /* Set gain boundaries */
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_SM(pd_gain_overlap,
+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
+ AR5K_REG_SM(gain_boundaries[0],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
+ AR5K_REG_SM(gain_boundaries[1],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
+ AR5K_REG_SM(gain_boundaries[2],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
+ AR5K_REG_SM(gain_boundaries[3],
+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
+ AR5K_PHY_TPC_RG5);
+
+ /* Used for setting rate power table */
+ ah->ah_txpower.txp_min_idx = pwr_min[0];
+
+}
+
+/* Write PDADC values on hw */
+static void
+ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah,
+ u8 pdcurves, u8 *pdg_to_idx)
+{
+ u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+ u32 reg;
+ u8 i;
+
+ /* Select the right pdgain curves */
+
+ /* Clear current settings */
+ reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
+ reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
+ AR5K_PHY_TPC_RG1_PDGAIN_2 |
+ AR5K_PHY_TPC_RG1_PDGAIN_3 |
+ AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
/*
- * RF2413 for some reason can't
- * transmit anything if we call
- * this funtion, so we skip it
- * until we fix txpower.
+ * Use pd_gains curve from eeprom
*
- * XXX: Assume same for RF2425
- * to be safe.
+ * This overrides the default setting from initvals
+ * in case some vendors (e.g. Zcomax) don't use the default
+ * curves. If we don't honor their settings we 'll get a
+ * 5dB (1 * gain overlap ?) drop.
*/
- if ((ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF2425))
- return 0;
+ reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
- /* Reset TX power values */
- memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
- ah->ah_txpower.txp_tpc = tpc;
-
- /* Initialize TX power table */
- ath5k_txpower_table(ah, channel, txpower);
+ switch (pdcurves) {
+ case 3:
+ reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
+ /* Fall through */
+ case 2:
+ reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
+ /* Fall through */
+ case 1:
+ reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
+ break;
+ }
+ ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
/*
* Write TX power values
*/
for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
ath5k_hw_reg_write(ah,
- ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) |
- (((ah->ah_txpower.txp_pcdac[(i << 1) ] << 8) | 0xff) & 0xffff),
- AR5K_PHY_PCDAC_TXPOWER(i));
+ ((pdadc_out[4*i + 0] & 0xff) << 0) |
+ ((pdadc_out[4*i + 1] & 0xff) << 8) |
+ ((pdadc_out[4*i + 2] & 0xff) << 16) |
+ ((pdadc_out[4*i + 3] & 0xff) << 24),
+ AR5K_PHY_PDADC_TXPOWER(i));
+ }
+}
+
+
+/*
+ * Common code for PCDAC/PDADC tables
+ */
+
+/*
+ * This is the main function that uses all of the above
+ * to set PCDAC/PDADC table on hw for the current channel.
+ * This table is used for tx power calibration on the basband,
+ * without it we get weird tx power levels and in some cases
+ * distorted spectral mask
+ */
+static int
+ath5k_setup_channel_powertable(struct ath5k_hw *ah,
+ struct ieee80211_channel *channel,
+ u8 ee_mode, u8 type)
+{
+ struct ath5k_pdgain_info *pdg_L, *pdg_R;
+ struct ath5k_chan_pcal_info *pcinfo_L;
+ struct ath5k_chan_pcal_info *pcinfo_R;
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
+ s16 table_min[AR5K_EEPROM_N_PD_GAINS];
+ s16 table_max[AR5K_EEPROM_N_PD_GAINS];
+ u8 *tmpL;
+ u8 *tmpR;
+ u32 target = channel->center_freq;
+ int pdg, i;
+
+ /* Get surounding freq piers for this channel */
+ ath5k_get_chan_pcal_surrounding_piers(ah, channel,
+ &pcinfo_L,
+ &pcinfo_R);
+
+ /* Loop over pd gain curves on
+ * surounding freq piers by index */
+ for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
+
+ /* Fill curves in reverse order
+ * from lower power (max gain)
+ * to higher power. Use curve -> idx
+ * backmaping we did on eeprom init */
+ u8 idx = pdg_curve_to_idx[pdg];
+
+ /* Grab the needed curves by index */
+ pdg_L = &pcinfo_L->pd_curves[idx];
+ pdg_R = &pcinfo_R->pd_curves[idx];
+
+ /* Initialize the temp tables */
+ tmpL = ah->ah_txpower.tmpL[pdg];
+ tmpR = ah->ah_txpower.tmpR[pdg];
+
+ /* Set curve's x boundaries and create
+ * curves so that they cover the same
+ * range (if we don't do that one table
+ * will have values on some range and the
+ * other one won't have any so interpolation
+ * will fail) */
+ table_min[pdg] = min(pdg_L->pd_pwr[0],
+ pdg_R->pd_pwr[0]) / 2;
+
+ table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+ pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;
+
+ /* Now create the curves on surrounding channels
+ * and interpolate if needed to get the final
+ * curve for this gain on this channel */
+ switch (type) {
+ case AR5K_PWRTABLE_LINEAR_PCDAC:
+ /* Override min/max so that we don't loose
+ * accuracy (don't divide by 2) */
+ table_min[pdg] = min(pdg_L->pd_pwr[0],
+ pdg_R->pd_pwr[0]);
+
+ table_max[pdg] =
+ max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+ pdg_R->pd_pwr[pdg_R->pd_points - 1]);
+
+ /* Override minimum so that we don't get
+ * out of bounds while extrapolating
+ * below. Don't do this when we have 2
+ * curves and we are on the high power curve
+ * because table_min is ok in this case */
+ if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
+
+ table_min[pdg] =
+ ath5k_get_linear_pcdac_min(pdg_L->pd_step,
+ pdg_R->pd_step,
+ pdg_L->pd_pwr,
+ pdg_R->pd_pwr);
+
+ /* Don't go too low because we will
+ * miss the upper part of the curve.
+ * Note: 126 = 31.5dB (max power supported)
+ * in 0.25dB units */
+ if (table_max[pdg] - table_min[pdg] > 126)
+ table_min[pdg] = table_max[pdg] - 126;
+ }
+
+ /* Fall through */
+ case AR5K_PWRTABLE_PWR_TO_PCDAC:
+ case AR5K_PWRTABLE_PWR_TO_PDADC:
+
+ ath5k_create_power_curve(table_min[pdg],
+ table_max[pdg],
+ pdg_L->pd_pwr,
+ pdg_L->pd_step,
+ pdg_L->pd_points, tmpL, type);
+
+ /* We are in a calibration
+ * pier, no need to interpolate
+ * between freq piers */
+ if (pcinfo_L == pcinfo_R)
+ continue;
+
+ ath5k_create_power_curve(table_min[pdg],
+ table_max[pdg],
+ pdg_R->pd_pwr,
+ pdg_R->pd_step,
+ pdg_R->pd_points, tmpR, type);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Interpolate between curves
+ * of surounding freq piers to
+ * get the final curve for this
+ * pd gain. Re-use tmpL for interpolation
+ * output */
+ for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
+ (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+ tmpL[i] = (u8) ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ (s16) tmpL[i],
+ (s16) tmpR[i]);
+ }
}
+ /* Now we have a set of curves for this
+ * channel on tmpL (x range is table_max - table_min
+ * and y values are tmpL[pdg][]) sorted in the same
+ * order as EEPROM (because we've used the backmaping).
+ * So for RF5112 it's from higher power to lower power
+ * and for RF2413 it's from lower power to higher power.
+ * For RF5111 we only have one curve. */
+
+ /* Fill min and max power levels for this
+ * channel by interpolating the values on
+ * surounding channels to complete the dataset */
+ ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ pcinfo_L->min_pwr, pcinfo_R->min_pwr);
+
+ ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
+ (s16) pcinfo_L->freq,
+ (s16) pcinfo_R->freq,
+ pcinfo_L->max_pwr, pcinfo_R->max_pwr);
+
+ /* We are ready to go, fill PCDAC/PDADC
+ * table and write settings on hardware */
+ switch (type) {
+ case AR5K_PWRTABLE_LINEAR_PCDAC:
+ /* For RF5112 we can have one or two curves
+ * and each curve covers a certain power lvl
+ * range so we need to do some more processing */
+ ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
+ ee->ee_pd_gains[ee_mode]);
+
+ /* Set txp.offset so that we can
+ * match max power value with max
+ * table index */
+ ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
+
+ /* Write settings on hw */
+ ath5k_setup_pcdac_table(ah);
+ break;
+ case AR5K_PWRTABLE_PWR_TO_PCDAC:
+ /* We are done for RF5111 since it has only
+ * one curve, just fit the curve on the table */
+ ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
+
+ /* No rate powertable adjustment for RF5111 */
+ ah->ah_txpower.txp_min_idx = 0;
+ ah->ah_txpower.txp_offset = 0;
+
+ /* Write settings on hw */
+ ath5k_setup_pcdac_table(ah);
+ break;
+ case AR5K_PWRTABLE_PWR_TO_PDADC:
+ /* Set PDADC boundaries and fill
+ * final PDADC table */
+ ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
+ ee->ee_pd_gains[ee_mode]);
+
+ /* Write settings on hw */
+ ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx);
+
+ /* Set txp.offset, note that table_min
+ * can be negative */
+ ah->ah_txpower.txp_offset = table_min[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Per-rate tx power setting
+ *
+ * This is the code that sets the desired tx power (below
+ * maximum) on hw for each rate (we also have TPC that sets
+ * power per packet). We do that by providing an index on the
+ * PCDAC/PDADC table we set up.
+ */
+
+/*
+ * Set rate power table
+ *
+ * For now we only limit txpower based on maximum tx power
+ * supported by hw (what's inside rate_info). We need to limit
+ * this even more, based on regulatory domain etc.
+ *
+ * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps)
+ * and is indexed as follows:
+ * rates[0] - rates[7] -> OFDM rates
+ * rates[8] - rates[14] -> CCK rates
+ * rates[15] -> XR rates (they all have the same power)
+ */
+static void
+ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
+ struct ath5k_rate_pcal_info *rate_info,
+ u8 ee_mode)
+{
+ unsigned int i;
+ u16 *rates;
+
+ /* max_pwr is power level we got from driver/user in 0.5dB
+ * units, switch to 0.25dB units so we can compare */
+ max_pwr *= 2;
+ max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
+
+ /* apply rate limits */
+ rates = ah->ah_txpower.txp_rates_power_table;
+
+ /* OFDM rates 6 to 24Mb/s */
+ for (i = 0; i < 5; i++)
+ rates[i] = min(max_pwr, rate_info->target_power_6to24);
+
+ /* Rest OFDM rates */
+ rates[5] = min(rates[0], rate_info->target_power_36);
+ rates[6] = min(rates[0], rate_info->target_power_48);
+ rates[7] = min(rates[0], rate_info->target_power_54);
+
+ /* CCK rates */
+ /* 1L */
+ rates[8] = min(rates[0], rate_info->target_power_6to24);
+ /* 2L */
+ rates[9] = min(rates[0], rate_info->target_power_36);
+ /* 2S */
+ rates[10] = min(rates[0], rate_info->target_power_36);
+ /* 5L */
+ rates[11] = min(rates[0], rate_info->target_power_48);
+ /* 5S */
+ rates[12] = min(rates[0], rate_info->target_power_48);
+ /* 11L */
+ rates[13] = min(rates[0], rate_info->target_power_54);
+ /* 11S */
+ rates[14] = min(rates[0], rate_info->target_power_54);
+
+ /* XR rates */
+ rates[15] = min(rates[0], rate_info->target_power_6to24);
+
+ /* CCK rates have different peak to average ratio
+ * so we have to tweak their power so that gainf
+ * correction works ok. For this we use OFDM to
+ * CCK delta from eeprom */
+ if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
+ (ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
+ for (i = 8; i <= 15; i++)
+ rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
+
+ ah->ah_txpower.txp_min_pwr = rates[7];
+ ah->ah_txpower.txp_max_pwr = rates[0];
+ ah->ah_txpower.txp_ofdm = rates[7];
+}
+
+
+/*
+ * Set transmition power
+ */
+int
+ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
+ u8 ee_mode, u8 txpower)
+{
+ struct ath5k_rate_pcal_info rate_info;
+ u8 type;
+ int ret;
+
+ ATH5K_TRACE(ah->ah_sc);
+ if (txpower > AR5K_TUNE_MAX_TXPOWER) {
+ ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
+ return -EINVAL;
+ }
+ if (txpower == 0)
+ txpower = AR5K_TUNE_DEFAULT_TXPOWER;
+
+ /* Reset TX power values */
+ memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
+ ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+ ah->ah_txpower.txp_min_pwr = 0;
+ ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER;
+
+ /* Initialize TX power table */
+ switch (ah->ah_radio) {
+ case AR5K_RF5111:
+ type = AR5K_PWRTABLE_PWR_TO_PCDAC;
+ break;
+ case AR5K_RF5112:
+ type = AR5K_PWRTABLE_LINEAR_PCDAC;
+ break;
+ case AR5K_RF2413:
+ case AR5K_RF5413:
+ case AR5K_RF2316:
+ case AR5K_RF2317:
+ case AR5K_RF2425:
+ type = AR5K_PWRTABLE_PWR_TO_PDADC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* FIXME: Only on channel/mode change */
+ ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type);
+ if (ret)
+ return ret;
+
+ /* Limit max power if we have a CTL available */
+ ath5k_get_max_ctl_power(ah, channel);
+
+ /* FIXME: Tx power limit for this regdomain
+ * XXX: Mac80211/CRDA will do that anyway ? */
+
+ /* FIXME: Antenna reduction stuff */
+
+ /* FIXME: Limit power on turbo modes */
+
+ /* FIXME: TPC scale reduction */
+
+ /* Get surounding channels for per-rate power table
+ * calibration */
+ ath5k_get_rate_pcal_data(ah, channel, &rate_info);
+
+ /* Setup rate power table */
+ ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);
+
+ /* Write rate power table on hw */
ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
@@ -1536,26 +2566,34 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
- if (ah->ah_txpower.txp_tpc)
+ /* FIXME: TPC support */
+ if (ah->ah_txpower.txp_tpc) {
ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
- else
+
+ ath5k_hw_reg_write(ah,
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
+ AR5K_TPC);
+ } else {
ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+ }
return 0;
}
-int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power)
+int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 mode, u8 txpower)
{
/*Just a try M.F.*/
struct ieee80211_channel *channel = &ah->ah_current_channel;
ATH5K_TRACE(ah->ah_sc);
ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER,
- "changing txpower to %d\n", power);
+ "changing txpower to %d\n", txpower);
- return ath5k_hw_txpower(ah, channel, power);
+ return ath5k_hw_txpower(ah, channel, mode, txpower);
}
#undef _ATH5K_PHY
diff --git a/trunk/drivers/net/wireless/ath5k/reg.h b/trunk/drivers/net/wireless/ath5k/reg.h
index 2dc008e10226..7070d1543cdc 100644
--- a/trunk/drivers/net/wireless/ath5k/reg.h
+++ b/trunk/drivers/net/wireless/ath5k/reg.h
@@ -1553,6 +1553,19 @@
/*===5212 Specific PCU registers===*/
+/*
+ * Transmit power control register
+ */
+#define AR5K_TPC 0x80e8
+#define AR5K_TPC_ACK 0x0000003f /* ack frames */
+#define AR5K_TPC_ACK_S 0
+#define AR5K_TPC_CTS 0x00003f00 /* cts frames */
+#define AR5K_TPC_CTS_S 8
+#define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */
+#define AR5K_TPC_CHIRP_S 16
+#define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */
+#define AR5K_TPC_DOPPLER_S 24
+
/*
* XR (eXtended Range) mode register
*/
@@ -2550,6 +2563,12 @@
#define AR5K_PHY_TPC_RG1 0xa258
#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000
#define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14
+#define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000
+#define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16
+#define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000
+#define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18
+#define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000
+#define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20
#define AR5K_PHY_TPC_RG5 0xa26C
#define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F
diff --git a/trunk/drivers/net/wireless/ath5k/reset.c b/trunk/drivers/net/wireless/ath5k/reset.c
index 685dc213edae..7a17d31b2fd9 100644
--- a/trunk/drivers/net/wireless/ath5k/reset.c
+++ b/trunk/drivers/net/wireless/ath5k/reset.c
@@ -664,29 +664,35 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
struct ieee80211_channel *channel, u8 *ant, u8 ee_mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ s16 cck_ofdm_pwr_delta;
- /* Set CCK to OFDM power delta */
- if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
- int16_t cck_ofdm_pwr_delta;
-
- /* Adjust power delta for channel 14 */
- if (channel->center_freq == 2484)
- cck_ofdm_pwr_delta =
- ((ee->ee_cck_ofdm_power_delta -
- ee->ee_scaled_cck_delta) * 2) / 10;
- else
- cck_ofdm_pwr_delta =
- (ee->ee_cck_ofdm_power_delta * 2) / 10;
+ /* Adjust power delta for channel 14 */
+ if (channel->center_freq == 2484)
+ cck_ofdm_pwr_delta =
+ ((ee->ee_cck_ofdm_power_delta -
+ ee->ee_scaled_cck_delta) * 2) / 10;
+ else
+ cck_ofdm_pwr_delta =
+ (ee->ee_cck_ofdm_power_delta * 2) / 10;
+ /* Set CCK to OFDM power delta on tx power
+ * adjustment register */
+ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
if (channel->hw_value == CHANNEL_G)
ath5k_hw_reg_write(ah,
- AR5K_REG_SM((ee->ee_cck_ofdm_power_delta * -1),
+ AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
AR5K_REG_SM((cck_ofdm_pwr_delta * -1),
AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX),
AR5K_PHY_TX_PWR_ADJ);
else
ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ);
+ } else {
+ /* For older revs we scale power on sw during tx power
+ * setup */
+ ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta;
+ ah->ah_txpower.txp_cck_ofdm_gainf_delta =
+ ee->ee_cck_ofdm_gain_delta;
}
/* Set antenna idle switch table */
@@ -994,7 +1000,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
/*
* Set TX power (FIXME)
*/
- ret = ath5k_hw_txpower(ah, channel, AR5K_TUNE_DEFAULT_TXPOWER);
+ ret = ath5k_hw_txpower(ah, channel, ee_mode,
+ AR5K_TUNE_DEFAULT_TXPOWER);
if (ret)
return ret;
diff --git a/trunk/drivers/net/wireless/ath9k/ahb.c b/trunk/drivers/net/wireless/ath9k/ahb.c
index 00cc7bb01f2e..0e65c51ba176 100644
--- a/trunk/drivers/net/wireless/ath9k/ahb.c
+++ b/trunk/drivers/net/wireless/ath9k/ahb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
* Copyright (c) 2009 Gabor Juhos
* Copyright (c) 2009 Imre Kaloz
*
diff --git a/trunk/drivers/net/wireless/ath9k/ani.c b/trunk/drivers/net/wireless/ath9k/ani.c
index a39eb760cbb7..6c5e887d50d7 100644
--- a/trunk/drivers/net/wireless/ath9k/ani.c
+++ b/trunk/drivers/net/wireless/ath9k/ani.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/ani.h b/trunk/drivers/net/wireless/ath9k/ani.h
index 7315761f6d74..08b4e7ed5ff0 100644
--- a/trunk/drivers/net/wireless/ath9k/ani.h
+++ b/trunk/drivers/net/wireless/ath9k/ani.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/ath9k.h b/trunk/drivers/net/wireless/ath9k/ath9k.h
index b64be8e9a690..2689a08a2844 100644
--- a/trunk/drivers/net/wireless/ath9k/ath9k.h
+++ b/trunk/drivers/net/wireless/ath9k/ath9k.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -295,13 +295,9 @@ struct ath_tx_control {
enum ath9k_internal_frame_type frame_type;
};
-struct ath_xmit_status {
- int retries;
- int flags;
#define ATH_TX_ERROR 0x01
#define ATH_TX_XRETRY 0x02
#define ATH_TX_BAR 0x04
-};
/* All RSSI values are noise floor adjusted */
struct ath_tx_stat {
@@ -390,6 +386,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
struct ath_vif {
int av_bslot;
+ __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
enum nl80211_iftype av_opmode;
struct ath_buf *av_bcbuf;
struct ath_tx_control av_btxctl;
@@ -406,7 +403,7 @@ struct ath_vif {
* number of beacon intervals, the game's up.
*/
#define BSTUCK_THRESH (9 * ATH_BCBUF)
-#define ATH_BCBUF 1
+#define ATH_BCBUF 4
#define ATH_DEFAULT_BINTVAL 100 /* TU */
#define ATH_DEFAULT_BMISS_LIMIT 10
#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
diff --git a/trunk/drivers/net/wireless/ath9k/beacon.c b/trunk/drivers/net/wireless/ath9k/beacon.c
index 039c78136c50..ec995730632d 100644
--- a/trunk/drivers/net/wireless/ath9k/beacon.c
+++ b/trunk/drivers/net/wireless/ath9k/beacon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -70,7 +70,8 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
ds = bf->bf_desc;
flags = ATH9K_TXDESC_NOACK;
- if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
+ if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
+ (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) &&
(ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
ds->ds_link = bf->bf_daddr; /* self-linked */
flags |= ATH9K_TXDESC_VEOL;
@@ -153,6 +154,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
bf->bf_mpdu = skb;
if (skb == NULL)
return NULL;
+ ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
+ avp->tsf_adjust;
info = IEEE80211_SKB_CB(skb);
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -253,7 +256,6 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
{
struct ath_softc *sc = aphy->sc;
struct ath_vif *avp;
- struct ieee80211_hdr *hdr;
struct ath_buf *bf;
struct sk_buff *skb;
__le64 tstamp;
@@ -316,42 +318,33 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
-
- /*
- * Calculate a TSF adjustment factor required for
- * staggered beacons. Note that we assume the format
- * of the beacon frame leaves the tstamp field immediately
- * following the header.
- */
+ /* Calculate a TSF adjustment factor required for staggered beacons. */
if (avp->av_bslot > 0) {
u64 tsfadjust;
- __le64 val;
int intval;
intval = sc->hw->conf.beacon_int ?
sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL;
/*
- * The beacon interval is in TU's; the TSF in usecs.
- * We figure out how many TU's to add to align the
- * timestamp then convert to TSF units and handle
- * byte swapping before writing it in the frame.
- * The hardware will then add this each time a beacon
- * frame is sent. Note that we align vif's 1..N
- * and leave vif 0 untouched. This means vap 0
- * has a timestamp in one beacon interval while the
- * others get a timestamp aligned to the next interval.
+ * Calculate the TSF offset for this beacon slot, i.e., the
+ * number of usecs that need to be added to the timestamp field
+ * in Beacon and Probe Response frames. Beacon slot 0 is
+ * processed at the correct offset, so it does not require TSF
+ * adjustment. Other slots are adjusted to get the timestamp
+ * close to the TBTT for the BSS.
*/
- tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF;
- val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */
+ tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
+ avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
DPRINTF(sc, ATH_DBG_BEACON,
"stagger beacons, bslot %d intval %u tsfadjust %llu\n",
avp->av_bslot, intval, (unsigned long long)tsfadjust);
- hdr = (struct ieee80211_hdr *)skb->data;
- memcpy(&hdr[1], &val, sizeof(val));
- }
+ ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
+ avp->tsf_adjust;
+ } else
+ avp->tsf_adjust = cpu_to_le64(0);
bf->bf_mpdu = skb;
bf->bf_buf_addr = bf->bf_dmacontext =
@@ -447,8 +440,16 @@ void ath_beacon_tasklet(unsigned long data)
tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU(tsf>>32, tsf);
slot = ((tsftu % intval) * ATH_BCBUF) / intval;
- vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
- aphy = sc->beacon.bslot_aphy[(slot + 1) % ATH_BCBUF];
+ /*
+ * Reverse the slot order to get slot 0 on the TBTT offset that does
+ * not require TSF adjustment and other slots adding
+ * slot/ATH_BCBUF * beacon_int to timestamp. For example, with
+ * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 ..
+ * and slot 0 is at correct offset to TBTT.
+ */
+ slot = ATH_BCBUF - slot - 1;
+ vif = sc->beacon.bslot[slot];
+ aphy = sc->beacon.bslot_aphy[slot];
DPRINTF(sc, ATH_DBG_BEACON,
"slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
@@ -728,6 +729,7 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
ath_beacon_config_ap(sc, &conf, avp);
break;
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
ath_beacon_config_adhoc(sc, &conf, avp, vif);
break;
case NL80211_IFTYPE_STATION:
diff --git a/trunk/drivers/net/wireless/ath9k/calib.c b/trunk/drivers/net/wireless/ath9k/calib.c
index c9446fb6b153..e2d62e97131c 100644
--- a/trunk/drivers/net/wireless/ath9k/calib.c
+++ b/trunk/drivers/net/wireless/ath9k/calib.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/calib.h b/trunk/drivers/net/wireless/ath9k/calib.h
index 32589e0c5018..1c74bd50700d 100644
--- a/trunk/drivers/net/wireless/ath9k/calib.h
+++ b/trunk/drivers/net/wireless/ath9k/calib.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/debug.c b/trunk/drivers/net/wireless/ath9k/debug.c
index 82573cadb1ab..fdf9528fa49b 100644
--- a/trunk/drivers/net/wireless/ath9k/debug.c
+++ b/trunk/drivers/net/wireless/ath9k/debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/debug.h b/trunk/drivers/net/wireless/ath9k/debug.h
index 065268b8568f..7b0e5419d2bc 100644
--- a/trunk/drivers/net/wireless/ath9k/debug.h
+++ b/trunk/drivers/net/wireless/ath9k/debug.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/eeprom.c b/trunk/drivers/net/wireless/ath9k/eeprom.c
index 183c949bcca1..ffc36b0361c7 100644
--- a/trunk/drivers/net/wireless/ath9k/eeprom.c
+++ b/trunk/drivers/net/wireless/ath9k/eeprom.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -342,8 +342,7 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah)
static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
{
#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
- struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
- u16 *eep_data;
+ u16 *eep_data = (u16 *)&ah->eeprom.map4k;
int addr, eep_start_loc = 0;
eep_start_loc = 64;
@@ -353,8 +352,6 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
"Reading from EEPROM, not flash\n");
}
- eep_data = (u16 *)eep;
-
for (addr = 0; addr < SIZE_EEPROM_4K; addr++) {
if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) {
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
@@ -363,6 +360,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
}
eep_data++;
}
+
return true;
#undef SIZE_EEPROM_4K
}
@@ -379,16 +377,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
if (!ath9k_hw_use_flash(ah)) {
-
if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
&magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Reading Magic # failed\n");
return false;
}
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Read Magic = 0x%04X\n", magic);
+ "Read Magic = 0x%04X\n", magic);
if (magic != AR5416_EEPROM_MAGIC) {
magic2 = swab16(magic);
@@ -401,16 +398,9 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
temp = swab16(*eepdata);
*eepdata = temp;
eepdata++;
-
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "0x%04X ", *eepdata);
-
- if (((addr + 1) % 6) == 0)
- DPRINTF(ah->ah_sc,
- ATH_DBG_EEPROM, "\n");
}
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Invalid EEPROM Magic. "
"endianness mismatch.\n");
return -EINVAL;
@@ -426,7 +416,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
else
el = ah->eeprom.map4k.baseEepHeader.length;
- if (el > sizeof(struct ar5416_eeprom_def))
+ if (el > sizeof(struct ar5416_eeprom_4k))
el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16);
else
el = el / sizeof(u16);
@@ -441,7 +431,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
u16 word;
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Endianness is not native.. Changing \n");
+ "EEPROM Endianness is not native.. Changing\n");
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -483,7 +473,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Bad EEPROM checksum 0x%x or revision 0x%04x\n",
sum, ah->eep_ops->get_eeprom_ver(ah));
return -EINVAL;
@@ -1203,57 +1193,63 @@ static void ath9k_hw_4k_set_addac(struct ath_hw *ah,
}
}
-static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
- struct ath9k_channel *chan)
+static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
+ struct modal_eep_4k_header *pModal,
+ struct ar5416_eeprom_4k *eep,
+ u8 txRxAttenLocal, int regChainOffset)
{
- struct modal_eep_4k_header *pModal;
- struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
- int regChainOffset;
- u8 txRxAttenLocal;
- u8 ob[5], db1[5], db2[5];
- u8 ant_div_control1, ant_div_control2;
- u32 regVal;
-
-
- pModal = &eep->modalHeader;
-
- txRxAttenLocal = 23;
-
- REG_WRITE(ah, AR_PHY_SWITCH_COM,
- ah->eep_ops->get_eeprom_antenna_cfg(ah, chan));
-
- regChainOffset = 0;
REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
pModal->antCtrlChain[0]);
REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
- (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
- ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
- SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
- SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+ (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
+ ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
+ SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+ SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_3) {
+ AR5416_EEP_MINOR_VER_3) {
txRxAttenLocal = pModal->txRxAttenCh[0];
+
REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]);
+ AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]);
REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
+ AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
- pModal->xatten2Margin[0]);
+ AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+ pModal->xatten2Margin[0]);
REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
+ AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
}
REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+ AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+ AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
if (AR_SREV_9285_11(ah))
REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
+}
+
+static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+{
+ struct modal_eep_4k_header *pModal;
+ struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+ u8 txRxAttenLocal;
+ u8 ob[5], db1[5], db2[5];
+ u8 ant_div_control1, ant_div_control2;
+ u32 regVal;
+
+ pModal = &eep->modalHeader;
+ txRxAttenLocal = 23;
+
+ REG_WRITE(ah, AR_PHY_SWITCH_COM,
+ ah->eep_ops->get_eeprom_antenna_cfg(ah, chan));
+
+ /* Single chain for 4K EEPROM*/
+ ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0);
/* Initialize Ant Diversity settings from EEPROM */
if (pModal->version == 3) {
@@ -1295,9 +1291,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
db2[4] = ((pModal->db2_234 >> 8) & 0xf);
} else if (pModal->version == 1) {
-
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Model version is set to 1 \n");
ob[0] = (pModal->ob_01 & 0xf);
ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf;
db1[0] = (pModal->db1_01 & 0xf);
@@ -1385,8 +1378,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
AR_PHY_SETTLING_SWITCH,
pModal->swSettleHt40);
}
-
- return true;
}
static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah,
@@ -1464,16 +1455,13 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah)
static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
{
#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16))
- struct ar5416_eeprom_def *eep = &ah->eeprom.def;
- u16 *eep_data;
+ u16 *eep_data = (u16 *)&ah->eeprom.def;
int addr, ar5416_eep_start_loc = 0x100;
- eep_data = (u16 *)eep;
-
for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) {
if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
eep_data)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Unable to read eeprom region\n");
return false;
}
@@ -1492,17 +1480,14 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
bool need_swap = false;
int i, addr, size;
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Reading Magic # failed\n");
+ if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n");
return false;
}
if (!ath9k_hw_use_flash(ah)) {
-
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Read Magic = 0x%04X\n", magic);
+ "Read Magic = 0x%04X\n", magic);
if (magic != AR5416_EEPROM_MAGIC) {
magic2 = swab16(magic);
@@ -1516,18 +1501,11 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
temp = swab16(*eepdata);
*eepdata = temp;
eepdata++;
-
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "0x%04X ", *eepdata);
-
- if (((addr + 1) % 6) == 0)
- DPRINTF(ah->ah_sc,
- ATH_DBG_EEPROM, "\n");
}
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Invalid EEPROM Magic. "
- "endianness mismatch.\n");
+ "Endianness mismatch.\n");
return -EINVAL;
}
}
@@ -1556,7 +1534,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
u16 word;
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Endianness is not native.. Changing \n");
+ "EEPROM Endianness is not native.. Changing.\n");
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -1602,7 +1580,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Bad EEPROM checksum 0x%x or revision 0x%04x\n",
sum, ah->eep_ops->get_eeprom_ver(ah));
return -EINVAL;
@@ -1614,7 +1592,6 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
-#define AR5416_VER_MASK (pBase->version & AR5416_EEP_VER_MINOR_MASK)
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct modal_eep_header *pModal = eep->modalHeader;
struct base_eep_header *pBase = &eep->baseEepHeader;
@@ -1681,21 +1658,73 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
default:
return 0;
}
-#undef AR5416_VER_MASK
}
-/* XXX: Clean me up, make me more legible */
-static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
+static void ath9k_hw_def_set_gain(struct ath_hw *ah,
+ struct modal_eep_header *pModal,
+ struct ar5416_eeprom_def *eep,
+ u8 txRxAttenLocal, int regChainOffset, int i)
+{
+ if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
+ txRxAttenLocal = pModal->txRxAttenCh[i];
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
+ pModal->bswMargin[i]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_DB,
+ pModal->bswAtten[i]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+ pModal->xatten2Margin[i]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_DB,
+ pModal->xatten2Db[i]);
+ } else {
+ REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
+ | SM(pModal-> bswMargin[i],
+ AR_PHY_GAIN_2GHZ_BSW_MARGIN));
+ REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
+ | SM(pModal->bswAtten[i],
+ AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+ }
+ }
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_RMW_FIELD(ah,
+ AR_PHY_RXGAIN + regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+ REG_RMW_FIELD(ah,
+ AR_PHY_RXGAIN + regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]);
+ } else {
+ REG_WRITE(ah,
+ AR_PHY_RXGAIN + regChainOffset,
+ (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) &
+ ~AR_PHY_RXGAIN_TXRX_ATTEN)
+ | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN));
+ REG_WRITE(ah,
+ AR_PHY_GAIN_2GHZ + regChainOffset,
+ (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
+ SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+ }
+}
+
+static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
struct ath9k_channel *chan)
{
-#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK)
struct modal_eep_header *pModal;
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
int i, regChainOffset;
u8 txRxAttenLocal;
pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
-
txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44;
REG_WRITE(ah, AR_PHY_SWITCH_COM,
@@ -1708,8 +1737,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
}
if (AR_SREV_5416_20_OR_LATER(ah) &&
- (ah->rxchainmask == 5 || ah->txchainmask == 5)
- && (i != 0))
+ (ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0))
regChainOffset = (i == 1) ? 0x2000 : 0x1000;
else
regChainOffset = i * 0x1000;
@@ -1718,9 +1746,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
pModal->antCtrlChain[i]);
REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
- (REG_READ(ah,
- AR_PHY_TIMING_CTRL4(0) +
- regChainOffset) &
+ (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
SM(pModal->iqCalICh[i],
@@ -1728,87 +1754,9 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
SM(pModal->iqCalQCh[i],
AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
- if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
- if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
- txRxAttenLocal = pModal->txRxAttenCh[i];
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
- pModal->
- bswMargin[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_DB,
- pModal->
- bswAtten[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
- pModal->
- xatten2Margin[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_DB,
- pModal->
- xatten2Db[i]);
- } else {
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
- | SM(pModal->
- bswMargin[i],
- AR_PHY_GAIN_2GHZ_BSW_MARGIN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
- | SM(pModal->bswAtten[i],
- AR_PHY_GAIN_2GHZ_BSW_ATTEN));
- }
- }
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- REG_RMW_FIELD(ah,
- AR_PHY_RXGAIN +
- regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_ATTEN,
- txRxAttenLocal);
- REG_RMW_FIELD(ah,
- AR_PHY_RXGAIN +
- regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_MARGIN,
- pModal->rxTxMarginCh[i]);
- } else {
- REG_WRITE(ah,
- AR_PHY_RXGAIN + regChainOffset,
- (REG_READ(ah,
- AR_PHY_RXGAIN +
- regChainOffset) &
- ~AR_PHY_RXGAIN_TXRX_ATTEN) |
- SM(txRxAttenLocal,
- AR_PHY_RXGAIN_TXRX_ATTEN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
- SM(pModal->rxTxMarginCh[i],
- AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
- }
- }
+ if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah))
+ ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal,
+ regChainOffset, i);
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
@@ -1855,8 +1803,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
AR_AN_TOP2_LOCALBIAS,
AR_AN_TOP2_LOCALBIAS_S,
pModal->local_bias);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "ForceXPAon: %d\n",
- pModal->force_xpaon);
REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG,
pModal->force_xpaon);
}
@@ -1882,6 +1828,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
pModal->txEndToRxOn);
+
if (AR_SREV_9280_10_OR_LATER(ah)) {
REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
pModal->thresh62);
@@ -1912,10 +1859,10 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
}
if (AR_SREV_9280_20_OR_LATER(ah) &&
- AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19)
+ AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19)
REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL,
- AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK,
- pModal->miscBits);
+ AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK,
+ pModal->miscBits);
if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) {
@@ -1926,18 +1873,15 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0);
else
REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE,
- eep->baseEepHeader.dacLpMode);
+ eep->baseEepHeader.dacLpMode);
REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP,
- pModal->miscBits >> 2);
+ pModal->miscBits >> 2);
REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9,
- AR_PHY_TX_DESIRED_SCALE_CCK,
- eep->baseEepHeader.desiredScaleCCK);
+ AR_PHY_TX_DESIRED_SCALE_CCK,
+ eep->baseEepHeader.desiredScaleCCK);
}
-
- return true;
-#undef AR5416_VER_MASK
}
static void ath9k_hw_def_set_addac(struct ath_hw *ah,
diff --git a/trunk/drivers/net/wireless/ath9k/eeprom.h b/trunk/drivers/net/wireless/ath9k/eeprom.h
index d6f6108f63c7..25b68c881ff1 100644
--- a/trunk/drivers/net/wireless/ath9k/eeprom.h
+++ b/trunk/drivers/net/wireless/ath9k/eeprom.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -95,6 +95,7 @@
#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5))
#define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM))
+#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK)
#define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \
ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
@@ -489,7 +490,7 @@ struct eeprom_ops {
u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
struct ath9k_channel *chan);
- bool (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
+ void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan);
int (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan,
u16 cfgCtl, u8 twiceAntennaReduction,
diff --git a/trunk/drivers/net/wireless/ath9k/hw.c b/trunk/drivers/net/wireless/ath9k/hw.c
index d494e98ba971..b15eaf8417ff 100644
--- a/trunk/drivers/net/wireless/ath9k/hw.c
+++ b/trunk/drivers/net/wireless/ath9k/hw.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -588,6 +588,10 @@ static int ath9k_hw_post_attach(struct ath_hw *ah)
ecode = ath9k_hw_eeprom_attach(ah);
if (ecode != 0)
return ecode;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n",
+ ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah));
+
ecode = ath9k_hw_rfattach(ah);
if (ecode != 0)
return ecode;
@@ -1444,6 +1448,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break;
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
| AR_STA_ID1_KSRCH_MODE);
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
@@ -2273,11 +2278,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
else
ath9k_hw_spur_mitigate(ah, chan);
- if (!ah->eep_ops->set_board_values(ah, chan)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "error setting board options\n");
- return -EIO;
- }
+ ah->eep_ops->set_board_values(ah, chan);
ath9k_hw_decrease_chain_power(ah, chan);
@@ -3149,6 +3150,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
flags |= AR_TBTT_TIMER_EN;
break;
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER,
diff --git a/trunk/drivers/net/wireless/ath9k/hw.h b/trunk/drivers/net/wireless/ath9k/hw.h
index dc681f011fdf..0b594e0ee260 100644
--- a/trunk/drivers/net/wireless/ath9k/hw.h
+++ b/trunk/drivers/net/wireless/ath9k/hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/initvals.h b/trunk/drivers/net/wireless/ath9k/initvals.h
index 1d60c3706f1c..e2f0a34b79a1 100644
--- a/trunk/drivers/net/wireless/ath9k/initvals.h
+++ b/trunk/drivers/net/wireless/ath9k/initvals.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/mac.c b/trunk/drivers/net/wireless/ath9k/mac.c
index f757bc7eec68..e0a6dee45839 100644
--- a/trunk/drivers/net/wireless/ath9k/mac.c
+++ b/trunk/drivers/net/wireless/ath9k/mac.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/mac.h b/trunk/drivers/net/wireless/ath9k/mac.h
index a75f65dae1d7..1176bce8b76c 100644
--- a/trunk/drivers/net/wireless/ath9k/mac.h
+++ b/trunk/drivers/net/wireless/ath9k/mac.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/main.c b/trunk/drivers/net/wireless/ath9k/main.c
index 8db75f6de53e..13d4e6756c99 100644
--- a/trunk/drivers/net/wireless/ath9k/main.c
+++ b/trunk/drivers/net/wireless/ath9k/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -940,18 +940,25 @@ static void ath_led_blink_work(struct work_struct *work)
if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED))
return;
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
- (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
+
+ if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
+ (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
+ ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
+ else
+ ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
+ (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work,
(sc->sc_flags & SC_OP_LED_ON) ?
msecs_to_jiffies(sc->led_off_duration) :
msecs_to_jiffies(sc->led_on_duration));
- sc->led_on_duration =
- max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25);
- sc->led_off_duration =
- max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10);
+ sc->led_on_duration = sc->led_on_cnt ?
+ max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
+ ATH_LED_ON_DURATION_IDLE;
+ sc->led_off_duration = sc->led_off_cnt ?
+ max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) :
+ ATH_LED_OFF_DURATION_IDLE;
sc->led_on_cnt = sc->led_off_cnt = 0;
if (sc->sc_flags & SC_OP_LED_ON)
sc->sc_flags &= ~SC_OP_LED_ON;
@@ -1592,7 +1599,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->reg_notifier = ath9k_reg_notifier;
hw->wiphy->strict_regulatory = true;
@@ -2200,18 +2208,13 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
ic_opmode = NL80211_IFTYPE_STATION;
break;
case NL80211_IFTYPE_ADHOC:
- if (sc->nbcnvifs >= ATH_BCBUF) {
- ret = -ENOBUFS;
- goto out;
- }
- ic_opmode = NL80211_IFTYPE_ADHOC;
- break;
case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
if (sc->nbcnvifs >= ATH_BCBUF) {
ret = -ENOBUFS;
goto out;
}
- ic_opmode = NL80211_IFTYPE_AP;
+ ic_opmode = conf->type;
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
@@ -2247,7 +2250,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
* Note we only do this (at the moment) for station mode.
*/
if ((conf->type == NL80211_IFTYPE_STATION) ||
- (conf->type == NL80211_IFTYPE_ADHOC)) {
+ (conf->type == NL80211_IFTYPE_ADHOC) ||
+ (conf->type == NL80211_IFTYPE_MESH_POINT)) {
if (ath9k_hw_phycounters(sc->sc_ah))
sc->imask |= ATH9K_INT_MIB;
sc->imask |= ATH9K_INT_TSFOOR;
@@ -2294,8 +2298,9 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
del_timer_sync(&sc->ani.timer);
/* Reclaim beacon resources */
- if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
- sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) {
+ if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
+ (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
+ (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
ath_beacon_return(sc, avp);
}
@@ -2428,6 +2433,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
switch (vif->type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
/* Set BSSID */
memcpy(sc->curbssid, conf->bssid, ETH_ALEN);
memcpy(avp->bssid, conf->bssid, ETH_ALEN);
@@ -2451,7 +2457,8 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
}
if ((vif->type == NL80211_IFTYPE_ADHOC) ||
- (vif->type == NL80211_IFTYPE_AP)) {
+ (vif->type == NL80211_IFTYPE_AP) ||
+ (vif->type == NL80211_IFTYPE_MESH_POINT)) {
if ((conf->changed & IEEE80211_IFCC_BEACON) ||
(conf->changed & IEEE80211_IFCC_BEACON_ENABLED &&
conf->enable_beacon)) {
@@ -2723,7 +2730,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
break;
- case IEEE80211_AMPDU_TX_RESUME:
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
ath_tx_aggr_resume(sc, sta, tid);
break;
default:
diff --git a/trunk/drivers/net/wireless/ath9k/pci.c b/trunk/drivers/net/wireless/ath9k/pci.c
index 9a58baabb9ca..6dbc58580abb 100644
--- a/trunk/drivers/net/wireless/ath9k/pci.c
+++ b/trunk/drivers/net/wireless/ath9k/pci.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -87,7 +87,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct ath_softc *sc;
struct ieee80211_hw *hw;
u8 csz;
- u32 val;
int ret = 0;
struct ath_hw *ah;
@@ -134,14 +133,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
- /*
- * Disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state.
- */
- pci_read_config_dword(pdev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
ret = pci_request_region(pdev, 0, "ath9k");
if (ret) {
dev_err(&pdev->dev, "PCI memory region reserve error\n");
@@ -253,21 +244,12 @@ static int ath_pci_resume(struct pci_dev *pdev)
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
- u32 val;
int err;
err = pci_enable_device(pdev);
if (err)
return err;
pci_restore_state(pdev);
- /*
- * Suspend/Resume resets the PCI configuration space, so we have to
- * re-disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state
- */
- pci_read_config_dword(pdev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
diff --git a/trunk/drivers/net/wireless/ath9k/phy.c b/trunk/drivers/net/wireless/ath9k/phy.c
index e1494bae0f9f..8bcba906929a 100644
--- a/trunk/drivers/net/wireless/ath9k/phy.c
+++ b/trunk/drivers/net/wireless/ath9k/phy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/phy.h b/trunk/drivers/net/wireless/ath9k/phy.h
index 1eac8c707342..0f7f8e0c9c95 100644
--- a/trunk/drivers/net/wireless/ath9k/phy.h
+++ b/trunk/drivers/net/wireless/ath9k/phy.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/rc.c b/trunk/drivers/net/wireless/ath9k/rc.c
index 832735677a46..824ccbb8b7b8 100644
--- a/trunk/drivers/net/wireless/ath9k/rc.c
+++ b/trunk/drivers/net/wireless/ath9k/rc.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2008 Atheros Communications, Inc.
+ * Copyright (c) 2004-2009 Atheros Communications, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -864,6 +864,8 @@ static void ath_rc_ratefind(struct ath_softc *sc,
rate_table, nrix, 1, 0);
ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
try_per_rate, nrix, 0);
+
+ tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
} else {
try_per_rate = (ATH_11N_TXMAXTRY/4);
/* Set the choosen rate. No RTS for first series entry. */
@@ -1468,16 +1470,18 @@ static void ath_rc_init(struct ath_softc *sc,
ath_rc_priv->ht_cap);
}
-static u8 ath_rc_build_ht_caps(struct ath_softc *sc, bool is_ht, bool is_cw40,
- bool is_sgi40)
+static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta,
+ bool is_cw40, bool is_sgi40)
{
u8 caps = 0;
- if (is_ht) {
+ if (sta->ht_cap.ht_supported) {
caps = WLAN_RC_HT_FLAG;
if (sc->sc_ah->caps.tx_chainmask != 1 &&
- ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL))
- caps |= WLAN_RC_DS_FLAG;
+ ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL)) {
+ if (sta->ht_cap.mcs.rx_mask[1])
+ caps |= WLAN_RC_DS_FLAG;
+ }
if (is_cw40)
caps |= WLAN_RC_40_FLAG;
if (is_sgi40)
@@ -1615,6 +1619,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
/* Choose rate table first */
if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) ||
+ (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) {
rate_table = ath_choose_rate_table(sc, sband->band,
sta->ht_cap.ht_supported,
@@ -1624,8 +1629,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
rate_table = sc->cur_rate_table;
}
- ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta->ht_cap.ht_supported,
- is_cw40, is_sgi40);
+ ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40);
ath_rc_init(sc, priv_sta, sband, sta, rate_table);
}
@@ -1659,8 +1663,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
rate_table = ath_choose_rate_table(sc, sband->band,
sta->ht_cap.ht_supported,
oper_cw40);
- ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc,
- sta->ht_cap.ht_supported,
+ ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta,
oper_cw40, oper_sgi40);
ath_rc_init(sc, priv_sta, sband, sta, rate_table);
diff --git a/trunk/drivers/net/wireless/ath9k/rc.h b/trunk/drivers/net/wireless/ath9k/rc.h
index db9b0b9a3431..199a3ce57d64 100644
--- a/trunk/drivers/net/wireless/ath9k/rc.h
+++ b/trunk/drivers/net/wireless/ath9k/rc.h
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2004 Sam Leffler, Errno Consulting
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/recv.c b/trunk/drivers/net/wireless/ath9k/recv.c
index 0bba17662a1f..71cb18d6757d 100644
--- a/trunk/drivers/net/wireless/ath9k/recv.c
+++ b/trunk/drivers/net/wireless/ath9k/recv.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -344,8 +344,13 @@ void ath_rx_cleanup(struct ath_softc *sc)
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = bf->bf_mpdu;
- if (skb)
+ if (skb) {
+ dma_unmap_single(sc->dev,
+ bf->bf_buf_addr,
+ sc->rx.bufsize,
+ DMA_FROM_DEVICE);
dev_kfree_skb(skb);
+ }
}
if (sc->rx.rxdma.dd_desc_len != 0)
diff --git a/trunk/drivers/net/wireless/ath9k/reg.h b/trunk/drivers/net/wireless/ath9k/reg.h
index d86e90e38173..52605246679f 100644
--- a/trunk/drivers/net/wireless/ath9k/reg.h
+++ b/trunk/drivers/net/wireless/ath9k/reg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/regd.c b/trunk/drivers/net/wireless/ath9k/regd.c
index b8f9b6d6bec4..4ca625102291 100644
--- a/trunk/drivers/net/wireless/ath9k/regd.c
+++ b/trunk/drivers/net/wireless/ath9k/regd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/regd.h b/trunk/drivers/net/wireless/ath9k/regd.h
index 8f885f3bc8df..9f5fbd4eea7a 100644
--- a/trunk/drivers/net/wireless/ath9k/regd.h
+++ b/trunk/drivers/net/wireless/ath9k/regd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/regd_common.h b/trunk/drivers/net/wireless/ath9k/regd_common.h
index b41d0002f3fe..4d0e298cd1c7 100644
--- a/trunk/drivers/net/wireless/ath9k/regd_common.h
+++ b/trunk/drivers/net/wireless/ath9k/regd_common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/trunk/drivers/net/wireless/ath9k/xmit.c b/trunk/drivers/net/wireless/ath9k/xmit.c
index e3f376611f85..689bdbf78808 100644
--- a/trunk/drivers/net/wireless/ath9k/xmit.c
+++ b/trunk/drivers/net/wireless/ath9k/xmit.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -64,6 +64,10 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
struct list_head *head);
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf);
+static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
+ int txok);
+static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
+ int nbad, int txok, bool update_rc);
/*********************/
/* Aggregation logic */
@@ -274,9 +278,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
struct list_head bf_head, bf_pending;
- u16 seq_st = 0;
+ u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0;
u32 ba[WME_BA_BMP_SIZE >> 5];
- int isaggr, txfail, txpending, sendbar = 0, needreset = 0;
+ int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
+ bool rc_update = true;
skb = (struct sk_buff *)bf->bf_mpdu;
hdr = (struct ieee80211_hdr *)skb->data;
@@ -316,6 +321,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
INIT_LIST_HEAD(&bf_pending);
INIT_LIST_HEAD(&bf_head);
+ nbad = ath_tx_num_badfrms(sc, bf, txok);
while (bf) {
txfail = txpending = 0;
bf_next = bf->bf_next;
@@ -323,8 +329,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) {
/* transmit completion, subframe is
* acked by block ack */
+ acked_cnt++;
} else if (!isaggr && txok) {
/* transmit completion */
+ acked_cnt++;
} else {
if (!(tid->state & AGGR_CLEANUP) &&
ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
@@ -335,6 +343,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
bf->bf_state.bf_type |= BUF_XRETRY;
txfail = 1;
sendbar = 1;
+ txfail_cnt++;
}
} else {
/*
@@ -361,6 +370,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
ath_tx_update_baw(sc, tid, bf->bf_seqno);
spin_unlock_bh(&txq->axq_lock);
+ if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
+ ath_tx_rc_status(bf, ds, nbad, txok, true);
+ rc_update = false;
+ } else {
+ ath_tx_rc_status(bf, ds, nbad, txok, false);
+ }
+
ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
} else {
/* retry the un-acked ones */
@@ -1734,7 +1750,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
/*****************/
static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
- struct ath_xmit_status *tx_status)
+ int tx_flags)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -1755,18 +1771,14 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
tx_info->rate_driver_data[0] = NULL;
}
- if (tx_status->flags & ATH_TX_BAR) {
+ if (tx_flags & ATH_TX_BAR)
tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
- tx_status->flags &= ~ATH_TX_BAR;
- }
- if (!(tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
+ if (!(tx_flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
}
- tx_info->status.rates[0].count = tx_status->retries + 1;
-
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
padsize = hdrlen & 3;
if (padsize && hdrlen >= 24) {
@@ -1789,29 +1801,22 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
int txok, int sendbar)
{
struct sk_buff *skb = bf->bf_mpdu;
- struct ath_xmit_status tx_status;
unsigned long flags;
+ int tx_flags = 0;
- /*
- * Set retry information.
- * NB: Don't use the information in the descriptor, because the frame
- * could be software retried.
- */
- tx_status.retries = bf->bf_retries;
- tx_status.flags = 0;
if (sendbar)
- tx_status.flags = ATH_TX_BAR;
+ tx_flags = ATH_TX_BAR;
if (!txok) {
- tx_status.flags |= ATH_TX_ERROR;
+ tx_flags |= ATH_TX_ERROR;
if (bf_isxretried(bf))
- tx_status.flags |= ATH_TX_XRETRY;
+ tx_flags |= ATH_TX_XRETRY;
}
dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
- ath_tx_complete(sc, skb, &tx_status);
+ ath_tx_complete(sc, skb, tx_flags);
/*
* Return the list of ath_buf of this mpdu to free queue
@@ -1852,27 +1857,40 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
return nbad;
}
-static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
+static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
+ int nbad, int txok, bool update_rc)
{
struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+ struct ieee80211_hw *hw = tx_info_priv->aphy->hw;
+ u8 i, tx_rateindex;
+
+ if (txok)
+ tx_info->status.ack_signal = ds->ds_txstat.ts_rssi;
- tx_info_priv->update_rc = false;
+ tx_rateindex = ds->ds_txstat.ts_rateindex;
+ WARN_ON(tx_rateindex >= hw->max_rates);
+
+ tx_info_priv->update_rc = update_rc;
if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
- (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
+ (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) {
if (ieee80211_is_data(hdr->frame_control)) {
memcpy(&tx_info_priv->tx, &ds->ds_txstat,
sizeof(tx_info_priv->tx));
tx_info_priv->n_frames = bf->bf_nframes;
tx_info_priv->n_bad_frames = nbad;
- tx_info_priv->update_rc = true;
}
}
+
+ for (i = tx_rateindex + 1; i < hw->max_rates; i++)
+ tx_info->status.rates[i].count = 0;
+
+ tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1;
}
static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
@@ -1897,7 +1915,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
struct ath_buf *bf, *lastbf, *bf_held = NULL;
struct list_head bf_head;
struct ath_desc *ds;
- int txok, nbad = 0;
+ int txok;
int status;
DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
@@ -1991,13 +2009,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
bf->bf_retries = ds->ds_txstat.ts_longretry;
if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY)
bf->bf_state.bf_type |= BUF_XRETRY;
- nbad = 0;
- } else {
- nbad = ath_tx_num_badfrms(sc, bf, txok);
+ ath_tx_rc_status(bf, ds, 0, txok, true);
}
- ath_tx_rc_status(bf, ds, nbad);
-
if (bf_isampdu(bf))
ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok);
else
diff --git a/trunk/drivers/net/wireless/b43/main.c b/trunk/drivers/net/wireless/b43/main.c
index b72ef3fd315a..4896e0831114 100644
--- a/trunk/drivers/net/wireless/b43/main.c
+++ b/trunk/drivers/net/wireless/b43/main.c
@@ -3993,6 +3993,8 @@ static void setup_struct_wldev_for_init(struct b43_wldev *dev)
dev->irq_reason = 0;
memset(dev->dma_reason, 0, sizeof(dev->dma_reason));
dev->irq_savedstate = B43_IRQ_MASKTEMPLATE;
+ if (b43_modparam_verbose < B43_VERBOSITY_DEBUG)
+ dev->irq_savedstate &= ~B43_IRQ_PHY_TXERR;
dev->mac_suspended = 1;
diff --git a/trunk/drivers/net/wireless/b43/xmit.c b/trunk/drivers/net/wireless/b43/xmit.c
index 0f53c7e5e01e..a63d88841df8 100644
--- a/trunk/drivers/net/wireless/b43/xmit.c
+++ b/trunk/drivers/net/wireless/b43/xmit.c
@@ -50,7 +50,7 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp)
}
/* Extract the bitrate index out of an OFDM PLCP header. */
-static u8 b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
+static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
{
int base = aphy ? 0 : 4;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/trunk/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
index 205603d082aa..73f93a0ff2df 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
@@ -233,7 +233,7 @@ struct iwl3945_eeprom {
#define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */
#define TFD_QUEUE_MIN 0
-#define TFD_QUEUE_MAX 6
+#define TFD_QUEUE_MAX 5 /* 4 DATA + 1 CMD */
#define IWL_NUM_SCAN_RATES (2)
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/trunk/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
index f65c308a6714..af6b9d444778 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
@@ -124,7 +124,7 @@ static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = {
#define IWL39_RATE_HIGH_TH 11520
#define IWL_SUCCESS_UP_TH 8960
#define IWL_SUCCESS_DOWN_TH 10880
-#define IWL_RATE_MIN_FAILURE_TH 8
+#define IWL_RATE_MIN_FAILURE_TH 6
#define IWL_RATE_MIN_SUCCESS_TH 8
#define IWL_RATE_DECREASE_TH 1920
#define IWL_RATE_RETRY_TH 15
@@ -488,7 +488,7 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
IWL_DEBUG_RATE(priv, "enter\n");
- retries = info->status.rates[0].count - 1;
+ retries = info->status.rates[0].count;
/* Sanity Check for retries */
if (retries > IWL_RATE_RETRY_TH)
retries = IWL_RATE_RETRY_TH;
@@ -791,16 +791,15 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n");
scale_action = -1;
-
/* No throughput measured yet for adjacent rates,
* try increase */
} else if ((low_tpt == IWL_INVALID_VALUE) &&
(high_tpt == IWL_INVALID_VALUE)) {
- if (high != IWL_RATE_INVALID && window->success_counter >= IWL_RATE_INCREASE_TH)
+ if (high != IWL_RATE_INVALID && window->success_ratio >= IWL_RATE_INCREASE_TH)
scale_action = 1;
else if (low != IWL_RATE_INVALID)
- scale_action = -1;
+ scale_action = 0;
/* Both adjacent throughputs are measured, but neither one has
* better throughput; we're using the best rate, don't change
@@ -826,14 +825,14 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
else {
IWL_DEBUG_RATE(priv,
"decrease rate because of high tpt\n");
- scale_action = -1;
+ scale_action = 0;
}
} else if (low_tpt != IWL_INVALID_VALUE) {
if (low_tpt > current_tpt) {
IWL_DEBUG_RATE(priv,
"decrease rate because of low tpt\n");
scale_action = -1;
- } else if (window->success_counter >= IWL_RATE_INCREASE_TH) {
+ } else if (window->success_ratio >= IWL_RATE_INCREASE_TH) {
/* Lower rate has better
* throughput,decrease rate */
scale_action = 1;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-3945.c b/trunk/drivers/net/wireless/iwlwifi/iwl-3945.c
index ba7e720e73c1..2399328e8de7 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -293,7 +293,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv,
if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) &&
(txq_id != IWL_CMD_QUEUE_NUM) &&
priv->mac80211_registered)
- ieee80211_wake_queue(priv->hw, txq_id);
+ iwl_wake_queue(priv, txq_id);
}
/**
@@ -747,11 +747,6 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
int i;
int counter;
- /* classify bd */
- if (txq->q.id == IWL_CMD_QUEUE_NUM)
- /* nothing to cleanup after for host commands */
- return;
-
/* sanity check */
counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags));
if (counter > NUM_TFD_CHUNKS) {
@@ -1046,7 +1041,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
goto error;
/* Tx queue(s) */
- for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
+ for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) {
slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
@@ -1184,7 +1179,7 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv)
IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", rev_id);
rc = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN);
- if(rc)
+ if (rc)
return rc;
priv->cfg->ops->lib->apm_ops.config(priv);
@@ -1239,8 +1234,12 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv)
int txq_id;
/* Tx queues */
- for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++)
- iwl_tx_queue_free(priv, txq_id);
+ for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++)
+ if (txq_id == IWL_CMD_QUEUE_NUM)
+ iwl_cmd_queue_free(priv);
+ else
+ iwl_tx_queue_free(priv, txq_id);
+
}
void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
@@ -1259,7 +1258,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
iwl_write_prph(priv, ALM_SCD_MODE_REG, 0);
/* reset TFD queues */
- for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
+ for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) {
iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0);
iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS,
FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id),
@@ -2488,6 +2487,9 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv)
return -ENOMEM;
}
+ /* Assign number of Usable TX queues */
+ priv->hw_params.max_txq_num = TFD_QUEUE_MAX;
+
priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd);
priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K;
priv->hw_params.max_pkt_size = 2342;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-4965.c b/trunk/drivers/net/wireless/iwlwifi/iwl-4965.c
index bd0140be774e..847a6220c5e6 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2178,10 +2178,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
(iwl_queue_space(&txq->q) > txq->q.low_mark) &&
(agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
if (agg->state == IWL_AGG_OFF)
- ieee80211_wake_queue(priv->hw, txq_id);
+ iwl_wake_queue(priv, txq_id);
else
- ieee80211_wake_queue(priv->hw,
- txq->swq_id);
+ iwl_wake_queue(priv, txq->swq_id);
}
}
} else {
@@ -2205,7 +2204,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark))
- ieee80211_wake_queue(priv->hw, txq_id);
+ iwl_wake_queue(priv, txq_id);
}
if (qc && likely(sta_id != IWL_INVALID_STATION))
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-5000.c b/trunk/drivers/net/wireless/iwlwifi/iwl-5000.c
index 08c19bea71e3..e5ca2511a81a 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -1077,7 +1077,7 @@ static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
(IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
- IWL_WARN(priv,
+ IWL_ERR(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWL50_FIRST_AMPDU_QUEUE,
IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1);
@@ -1295,10 +1295,9 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
(iwl_queue_space(&txq->q) > txq->q.low_mark) &&
(agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
if (agg->state == IWL_AGG_OFF)
- ieee80211_wake_queue(priv->hw, txq_id);
+ iwl_wake_queue(priv, txq_id);
else
- ieee80211_wake_queue(priv->hw,
- txq->swq_id);
+ iwl_wake_queue(priv, txq->swq_id);
}
}
} else {
@@ -1324,7 +1323,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark))
- ieee80211_wake_queue(priv->hw, txq_id);
+ iwl_wake_queue(priv, txq_id);
}
if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-agn.c b/trunk/drivers/net/wireless/iwlwifi/iwl-agn.c
index 0db3bc011ac2..663dc83be501 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1567,9 +1567,8 @@ static void iwl_alive_start(struct iwl_priv *priv)
if (iwl_is_associated(priv)) {
struct iwl_rxon_cmd *active_rxon =
(struct iwl_rxon_cmd *)&priv->active_rxon;
-
- memcpy(&priv->staging_rxon, &priv->active_rxon,
- sizeof(priv->staging_rxon));
+ /* apply any changes in staging */
+ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
} else {
/* Initialize our rx_config data */
@@ -2184,110 +2183,112 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info;
struct ieee80211_conf *conf = &hw->conf;
- unsigned long flags;
+ unsigned long flags = 0;
int ret = 0;
- u16 channel;
+ u16 ch;
+ int scan_active = 0;
mutex_lock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "enter to channel %d\n", conf->channel->hw_value);
+ IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
+ conf->channel->hw_value, changed);
- priv->current_ht_config.is_ht = conf_is_ht(conf);
-
- if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n");
- goto out;
+ if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
+ test_bit(STATUS_SCANNING, &priv->status))) {
+ scan_active = 1;
+ IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
}
- if (!conf->radio_enabled)
- iwl_radio_kill_sw_disable_radio(priv);
- if (!iwl_is_ready(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
- ret = -EIO;
- goto out;
- }
+ /* during scanning mac80211 will delay channel setting until
+ * scan finish with changed = 0
+ */
+ if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+ if (scan_active)
+ goto set_ch_out;
+
+ ch = ieee80211_frequency_to_channel(conf->channel->center_freq);
+ ch_info = iwl_get_channel_info(priv, conf->channel->band, ch);
+ if (!is_channel_valid(ch_info)) {
+ IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
+ ret = -EINVAL;
+ goto set_ch_out;
+ }
- if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
- test_bit(STATUS_SCANNING, &priv->status))) {
- IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
- mutex_unlock(&priv->mutex);
- return 0;
- }
+ if (priv->iw_mode == NL80211_IFTYPE_ADHOC &&
+ !is_channel_ibss(ch_info)) {
+ IWL_ERR(priv, "channel %d in band %d not "
+ "IBSS channel\n",
+ conf->channel->hw_value, conf->channel->band);
+ ret = -EINVAL;
+ goto set_ch_out;
+ }
- channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
- ch_info = iwl_get_channel_info(priv, conf->channel->band, channel);
- if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
- ret = -EINVAL;
- goto out;
- }
+ priv->current_ht_config.is_ht = conf_is_ht(conf);
- if (priv->iw_mode == NL80211_IFTYPE_ADHOC &&
- !is_channel_ibss(ch_info)) {
- IWL_ERR(priv, "channel %d in band %d not IBSS channel\n",
- conf->channel->hw_value, conf->channel->band);
- ret = -EINVAL;
- goto out;
- }
+ spin_lock_irqsave(&priv->lock, flags);
- spin_lock_irqsave(&priv->lock, flags);
+ /* if we are switching from ht to 2.4 clear flags
+ * from any ht related info since 2.4 does not
+ * support ht */
+ if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
+ priv->staging_rxon.flags = 0;
- /* if we are switching from ht to 2.4 clear flags
- * from any ht related info since 2.4 does not
- * support ht */
- if ((le16_to_cpu(priv->staging_rxon.channel) != channel)
-#ifdef IEEE80211_CONF_CHANNEL_SWITCH
- && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH)
-#endif
- )
- priv->staging_rxon.flags = 0;
+ iwl_set_rxon_channel(priv, conf->channel);
- iwl_set_rxon_channel(priv, conf->channel);
+ iwl_set_flags_for_band(priv, conf->channel->band);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_ch_out:
+ /* The list of supported rates and rate mask can be different
+ * for each band; since the band may have changed, reset
+ * the rate mask to what mac80211 lists */
+ iwl_set_rate(priv);
+ }
- iwl_set_flags_for_band(priv, conf->channel->band);
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (conf->flags & IEEE80211_CONF_PS)
+ ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3);
+ else
+ ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM);
+ if (ret)
+ IWL_DEBUG_MAC80211(priv, "Error setting power level\n");
- /* The list of supported rates and rate mask can be different
- * for each band; since the band may have changed, reset
- * the rate mask to what mac80211 lists */
- iwl_set_rate(priv);
+ }
- spin_unlock_irqrestore(&priv->lock, flags);
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
+ priv->tx_power_user_lmt, conf->power_level);
-#ifdef IEEE80211_CONF_CHANNEL_SWITCH
- if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
- iwl_hw_channel_switch(priv, conf->channel);
- goto out;
+ iwl_set_tx_power(priv, conf->power_level, false);
+ }
+
+ /* call to ensure that 4965 rx_chain is set properly in monitor mode */
+ iwl_set_rxon_chain(priv);
+
+ if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+ if (conf->radio_enabled &&
+ iwl_radio_kill_sw_enable_radio(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - "
+ "waiting for uCode\n");
+ goto out;
+ }
+
+ if (!conf->radio_enabled)
+ iwl_radio_kill_sw_disable_radio(priv);
}
-#endif
if (!conf->radio_enabled) {
IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
goto out;
}
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - RF kill\n");
- ret = -EIO;
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
goto out;
}
- if (conf->flags & IEEE80211_CONF_PS)
- ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3);
- else
- ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM);
- if (ret)
- IWL_DEBUG_MAC80211(priv, "Error setting power level\n");
-
- IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
- priv->tx_power_user_lmt, conf->power_level);
-
- iwl_set_tx_power(priv, conf->power_level, false);
-
- iwl_set_rate(priv);
-
- /* call to ensure that 4965 rx_chain is set properly in monitor mode */
- iwl_set_rxon_chain(priv);
+ if (scan_active)
+ goto out;
if (memcmp(&priv->active_rxon,
&priv->staging_rxon, sizeof(priv->staging_rxon)))
@@ -2295,9 +2296,9 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
else
IWL_DEBUG_INFO(priv, "No re-sending same RXON configuration.\n");
- IWL_DEBUG_MAC80211(priv, "leave\n");
out:
+ IWL_DEBUG_MAC80211(priv, "leave\n");
mutex_unlock(&priv->mutex);
return ret;
}
@@ -2682,6 +2683,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
struct iwl_priv *priv = hw->priv;
+ int ret;
IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
sta->addr, tid);
@@ -2695,13 +2697,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
return iwl_sta_rx_agg_start(priv, sta->addr, tid, *ssn);
case IEEE80211_AMPDU_RX_STOP:
IWL_DEBUG_HT(priv, "stop Rx\n");
- return iwl_sta_rx_agg_stop(priv, sta->addr, tid);
+ ret = iwl_sta_rx_agg_stop(priv, sta->addr, tid);
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return 0;
+ else
+ return ret;
case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT(priv, "start Tx\n");
return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
case IEEE80211_AMPDU_TX_STOP:
IWL_DEBUG_HT(priv, "stop Tx\n");
- return iwl_tx_agg_stop(priv, sta->addr, tid);
+ ret = iwl_tx_agg_stop(priv, sta->addr, tid);
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return 0;
+ else
+ return ret;
default:
IWL_DEBUG_HT(priv, "unknown\n");
return -EINVAL;
@@ -3083,11 +3093,6 @@ static ssize_t store_power_level(struct device *d,
mutex_lock(&priv->mutex);
- if (!iwl_is_ready(priv)) {
- ret = -EAGAIN;
- goto out;
- }
-
ret = strict_strtoul(buf, 10, &mode);
if (ret)
goto out;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-core.c b/trunk/drivers/net/wireless/iwlwifi/iwl-core.c
index 085e9cf1cac9..c54fb93e9d72 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -1298,6 +1298,7 @@ int iwl_setup_mac(struct iwl_priv *priv)
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM |
IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_SUPPORTS_PS;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
@@ -1308,9 +1309,6 @@ int iwl_setup_mac(struct iwl_priv *priv)
/* Default value; 4 EDCA QOS priorities */
hw->queues = 4;
- /* queues to support 11n aggregation */
- if (priv->cfg->sku & IWL_SKU_N)
- hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues;
hw->conf.beacon_int = 100;
hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
@@ -1437,6 +1435,10 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
priv->tx_power_user_lmt = tx_power;
+ /* if nic is not up don't send command */
+ if (!iwl_is_ready_rf(priv))
+ return ret;
+
if (force && priv->cfg->ops->lib->send_tx_power)
ret = priv->cfg->ops->lib->send_tx_power(priv);
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-core.h b/trunk/drivers/net/wireless/iwlwifi/iwl-core.h
index 27310fec2e43..a8eac8c3c1fa 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -264,6 +264,7 @@ void iwl_rx_reply_error(struct iwl_priv *priv,
* RX
******************************************************/
void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
+void iwl_cmd_queue_free(struct iwl_priv *priv);
int iwl_rx_queue_alloc(struct iwl_priv *priv);
void iwl_rx_handle(struct iwl_priv *priv);
int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/trunk/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 36cfeccfafbc..64eb585f1578 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -425,6 +425,56 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
return ret;
}
+static ssize_t iwl_dbgfs_status_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos) {
+
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ char buf[512];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n",
+ test_bit(STATUS_HCMD_ACTIVE, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_SYNC_ACTIVE: %d\n",
+ test_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n",
+ test_bit(STATUS_INT_ENABLED, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
+ test_bit(STATUS_RF_KILL_HW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_SW:\t %d\n",
+ test_bit(STATUS_RF_KILL_SW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n",
+ test_bit(STATUS_INIT, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
+ test_bit(STATUS_ALIVE, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n",
+ test_bit(STATUS_READY, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_TEMPERATURE:\t %d\n",
+ test_bit(STATUS_TEMPERATURE, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n",
+ test_bit(STATUS_GEO_CONFIGURED, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n",
+ test_bit(STATUS_EXIT_PENDING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_IN_SUSPEND:\t %d\n",
+ test_bit(STATUS_IN_SUSPEND, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n",
+ test_bit(STATUS_STATISTICS, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n",
+ test_bit(STATUS_SCANNING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n",
+ test_bit(STATUS_SCAN_ABORTING, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
+ test_bit(STATUS_SCAN_HW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
+ test_bit(STATUS_POWER_PMI, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
+ test_bit(STATUS_FW_ERROR, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_MODE_PENDING:\t %d\n",
+ test_bit(STATUS_MODE_PENDING, &priv->status));
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
DEBUGFS_READ_WRITE_FILE_OPS(sram);
DEBUGFS_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_FILE_OPS(eeprom);
@@ -432,6 +482,7 @@ DEBUGFS_READ_FILE_OPS(stations);
DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_FILE_OPS(channels);
+DEBUGFS_READ_FILE_OPS(status);
/*
* Create the debugfs files and directories
@@ -466,7 +517,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(rx_statistics, data);
DEBUGFS_ADD_FILE(tx_statistics, data);
DEBUGFS_ADD_FILE(channels, data);
- DEBUGFS_ADD_X32(status, data, (u32 *)&priv->status);
+ DEBUGFS_ADD_FILE(status, data);
DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal);
DEBUGFS_ADD_BOOL(disable_chain_noise, rf,
&priv->disable_chain_noise_cal);
@@ -496,6 +547,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status);
DEBUGFS_REMOVE(priv->dbgfs->dir_data);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise);
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-dev.h b/trunk/drivers/net/wireless/iwlwifi/iwl-dev.h
index 0baae8022824..ec9a13846edd 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -996,6 +996,12 @@ struct iwl_priv {
u8 key_mapping_key;
unsigned long ucode_key_table;
+ /* queue refcounts */
+#define IWL_MAX_HW_QUEUES 32
+ unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+ /* for each AC */
+ atomic_t queue_stop_count[4];
+
/* Indication if ieee80211_ops->open has been called */
u8 is_open;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-helpers.h b/trunk/drivers/net/wireless/iwlwifi/iwl-helpers.h
index fb64d297dd4e..a1328c3c81ae 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-helpers.h
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-helpers.h
@@ -93,4 +93,56 @@ static inline int iwl_alloc_fw_desc(struct pci_dev *pci_dev,
return (desc->v_addr != NULL) ? 0 : -ENOMEM;
}
+/*
+ * we have 8 bits used like this:
+ *
+ * 7 6 5 4 3 2 1 0
+ * | | | | | | | |
+ * | | | | | | +-+-------- AC queue (0-3)
+ * | | | | | |
+ * | +-+-+-+-+------------ HW A-MPDU queue
+ * |
+ * +---------------------- indicates agg queue
+ */
+static inline u8 iwl_virtual_agg_queue_num(u8 ac, u8 hwq)
+{
+ BUG_ON(ac > 3); /* only have 2 bits */
+ BUG_ON(hwq > 31); /* only have 5 bits */
+
+ return 0x80 | (hwq << 2) | ac;
+}
+
+static inline void iwl_wake_queue(struct iwl_priv *priv, u8 queue)
+{
+ u8 ac = queue;
+ u8 hwq = queue;
+
+ if (queue & 0x80) {
+ ac = queue & 3;
+ hwq = (queue >> 2) & 0x1f;
+ }
+
+ if (test_and_clear_bit(hwq, priv->queue_stopped))
+ if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0)
+ ieee80211_wake_queue(priv->hw, ac);
+}
+
+static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue)
+{
+ u8 ac = queue;
+ u8 hwq = queue;
+
+ if (queue & 0x80) {
+ ac = queue & 3;
+ hwq = (queue >> 2) & 0x1f;
+ }
+
+ if (!test_and_set_bit(hwq, priv->queue_stopped))
+ if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0)
+ ieee80211_stop_queue(priv->hw, ac);
+}
+
+#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
+#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
+
#endif /* __iwl_helpers_h__ */
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-power.c b/trunk/drivers/net/wireless/iwlwifi/iwl-power.c
index 18b7e4195ea1..47c894530eb5 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -273,7 +273,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
if (priv->iw_mode != NL80211_IFTYPE_STATION)
final_mode = IWL_POWER_MODE_CAM;
- if (!iwl_is_rfkill(priv) && !setting->power_disabled &&
+ if (iwl_is_ready_rf(priv) && !setting->power_disabled &&
((setting->power_mode != final_mode) || force)) {
struct iwl_powertable_cmd cmd;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-sta.c b/trunk/drivers/net/wireless/iwlwifi/iwl-sta.c
index 1684490d93c0..5798fe49c771 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -1138,8 +1138,10 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
int sta_id;
sta_id = iwl_find_station(priv, addr);
- if (sta_id == IWL_INVALID_STATION)
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
return -ENXIO;
+ }
spin_lock_irqsave(&priv->sta_lock, flags);
priv->stations[sta_id].sta.station_flags_msk = 0;
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-tx.c b/trunk/drivers/net/wireless/iwlwifi/iwl-tx.c
index dff60fb70214..1f117a49c569 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -174,7 +174,7 @@ EXPORT_SYMBOL(iwl_tx_queue_free);
* Free all buffers.
* 0-fill, but do not free "txq" descriptor structure.
*/
-static void iwl_cmd_queue_free(struct iwl_priv *priv)
+void iwl_cmd_queue_free(struct iwl_priv *priv)
{
struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
struct iwl_queue *q = &txq->q;
@@ -193,12 +193,14 @@ static void iwl_cmd_queue_free(struct iwl_priv *priv)
/* De-alloc circular buffer of TFDs */
if (txq->q.n_bd)
- pci_free_consistent(dev, sizeof(struct iwl_tfd) *
+ pci_free_consistent(dev, priv->hw_params.tfd_size *
txq->q.n_bd, txq->tfds, txq->q.dma_addr);
/* 0-fill queue descriptor structure */
memset(txq, 0, sizeof(*txq));
}
+EXPORT_SYMBOL(iwl_cmd_queue_free);
+
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
*
@@ -761,8 +763,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
hdr->seq_ctrl |= cpu_to_le16(seq_number);
seq_number += 0x10;
/* aggregation is on for this */
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
+ swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id);
+ }
priv->stations[sta_id].tid[tid].tfds_in_queue++;
}
@@ -893,7 +897,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwl_txq_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->lock, flags);
} else {
- ieee80211_stop_queue(priv->hw, txq->swq_id);
+ iwl_stop_queue(priv, txq->swq_id);
}
}
@@ -1221,8 +1225,10 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid)
sta_id = iwl_find_station(priv, ra);
- if (sta_id == IWL_INVALID_STATION)
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
return -ENXIO;
+ }
if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON)
IWL_WARN(priv, "Stopping AGG while state not IWL_AGG_ON\n");
@@ -1429,7 +1435,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
priv->mac80211_registered &&
(agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
- ieee80211_wake_queue(priv->hw, txq->swq_id);
+ iwl_wake_queue(priv, txq->swq_id);
iwl_txq_check_empty(priv, sta_id, tid, scd_flow);
}
diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl3945-base.c b/trunk/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 4465320f2735..a71b08ca7c71 100644
--- a/trunk/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/trunk/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -485,14 +485,14 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
memcpy(priv->stations_39[sta_id].sta.key.key, keyconf->key,
keyconf->keylen);
- if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
+ if ((priv->stations_39[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
== STA_KEY_FLG_NO_ENC)
- priv->stations[sta_id].sta.key.key_offset =
+ priv->stations_39[sta_id].sta.key.key_offset =
iwl_get_free_ucode_key_index(priv);
/* else, we are overriding an existing key => no need to allocated room
* in uCode. */
- WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
+ WARN(priv->stations_39[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
"no space for a new key");
priv->stations_39[sta_id].sta.key.key_flags = key_flags;
@@ -560,7 +560,7 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv,
ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id);
break;
default:
- IWL_ERR(priv,"Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
+ IWL_ERR(priv, "Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
ret = -EINVAL;
}
@@ -1168,7 +1168,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
spin_unlock_irqrestore(&priv->lock, flags);
}
- ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
+ iwl_stop_queue(priv, skb_get_queue_mapping(skb));
}
return 0;
@@ -3773,15 +3773,19 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, u32 changed)
}
#endif
- if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) {
- IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n");
- goto out;
- }
+ if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+ if (conf->radio_enabled &&
+ iwl_radio_kill_sw_enable_radio(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - "
+ "waiting for uCode\n");
+ goto out;
+ }
- if (!conf->radio_enabled) {
- iwl_radio_kill_sw_disable_radio(priv);
- IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
- goto out;
+ if (!conf->radio_enabled) {
+ iwl_radio_kill_sw_disable_radio(priv);
+ IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
+ goto out;
+ }
}
if (iwl_is_rfkill(priv)) {
@@ -4546,11 +4550,6 @@ static ssize_t store_power_level(struct device *d,
mutex_lock(&priv->mutex);
- if (!iwl_is_ready(priv)) {
- ret = -EAGAIN;
- goto out;
- }
-
ret = strict_strtoul(buf, 10, &mode);
if (ret)
goto out;
@@ -4905,7 +4904,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv)
/* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_SPECTRUM_MGMT;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
diff --git a/trunk/drivers/net/wireless/libertas/radiotap.h b/trunk/drivers/net/wireless/libertas/radiotap.h
index f8eb9097ff0a..d16b26416e82 100644
--- a/trunk/drivers/net/wireless/libertas/radiotap.h
+++ b/trunk/drivers/net/wireless/libertas/radiotap.h
@@ -33,22 +33,12 @@ struct rx_radiotap_hdr {
struct ieee80211_radiotap_header hdr;
u8 flags;
u8 rate;
- u16 chan_freq;
- u16 chan_flags;
- u8 antenna;
u8 antsignal;
- u16 rx_flags;
-#if 0
- u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
-#endif
} __attribute__ ((packed));
#define RX_RADIOTAP_PRESENT ( \
(1 << IEEE80211_RADIOTAP_FLAGS) | \
(1 << IEEE80211_RADIOTAP_RATE) | \
- (1 << IEEE80211_RADIOTAP_CHANNEL) | \
- (1 << IEEE80211_RADIOTAP_ANTENNA) | \
(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
- (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \
0)
diff --git a/trunk/drivers/net/wireless/libertas/rx.c b/trunk/drivers/net/wireless/libertas/rx.c
index 4f60948dde9c..63d7e19ce9bd 100644
--- a/trunk/drivers/net/wireless/libertas/rx.c
+++ b/trunk/drivers/net/wireless/libertas/rx.c
@@ -351,19 +351,11 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
radiotap_hdr.hdr.it_pad = 0;
radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr));
radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT);
- /* unknown values */
- radiotap_hdr.flags = 0;
- radiotap_hdr.chan_freq = 0;
- radiotap_hdr.chan_flags = 0;
- radiotap_hdr.antenna = 0;
- /* known values */
+ if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
+ radiotap_hdr.flags |= IEEE80211_RADIOTAP_F_BADFCS;
radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
/* XXX must check no carryout */
radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
- radiotap_hdr.rx_flags = 0;
- if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
- radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS;
- //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18);
/* chop the rxpd */
skb_pull(skb, sizeof(struct rxpd));
diff --git a/trunk/drivers/net/wireless/mac80211_hwsim.c b/trunk/drivers/net/wireless/mac80211_hwsim.c
index 2368b7f825a2..d4fdc8b7d7d8 100644
--- a/trunk/drivers/net/wireless/mac80211_hwsim.c
+++ b/trunk/drivers/net/wireless/mac80211_hwsim.c
@@ -933,7 +933,6 @@ static int __init init_mac80211_hwsim(void)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT);
- hw->ampdu_queues = 1;
hw->flags = IEEE80211_HW_MFP_CAPABLE;
@@ -1041,6 +1040,9 @@ static int __init init_mac80211_hwsim(void)
break;
}
+ /* give the regulatory workqueue a chance to run */
+ if (regtest)
+ schedule_timeout_interruptible(1);
err = ieee80211_register_hw(hw);
if (err < 0) {
printk(KERN_DEBUG "mac80211_hwsim: "
diff --git a/trunk/drivers/net/wireless/p54/Kconfig b/trunk/drivers/net/wireless/p54/Kconfig
index cfc5f41aa136..b45d6a4ed1e8 100644
--- a/trunk/drivers/net/wireless/p54/Kconfig
+++ b/trunk/drivers/net/wireless/p54/Kconfig
@@ -1,9 +1,10 @@
config P54_COMMON
tristate "Softmac Prism54 support"
- depends on MAC80211 && WLAN_80211 && FW_LOADER && EXPERIMENTAL
+ depends on MAC80211 && WLAN_80211 && EXPERIMENTAL
+ select FW_LOADER
---help---
- This is common code for isl38xx based cards.
- This module does nothing by itself - the USB/PCI frontends
+ This is common code for isl38xx/stlc45xx based modules.
+ This module does nothing by itself - the USB/PCI/SPI front-ends
also need to be enabled in order to support any devices.
These devices require softmac firmware which can be found at
@@ -17,31 +18,6 @@ config P54_USB
select CRC32
---help---
This driver is for USB isl38xx based wireless cards.
- These are USB based adapters found in devices such as:
-
- 3COM 3CRWE254G72
- SMC 2862W-G
- Accton 802.11g WN4501 USB
- Siemens Gigaset USB
- Netgear WG121
- Netgear WG111
- Medion 40900, Roper Europe
- Shuttle PN15, Airvast WM168g, IOGear GWU513
- Linksys WUSB54G
- Linksys WUSB54G Portable
- DLink DWL-G120 Spinnaker
- DLink DWL-G122
- Belkin F5D7050 ver 1000
- Cohiba Proto board
- SMC 2862W-G version 2
- U.S. Robotics U5 802.11g Adapter
- FUJITSU E-5400 USB D1700
- Sagem XG703A
- DLink DWL-G120 Cohiba
- Spinnaker Proto board
- Linksys WUSB54AG
- Inventel UR054G
- Spinnaker DUT
These devices require softmac firmware which can be found at
http://prism54.org/
@@ -64,10 +40,15 @@ config P54_PCI
config P54_SPI
tristate "Prism54 SPI (stlc45xx) support"
- depends on P54_COMMON && SPI_MASTER
+ depends on P54_COMMON && SPI_MASTER && GENERIC_HARDIRQS
---help---
This driver is for stlc4550 or stlc4560 based wireless chips.
This driver is experimental, untested and will probably only work on
Nokia's N800/N810 Portable Internet Tablet.
If you choose to build a module, it'll be called p54spi.
+
+config P54_LEDS
+ bool
+ depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON)
+ default y
diff --git a/trunk/drivers/net/wireless/p54/p54common.c b/trunk/drivers/net/wireless/p54/p54common.c
index 0a989834b70d..0c1b0577d4ee 100644
--- a/trunk/drivers/net/wireless/p54/p54common.c
+++ b/trunk/drivers/net/wireless/p54/p54common.c
@@ -21,9 +21,9 @@
#include
#include
-#ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
#include
-#endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
#include "p54.h"
#include "p54common.h"
@@ -2420,7 +2420,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
return 0;
}
-#ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
static void p54_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
@@ -2508,7 +2508,7 @@ static void p54_unregister_leds(struct ieee80211_hw *dev)
if (priv->assoc_led.registered)
led_classdev_unregister(&priv->assoc_led.led_dev);
}
-#endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
static const struct ieee80211_ops p54_ops = {
.tx = p54_tx,
@@ -2592,11 +2592,11 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
return err;
}
- #ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
err = p54_init_leds(dev);
if (err)
return err;
- #endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
return 0;
@@ -2610,9 +2610,9 @@ void p54_free_common(struct ieee80211_hw *dev)
kfree(priv->output_limit);
kfree(priv->curve_data);
- #ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
p54_unregister_leds(dev);
- #endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
}
EXPORT_SYMBOL_GPL(p54_free_common);
diff --git a/trunk/drivers/net/wireless/rt2x00/rt73usb.c b/trunk/drivers/net/wireless/rt2x00/rt73usb.c
index 24fdfdfee3df..420fff42c0dd 100644
--- a/trunk/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/trunk/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2425,6 +2425,8 @@ static struct usb_device_id rt73usb_device_table[] = {
{ USB_DEVICE(0x0df6, 0x9712), USB_DEVICE_DATA(&rt73usb_ops) },
/* Surecom */
{ USB_DEVICE(0x0769, 0x31f3), USB_DEVICE_DATA(&rt73usb_ops) },
+ /* Tilgin */
+ { USB_DEVICE(0x6933, 0x5001), USB_DEVICE_DATA(&rt73usb_ops) },
/* Philips */
{ USB_DEVICE(0x0471, 0x200a), USB_DEVICE_DATA(&rt73usb_ops) },
/* Planex */
diff --git a/trunk/drivers/net/wireless/wavelan.c b/trunk/drivers/net/wireless/wavelan.c
index b728541f2fb5..3ab3eb957189 100644
--- a/trunk/drivers/net/wireless/wavelan.c
+++ b/trunk/drivers/net/wireless/wavelan.c
@@ -735,9 +735,9 @@ if (lp->tx_n_in_use > 0)
if (tx_status & AC_SFLD_OK) {
int ncollisions;
- lp->stats.tx_packets++;
+ dev->stats.tx_packets++;
ncollisions = tx_status & AC_SFLD_MAXCOL;
- lp->stats.collisions += ncollisions;
+ dev->stats.collisions += ncollisions;
#ifdef DEBUG_TX_INFO
if (ncollisions > 0)
printk(KERN_DEBUG
@@ -745,9 +745,9 @@ if (lp->tx_n_in_use > 0)
dev->name, ncollisions);
#endif
} else {
- lp->stats.tx_errors++;
+ dev->stats.tx_errors++;
if (tx_status & AC_SFLD_S10) {
- lp->stats.tx_carrier_errors++;
+ dev->stats.tx_carrier_errors++;
#ifdef DEBUG_TX_FAIL
printk(KERN_DEBUG
"%s: wv_complete(): tx error: no CS.\n",
@@ -755,7 +755,7 @@ if (lp->tx_n_in_use > 0)
#endif
}
if (tx_status & AC_SFLD_S9) {
- lp->stats.tx_carrier_errors++;
+ dev->stats.tx_carrier_errors++;
#ifdef DEBUG_TX_FAIL
printk(KERN_DEBUG
"%s: wv_complete(): tx error: lost CTS.\n",
@@ -763,7 +763,7 @@ if (lp->tx_n_in_use > 0)
#endif
}
if (tx_status & AC_SFLD_S8) {
- lp->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
#ifdef DEBUG_TX_FAIL
printk(KERN_DEBUG
"%s: wv_complete(): tx error: slow DMA.\n",
@@ -771,7 +771,7 @@ if (lp->tx_n_in_use > 0)
#endif
}
if (tx_status & AC_SFLD_S6) {
- lp->stats.tx_heartbeat_errors++;
+ dev->stats.tx_heartbeat_errors++;
#ifdef DEBUG_TX_FAIL
printk(KERN_DEBUG
"%s: wv_complete(): tx error: heart beat.\n",
@@ -779,7 +779,7 @@ if (lp->tx_n_in_use > 0)
#endif
}
if (tx_status & AC_SFLD_S5) {
- lp->stats.tx_aborted_errors++;
+ dev->stats.tx_aborted_errors++;
#ifdef DEBUG_TX_FAIL
printk(KERN_DEBUG
"%s: wv_complete(): tx error: too many collisions.\n",
@@ -1346,20 +1346,6 @@ static void wv_init_info(struct net_device * dev)
* or wireless extensions
*/
-/*------------------------------------------------------------------*/
-/*
- * Get the current Ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats *wavelan_get_stats(struct net_device * dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
- return &((net_local *)netdev_priv(dev))->stats;
-}
/*------------------------------------------------------------------*/
/*
@@ -2466,7 +2452,7 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
"%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
dev->name, sksize);
#endif
- lp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
return;
}
@@ -2526,8 +2512,8 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
netif_rx(skb);
/* Keep statistics up to date */
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += sksize;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += sksize;
#ifdef DEBUG_RX_TRACE
printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
@@ -2608,7 +2594,7 @@ static void wv_receive(struct net_device * dev)
#endif
} else { /* If reception was no successful */
- lp->stats.rx_errors++;
+ dev->stats.rx_errors++;
#ifdef DEBUG_RX_INFO
printk(KERN_DEBUG
@@ -2624,7 +2610,7 @@ static void wv_receive(struct net_device * dev)
#endif
if ((fd.fd_status & FD_STATUS_S7) != 0) {
- lp->stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
#ifdef DEBUG_RX_FAIL
printk(KERN_DEBUG
"%s: wv_receive(): frame too short.\n",
@@ -2633,7 +2619,7 @@ static void wv_receive(struct net_device * dev)
}
if ((fd.fd_status & FD_STATUS_S8) != 0) {
- lp->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
#ifdef DEBUG_RX_FAIL
printk(KERN_DEBUG
"%s: wv_receive(): rx DMA overrun.\n",
@@ -2642,7 +2628,7 @@ static void wv_receive(struct net_device * dev)
}
if ((fd.fd_status & FD_STATUS_S9) != 0) {
- lp->stats.rx_fifo_errors++;
+ dev->stats.rx_fifo_errors++;
#ifdef DEBUG_RX_FAIL
printk(KERN_DEBUG
"%s: wv_receive(): ran out of resources.\n",
@@ -2651,7 +2637,7 @@ static void wv_receive(struct net_device * dev)
}
if ((fd.fd_status & FD_STATUS_S10) != 0) {
- lp->stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
#ifdef DEBUG_RX_FAIL
printk(KERN_DEBUG
"%s: wv_receive(): alignment error.\n",
@@ -2660,7 +2646,7 @@ static void wv_receive(struct net_device * dev)
}
if ((fd.fd_status & FD_STATUS_S11) != 0) {
- lp->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
#ifdef DEBUG_RX_FAIL
printk(KERN_DEBUG
"%s: wv_receive(): CRC error.\n",
@@ -2826,7 +2812,7 @@ static int wv_packet_write(struct net_device * dev, void *buf, short length)
dev->trans_start = jiffies;
/* Keep stats up to date. */
- lp->stats.tx_bytes += length;
+ dev->stats.tx_bytes += length;
if (lp->tx_first_in_use == I82586NULL)
lp->tx_first_in_use = txblock;
@@ -4038,6 +4024,22 @@ static int wavelan_close(struct net_device * dev)
return 0;
}
+static const struct net_device_ops wavelan_netdev_ops = {
+ .ndo_open = wavelan_open,
+ .ndo_stop = wavelan_close,
+ .ndo_start_xmit = wavelan_packet_xmit,
+ .ndo_set_multicast_list = wavelan_set_multicast_list,
+ .ndo_tx_timeout = wavelan_watchdog,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef SET_MAC_ADDRESS
+ .ndo_set_mac_address = wavelan_set_mac_address
+#else
+ .ndo_set_mac_address = eth_mac_addr,
+#endif
+};
+
+
/*------------------------------------------------------------------*/
/*
* Probe an I/O address, and if the WaveLAN is there configure the
@@ -4130,17 +4132,8 @@ static int __init wavelan_config(struct net_device *dev, unsigned short ioaddr)
/* Init spinlock */
spin_lock_init(&lp->spinlock);
- dev->open = wavelan_open;
- dev->stop = wavelan_close;
- dev->hard_start_xmit = wavelan_packet_xmit;
- dev->get_stats = wavelan_get_stats;
- dev->set_multicast_list = &wavelan_set_multicast_list;
- dev->tx_timeout = &wavelan_watchdog;
- dev->watchdog_timeo = WATCHDOG_JIFFIES;
-#ifdef SET_MAC_ADDRESS
- dev->set_mac_address = &wavelan_set_mac_address;
-#endif /* SET_MAC_ADDRESS */
-
+ dev->netdev_ops = &wavelan_netdev_ops;
+ dev->watchdog_timeo = WATCHDOG_JIFFIES;
dev->wireless_handlers = &wavelan_handler_def;
lp->wireless_data.spy_data = &lp->spy_data;
dev->wireless_data = &lp->wireless_data;
diff --git a/trunk/drivers/net/wireless/wavelan.p.h b/trunk/drivers/net/wireless/wavelan.p.h
index 44d31bbf39e4..2daa0210d789 100644
--- a/trunk/drivers/net/wireless/wavelan.p.h
+++ b/trunk/drivers/net/wireless/wavelan.p.h
@@ -459,11 +459,9 @@ static const char *version = "wavelan.c : v24 (SMP + wireless extensions) 11/12/
/****************************** TYPES ******************************/
/* Shortcuts */
-typedef struct net_device_stats en_stats;
typedef struct iw_statistics iw_stats;
typedef struct iw_quality iw_qual;
-typedef struct iw_freq iw_freq;
-typedef struct net_local net_local;
+typedef struct iw_freq iw_freq;typedef struct net_local net_local;
typedef struct timer_list timer_list;
/* Basic types */
@@ -475,15 +473,12 @@ typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */
* For each network interface, Linux keeps data in two structures: "device"
* keeps the generic data (same format for everybody) and "net_local" keeps
* additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
*/
struct net_local
{
net_local * next; /* linked list of the devices */
struct net_device * dev; /* reverse link */
spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
- en_stats stats; /* Ethernet interface statistics */
int nresets; /* number of hardware resets */
u_char reconfig_82586; /* We need to reconfigure the controller. */
u_char promiscuous; /* promiscuous mode */
@@ -601,8 +596,6 @@ static void
static inline void
wv_init_info(struct net_device *); /* display startup info */
/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats *
- wavelan_get_stats(struct net_device *); /* Give stats /proc/net/dev */
static iw_stats *
wavelan_get_wireless_stats(struct net_device *);
static void
diff --git a/trunk/drivers/net/xen-netfront.c b/trunk/drivers/net/xen-netfront.c
index 9f102a6535c4..f67325387902 100644
--- a/trunk/drivers/net/xen-netfront.c
+++ b/trunk/drivers/net/xen-netfront.c
@@ -1511,7 +1511,7 @@ static int xennet_set_tso(struct net_device *dev, u32 data)
static void xennet_set_features(struct net_device *dev)
{
/* Turn off all GSO bits except ROBUST. */
- dev->features &= (1 << NETIF_F_GSO_SHIFT) - 1;
+ dev->features &= ~NETIF_F_GSO_MASK;
dev->features |= NETIF_F_GSO_ROBUST;
xennet_set_sg(dev, 0);
diff --git a/trunk/drivers/s390/scsi/zfcp_aux.c b/trunk/drivers/s390/scsi/zfcp_aux.c
index 8af7dfbe022c..616c60ffcf2c 100644
--- a/trunk/drivers/s390/scsi/zfcp_aux.c
+++ b/trunk/drivers/s390/scsi/zfcp_aux.c
@@ -3,7 +3,7 @@
*
* Module interface and handling of zfcp data structures.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
/*
@@ -249,8 +249,8 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
struct zfcp_port *port;
list_for_each_entry(port, &adapter->port_list_head, list)
- if ((port->wwpn == wwpn) && !(atomic_read(&port->status) &
- (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE)))
+ if ((port->wwpn == wwpn) &&
+ !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE))
return port;
return NULL;
}
@@ -421,7 +421,8 @@ int zfcp_status_read_refill(struct zfcp_adapter *adapter)
while (atomic_read(&adapter->stat_miss) > 0)
if (zfcp_fsf_status_read(adapter)) {
if (atomic_read(&adapter->stat_miss) >= 16) {
- zfcp_erp_adapter_reopen(adapter, 0, 103, NULL);
+ zfcp_erp_adapter_reopen(adapter, 0, "axsref1",
+ NULL);
return 1;
}
break;
@@ -501,6 +502,7 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
spin_lock_init(&adapter->scsi_dbf_lock);
spin_lock_init(&adapter->rec_dbf_lock);
spin_lock_init(&adapter->req_q_lock);
+ spin_lock_init(&adapter->qdio_stat_lock);
rwlock_init(&adapter->erp_lock);
rwlock_init(&adapter->abort_lock);
@@ -522,7 +524,6 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
goto sysfs_failed;
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
- zfcp_fc_nameserver_init(adapter);
if (!zfcp_adapter_scsi_register(adapter))
return 0;
@@ -552,6 +553,7 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
cancel_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
+ cancel_delayed_work_sync(&adapter->nsp.work);
zfcp_adapter_scsi_unregister(adapter);
sysfs_remove_group(&adapter->ccw_device->dev.kobj,
&zfcp_sysfs_adapter_attrs);
@@ -603,10 +605,13 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
init_waitqueue_head(&port->remove_wq);
INIT_LIST_HEAD(&port->unit_list_head);
INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup);
+ INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
+ INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
port->adapter = adapter;
port->d_id = d_id;
port->wwpn = wwpn;
+ port->rport_task = RPORT_NONE;
/* mark port unusable as long as sysfs registration is not complete */
atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
@@ -620,11 +625,10 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
dev_set_drvdata(&port->sysfs_device, port);
read_lock_irq(&zfcp_data.config_lock);
- if (!(status & ZFCP_STATUS_PORT_NO_WWPN))
- if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
- read_unlock_irq(&zfcp_data.config_lock);
- goto err_out_free;
- }
+ if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
+ read_unlock_irq(&zfcp_data.config_lock);
+ goto err_out_free;
+ }
read_unlock_irq(&zfcp_data.config_lock);
if (device_register(&port->sysfs_device))
diff --git a/trunk/drivers/s390/scsi/zfcp_ccw.c b/trunk/drivers/s390/scsi/zfcp_ccw.c
index 285881f07648..1fe1e2eda512 100644
--- a/trunk/drivers/s390/scsi/zfcp_ccw.c
+++ b/trunk/drivers/s390/scsi/zfcp_ccw.c
@@ -3,7 +3,7 @@
*
* Registration and callback for the s390 common I/O layer.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -72,8 +72,7 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device)
list_for_each_entry_safe(port, p, &port_remove_lh, list) {
list_for_each_entry_safe(unit, u, &unit_remove_lh, list) {
- if (atomic_read(&unit->status) &
- ZFCP_STATUS_UNIT_REGISTERED)
+ if (unit->device)
scsi_remove_device(unit->device);
zfcp_unit_dequeue(unit);
}
@@ -109,11 +108,12 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
adapter->req_no = 0;
+ zfcp_fc_nameserver_init(adapter);
- zfcp_erp_modify_adapter_status(adapter, 10, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "ccsonl1", NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
- zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 85,
- NULL);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "ccsonl2", NULL);
zfcp_erp_wait(adapter);
up(&zfcp_data.config_sema);
flush_work(&adapter->scan_work);
@@ -137,7 +137,7 @@ static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
- zfcp_erp_adapter_shutdown(adapter, 0, 86, NULL);
+ zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
zfcp_erp_wait(adapter);
zfcp_erp_thread_kill(adapter);
up(&zfcp_data.config_sema);
@@ -160,21 +160,21 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
case CIO_GONE:
dev_warn(&adapter->ccw_device->dev,
"The FCP device has been detached\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL);
+ zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1", NULL);
break;
case CIO_NO_PATH:
dev_warn(&adapter->ccw_device->dev,
"The CHPID for the FCP device is offline\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL);
+ zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2", NULL);
break;
case CIO_OPER:
dev_info(&adapter->ccw_device->dev,
"The FCP device is operational again\n");
- zfcp_erp_modify_adapter_status(adapter, 11, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "ccnoti3", NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
- 89, NULL);
+ "ccnoti4", NULL);
break;
}
return 1;
@@ -190,7 +190,7 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev)
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&cdev->dev);
- zfcp_erp_adapter_shutdown(adapter, 0, 90, NULL);
+ zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
zfcp_erp_wait(adapter);
up(&zfcp_data.config_sema);
}
diff --git a/trunk/drivers/s390/scsi/zfcp_dbf.c b/trunk/drivers/s390/scsi/zfcp_dbf.c
index cb6df609953e..0a1a5dd8d018 100644
--- a/trunk/drivers/s390/scsi/zfcp_dbf.c
+++ b/trunk/drivers/s390/scsi/zfcp_dbf.c
@@ -490,172 +490,17 @@ static const char *zfcp_rec_dbf_tags[] = {
[ZFCP_REC_DBF_ID_ACTION] = "action",
};
-static const char *zfcp_rec_dbf_ids[] = {
- [1] = "new",
- [2] = "ready",
- [3] = "kill",
- [4] = "down sleep",
- [5] = "down wakeup",
- [6] = "down sleep ecd",
- [7] = "down wakeup ecd",
- [8] = "down sleep epd",
- [9] = "down wakeup epd",
- [10] = "online",
- [11] = "operational",
- [12] = "scsi slave destroy",
- [13] = "propagate failed adapter",
- [14] = "propagate failed port",
- [15] = "block adapter",
- [16] = "unblock adapter",
- [17] = "block port",
- [18] = "unblock port",
- [19] = "block unit",
- [20] = "unblock unit",
- [21] = "unit recovery failed",
- [22] = "port recovery failed",
- [23] = "adapter recovery failed",
- [24] = "qdio queues down",
- [25] = "p2p failed",
- [26] = "nameserver lookup failed",
- [27] = "nameserver port failed",
- [28] = "link up",
- [29] = "link down",
- [30] = "link up status read",
- [31] = "open port failed",
- [32] = "",
- [33] = "close port",
- [34] = "open unit failed",
- [35] = "exclusive open unit failed",
- [36] = "shared open unit failed",
- [37] = "link down",
- [38] = "link down status read no link",
- [39] = "link down status read fdisc login",
- [40] = "link down status read firmware update",
- [41] = "link down status read unknown reason",
- [42] = "link down ecd incomplete",
- [43] = "link down epd incomplete",
- [44] = "sysfs adapter recovery",
- [45] = "sysfs port recovery",
- [46] = "sysfs unit recovery",
- [47] = "port boxed abort",
- [48] = "unit boxed abort",
- [49] = "port boxed ct",
- [50] = "port boxed close physical",
- [51] = "port boxed open unit",
- [52] = "port boxed close unit",
- [53] = "port boxed fcp",
- [54] = "unit boxed fcp",
- [55] = "port access denied",
- [56] = "",
- [57] = "",
- [58] = "",
- [59] = "unit access denied",
- [60] = "shared unit access denied open unit",
- [61] = "",
- [62] = "request timeout",
- [63] = "adisc link test reject or timeout",
- [64] = "adisc link test d_id changed",
- [65] = "adisc link test failed",
- [66] = "recovery out of memory",
- [67] = "adapter recovery repeated after state change",
- [68] = "port recovery repeated after state change",
- [69] = "unit recovery repeated after state change",
- [70] = "port recovery follow-up after successful adapter recovery",
- [71] = "adapter recovery escalation after failed adapter recovery",
- [72] = "port recovery follow-up after successful physical port "
- "recovery",
- [73] = "adapter recovery escalation after failed physical port "
- "recovery",
- [74] = "unit recovery follow-up after successful port recovery",
- [75] = "physical port recovery escalation after failed port "
- "recovery",
- [76] = "port recovery escalation after failed unit recovery",
- [77] = "",
- [78] = "duplicate request id",
- [79] = "link down",
- [80] = "exclusive read-only unit access unsupported",
- [81] = "shared read-write unit access unsupported",
- [82] = "incoming rscn",
- [83] = "incoming wwpn",
- [84] = "wka port handle not valid close port",
- [85] = "online",
- [86] = "offline",
- [87] = "ccw device gone",
- [88] = "ccw device no path",
- [89] = "ccw device operational",
- [90] = "ccw device shutdown",
- [91] = "sysfs port addition",
- [92] = "sysfs port removal",
- [93] = "sysfs adapter recovery",
- [94] = "sysfs unit addition",
- [95] = "sysfs unit removal",
- [96] = "sysfs port recovery",
- [97] = "sysfs unit recovery",
- [98] = "sequence number mismatch",
- [99] = "link up",
- [100] = "error state",
- [101] = "status read physical port closed",
- [102] = "link up status read",
- [103] = "too many failed status read buffers",
- [104] = "port handle not valid abort",
- [105] = "lun handle not valid abort",
- [106] = "port handle not valid ct",
- [107] = "port handle not valid close port",
- [108] = "port handle not valid close physical port",
- [109] = "port handle not valid open unit",
- [110] = "port handle not valid close unit",
- [111] = "lun handle not valid close unit",
- [112] = "port handle not valid fcp",
- [113] = "lun handle not valid fcp",
- [114] = "handle mismatch fcp",
- [115] = "lun not valid fcp",
- [116] = "qdio send failed",
- [117] = "version mismatch",
- [118] = "incompatible qtcb type",
- [119] = "unknown protocol status",
- [120] = "unknown fsf command",
- [121] = "no recommendation for status qualifier",
- [122] = "status read physical port closed in error",
- [123] = "fc service class not supported",
- [124] = "",
- [125] = "need newer zfcp",
- [126] = "need newer microcode",
- [127] = "arbitrated loop not supported",
- [128] = "",
- [129] = "qtcb size mismatch",
- [130] = "unknown fsf status ecd",
- [131] = "fcp request too big",
- [132] = "",
- [133] = "data direction not valid fcp",
- [134] = "command length not valid fcp",
- [135] = "status read act update",
- [136] = "status read cfdc update",
- [137] = "hbaapi port open",
- [138] = "hbaapi unit open",
- [139] = "hbaapi unit shutdown",
- [140] = "qdio error outbound",
- [141] = "scsi host reset",
- [142] = "dismissing fsf request for recovery action",
- [143] = "recovery action timed out",
- [144] = "recovery action gone",
- [145] = "recovery action being processed",
- [146] = "recovery action ready for next step",
- [147] = "qdio error inbound",
- [148] = "nameserver needed for port scan",
- [149] = "port scan",
- [150] = "ptp attach",
- [151] = "port validation failed",
-};
-
static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
char *buf, const char *_rec)
{
struct zfcp_rec_dbf_record *r = (struct zfcp_rec_dbf_record *)_rec;
char *p = buf;
+ char hint[ZFCP_DBF_ID_SIZE + 1];
+ memcpy(hint, r->id2, ZFCP_DBF_ID_SIZE);
+ hint[ZFCP_DBF_ID_SIZE] = 0;
zfcp_dbf_outs(&p, "tag", zfcp_rec_dbf_tags[r->id]);
- zfcp_dbf_outs(&p, "hint", zfcp_rec_dbf_ids[r->id2]);
- zfcp_dbf_out(&p, "id", "%d", r->id2);
+ zfcp_dbf_outs(&p, "hint", hint);
switch (r->id) {
case ZFCP_REC_DBF_ID_THREAD:
zfcp_dbf_out(&p, "total", "%d", r->u.thread.total);
@@ -707,7 +552,7 @@ static struct debug_view zfcp_rec_dbf_view = {
* @adapter: adapter
* This function assumes that the caller is holding erp_lock.
*/
-void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
+void zfcp_rec_dbf_event_thread(char *id2, struct zfcp_adapter *adapter)
{
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
unsigned long flags = 0;
@@ -723,7 +568,7 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_THREAD;
- r->id2 = id2;
+ memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
r->u.thread.total = total;
r->u.thread.ready = ready;
r->u.thread.running = running;
@@ -737,7 +582,7 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
* @adapter: adapter
* This function assumes that the caller does not hold erp_lock.
*/
-void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
+void zfcp_rec_dbf_event_thread_lock(char *id2, struct zfcp_adapter *adapter)
{
unsigned long flags;
@@ -746,7 +591,7 @@ void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
read_unlock_irqrestore(&adapter->erp_lock, flags);
}
-static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
+static void zfcp_rec_dbf_event_target(char *id2, void *ref,
struct zfcp_adapter *adapter,
atomic_t *status, atomic_t *erp_count,
u64 wwpn, u32 d_id, u64 fcp_lun)
@@ -757,7 +602,7 @@ static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_TARGET;
- r->id2 = id2;
+ memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
r->u.target.ref = (unsigned long)ref;
r->u.target.status = atomic_read(status);
r->u.target.wwpn = wwpn;
@@ -774,7 +619,8 @@ static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
* @ref: additional reference (e.g. request)
* @adapter: adapter
*/
-void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *adapter)
+void zfcp_rec_dbf_event_adapter(char *id, void *ref,
+ struct zfcp_adapter *adapter)
{
zfcp_rec_dbf_event_target(id, ref, adapter, &adapter->status,
&adapter->erp_counter, 0, 0, 0);
@@ -786,7 +632,7 @@ void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *adapter)
* @ref: additional reference (e.g. request)
* @port: port
*/
-void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port)
+void zfcp_rec_dbf_event_port(char *id, void *ref, struct zfcp_port *port)
{
struct zfcp_adapter *adapter = port->adapter;
@@ -801,7 +647,7 @@ void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port)
* @ref: additional reference (e.g. request)
* @unit: unit
*/
-void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit)
+void zfcp_rec_dbf_event_unit(char *id, void *ref, struct zfcp_unit *unit)
{
struct zfcp_port *port = unit->port;
struct zfcp_adapter *adapter = port->adapter;
@@ -822,7 +668,7 @@ void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit)
* @port: port
* @unit: unit
*/
-void zfcp_rec_dbf_event_trigger(u8 id2, void *ref, u8 want, u8 need,
+void zfcp_rec_dbf_event_trigger(char *id2, void *ref, u8 want, u8 need,
void *action, struct zfcp_adapter *adapter,
struct zfcp_port *port, struct zfcp_unit *unit)
{
@@ -832,7 +678,7 @@ void zfcp_rec_dbf_event_trigger(u8 id2, void *ref, u8 want, u8 need,
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_TRIGGER;
- r->id2 = id2;
+ memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
r->u.trigger.ref = (unsigned long)ref;
r->u.trigger.want = want;
r->u.trigger.need = need;
@@ -855,7 +701,7 @@ void zfcp_rec_dbf_event_trigger(u8 id2, void *ref, u8 want, u8 need,
* @id2: identifier
* @erp_action: error recovery action struct pointer
*/
-void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
+void zfcp_rec_dbf_event_action(char *id2, struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
@@ -864,7 +710,7 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_ACTION;
- r->id2 = id2;
+ memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
r->u.action.action = (unsigned long)erp_action;
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
diff --git a/trunk/drivers/s390/scsi/zfcp_dbf.h b/trunk/drivers/s390/scsi/zfcp_dbf.h
index 74998ff88e57..a573f7344dd6 100644
--- a/trunk/drivers/s390/scsi/zfcp_dbf.h
+++ b/trunk/drivers/s390/scsi/zfcp_dbf.h
@@ -25,6 +25,7 @@
#include "zfcp_fsf.h"
#define ZFCP_DBF_TAG_SIZE 4
+#define ZFCP_DBF_ID_SIZE 7
struct zfcp_dbf_dump {
u8 tag[ZFCP_DBF_TAG_SIZE];
@@ -70,7 +71,7 @@ struct zfcp_rec_dbf_record_action {
struct zfcp_rec_dbf_record {
u8 id;
- u8 id2;
+ char id2[7];
union {
struct zfcp_rec_dbf_record_action action;
struct zfcp_rec_dbf_record_thread thread;
diff --git a/trunk/drivers/s390/scsi/zfcp_def.h b/trunk/drivers/s390/scsi/zfcp_def.h
index 510662783a6f..a0318630f047 100644
--- a/trunk/drivers/s390/scsi/zfcp_def.h
+++ b/trunk/drivers/s390/scsi/zfcp_def.h
@@ -3,7 +3,7 @@
*
* Global definitions for the zfcp device driver.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#ifndef ZFCP_DEF_H
@@ -243,9 +243,6 @@ struct zfcp_ls_adisc {
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
-#define ZFCP_STATUS_PORT_PHYS_CLOSING 0x00000004
-#define ZFCP_STATUS_PORT_NO_WWPN 0x00000008
-#define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020
/* well known address (WKA) port status*/
enum zfcp_wka_status {
@@ -258,7 +255,6 @@ enum zfcp_wka_status {
/* logical unit status */
#define ZFCP_STATUS_UNIT_SHARED 0x00000004
#define ZFCP_STATUS_UNIT_READONLY 0x00000008
-#define ZFCP_STATUS_UNIT_REGISTERED 0x00000010
#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020
/* FSF request status (this does not have a common part) */
@@ -447,8 +443,9 @@ struct zfcp_adapter {
spinlock_t req_list_lock; /* request list lock */
struct zfcp_qdio_queue req_q; /* request queue */
spinlock_t req_q_lock; /* for operations on queue */
- int req_q_pci_batch; /* SBALs since PCI indication
- was last set */
+ ktime_t req_q_time; /* time of last fill level change */
+ u64 req_q_util; /* for accounting */
+ spinlock_t qdio_stat_lock;
u32 fsf_req_seq_no; /* FSF cmnd seq number */
wait_queue_head_t request_wq; /* can be used to wait for
more avaliable SBALs */
@@ -514,6 +511,9 @@ struct zfcp_port {
u32 maxframe_size;
u32 supported_classes;
struct work_struct gid_pn_work;
+ struct work_struct test_link_work;
+ struct work_struct rport_work;
+ enum { RPORT_NONE, RPORT_ADD, RPORT_DEL } rport_task;
};
struct zfcp_unit {
@@ -587,9 +587,6 @@ struct zfcp_fsf_req_qtcb {
/********************** ZFCP SPECIFIC DEFINES ********************************/
-#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
-#define ZFCP_REQ_NO_QTCB 0x00000008
-
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
diff --git a/trunk/drivers/s390/scsi/zfcp_erp.c b/trunk/drivers/s390/scsi/zfcp_erp.c
index 387a3af528ac..631bdb1dfd6c 100644
--- a/trunk/drivers/s390/scsi/zfcp_erp.c
+++ b/trunk/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -55,7 +55,7 @@ enum zfcp_erp_act_result {
static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask)
{
- zfcp_erp_modify_adapter_status(adapter, 15, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "erablk1", NULL,
ZFCP_STATUS_COMMON_UNBLOCKED | mask,
ZFCP_CLEAR);
}
@@ -75,9 +75,9 @@ static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
struct zfcp_adapter *adapter = act->adapter;
list_move(&act->list, &act->adapter->erp_ready_head);
- zfcp_rec_dbf_event_action(146, act);
+ zfcp_rec_dbf_event_action("erardy1", act);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(2, adapter);
+ zfcp_rec_dbf_event_thread("erardy2", adapter);
}
static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
@@ -208,7 +208,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
struct zfcp_port *port,
- struct zfcp_unit *unit, u8 id, void *ref)
+ struct zfcp_unit *unit, char *id, void *ref)
{
int retval = 1, need;
struct zfcp_erp_action *act = NULL;
@@ -228,7 +228,7 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
++adapter->erp_total_count;
list_add_tail(&act->list, &adapter->erp_ready_head);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(1, adapter);
+ zfcp_rec_dbf_event_thread("eracte1", adapter);
retval = 0;
out:
zfcp_rec_dbf_event_trigger(id, ref, want, need, act,
@@ -237,13 +237,14 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
}
static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
- int clear_mask, u8 id, void *ref)
+ int clear_mask, char *id, void *ref)
{
zfcp_erp_adapter_block(adapter, clear_mask);
+ zfcp_scsi_schedule_rports_block(adapter);
/* ensure propagation of failed status to new devices */
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
- zfcp_erp_adapter_failed(adapter, 13, NULL);
+ zfcp_erp_adapter_failed(adapter, "erareo1", NULL);
return -EIO;
}
return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
@@ -258,7 +259,7 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
* @ref: Reference for debug trace event.
*/
void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
- u8 id, void *ref)
+ char *id, void *ref)
{
unsigned long flags;
@@ -277,7 +278,7 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
* @ref: Reference for debug trace event.
*/
void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
- u8 id, void *ref)
+ char *id, void *ref)
{
int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
zfcp_erp_adapter_reopen(adapter, clear | flags, id, ref);
@@ -290,7 +291,8 @@ void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
* @id: Id for debug trace event.
* @ref: Reference for debug trace event.
*/
-void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, u8 id, void *ref)
+void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *id,
+ void *ref)
{
int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
zfcp_erp_port_reopen(port, clear | flags, id, ref);
@@ -303,7 +305,8 @@ void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, u8 id, void *ref)
* @id: Id for debug trace event.
* @ref: Reference for debug trace event.
*/
-void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, u8 id, void *ref)
+void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, char *id,
+ void *ref)
{
int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
zfcp_erp_unit_reopen(unit, clear | flags, id, ref);
@@ -311,15 +314,16 @@ void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, u8 id, void *ref)
static void zfcp_erp_port_block(struct zfcp_port *port, int clear)
{
- zfcp_erp_modify_port_status(port, 17, NULL,
+ zfcp_erp_modify_port_status(port, "erpblk1", NULL,
ZFCP_STATUS_COMMON_UNBLOCKED | clear,
ZFCP_CLEAR);
}
static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
- int clear, u8 id, void *ref)
+ int clear, char *id, void *ref)
{
zfcp_erp_port_block(port, clear);
+ zfcp_scsi_schedule_rport_block(port);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
return;
@@ -334,7 +338,7 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
* @id: Id for debug trace event.
* @ref: Reference for debug trace event.
*/
-void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, u8 id,
+void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id,
void *ref)
{
unsigned long flags;
@@ -347,14 +351,15 @@ void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, u8 id,
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id,
+static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
void *ref)
{
zfcp_erp_port_block(port, clear);
+ zfcp_scsi_schedule_rport_block(port);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
/* ensure propagation of failed status to new devices */
- zfcp_erp_port_failed(port, 14, NULL);
+ zfcp_erp_port_failed(port, "erpreo1", NULL);
return -EIO;
}
@@ -369,7 +374,7 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id,
*
* Returns 0 if recovery has been triggered, < 0 if not.
*/
-int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id, void *ref)
+int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, void *ref)
{
unsigned long flags;
int retval;
@@ -386,12 +391,12 @@ int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id, void *ref)
static void zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
{
- zfcp_erp_modify_unit_status(unit, 19, NULL,
+ zfcp_erp_modify_unit_status(unit, "erublk1", NULL,
ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
ZFCP_CLEAR);
}
-static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id,
+static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, char *id,
void *ref)
{
struct zfcp_adapter *adapter = unit->port->adapter;
@@ -411,7 +416,8 @@ static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id,
* @clear_mask: specifies flags in unit status to be cleared
* Return: 0 on success, < 0 on error
*/
-void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id, void *ref)
+void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, char *id,
+ void *ref)
{
unsigned long flags;
struct zfcp_port *port = unit->port;
@@ -437,28 +443,28 @@ static int status_change_clear(unsigned long mask, atomic_t *status)
static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
- zfcp_rec_dbf_event_adapter(16, NULL, adapter);
+ zfcp_rec_dbf_event_adapter("eraubl1", NULL, adapter);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
}
static void zfcp_erp_port_unblock(struct zfcp_port *port)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
- zfcp_rec_dbf_event_port(18, NULL, port);
+ zfcp_rec_dbf_event_port("erpubl1", NULL, port);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
}
static void zfcp_erp_unit_unblock(struct zfcp_unit *unit)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))
- zfcp_rec_dbf_event_unit(20, NULL, unit);
+ zfcp_rec_dbf_event_unit("eruubl1", NULL, unit);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
}
static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
{
list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
- zfcp_rec_dbf_event_action(145, erp_action);
+ zfcp_rec_dbf_event_action("erator1", erp_action);
}
static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
@@ -474,11 +480,11 @@ static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
ZFCP_STATUS_ERP_TIMEDOUT)) {
act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
- zfcp_rec_dbf_event_action(142, act);
+ zfcp_rec_dbf_event_action("erscf_1", act);
act->fsf_req->erp_action = NULL;
}
if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
- zfcp_rec_dbf_event_action(143, act);
+ zfcp_rec_dbf_event_action("erscf_2", act);
if (act->fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
ZFCP_STATUS_FSFREQ_DISMISSED))
act->fsf_req = NULL;
@@ -530,7 +536,7 @@ static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
}
static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
- int clear, u8 id, void *ref)
+ int clear, char *id, void *ref)
{
struct zfcp_port *port;
@@ -538,8 +544,8 @@ static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
_zfcp_erp_port_reopen(port, clear, id, ref);
}
-static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear, u8 id,
- void *ref)
+static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
+ char *id, void *ref)
{
struct zfcp_unit *unit;
@@ -559,28 +565,28 @@ static void zfcp_erp_strategy_followup_actions(struct zfcp_erp_action *act)
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen_all(adapter, 0, 70, NULL);
+ _zfcp_erp_port_reopen_all(adapter, 0, "ersfa_1", NULL);
else
- _zfcp_erp_adapter_reopen(adapter, 0, 71, NULL);
+ _zfcp_erp_adapter_reopen(adapter, 0, "ersfa_2", NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen(port, 0, 72, NULL);
+ _zfcp_erp_port_reopen(port, 0, "ersfa_3", NULL);
else
- _zfcp_erp_adapter_reopen(adapter, 0, 73, NULL);
+ _zfcp_erp_adapter_reopen(adapter, 0, "ersfa_4", NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_unit_reopen_all(port, 0, 74, NULL);
+ _zfcp_erp_unit_reopen_all(port, 0, "ersfa_5", NULL);
else
- _zfcp_erp_port_forced_reopen(port, 0, 75, NULL);
+ _zfcp_erp_port_forced_reopen(port, 0, "ersfa_6", NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_UNIT:
if (status != ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen(unit->port, 0, 76, NULL);
+ _zfcp_erp_port_reopen(unit->port, 0, "ersfa_7", NULL);
break;
}
}
@@ -617,7 +623,7 @@ static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter)
adapter->peer_d_id);
if (IS_ERR(port)) /* error or port already attached */
return;
- _zfcp_erp_port_reopen(port, 0, 150, NULL);
+ _zfcp_erp_port_reopen(port, 0, "ereptp1", NULL);
}
static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
@@ -640,9 +646,9 @@ static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
return ZFCP_ERP_FAILED;
}
- zfcp_rec_dbf_event_thread_lock(6, adapter);
+ zfcp_rec_dbf_event_thread_lock("erasfx1", adapter);
down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock(7, adapter);
+ zfcp_rec_dbf_event_thread_lock("erasfx2", adapter);
if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT)
break;
@@ -681,9 +687,9 @@ static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act)
if (ret)
return ZFCP_ERP_FAILED;
- zfcp_rec_dbf_event_thread_lock(8, adapter);
+ zfcp_rec_dbf_event_thread_lock("erasox1", adapter);
down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock(9, adapter);
+ zfcp_rec_dbf_event_thread_lock("erasox2", adapter);
if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
return ZFCP_ERP_FAILED;
@@ -705,60 +711,59 @@ static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act)
return ZFCP_ERP_SUCCEEDED;
}
-static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *act,
- int close)
+static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act)
{
- int retval = ZFCP_ERP_SUCCEEDED;
struct zfcp_adapter *adapter = act->adapter;
- if (close)
- goto close_only;
-
- retval = zfcp_erp_adapter_strategy_open_qdio(act);
- if (retval != ZFCP_ERP_SUCCEEDED)
- goto failed_qdio;
-
- retval = zfcp_erp_adapter_strategy_open_fsf(act);
- if (retval != ZFCP_ERP_SUCCEEDED)
- goto failed_openfcp;
-
- atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &act->adapter->status);
-
- return ZFCP_ERP_SUCCEEDED;
-
- close_only:
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
- &act->adapter->status);
-
- failed_openfcp:
/* close queues to ensure that buffers are not accessed by adapter */
zfcp_qdio_close(adapter);
zfcp_fsf_req_dismiss_all(adapter);
adapter->fsf_req_seq_no = 0;
/* all ports and units are closed */
- zfcp_erp_modify_adapter_status(adapter, 24, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "erascl1", NULL,
ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
- failed_qdio:
+
atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
- ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
- &act->adapter->status);
- return retval;
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
}
-static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act)
+static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act)
{
- int retval;
+ struct zfcp_adapter *adapter = act->adapter;
- zfcp_erp_adapter_strategy_generic(act, 1); /* close */
- if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
- return ZFCP_ERP_EXIT;
+ if (zfcp_erp_adapter_strategy_open_qdio(act)) {
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &adapter->status);
+ return ZFCP_ERP_FAILED;
+ }
+
+ if (zfcp_erp_adapter_strategy_open_fsf(act)) {
+ zfcp_erp_adapter_strategy_close(act);
+ return ZFCP_ERP_FAILED;
+ }
+
+ atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &adapter->status);
+
+ return ZFCP_ERP_SUCCEEDED;
+}
- retval = zfcp_erp_adapter_strategy_generic(act, 0); /* open */
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act)
+{
+ struct zfcp_adapter *adapter = act->adapter;
- if (retval == ZFCP_ERP_FAILED)
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN) {
+ zfcp_erp_adapter_strategy_close(act);
+ if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
+ }
+
+ if (zfcp_erp_adapter_strategy_open(act)) {
ssleep(8);
+ return ZFCP_ERP_FAILED;
+ }
- return retval;
+ return ZFCP_ERP_SUCCEEDED;
}
static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
@@ -777,10 +782,7 @@ static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
{
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
- ZFCP_STATUS_PORT_PHYS_CLOSING |
- ZFCP_STATUS_PORT_INVALID_WWPN,
- &port->status);
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status);
}
static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
@@ -836,7 +838,7 @@ static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
struct zfcp_port *port = act->port;
if (port->wwpn != adapter->peer_wwpn) {
- zfcp_erp_port_failed(port, 25, NULL);
+ zfcp_erp_port_failed(port, "eroptp1", NULL);
return ZFCP_ERP_FAILED;
}
port->d_id = adapter->peer_d_id;
@@ -855,7 +857,7 @@ void zfcp_erp_port_strategy_open_lookup(struct work_struct *work)
port->erp_action.step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
if (retval)
zfcp_erp_notify(&port->erp_action, ZFCP_ERP_FAILED);
-
+ zfcp_port_put(port);
}
static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
@@ -871,17 +873,15 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
return zfcp_erp_open_ptp_port(act);
if (!port->d_id) {
- queue_work(zfcp_data.work_queue, &port->gid_pn_work);
+ zfcp_port_get(port);
+ if (!queue_work(zfcp_data.work_queue,
+ &port->gid_pn_work))
+ zfcp_port_put(port);
return ZFCP_ERP_CONTINUES;
}
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
- if (!port->d_id) {
- if (p_status & (ZFCP_STATUS_PORT_INVALID_WWPN)) {
- zfcp_erp_port_failed(port, 26, NULL);
- return ZFCP_ERP_EXIT;
- }
+ if (!port->d_id)
return ZFCP_ERP_FAILED;
- }
return zfcp_erp_port_strategy_open_port(act);
case ZFCP_ERP_STEP_PORT_OPENING:
@@ -995,7 +995,7 @@ static int zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
"port 0x%016Lx\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_unit_failed(unit, 21, NULL);
+ zfcp_erp_unit_failed(unit, "erusck1", NULL);
}
break;
}
@@ -1025,7 +1025,7 @@ static int zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
dev_err(&port->adapter->ccw_device->dev,
"ERP failed for remote port 0x%016Lx\n",
(unsigned long long)port->wwpn);
- zfcp_erp_port_failed(port, 22, NULL);
+ zfcp_erp_port_failed(port, "erpsck1", NULL);
}
break;
}
@@ -1052,7 +1052,7 @@ static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter,
dev_err(&adapter->ccw_device->dev,
"ERP cannot recover an error "
"on the FCP device\n");
- zfcp_erp_adapter_failed(adapter, 23, NULL);
+ zfcp_erp_adapter_failed(adapter, "erasck1", NULL);
}
break;
}
@@ -1117,7 +1117,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) {
_zfcp_erp_adapter_reopen(adapter,
ZFCP_STATUS_COMMON_ERP_FAILED,
- 67, NULL);
+ "ersscg1", NULL);
return ZFCP_ERP_EXIT;
}
break;
@@ -1127,7 +1127,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
if (zfcp_erp_strat_change_det(&port->status, erp_status)) {
_zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_ERP_FAILED,
- 68, NULL);
+ "ersscg2", NULL);
return ZFCP_ERP_EXIT;
}
break;
@@ -1136,7 +1136,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
if (zfcp_erp_strat_change_det(&unit->status, erp_status)) {
_zfcp_erp_unit_reopen(unit,
ZFCP_STATUS_COMMON_ERP_FAILED,
- 69, NULL);
+ "ersscg3", NULL);
return ZFCP_ERP_EXIT;
}
break;
@@ -1155,7 +1155,7 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
list_del(&erp_action->list);
- zfcp_rec_dbf_event_action(144, erp_action);
+ zfcp_rec_dbf_event_action("eractd1", erp_action);
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
@@ -1214,38 +1214,8 @@ static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
INIT_WORK(&p->work, zfcp_erp_scsi_scan);
p->unit = unit;
- queue_work(zfcp_data.work_queue, &p->work);
-}
-
-static void zfcp_erp_rport_register(struct zfcp_port *port)
-{
- struct fc_rport_identifiers ids;
- ids.node_name = port->wwnn;
- ids.port_name = port->wwpn;
- ids.port_id = port->d_id;
- ids.roles = FC_RPORT_ROLE_FCP_TARGET;
- port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
- if (!port->rport) {
- dev_err(&port->adapter->ccw_device->dev,
- "Registering port 0x%016Lx failed\n",
- (unsigned long long)port->wwpn);
- return;
- }
-
- scsi_target_unblock(&port->rport->dev);
- port->rport->maxframe_size = port->maxframe_size;
- port->rport->supported_classes = port->supported_classes;
-}
-
-static void zfcp_erp_rports_del(struct zfcp_adapter *adapter)
-{
- struct zfcp_port *port;
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if (!port->rport)
- continue;
- fc_remote_port_delete(port->rport);
- port->rport = NULL;
- }
+ if (!queue_work(zfcp_data.work_queue, &p->work))
+ zfcp_unit_put(unit);
}
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
@@ -1256,10 +1226,8 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
switch (act->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if ((result == ZFCP_ERP_SUCCEEDED) &&
- !unit->device && port->rport) {
- atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED,
- &unit->status);
+ flush_work(&port->rport_work);
+ if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
if (!(atomic_read(&unit->status) &
ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
zfcp_erp_schedule_work(unit);
@@ -1269,27 +1237,17 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (atomic_read(&port->status) & ZFCP_STATUS_PORT_NO_WWPN) {
- zfcp_port_put(port);
- return;
- }
- if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport)
- zfcp_erp_rport_register(port);
- if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) {
- fc_remote_port_delete(port->rport);
- port->rport = NULL;
- }
+ if (result == ZFCP_ERP_SUCCEEDED)
+ zfcp_scsi_schedule_rport_register(port);
zfcp_port_put(port);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (result != ZFCP_ERP_SUCCEEDED) {
- unregister_service_level(&adapter->service_level);
- zfcp_erp_rports_del(adapter);
- } else {
+ if (result == ZFCP_ERP_SUCCEEDED) {
register_service_level(&adapter->service_level);
schedule_work(&adapter->scan_work);
- }
+ } else
+ unregister_service_level(&adapter->service_level);
zfcp_adapter_put(adapter);
break;
}
@@ -1346,7 +1304,7 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
}
if (adapter->erp_total_count == adapter->erp_low_mem_count)
- _zfcp_erp_adapter_reopen(adapter, 0, 66, NULL);
+ _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1", NULL);
else {
zfcp_erp_strategy_memwait(erp_action);
retval = ZFCP_ERP_CONTINUES;
@@ -1406,9 +1364,9 @@ static int zfcp_erp_thread(void *data)
zfcp_erp_wakeup(adapter);
}
- zfcp_rec_dbf_event_thread_lock(4, adapter);
+ zfcp_rec_dbf_event_thread_lock("erthrd1", adapter);
ignore = down_interruptible(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock(5, adapter);
+ zfcp_rec_dbf_event_thread_lock("erthrd2", adapter);
}
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
@@ -1453,7 +1411,7 @@ void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
{
atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock(3, adapter);
+ zfcp_rec_dbf_event_thread_lock("erthrk1", adapter);
wait_event(adapter->erp_thread_wqh,
!(atomic_read(&adapter->status) &
@@ -1469,7 +1427,7 @@ void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
* @id: Event id for debug trace.
* @ref: Reference for debug trace.
*/
-void zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
+void zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, char *id, void *ref)
{
zfcp_erp_modify_adapter_status(adapter, id, ref,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
@@ -1481,7 +1439,7 @@ void zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
* @id: Event id for debug trace.
* @ref: Reference for debug trace.
*/
-void zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
+void zfcp_erp_port_failed(struct zfcp_port *port, char *id, void *ref)
{
zfcp_erp_modify_port_status(port, id, ref,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
@@ -1493,7 +1451,7 @@ void zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
* @id: Event id for debug trace.
* @ref: Reference for debug trace.
*/
-void zfcp_erp_unit_failed(struct zfcp_unit *unit, u8 id, void *ref)
+void zfcp_erp_unit_failed(struct zfcp_unit *unit, char *id, void *ref)
{
zfcp_erp_modify_unit_status(unit, id, ref,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
@@ -1520,7 +1478,7 @@ void zfcp_erp_wait(struct zfcp_adapter *adapter)
*
* Changes in common status bits are propagated to attached ports and units.
*/
-void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
+void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
void *ref, u32 mask, int set_or_clear)
{
struct zfcp_port *port;
@@ -1554,7 +1512,7 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
*
* Changes in common status bits are propagated to attached units.
*/
-void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
+void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
u32 mask, int set_or_clear)
{
struct zfcp_unit *unit;
@@ -1586,7 +1544,7 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
* @mask: status bits to change
* @set_or_clear: ZFCP_SET or ZFCP_CLEAR
*/
-void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
+void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, char *id, void *ref,
u32 mask, int set_or_clear)
{
if (set_or_clear == ZFCP_SET) {
@@ -1609,7 +1567,7 @@ void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
* @id: The debug trace id.
* @id: Reference for the debug trace.
*/
-void zfcp_erp_port_boxed(struct zfcp_port *port, u8 id, void *ref)
+void zfcp_erp_port_boxed(struct zfcp_port *port, char *id, void *ref)
{
unsigned long flags;
@@ -1626,7 +1584,7 @@ void zfcp_erp_port_boxed(struct zfcp_port *port, u8 id, void *ref)
* @id: The debug trace id.
* @id: Reference for the debug trace.
*/
-void zfcp_erp_unit_boxed(struct zfcp_unit *unit, u8 id, void *ref)
+void zfcp_erp_unit_boxed(struct zfcp_unit *unit, char *id, void *ref)
{
zfcp_erp_modify_unit_status(unit, id, ref,
ZFCP_STATUS_COMMON_ACCESS_BOXED, ZFCP_SET);
@@ -1642,7 +1600,7 @@ void zfcp_erp_unit_boxed(struct zfcp_unit *unit, u8 id, void *ref)
* Since the adapter has denied access, stop using the port and the
* attached units.
*/
-void zfcp_erp_port_access_denied(struct zfcp_port *port, u8 id, void *ref)
+void zfcp_erp_port_access_denied(struct zfcp_port *port, char *id, void *ref)
{
unsigned long flags;
@@ -1661,14 +1619,14 @@ void zfcp_erp_port_access_denied(struct zfcp_port *port, u8 id, void *ref)
*
* Since the adapter has denied access, stop using the unit.
*/
-void zfcp_erp_unit_access_denied(struct zfcp_unit *unit, u8 id, void *ref)
+void zfcp_erp_unit_access_denied(struct zfcp_unit *unit, char *id, void *ref)
{
zfcp_erp_modify_unit_status(unit, id, ref,
ZFCP_STATUS_COMMON_ERP_FAILED |
ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
}
-static void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id,
+static void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, char *id,
void *ref)
{
int status = atomic_read(&unit->status);
@@ -1679,7 +1637,7 @@ static void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id,
zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
-static void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id,
+static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id,
void *ref)
{
struct zfcp_unit *unit;
@@ -1701,7 +1659,7 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id,
* @id: Id for debug trace
* @ref: Reference for debug trace
*/
-void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, u8 id,
+void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, char *id,
void *ref)
{
struct zfcp_port *port;
diff --git a/trunk/drivers/s390/scsi/zfcp_ext.h b/trunk/drivers/s390/scsi/zfcp_ext.h
index b5adeda93e1d..f6399ca97bcb 100644
--- a/trunk/drivers/s390/scsi/zfcp_ext.h
+++ b/trunk/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#ifndef ZFCP_EXT_H
@@ -35,15 +35,15 @@ extern struct miscdevice zfcp_cfdc_misc;
/* zfcp_dbf.c */
extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_thread(u8, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_thread_lock(u8, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_adapter(u8, void *, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_port(u8, void *, struct zfcp_port *);
-extern void zfcp_rec_dbf_event_unit(u8, void *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_trigger(u8, void *, u8, u8, void *,
+extern void zfcp_rec_dbf_event_thread(char *, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_thread_lock(char *, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_adapter(char *, void *, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_port(char *, void *, struct zfcp_port *);
+extern void zfcp_rec_dbf_event_unit(char *, void *, struct zfcp_unit *);
+extern void zfcp_rec_dbf_event_trigger(char *, void *, u8, u8, void *,
struct zfcp_adapter *,
struct zfcp_port *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_action(u8, struct zfcp_erp_action *);
+extern void zfcp_rec_dbf_event_action(char *, struct zfcp_erp_action *);
extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
struct fsf_status_read_buffer *);
@@ -66,31 +66,34 @@ extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
struct scsi_cmnd *);
/* zfcp_erp.c */
-extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
- u32, int);
-extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
-extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
-extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
-extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
+extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, char *,
+ void *, u32, int);
+extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *, void *);
+extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *,
+ void *);
+extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, char *, void *);
+extern void zfcp_erp_modify_port_status(struct zfcp_port *, char *, void *, u32,
int);
-extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
-extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
-extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
-extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
-extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
+extern int zfcp_erp_port_reopen(struct zfcp_port *, int, char *, void *);
+extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *, void *);
+extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *,
+ void *);
+extern void zfcp_erp_port_failed(struct zfcp_port *, char *, void *);
+extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, char *, void *, u32,
int);
-extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
-extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
-extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
+extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, char *, void *);
+extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, char *, void *);
+extern void zfcp_erp_unit_failed(struct zfcp_unit *, char *, void *);
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
extern void zfcp_erp_thread_kill(struct zfcp_adapter *);
extern void zfcp_erp_wait(struct zfcp_adapter *);
extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
-extern void zfcp_erp_port_boxed(struct zfcp_port *, u8, void *);
-extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8, void *);
-extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8, void *);
-extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8, void *);
-extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
+extern void zfcp_erp_port_boxed(struct zfcp_port *, char *, void *);
+extern void zfcp_erp_unit_boxed(struct zfcp_unit *, char *, void *);
+extern void zfcp_erp_port_access_denied(struct zfcp_port *, char *, void *);
+extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, char *, void *);
+extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, char *,
+ void *);
extern void zfcp_erp_timeout_handler(unsigned long);
extern void zfcp_erp_port_strategy_open_lookup(struct work_struct *);
@@ -101,6 +104,7 @@ extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
extern int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *);
extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
extern void zfcp_test_link(struct zfcp_port *);
+extern void zfcp_fc_link_test_work(struct work_struct *);
extern void zfcp_fc_nameserver_init(struct zfcp_adapter *);
/* zfcp_fsf.c */
@@ -125,16 +129,13 @@ extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
struct zfcp_erp_action *);
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
-extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
- struct zfcp_unit *,
- struct scsi_cmnd *, int, int);
+extern int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *,
+ struct scsi_cmnd *);
extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
-extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *,
- struct zfcp_unit *, u8, int);
+extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *, u8);
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
- struct zfcp_adapter *,
- struct zfcp_unit *, int);
+ struct zfcp_unit *);
/* zfcp_qdio.c */
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
@@ -153,6 +154,10 @@ extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern struct fc_function_template zfcp_transport_functions;
+extern void zfcp_scsi_rport_work(struct work_struct *);
+extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
+extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
+extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);
/* zfcp_sysfs.c */
extern struct attribute_group zfcp_sysfs_unit_attrs;
diff --git a/trunk/drivers/s390/scsi/zfcp_fc.c b/trunk/drivers/s390/scsi/zfcp_fc.c
index eabdfe24456e..aab8123c5966 100644
--- a/trunk/drivers/s390/scsi/zfcp_fc.c
+++ b/trunk/drivers/s390/scsi/zfcp_fc.c
@@ -3,7 +3,7 @@
*
* Fibre Channel related functions for the zfcp device driver.
*
- * Copyright IBM Corporation 2008
+ * Copyright IBM Corporation 2008, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -98,8 +98,12 @@ static void zfcp_wka_port_offline(struct work_struct *work)
struct zfcp_wka_port *wka_port =
container_of(dw, struct zfcp_wka_port, work);
- wait_event(wka_port->completion_wq,
- atomic_read(&wka_port->refcount) == 0);
+ /* Don't wait forvever. If the wka_port is too busy take it offline
+ through a new call later */
+ if (!wait_event_timeout(wka_port->completion_wq,
+ atomic_read(&wka_port->refcount) == 0,
+ HZ >> 1))
+ return;
mutex_lock(&wka_port->mutex);
if ((atomic_read(&wka_port->refcount) != 0) ||
@@ -145,16 +149,10 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
struct zfcp_port *port;
read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
- if (!(atomic_read(&port->status) & ZFCP_STATUS_PORT_PHYS_OPEN))
- /* Try to connect to unused ports anyway. */
- zfcp_erp_port_reopen(port,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- 82, fsf_req);
- else if ((port->d_id & range) == (elem->nport_did & range))
- /* Check connection status for connected ports */
+ list_for_each_entry(port, &fsf_req->adapter->port_list_head, list)
+ if ((port->d_id & range) == (elem->nport_did & range))
zfcp_test_link(port);
- }
+
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
@@ -196,7 +194,7 @@ static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn)
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (port && (port->wwpn == wwpn))
- zfcp_erp_port_forced_reopen(port, 0, 83, req);
+ zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req);
}
static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
@@ -259,10 +257,9 @@ static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
if (ct->status)
return;
- if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
- atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+ if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT)
return;
- }
+
/* paranoia */
if (ct_iu_req->wwpn != port->wwpn)
return;
@@ -375,16 +372,22 @@ static void zfcp_fc_adisc_handler(unsigned long data)
if (adisc->els.status) {
/* request rejected or timed out */
- zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
+ zfcp_erp_port_forced_reopen(port, 0, "fcadh_1", NULL);
goto out;
}
if (!port->wwnn)
port->wwnn = ls_adisc->wwnn;
- if (port->wwpn != ls_adisc->wwpn)
- zfcp_erp_port_reopen(port, 0, 64, NULL);
+ if ((port->wwpn != ls_adisc->wwpn) ||
+ !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) {
+ zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fcadh_2", NULL);
+ goto out;
+ }
+ /* port is good, unblock rport without going through erp */
+ zfcp_scsi_schedule_rport_register(port);
out:
zfcp_port_put(port);
kfree(adisc);
@@ -422,6 +425,31 @@ static int zfcp_fc_adisc(struct zfcp_port *port)
return zfcp_fsf_send_els(&adisc->els);
}
+void zfcp_fc_link_test_work(struct work_struct *work)
+{
+ struct zfcp_port *port =
+ container_of(work, struct zfcp_port, test_link_work);
+ int retval;
+
+ if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) {
+ zfcp_port_put(port);
+ return; /* port erp is running and will update rport status */
+ }
+
+ zfcp_port_get(port);
+ port->rport_task = RPORT_DEL;
+ zfcp_scsi_rport_work(&port->rport_work);
+
+ retval = zfcp_fc_adisc(port);
+ if (retval == 0)
+ return;
+
+ /* send of ADISC was not possible */
+ zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
+
+ zfcp_port_put(port);
+}
+
/**
* zfcp_test_link - lightweight link test procedure
* @port: port to be tested
@@ -432,17 +460,9 @@ static int zfcp_fc_adisc(struct zfcp_port *port)
*/
void zfcp_test_link(struct zfcp_port *port)
{
- int retval;
-
zfcp_port_get(port);
- retval = zfcp_fc_adisc(port);
- if (retval == 0)
- return;
-
- /* send of ADISC was not possible */
- zfcp_port_put(port);
- if (retval != -EBUSY)
- zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+ if (!queue_work(zfcp_data.work_queue, &port->test_link_work))
+ zfcp_port_put(port);
}
static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
@@ -529,7 +549,7 @@ static void zfcp_validate_port(struct zfcp_port *port)
zfcp_port_put(port);
return;
}
- zfcp_erp_port_shutdown(port, 0, 151, NULL);
+ zfcp_erp_port_shutdown(port, 0, "fcpval1", NULL);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
zfcp_port_dequeue(port);
@@ -592,7 +612,7 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
if (IS_ERR(port))
ret = PTR_ERR(port);
else
- zfcp_erp_port_reopen(port, 0, 149, NULL);
+ zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL);
}
zfcp_erp_wait(adapter);
diff --git a/trunk/drivers/s390/scsi/zfcp_fsf.c b/trunk/drivers/s390/scsi/zfcp_fsf.c
index e6416f8541b0..b29f3121b666 100644
--- a/trunk/drivers/s390/scsi/zfcp_fsf.c
+++ b/trunk/drivers/s390/scsi/zfcp_fsf.c
@@ -3,7 +3,7 @@
*
* Implementation of FSF commands.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -12,11 +12,14 @@
#include
#include "zfcp_ext.h"
+#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
+#define ZFCP_REQ_NO_QTCB 0x00000008
+
static void zfcp_fsf_request_timeout_handler(unsigned long data)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
- zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
- NULL);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fsrth_1", NULL);
}
static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req,
@@ -75,7 +78,7 @@ static void zfcp_fsf_access_denied_port(struct zfcp_fsf_req *req,
(unsigned long long)port->wwpn);
zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
- zfcp_erp_port_access_denied(port, 55, req);
+ zfcp_erp_port_access_denied(port, "fspad_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
@@ -89,7 +92,7 @@ static void zfcp_fsf_access_denied_unit(struct zfcp_fsf_req *req,
(unsigned long long)unit->port->wwpn);
zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
- zfcp_erp_unit_access_denied(unit, 59, req);
+ zfcp_erp_unit_access_denied(unit, "fsuad_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
@@ -97,7 +100,7 @@ static void zfcp_fsf_class_not_supp(struct zfcp_fsf_req *req)
{
dev_err(&req->adapter->ccw_device->dev, "FCP device not "
"operational because of an unsupported FC class\n");
- zfcp_erp_adapter_shutdown(req->adapter, 0, 123, req);
+ zfcp_erp_adapter_shutdown(req->adapter, 0, "fscns_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
@@ -159,20 +162,13 @@ static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
list_for_each_entry(port, &adapter->port_list_head, list)
if (port->d_id == d_id) {
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
- switch (sr_buf->status_subtype) {
- case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
- zfcp_erp_port_reopen(port, 0, 101, req);
- break;
- case FSF_STATUS_READ_SUB_ERROR_PORT:
- zfcp_erp_port_shutdown(port, 0, 122, req);
- break;
- }
+ zfcp_erp_port_reopen(port, 0, "fssrpc1", req);
return;
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, u8 id,
+static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id,
struct fsf_link_down_info *link_down)
{
struct zfcp_adapter *adapter = req->adapter;
@@ -181,6 +177,7 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, u8 id,
return;
atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
+ zfcp_scsi_schedule_rports_block(adapter);
if (!link_down)
goto out;
@@ -261,13 +258,13 @@ static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req)
switch (sr_buf->status_subtype) {
case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK:
- zfcp_fsf_link_down_info_eval(req, 38, ldi);
+ zfcp_fsf_link_down_info_eval(req, "fssrld1", ldi);
break;
case FSF_STATUS_READ_SUB_FDISC_FAILED:
- zfcp_fsf_link_down_info_eval(req, 39, ldi);
+ zfcp_fsf_link_down_info_eval(req, "fssrld2", ldi);
break;
case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
- zfcp_fsf_link_down_info_eval(req, 40, NULL);
+ zfcp_fsf_link_down_info_eval(req, "fssrld3", NULL);
};
}
@@ -307,22 +304,23 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
dev_info(&adapter->ccw_device->dev,
"The local link has been restored\n");
/* All ports should be marked as ready to run again */
- zfcp_erp_modify_adapter_status(adapter, 30, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "fssrh_1", NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter,
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
ZFCP_STATUS_COMMON_ERP_FAILED,
- 102, req);
+ "fssrh_2", req);
break;
case FSF_STATUS_READ_NOTIFICATION_LOST:
if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_ACT_UPDATED)
- zfcp_erp_adapter_access_changed(adapter, 135, req);
+ zfcp_erp_adapter_access_changed(adapter, "fssrh_3",
+ req);
if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
schedule_work(&adapter->scan_work);
break;
case FSF_STATUS_READ_CFDC_UPDATED:
- zfcp_erp_adapter_access_changed(adapter, 136, req);
+ zfcp_erp_adapter_access_changed(adapter, "fssrh_4", req);
break;
case FSF_STATUS_READ_FEATURE_UPDATE_ALERT:
adapter->adapter_features = sr_buf->payload.word[0];
@@ -351,7 +349,7 @@ static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
dev_err(&req->adapter->ccw_device->dev,
"The FCP adapter reported a problem "
"that cannot be recovered\n");
- zfcp_erp_adapter_shutdown(req->adapter, 0, 121, req);
+ zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfsqe1", req);
break;
}
/* all non-return stats set FSFREQ_ERROR*/
@@ -368,7 +366,7 @@ static void zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *req)
dev_err(&req->adapter->ccw_device->dev,
"The FCP adapter does not recognize the command 0x%x\n",
req->qtcb->header.fsf_command);
- zfcp_erp_adapter_shutdown(req->adapter, 0, 120, req);
+ zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfse_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
@@ -400,17 +398,17 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
"QTCB version 0x%x not supported by FCP adapter "
"(0x%x to 0x%x)\n", FSF_QTCB_CURRENT_VERSION,
psq->word[0], psq->word[1]);
- zfcp_erp_adapter_shutdown(adapter, 0, 117, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fspse_1", req);
break;
case FSF_PROT_ERROR_STATE:
case FSF_PROT_SEQ_NUMB_ERROR:
- zfcp_erp_adapter_reopen(adapter, 0, 98, req);
+ zfcp_erp_adapter_reopen(adapter, 0, "fspse_2", req);
req->status |= ZFCP_STATUS_FSFREQ_RETRY;
break;
case FSF_PROT_UNSUPP_QTCB_TYPE:
dev_err(&adapter->ccw_device->dev,
"The QTCB type is not supported by the FCP adapter\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 118, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fspse_3", req);
break;
case FSF_PROT_HOST_CONNECTION_INITIALIZING:
atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
@@ -420,27 +418,29 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
dev_err(&adapter->ccw_device->dev,
"0x%Lx is an ambiguous request identifier\n",
(unsigned long long)qtcb->bottom.support.req_handle);
- zfcp_erp_adapter_shutdown(adapter, 0, 78, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fspse_4", req);
break;
case FSF_PROT_LINK_DOWN:
- zfcp_fsf_link_down_info_eval(req, 37, &psq->link_down_info);
+ zfcp_fsf_link_down_info_eval(req, "fspse_5",
+ &psq->link_down_info);
/* FIXME: reopening adapter now? better wait for link up */
- zfcp_erp_adapter_reopen(adapter, 0, 79, req);
+ zfcp_erp_adapter_reopen(adapter, 0, "fspse_6", req);
break;
case FSF_PROT_REEST_QUEUE:
/* All ports should be marked as ready to run again */
- zfcp_erp_modify_adapter_status(adapter, 28, NULL,
+ zfcp_erp_modify_adapter_status(adapter, "fspse_7", NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter,
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
- ZFCP_STATUS_COMMON_ERP_FAILED, 99, req);
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fspse_8", req);
break;
default:
dev_err(&adapter->ccw_device->dev,
"0x%x is not a valid transfer protocol status\n",
qtcb->prefix.prot_status);
- zfcp_erp_adapter_shutdown(adapter, 0, 119, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fspse_9", req);
}
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
@@ -526,7 +526,7 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
dev_err(&adapter->ccw_device->dev,
"Unknown or unsupported arbitrated loop "
"fibre channel topology detected\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 127, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fsece_1", req);
return -EIO;
}
@@ -560,7 +560,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
"FCP adapter maximum QTCB size (%d bytes) "
"is too small\n",
bottom->max_qtcb_size);
- zfcp_erp_adapter_shutdown(adapter, 0, 129, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh1", req);
return;
}
atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
@@ -577,11 +577,11 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
&adapter->status);
- zfcp_fsf_link_down_info_eval(req, 42,
+ zfcp_fsf_link_down_info_eval(req, "fsecdh2",
&qtcb->header.fsf_status_qual.link_down_info);
break;
default:
- zfcp_erp_adapter_shutdown(adapter, 0, 130, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh3", req);
return;
}
@@ -597,14 +597,14 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
dev_err(&adapter->ccw_device->dev,
"The FCP adapter only supports newer "
"control block versions\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 125, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh4", req);
return;
}
if (FSF_QTCB_CURRENT_VERSION > bottom->high_qtcb_version) {
dev_err(&adapter->ccw_device->dev,
"The FCP adapter only supports older "
"control block versions\n");
- zfcp_erp_adapter_shutdown(adapter, 0, 126, req);
+ zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh5", req);
}
}
@@ -617,9 +617,10 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
if (req->data)
memcpy(req->data, bottom, sizeof(*bottom));
- if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
+ if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) {
fc_host_permanent_port_name(shost) = bottom->wwpn;
- else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ } else
fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
fc_host_supported_speeds(shost) = bottom->supported_speed;
@@ -638,20 +639,12 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
break;
case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
zfcp_fsf_exchange_port_evaluate(req);
- zfcp_fsf_link_down_info_eval(req, 43,
+ zfcp_fsf_link_down_info_eval(req, "fsepdh1",
&qtcb->header.fsf_status_qual.link_down_info);
break;
}
}
-static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
-{
- if (atomic_read(&adapter->req_q.count) > 0)
- return 1;
- atomic_inc(&adapter->qdio_outb_full);
- return 0;
-}
-
static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
__releases(&adapter->req_q_lock)
__acquires(&adapter->req_q_lock)
@@ -735,7 +728,7 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
req->adapter = adapter;
req->fsf_command = fsf_cmd;
- req->req_id = adapter->req_no++;
+ req->req_id = adapter->req_no;
req->sbal_number = 1;
req->sbal_first = req_q->first;
req->sbal_last = req_q->first;
@@ -791,13 +784,14 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
if (zfcp_reqlist_find_safe(adapter, req))
zfcp_reqlist_remove(adapter, req);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- zfcp_erp_adapter_reopen(adapter, 0, 116, req);
+ zfcp_erp_adapter_reopen(adapter, 0, "fsrs__1", req);
return -EIO;
}
/* Don't increase for unsolicited status */
if (req->qtcb)
adapter->fsf_req_seq_no++;
+ adapter->req_no++;
return 0;
}
@@ -870,14 +864,14 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
if (fsq->word[0] == fsq->word[1]) {
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 104,
- req);
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0,
+ "fsafch1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
break;
case FSF_LUN_HANDLE_NOT_VALID:
if (fsq->word[0] == fsq->word[1]) {
- zfcp_erp_port_reopen(unit->port, 0, 105, req);
+ zfcp_erp_port_reopen(unit->port, 0, "fsafch2", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
break;
@@ -885,12 +879,12 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(unit->port, 47, req);
+ zfcp_erp_port_boxed(unit->port, "fsafch3", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
case FSF_LUN_BOXED:
- zfcp_erp_unit_boxed(unit, 48, req);
+ zfcp_erp_unit_boxed(unit, "fsafch4", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
@@ -912,27 +906,22 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
/**
* zfcp_fsf_abort_fcp_command - abort running SCSI command
* @old_req_id: unsigned long
- * @adapter: pointer to struct zfcp_adapter
* @unit: pointer to struct zfcp_unit
- * @req_flags: integer specifying the request flags
* Returns: pointer to struct zfcp_fsf_req
- *
- * FIXME(design): should be watched by a timeout !!!
*/
struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
- struct zfcp_adapter *adapter,
- struct zfcp_unit *unit,
- int req_flags)
+ struct zfcp_unit *unit)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
+ struct zfcp_adapter *adapter = unit->port->adapter;
- spin_lock(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
- req_flags, adapter->pool.fsf_req_abort);
+ 0, adapter->pool.fsf_req_abort);
if (IS_ERR(req)) {
req = NULL;
goto out;
@@ -960,7 +949,7 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
zfcp_fsf_req_free(req);
req = NULL;
out:
- spin_unlock(&adapter->req_q_lock);
+ spin_unlock_bh(&adapter->req_q_lock);
return req;
}
@@ -998,7 +987,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
ZFCP_STATUS_FSFREQ_RETRY;
break;
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(adapter, 0, 106, req);
+ zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req);
case FSF_GENERIC_COMMAND_REJECTED:
case FSF_PAYLOAD_SIZE_MISMATCH:
case FSF_REQUEST_SIZE_TOO_LARGE:
@@ -1174,12 +1163,8 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
struct fsf_qtcb_bottom_support *bottom;
int ret = -EIO;
- if (unlikely(!(atomic_read(&els->port->status) &
- ZFCP_STATUS_COMMON_UNBLOCKED)))
- return -EBUSY;
-
- spin_lock(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
ZFCP_REQ_AUTO_CLEANUP, NULL);
@@ -1212,7 +1197,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
failed_send:
zfcp_fsf_req_free(req);
out:
- spin_unlock(&adapter->req_q_lock);
+ spin_unlock_bh(&adapter->req_q_lock);
return ret;
}
@@ -1224,7 +1209,7 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
int retval = -EIO;
spin_lock_bh(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
req = zfcp_fsf_req_create(adapter,
FSF_QTCB_EXCHANGE_CONFIG_DATA,
@@ -1320,7 +1305,7 @@ int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
return -EOPNOTSUPP;
spin_lock_bh(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
ZFCP_REQ_AUTO_CLEANUP,
@@ -1366,7 +1351,7 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
return -EOPNOTSUPP;
spin_lock_bh(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
@@ -1416,7 +1401,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
"Not enough FCP adapter resources to open "
"remote port 0x%016Lx\n",
(unsigned long long)port->wwpn);
- zfcp_erp_port_failed(port, 31, req);
+ zfcp_erp_port_failed(port, "fsoph_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
@@ -1522,13 +1507,13 @@ static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req)
switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(port->adapter, 0, 107, req);
+ zfcp_erp_adapter_reopen(port->adapter, 0, "fscph_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
break;
case FSF_GOOD:
- zfcp_erp_modify_port_status(port, 33, req,
+ zfcp_erp_modify_port_status(port, "fscph_2", req,
ZFCP_STATUS_COMMON_OPEN,
ZFCP_CLEAR);
break;
@@ -1657,7 +1642,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) {
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- zfcp_erp_adapter_reopen(wka_port->adapter, 0, 84, req);
+ zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1", req);
}
wka_port->status = ZFCP_WKA_PORT_OFFLINE;
@@ -1712,18 +1697,18 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
struct zfcp_unit *unit;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
- goto skip_fsfstatus;
+ return;
switch (header->fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(port->adapter, 0, 108, req);
+ zfcp_erp_adapter_reopen(port->adapter, 0, "fscpph1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ACCESS_DENIED:
zfcp_fsf_access_denied_port(req, port);
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(port, 50, req);
+ zfcp_erp_port_boxed(port, "fscpph2", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
/* can't use generic zfcp_erp_modify_port_status because
@@ -1752,8 +1737,6 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
&unit->status);
break;
}
-skip_fsfstatus:
- atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
}
/**
@@ -1789,8 +1772,6 @@ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
req->erp_action = erp_action;
req->handler = zfcp_fsf_close_physical_port_handler;
erp_action->fsf_req = req;
- atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
- &erp_action->port->status);
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
@@ -1825,7 +1806,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 109, req);
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, "fsouh_1", req);
/* fall through */
case FSF_LUN_ALREADY_OPEN:
break;
@@ -1835,7 +1816,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(unit->port, 51, req);
+ zfcp_erp_port_boxed(unit->port, "fsouh_2", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
@@ -1851,7 +1832,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
else
zfcp_act_eval_err(adapter,
header->fsf_status_qual.word[2]);
- zfcp_erp_unit_access_denied(unit, 60, req);
+ zfcp_erp_unit_access_denied(unit, "fsouh_3", req);
atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1862,7 +1843,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
"0x%016Lx on port 0x%016Lx\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_unit_failed(unit, 34, req);
+ zfcp_erp_unit_failed(unit, "fsouh_4", req);
/* fall through */
case FSF_INVALID_COMMAND_OPTION:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1911,9 +1892,9 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
"port 0x%016Lx)\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_unit_failed(unit, 35, req);
+ zfcp_erp_unit_failed(unit, "fsouh_5", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- zfcp_erp_unit_shutdown(unit, 0, 80, req);
+ zfcp_erp_unit_shutdown(unit, 0, "fsouh_6", req);
} else if (!exclusive && readwrite) {
dev_err(&adapter->ccw_device->dev,
"Shared read-write access not "
@@ -1921,9 +1902,9 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
"0x%016Lx)\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_unit_failed(unit, 36, req);
+ zfcp_erp_unit_failed(unit, "fsouh_7", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- zfcp_erp_unit_shutdown(unit, 0, 81, req);
+ zfcp_erp_unit_shutdown(unit, 0, "fsouh_8", req);
}
}
break;
@@ -1988,15 +1969,15 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 110, req);
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, "fscuh_1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_LUN_HANDLE_NOT_VALID:
- zfcp_erp_port_reopen(unit->port, 0, 111, req);
+ zfcp_erp_port_reopen(unit->port, 0, "fscuh_2", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(unit->port, 52, req);
+ zfcp_erp_port_boxed(unit->port, "fscuh_3", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
@@ -2073,7 +2054,6 @@ static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
struct fsf_qual_latency_info *lat_inf;
struct latency_cont *lat;
struct zfcp_unit *unit = req->unit;
- unsigned long flags;
lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info;
@@ -2091,11 +2071,11 @@ static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
return;
}
- spin_lock_irqsave(&unit->latencies.lock, flags);
+ spin_lock(&unit->latencies.lock);
zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
lat->counter++;
- spin_unlock_irqrestore(&unit->latencies.lock, flags);
+ spin_unlock(&unit->latencies.lock);
}
#ifdef CONFIG_BLK_DEV_IO_TRACE
@@ -2147,7 +2127,6 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
set_host_byte(scpnt, DID_SOFT_ERROR);
- set_driver_byte(scpnt, SUGGEST_RETRY);
goto skip_fsfstatus;
}
@@ -2237,12 +2216,12 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status) {
case FSF_HANDLE_MISMATCH:
case FSF_PORT_HANDLE_NOT_VALID:
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 112, req);
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, "fssfch1", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_FCPLUN_NOT_VALID:
case FSF_LUN_HANDLE_NOT_VALID:
- zfcp_erp_port_reopen(unit->port, 0, 113, req);
+ zfcp_erp_port_reopen(unit->port, 0, "fssfch2", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
@@ -2258,7 +2237,8 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
req->qtcb->bottom.io.data_direction,
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 133, req);
+ zfcp_erp_adapter_shutdown(unit->port->adapter, 0, "fssfch3",
+ req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_CMND_LENGTH_NOT_VALID:
@@ -2268,16 +2248,17 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
req->qtcb->bottom.io.fcp_cmnd_length,
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 134, req);
+ zfcp_erp_adapter_shutdown(unit->port->adapter, 0, "fssfch4",
+ req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(unit->port, 53, req);
+ zfcp_erp_port_boxed(unit->port, "fssfch5", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
case FSF_LUN_BOXED:
- zfcp_erp_unit_boxed(unit, 54, req);
+ zfcp_erp_unit_boxed(unit, "fssfch6", req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
ZFCP_STATUS_FSFREQ_RETRY;
break;
@@ -2314,30 +2295,29 @@ static void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, u32 fcp_dl)
/**
* zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
- * @adapter: adapter where scsi command is issued
* @unit: unit where command is sent to
* @scsi_cmnd: scsi command to be sent
- * @timer: timer to be started when request is initiated
- * @req_flags: flags for fsf_request
*/
-int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
- struct zfcp_unit *unit,
- struct scsi_cmnd *scsi_cmnd,
- int use_timer, int req_flags)
+int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
+ struct scsi_cmnd *scsi_cmnd)
{
struct zfcp_fsf_req *req;
struct fcp_cmnd_iu *fcp_cmnd_iu;
unsigned int sbtype;
int real_bytes, retval = -EIO;
+ struct zfcp_adapter *adapter = unit->port->adapter;
if (unlikely(!(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_UNBLOCKED)))
return -EBUSY;
spin_lock(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ if (atomic_read(&adapter->req_q.count) <= 0) {
+ atomic_inc(&adapter->qdio_outb_full);
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ }
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND,
+ ZFCP_REQ_AUTO_CLEANUP,
adapter->pool.fsf_req_scsi);
if (IS_ERR(req)) {
retval = PTR_ERR(req);
@@ -2411,7 +2391,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
"on port 0x%016Lx closed\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
- zfcp_erp_unit_shutdown(unit, 0, 131, req);
+ zfcp_erp_unit_shutdown(unit, 0, "fssfct1", req);
retval = -EINVAL;
}
goto failed_scsi_cmnd;
@@ -2419,9 +2399,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
- if (use_timer)
- zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
-
retval = zfcp_fsf_req_send(req);
if (unlikely(retval))
goto failed_scsi_cmnd;
@@ -2439,28 +2416,25 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
/**
* zfcp_fsf_send_fcp_ctm - send SCSI task management command
- * @adapter: pointer to struct zfcp-adapter
* @unit: pointer to struct zfcp_unit
* @tm_flags: unsigned byte for task management flags
- * @req_flags: int request flags
* Returns: on success pointer to struct fsf_req, NULL otherwise
*/
-struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *adapter,
- struct zfcp_unit *unit,
- u8 tm_flags, int req_flags)
+struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
struct fcp_cmnd_iu *fcp_cmnd_iu;
+ struct zfcp_adapter *adapter = unit->port->adapter;
if (unlikely(!(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_UNBLOCKED)))
return NULL;
- spin_lock(&adapter->req_q_lock);
- if (!zfcp_fsf_sbal_available(adapter))
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, 0,
adapter->pool.fsf_req_scsi);
if (IS_ERR(req)) {
req = NULL;
@@ -2492,7 +2466,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *adapter,
zfcp_fsf_req_free(req);
req = NULL;
out:
- spin_unlock(&adapter->req_q_lock);
+ spin_unlock_bh(&adapter->req_q_lock);
return req;
}
diff --git a/trunk/drivers/s390/scsi/zfcp_fsf.h b/trunk/drivers/s390/scsi/zfcp_fsf.h
index 8bb200252347..df7f232faba8 100644
--- a/trunk/drivers/s390/scsi/zfcp_fsf.h
+++ b/trunk/drivers/s390/scsi/zfcp_fsf.h
@@ -127,10 +127,6 @@
#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A
#define FSF_STATUS_READ_FEATURE_UPDATE_ALERT 0x0000000C
-/* status subtypes in status read buffer */
-#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT 0x00000001
-#define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002
-
/* status subtypes for link down */
#define FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK 0x00000000
#define FSF_STATUS_READ_SUB_FDISC_FAILED 0x00000001
diff --git a/trunk/drivers/s390/scsi/zfcp_qdio.c b/trunk/drivers/s390/scsi/zfcp_qdio.c
index 33e0a206a0a4..e0a215309df0 100644
--- a/trunk/drivers/s390/scsi/zfcp_qdio.c
+++ b/trunk/drivers/s390/scsi/zfcp_qdio.c
@@ -11,9 +11,6 @@
#include "zfcp_ext.h"
-/* FIXME(tune): free space should be one max. SBAL chain plus what? */
-#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
- - (FSF_MAX_SBALS_PER_REQ + 4))
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
@@ -58,7 +55,7 @@ void zfcp_qdio_free(struct zfcp_adapter *adapter)
}
}
-static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, u8 id)
+static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, char *id)
{
dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n");
@@ -77,6 +74,23 @@ static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt)
}
}
+/* this needs to be called prior to updating the queue fill level */
+static void zfcp_qdio_account(struct zfcp_adapter *adapter)
+{
+ ktime_t now;
+ s64 span;
+ int free, used;
+
+ spin_lock(&adapter->qdio_stat_lock);
+ now = ktime_get();
+ span = ktime_us_delta(now, adapter->req_q_time);
+ free = max(0, atomic_read(&adapter->req_q.count));
+ used = QDIO_MAX_BUFFERS_PER_Q - free;
+ adapter->req_q_util += used * span;
+ adapter->req_q_time = now;
+ spin_unlock(&adapter->qdio_stat_lock);
+}
+
static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
int queue_no, int first, int count,
unsigned long parm)
@@ -86,13 +100,14 @@ static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
if (unlikely(qdio_err)) {
zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
- zfcp_qdio_handler_error(adapter, 140);
+ zfcp_qdio_handler_error(adapter, "qdireq1");
return;
}
/* cleanup all SBALs being program-owned now */
zfcp_qdio_zero_sbals(queue->sbal, first, count);
+ zfcp_qdio_account(adapter);
atomic_add(count, &queue->count);
wake_up(&adapter->request_wq);
}
@@ -154,7 +169,7 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
if (unlikely(qdio_err)) {
zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
- zfcp_qdio_handler_error(adapter, 147);
+ zfcp_qdio_handler_error(adapter, "qdires1");
return;
}
@@ -346,21 +361,12 @@ int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
struct zfcp_qdio_queue *req_q = &adapter->req_q;
int first = fsf_req->sbal_first;
int count = fsf_req->sbal_number;
- int retval, pci, pci_batch;
- struct qdio_buffer_element *sbale;
+ int retval;
+ unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
- /* acknowledgements for transferred buffers */
- pci_batch = adapter->req_q_pci_batch + count;
- if (unlikely(pci_batch >= ZFCP_QDIO_PCI_INTERVAL)) {
- pci_batch %= ZFCP_QDIO_PCI_INTERVAL;
- pci = first + count - (pci_batch + 1);
- pci %= QDIO_MAX_BUFFERS_PER_Q;
- sbale = zfcp_qdio_sbale(req_q, pci, 0);
- sbale->flags |= SBAL_FLAGS0_PCI;
- }
+ zfcp_qdio_account(adapter);
- retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, first,
- count);
+ retval = do_QDIO(adapter->ccw_device, qdio_flags, 0, first, count);
if (unlikely(retval)) {
zfcp_qdio_zero_sbals(req_q->sbal, first, count);
return retval;
@@ -370,7 +376,6 @@ int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
atomic_sub(count, &req_q->count);
req_q->first += count;
req_q->first %= QDIO_MAX_BUFFERS_PER_Q;
- adapter->req_q_pci_batch = pci_batch;
return 0;
}
@@ -441,7 +446,6 @@ void zfcp_qdio_close(struct zfcp_adapter *adapter)
}
req_q->first = 0;
atomic_set(&req_q->count, 0);
- adapter->req_q_pci_batch = 0;
adapter->resp_q.first = 0;
atomic_set(&adapter->resp_q.count, 0);
}
@@ -479,7 +483,6 @@ int zfcp_qdio_open(struct zfcp_adapter *adapter)
/* set index of first avalable SBALS / number of available SBALS */
adapter->req_q.first = 0;
atomic_set(&adapter->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
- adapter->req_q_pci_batch = 0;
return 0;
diff --git a/trunk/drivers/s390/scsi/zfcp_scsi.c b/trunk/drivers/s390/scsi/zfcp_scsi.c
index 9dc42a68fbdd..58201e1ae478 100644
--- a/trunk/drivers/s390/scsi/zfcp_scsi.c
+++ b/trunk/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -27,9 +27,7 @@ char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
- atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
unit->device = NULL;
- zfcp_erp_unit_failed(unit, 12, NULL);
zfcp_unit_put(unit);
}
@@ -58,8 +56,8 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
{
struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
- int status;
- int ret;
+ int status, scsi_result, ret;
+ struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device));
/* reset the status for this request */
scpnt->result = 0;
@@ -81,6 +79,14 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
return 0;
}
+ scsi_result = fc_remote_port_chkready(rport);
+ if (unlikely(scsi_result)) {
+ scpnt->result = scsi_result;
+ zfcp_scsi_dbf_event_result("fail", 4, adapter, scpnt, NULL);
+ scpnt->scsi_done(scpnt);
+ return 0;
+ }
+
status = atomic_read(&unit->status);
if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
!(status & ZFCP_STATUS_COMMON_RUNNING))) {
@@ -88,8 +94,7 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
return 0;;
}
- ret = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, 0,
- ZFCP_REQ_AUTO_CLEANUP);
+ ret = zfcp_fsf_send_fcp_command_task(unit, scpnt);
if (unlikely(ret == -EBUSY))
return SCSI_MLQUEUE_DEVICE_BUSY;
else if (unlikely(ret < 0))
@@ -133,8 +138,7 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
read_lock_irqsave(&zfcp_data.config_lock, flags);
unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
- if (unit &&
- (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_REGISTERED)) {
+ if (unit) {
sdp->hostdata = unit;
unit->device = sdp;
zfcp_unit_get(unit);
@@ -147,79 +151,91 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
{
- struct Scsi_Host *scsi_host;
- struct zfcp_adapter *adapter;
- struct zfcp_unit *unit;
- struct zfcp_fsf_req *fsf_req;
+ struct Scsi_Host *scsi_host = scpnt->device->host;
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *) scsi_host->hostdata[0];
+ struct zfcp_unit *unit = scpnt->device->hostdata;
+ struct zfcp_fsf_req *old_req, *abrt_req;
unsigned long flags;
unsigned long old_req_id = (unsigned long) scpnt->host_scribble;
int retval = SUCCESS;
-
- scsi_host = scpnt->device->host;
- adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
- unit = scpnt->device->hostdata;
+ int retry = 3;
/* avoid race condition between late normal completion and abort */
write_lock_irqsave(&adapter->abort_lock, flags);
- /* Check whether corresponding fsf_req is still pending */
spin_lock(&adapter->req_list_lock);
- fsf_req = zfcp_reqlist_find(adapter, old_req_id);
+ old_req = zfcp_reqlist_find(adapter, old_req_id);
spin_unlock(&adapter->req_list_lock);
- if (!fsf_req) {
+ if (!old_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
- zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, 0);
- return retval;
+ zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL,
+ old_req_id);
+ return SUCCESS;
}
- fsf_req->data = NULL;
+ old_req->data = NULL;
/* don't access old fsf_req after releasing the abort_lock */
write_unlock_irqrestore(&adapter->abort_lock, flags);
- fsf_req = zfcp_fsf_abort_fcp_command(old_req_id, adapter, unit, 0);
- if (!fsf_req) {
- zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
- old_req_id);
- retval = FAILED;
- return retval;
+ while (retry--) {
+ abrt_req = zfcp_fsf_abort_fcp_command(old_req_id, unit);
+ if (abrt_req)
+ break;
+
+ zfcp_erp_wait(adapter);
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_RUNNING)) {
+ zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
+ old_req_id);
+ return SUCCESS;
+ }
}
+ if (!abrt_req)
+ return FAILED;
- __wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_event(abrt_req->completion_wq,
+ abrt_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
- zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, fsf_req, 0);
- } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
- zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, fsf_req, 0);
- } else {
- zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, fsf_req, 0);
+ if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED)
+ zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, abrt_req, 0);
+ else if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED)
+ zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, abrt_req, 0);
+ else {
+ zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, abrt_req, 0);
retval = FAILED;
}
- zfcp_fsf_req_free(fsf_req);
-
+ zfcp_fsf_req_free(abrt_req);
return retval;
}
-static int zfcp_task_mgmt_function(struct zfcp_unit *unit, u8 tm_flags,
- struct scsi_cmnd *scpnt)
+static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
+ struct zfcp_unit *unit = scpnt->device->hostdata;
struct zfcp_adapter *adapter = unit->port->adapter;
struct zfcp_fsf_req *fsf_req;
int retval = SUCCESS;
-
- /* issue task management function */
- fsf_req = zfcp_fsf_send_fcp_ctm(adapter, unit, tm_flags, 0);
- if (!fsf_req) {
- zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, scpnt);
- return FAILED;
+ int retry = 3;
+
+ while (retry--) {
+ fsf_req = zfcp_fsf_send_fcp_ctm(unit, tm_flags);
+ if (fsf_req)
+ break;
+
+ zfcp_erp_wait(adapter);
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_RUNNING)) {
+ zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit,
+ scpnt);
+ return SUCCESS;
+ }
}
+ if (!fsf_req)
+ return FAILED;
- __wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_event(fsf_req->completion_wq,
+ fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
- /*
- * check completion status of task management function
- */
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt);
retval = FAILED;
@@ -230,40 +246,25 @@ static int zfcp_task_mgmt_function(struct zfcp_unit *unit, u8 tm_flags,
zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt);
zfcp_fsf_req_free(fsf_req);
-
return retval;
}
static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
{
- struct zfcp_unit *unit = scpnt->device->hostdata;
-
- if (!unit) {
- WARN_ON(1);
- return SUCCESS;
- }
- return zfcp_task_mgmt_function(unit, FCP_LOGICAL_UNIT_RESET, scpnt);
+ return zfcp_task_mgmt_function(scpnt, FCP_LOGICAL_UNIT_RESET);
}
static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
{
- struct zfcp_unit *unit = scpnt->device->hostdata;
-
- if (!unit) {
- WARN_ON(1);
- return SUCCESS;
- }
- return zfcp_task_mgmt_function(unit, FCP_TARGET_RESET, scpnt);
+ return zfcp_task_mgmt_function(scpnt, FCP_TARGET_RESET);
}
static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
{
- struct zfcp_unit *unit;
- struct zfcp_adapter *adapter;
+ struct zfcp_unit *unit = scpnt->device->hostdata;
+ struct zfcp_adapter *adapter = unit->port->adapter;
- unit = scpnt->device->hostdata;
- adapter = unit->port->adapter;
- zfcp_erp_adapter_reopen(adapter, 0, 141, scpnt);
+ zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt);
zfcp_erp_wait(adapter);
return SUCCESS;
@@ -479,6 +480,109 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
rport->dev_loss_tmo = timeout;
}
+/**
+ * zfcp_scsi_dev_loss_tmo_callbk - Free any reference to rport
+ * @rport: The rport that is about to be deleted.
+ */
+static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
+{
+ struct zfcp_port *port = rport->dd_data;
+
+ write_lock_irq(&zfcp_data.config_lock);
+ port->rport = NULL;
+ write_unlock_irq(&zfcp_data.config_lock);
+}
+
+/**
+ * zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport
+ * @rport: The FC rport where to teminate I/O
+ *
+ * Abort all pending SCSI commands for a port by closing the
+ * port. Using a reopen for avoids a conflict with a shutdown
+ * overwriting a reopen.
+ */
+static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
+{
+ struct zfcp_port *port = rport->dd_data;
+
+ zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
+}
+
+static void zfcp_scsi_rport_register(struct zfcp_port *port)
+{
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
+
+ ids.node_name = port->wwnn;
+ ids.port_name = port->wwpn;
+ ids.port_id = port->d_id;
+ ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+
+ rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
+ if (!rport) {
+ dev_err(&port->adapter->ccw_device->dev,
+ "Registering port 0x%016Lx failed\n",
+ (unsigned long long)port->wwpn);
+ return;
+ }
+
+ rport->dd_data = port;
+ rport->maxframe_size = port->maxframe_size;
+ rport->supported_classes = port->supported_classes;
+ port->rport = rport;
+}
+
+static void zfcp_scsi_rport_block(struct zfcp_port *port)
+{
+ if (port->rport)
+ fc_remote_port_delete(port->rport);
+}
+
+void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
+{
+ zfcp_port_get(port);
+ port->rport_task = RPORT_ADD;
+
+ if (!queue_work(zfcp_data.work_queue, &port->rport_work))
+ zfcp_port_put(port);
+}
+
+void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
+{
+ zfcp_port_get(port);
+ port->rport_task = RPORT_DEL;
+
+ if (!queue_work(zfcp_data.work_queue, &port->rport_work))
+ zfcp_port_put(port);
+}
+
+void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
+
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_scsi_schedule_rport_block(port);
+}
+
+void zfcp_scsi_rport_work(struct work_struct *work)
+{
+ struct zfcp_port *port = container_of(work, struct zfcp_port,
+ rport_work);
+
+ while (port->rport_task) {
+ if (port->rport_task == RPORT_ADD) {
+ port->rport_task = RPORT_NONE;
+ zfcp_scsi_rport_register(port);
+ } else {
+ port->rport_task = RPORT_NONE;
+ zfcp_scsi_rport_block(port);
+ }
+ }
+
+ zfcp_port_put(port);
+}
+
+
struct fc_function_template zfcp_transport_functions = {
.show_starget_port_id = 1,
.show_starget_port_name = 1,
@@ -497,6 +601,8 @@ struct fc_function_template zfcp_transport_functions = {
.reset_fc_host_stats = zfcp_reset_fc_host_stats,
.set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo,
.get_host_port_state = zfcp_get_host_port_state,
+ .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk,
+ .terminate_rport_io = zfcp_scsi_terminate_rport_io,
.show_host_port_state = 1,
/* no functions registered for following dynamic attributes but
directly set by LLDD */
diff --git a/trunk/drivers/s390/scsi/zfcp_sysfs.c b/trunk/drivers/s390/scsi/zfcp_sysfs.c
index 899af2b45b1e..9a3b8e261c0a 100644
--- a/trunk/drivers/s390/scsi/zfcp_sysfs.c
+++ b/trunk/drivers/s390/scsi/zfcp_sysfs.c
@@ -112,9 +112,9 @@ static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
zfcp_sysfs_##_feat##_failed_show, \
zfcp_sysfs_##_feat##_failed_store);
-ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, 44, 93);
-ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, 45, 96);
-ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, 46, 97);
+ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, "syafai1", "syafai2");
+ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, "sypfai1", "sypfai2");
+ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, "syufai1", "syufai2");
static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
struct device_attribute *attr,
@@ -168,7 +168,7 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
goto out;
}
- zfcp_erp_port_shutdown(port, 0, 92, NULL);
+ zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
zfcp_port_dequeue(port);
@@ -222,7 +222,7 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
retval = 0;
- zfcp_erp_unit_reopen(unit, 0, 94, NULL);
+ zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
out:
@@ -268,7 +268,7 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
goto out;
}
- zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
+ zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
zfcp_unit_dequeue(unit);
@@ -318,10 +318,9 @@ zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
struct zfcp_unit *unit = sdev->hostdata; \
struct zfcp_latencies *lat = &unit->latencies; \
struct zfcp_adapter *adapter = unit->port->adapter; \
- unsigned long flags; \
unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \
\
- spin_lock_irqsave(&lat->lock, flags); \
+ spin_lock_bh(&lat->lock); \
fsum = lat->_name.fabric.sum * adapter->timer_ticks; \
fmin = lat->_name.fabric.min * adapter->timer_ticks; \
fmax = lat->_name.fabric.max * adapter->timer_ticks; \
@@ -329,7 +328,7 @@ zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
cmin = lat->_name.channel.min * adapter->timer_ticks; \
cmax = lat->_name.channel.max * adapter->timer_ticks; \
cc = lat->_name.counter; \
- spin_unlock_irqrestore(&lat->lock, flags); \
+ spin_unlock_bh(&lat->lock); \
\
do_div(fsum, 1000); \
do_div(fmin, 1000); \
@@ -487,7 +486,8 @@ static ssize_t zfcp_sysfs_adapter_q_full_show(struct device *dev,
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) scsi_host->hostdata[0];
- return sprintf(buf, "%d\n", atomic_read(&adapter->qdio_outb_full));
+ return sprintf(buf, "%d %llu\n", atomic_read(&adapter->qdio_outb_full),
+ (unsigned long long)adapter->req_q_util);
}
static DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, NULL);
diff --git a/trunk/drivers/scsi/3w-9xxx.c b/trunk/drivers/scsi/3w-9xxx.c
index 5311317c2e4c..a12783ebb42d 100644
--- a/trunk/drivers/scsi/3w-9xxx.c
+++ b/trunk/drivers/scsi/3w-9xxx.c
@@ -4,7 +4,7 @@
Written By: Adam Radford
Modifications By: Tom Couch
- Copyright (C) 2004-2008 Applied Micro Circuits Corporation.
+ Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -75,6 +75,7 @@
Add MSI support and "use_msi" module parameter.
Fix bug in twa_get_param() on 4GB+.
Use pci_resource_len() for ioremap().
+ 2.26.02.012 - Add power management support.
*/
#include
@@ -99,7 +100,7 @@
#include "3w-9xxx.h"
/* Globals */
-#define TW_DRIVER_VERSION "2.26.02.011"
+#define TW_DRIVER_VERSION "2.26.02.012"
static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
static unsigned int twa_device_extension_count;
static int twa_major = -1;
@@ -2182,6 +2183,98 @@ static void twa_remove(struct pci_dev *pdev)
twa_device_extension_count--;
} /* End twa_remove() */
+#ifdef CONFIG_PM
+/* This function is called on PCI suspend */
+static int twa_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-9xxx: Suspending host %d.\n", tw_dev->host->host_no);
+
+ TW_DISABLE_INTERRUPTS(tw_dev);
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_disable_msi(pdev);
+
+ /* Tell the card we are shutting down */
+ if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x38, "Connection shutdown failed during suspend");
+ } else {
+ printk(KERN_WARNING "3w-9xxx: Suspend complete.\n");
+ }
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+} /* End twa_suspend() */
+
+/* This function is called on PCI resume */
+static int twa_resume(struct pci_dev *pdev)
+{
+ int retval = 0;
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-9xxx: Resuming host %d.\n", tw_dev->host->host_no);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x39, "Enable device failed during resume");
+ return retval;
+ }
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ if (pci_set_dma_mask(pdev, DMA_64BIT_MASK)
+ || pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)
+ || pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
+ TW_PRINTK(host, TW_DRIVER, 0x40, "Failed to set dma mask during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Initialize the card */
+ if (twa_reset_sequence(tw_dev, 0)) {
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twa_interrupt, IRQF_SHARED, "3w-9xxx", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x42, "Error requesting IRQ during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now enable MSI if enabled */
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_enable_msi(pdev);
+
+ /* Re-enable interrupts on the card */
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+ printk(KERN_WARNING "3w-9xxx: Resume complete.\n");
+ return 0;
+
+out_disable_device:
+ scsi_remove_host(host);
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twa_resume() */
+#endif
+
/* PCI Devices supported by this driver */
static struct pci_device_id twa_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
@@ -2202,6 +2295,10 @@ static struct pci_driver twa_driver = {
.id_table = twa_pci_tbl,
.probe = twa_probe,
.remove = twa_remove,
+#ifdef CONFIG_PM
+ .suspend = twa_suspend,
+ .resume = twa_resume,
+#endif
.shutdown = twa_shutdown
};
diff --git a/trunk/drivers/scsi/3w-9xxx.h b/trunk/drivers/scsi/3w-9xxx.h
index 1729a8785fea..2893eec78ed2 100644
--- a/trunk/drivers/scsi/3w-9xxx.h
+++ b/trunk/drivers/scsi/3w-9xxx.h
@@ -4,7 +4,7 @@
Written By: Adam Radford
Modifications By: Tom Couch
- Copyright (C) 2004-2008 Applied Micro Circuits Corporation.
+ Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/trunk/drivers/scsi/Kconfig b/trunk/drivers/scsi/Kconfig
index 256c7bec7bd7..e2f44e6c0bcb 100644
--- a/trunk/drivers/scsi/Kconfig
+++ b/trunk/drivers/scsi/Kconfig
@@ -224,14 +224,15 @@ config SCSI_LOGGING
can enable logging by saying Y to "/proc file system support" and
"Sysctl support" below and executing the command
- echo "scsi log token [level]" > /proc/scsi/scsi
+ echo > /proc/sys/dev/scsi/logging_level
- at boot time after the /proc file system has been mounted.
+ where is a four byte value representing the logging type
+ and logging level for each type of logging selected.
- There are a number of things that can be used for 'token' (you can
- find them in the source: ), and this
- allows you to select the types of information you want, and the
- level allows you to select the level of verbosity.
+ There are a number of logging types and you can find them in the
+ source at . The logging levels
+ are also described in that file and they determine the verbosity of
+ the logging for each logging type.
If you say N here, it may be harder to track down some types of SCSI
problems. If you say Y here your kernel will be somewhat larger, but
@@ -570,6 +571,7 @@ config SCSI_ARCMSR_AER
To enable this function, choose Y here.
source "drivers/scsi/megaraid/Kconfig.megaraid"
+source "drivers/scsi/mpt2sas/Kconfig"
config SCSI_HPTIOP
tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
@@ -608,6 +610,7 @@ config SCSI_FLASHPOINT
config LIBFC
tristate "LibFC module"
select SCSI_FC_ATTRS
+ select CRC32
---help---
Fibre Channel library module
@@ -1535,6 +1538,7 @@ config SCSI_NSP32
config SCSI_DEBUG
tristate "SCSI debugging host simulator"
depends on SCSI
+ select CRC_T10DIF
help
This is a host adapter simulator that can simulate multiple hosts
each with multiple dummy SCSI devices (disks). It defaults to one
@@ -1803,4 +1807,6 @@ source "drivers/scsi/pcmcia/Kconfig"
source "drivers/scsi/device_handler/Kconfig"
+source "drivers/scsi/osd/Kconfig"
+
endmenu
diff --git a/trunk/drivers/scsi/Makefile b/trunk/drivers/scsi/Makefile
index 7461eb09a031..cf7929634668 100644
--- a/trunk/drivers/scsi/Makefile
+++ b/trunk/drivers/scsi/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
+obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
obj-$(CONFIG_SCSI_GDTH) += gdth.o
@@ -137,6 +138,8 @@ obj-$(CONFIG_CHR_DEV_SG) += sg.o
obj-$(CONFIG_CHR_DEV_SCH) += ch.o
obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o
+obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
+
# This goes last, so that "real" scsi devices probe earlier
obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
diff --git a/trunk/drivers/scsi/ch.c b/trunk/drivers/scsi/ch.c
index af9725409f43..7b1633a8c15a 100644
--- a/trunk/drivers/scsi/ch.c
+++ b/trunk/drivers/scsi/ch.c
@@ -41,6 +41,7 @@ MODULE_DESCRIPTION("device driver for scsi media changer devices");
MODULE_AUTHOR("Gerd Knorr ");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_MEDIUM_CHANGER);
static int init = 1;
module_param(init, int, 0444);
diff --git a/trunk/drivers/scsi/constants.c b/trunk/drivers/scsi/constants.c
index 4003deefb7d8..e79e18101f87 100644
--- a/trunk/drivers/scsi/constants.c
+++ b/trunk/drivers/scsi/constants.c
@@ -1373,21 +1373,14 @@ static const char * const driverbyte_table[]={
"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"};
#define NUM_DRIVERBYTE_STRS ARRAY_SIZE(driverbyte_table)
-static const char * const driversuggest_table[]={"SUGGEST_OK",
-"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
-"SUGGEST_5", "SUGGEST_6", "SUGGEST_7", "SUGGEST_SENSE"};
-#define NUM_SUGGEST_STRS ARRAY_SIZE(driversuggest_table)
-
void scsi_show_result(int result)
{
int hb = host_byte(result);
- int db = (driver_byte(result) & DRIVER_MASK);
- int su = ((driver_byte(result) & SUGGEST_MASK) >> 4);
+ int db = driver_byte(result);
- printk("Result: hostbyte=%s driverbyte=%s,%s\n",
+ printk("Result: hostbyte=%s driverbyte=%s\n",
(hb < NUM_HOSTBYTE_STRS ? hostbyte_table[hb] : "invalid"),
- (db < NUM_DRIVERBYTE_STRS ? driverbyte_table[db] : "invalid"),
- (su < NUM_SUGGEST_STRS ? driversuggest_table[su] : "invalid"));
+ (db < NUM_DRIVERBYTE_STRS ? driverbyte_table[db] : "invalid"));
}
#else
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.c
index a83d36e4926f..4eb6f5593b3e 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.c
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.c
@@ -196,7 +196,7 @@ static inline int ddp_alloc_gl_skb(struct cxgb3i_ddp_info *ddp, int idx,
}
/**
- * cxgb3i_ddp_find_page_index - return ddp page index for a given page size.
+ * cxgb3i_ddp_find_page_index - return ddp page index for a given page size
* @pgsz: page size
* return the ddp page index, if no match is found return DDP_PGIDX_MAX.
*/
@@ -355,8 +355,7 @@ EXPORT_SYMBOL_GPL(cxgb3i_ddp_release_gl);
* @tdev: t3cdev adapter
* @tid: connection id
* @tformat: tag format
- * @tagp: the s/w tag, if ddp setup is successful, it will be updated with
- * ddp/hw tag
+ * @tagp: contains s/w tag initially, will be updated with ddp/hw tag
* @gl: the page momory list
* @gfp: allocation mode
*
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.h b/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.h
index 3faae7831c83..75a63a81e873 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.h
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_ddp.h
@@ -185,12 +185,11 @@ static inline int cxgb3i_is_ddp_tag(struct cxgb3i_tag_format *tformat, u32 tag)
}
/**
- * cxgb3i_sw_tag_usable - check if a given s/w tag has enough bits left for
- * the reserved/hw bits
+ * cxgb3i_sw_tag_usable - check if s/w tag has enough bits left for hw bits
* @tformat: tag format information
* @sw_tag: s/w tag to be checked
*
- * return true if the tag is a ddp tag, false otherwise.
+ * return true if the tag can be used for hw ddp tag, false otherwise.
*/
static inline int cxgb3i_sw_tag_usable(struct cxgb3i_tag_format *tformat,
u32 sw_tag)
@@ -222,8 +221,7 @@ static inline u32 cxgb3i_set_non_ddp_tag(struct cxgb3i_tag_format *tformat,
}
/**
- * cxgb3i_ddp_tag_base - shift the s/w tag bits so that reserved bits are not
- * used.
+ * cxgb3i_ddp_tag_base - shift s/w tag bits so that reserved bits are not used
* @tformat: tag format information
* @sw_tag: s/w tag to be checked
*/
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/trunk/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index fa2a44f37b36..e185dedc4c1f 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -101,8 +101,7 @@ struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *t3dev)
}
/**
- * cxgb3i_adapter_remove - release all the resources held and cleanup any
- * h/w settings
+ * cxgb3i_adapter_remove - release the resources held and cleanup h/w settings
* @t3dev: t3cdev adapter
*/
void cxgb3i_adapter_remove(struct t3cdev *t3dev)
@@ -135,8 +134,7 @@ void cxgb3i_adapter_remove(struct t3cdev *t3dev)
}
/**
- * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure with a given
- * net_device
+ * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure via net_device
* @t3dev: t3cdev adapter
*/
struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
@@ -170,8 +168,7 @@ struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic,
int err;
shost = iscsi_host_alloc(&cxgb3i_host_template,
- sizeof(struct cxgb3i_hba),
- CXGB3I_SCSI_QDEPTH_DFLT);
+ sizeof(struct cxgb3i_hba), 1);
if (!shost) {
cxgb3i_log_info("iscsi_host_alloc failed.\n");
return NULL;
@@ -335,13 +332,12 @@ static void cxgb3i_ep_disconnect(struct iscsi_endpoint *ep)
* @cmds_max: max # of commands
* @qdepth: scsi queue depth
* @initial_cmdsn: initial iscsi CMDSN for this session
- * @host_no: pointer to return host no
*
* Creates a new iSCSI session
*/
static struct iscsi_cls_session *
cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth,
- u32 initial_cmdsn, u32 *host_no)
+ u32 initial_cmdsn)
{
struct cxgb3i_endpoint *cep;
struct cxgb3i_hba *hba;
@@ -360,8 +356,6 @@ cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth,
cxgb3i_api_debug("ep 0x%p, cep 0x%p, hba 0x%p.\n", ep, cep, hba);
BUG_ON(hba != iscsi_host_priv(shost));
- *host_no = shost->host_no;
-
cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost,
cmds_max,
sizeof(struct iscsi_tcp_task) +
@@ -394,9 +388,9 @@ static void cxgb3i_session_destroy(struct iscsi_cls_session *cls_session)
}
/**
- * cxgb3i_conn_max_xmit_dlength -- check the max. xmit pdu segment size,
- * reduce it to be within the hardware limit if needed
+ * cxgb3i_conn_max_xmit_dlength -- calc the max. xmit pdu segment size
* @conn: iscsi connection
+ * check the max. xmit pdu payload, reduce it if needed
*/
static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn)
@@ -417,8 +411,7 @@ static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn)
}
/**
- * cxgb3i_conn_max_recv_dlength -- check the max. recv pdu segment size against
- * the hardware limit
+ * cxgb3i_conn_max_recv_dlength -- check the max. recv pdu segment size
* @conn: iscsi connection
* return 0 if the value is valid, < 0 otherwise.
*/
@@ -759,9 +752,9 @@ static void cxgb3i_parse_itt(struct iscsi_conn *conn, itt_t itt,
/**
* cxgb3i_reserve_itt - generate tag for a give task
- * Try to set up ddp for a scsi read task.
* @task: iscsi task
* @hdr_itt: tag, filled in by this function
+ * Set up ddp for scsi read tasks if possible.
*/
int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
{
@@ -809,9 +802,9 @@ int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
/**
* cxgb3i_release_itt - release the tag for a given task
- * if the tag is a ddp tag, release the ddp setup
* @task: iscsi task
* @hdr_itt: tag
+ * If the tag is a ddp tag, release the ddp setup
*/
void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt)
{
@@ -843,7 +836,7 @@ static struct scsi_host_template cxgb3i_host_template = {
.can_queue = CXGB3I_SCSI_QDEPTH_DFLT - 1,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
- .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .cmd_per_lun = CXGB3I_SCSI_QDEPTH_DFLT,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_target_reset,
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.c b/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.c
index de3b3b614cca..c2e434e54e28 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.c
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.c
@@ -1417,8 +1417,7 @@ static void c3cn_active_close(struct s3_conn *c3cn)
}
/**
- * cxgb3i_c3cn_release - close and release an iscsi tcp connection and any
- * resource held
+ * cxgb3i_c3cn_release - close and release an iscsi tcp connection
* @c3cn: the iscsi tcp connection
*/
void cxgb3i_c3cn_release(struct s3_conn *c3cn)
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.h b/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.h
index 6344b9eb2589..275f23f16eb7 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.h
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_offload.h
@@ -139,6 +139,7 @@ enum c3cn_flags {
/**
* cxgb3i_sdev_data - Per adapter data.
+ *
* Linked off of each Ethernet device port on the adapter.
* Also available via the t3cdev structure since we have pointers to our port
* net_device's there ...
diff --git a/trunk/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/trunk/drivers/scsi/cxgb3i/cxgb3i_pdu.c
index 17115c230d65..7eebc9a7cb35 100644
--- a/trunk/drivers/scsi/cxgb3i/cxgb3i_pdu.c
+++ b/trunk/drivers/scsi/cxgb3i/cxgb3i_pdu.c
@@ -479,7 +479,7 @@ void cxgb3i_conn_tx_open(struct s3_conn *c3cn)
cxgb3i_tx_debug("cn 0x%p.\n", c3cn);
if (conn) {
cxgb3i_tx_debug("cn 0x%p, cid %d.\n", c3cn, conn->id);
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
}
}
diff --git a/trunk/drivers/scsi/device_handler/scsi_dh_alua.c b/trunk/drivers/scsi/device_handler/scsi_dh_alua.c
index e356b43753ff..dba154c8ff64 100644
--- a/trunk/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/trunk/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -247,8 +247,8 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
/* Prepare the data buffer */
memset(h->buff, 0, stpg_len);
h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f;
- h->buff[6] = (h->group_id >> 8) & 0x0f;
- h->buff[7] = h->group_id & 0x0f;
+ h->buff[6] = (h->group_id >> 8) & 0xff;
+ h->buff[7] = h->group_id & 0xff;
rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
if (!rq)
@@ -461,6 +461,15 @@ static int alua_check_sense(struct scsi_device *sdev,
*/
return ADD_TO_MLQUEUE;
}
+ if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) {
+ /*
+ * REPORTED_LUNS_DATA_HAS_CHANGED is reported
+ * when switching controllers on targets like
+ * Intel Multi-Flex. We can just retry.
+ */
+ return ADD_TO_MLQUEUE;
+ }
+
break;
}
@@ -691,6 +700,7 @@ static const struct scsi_dh_devlist alua_dev_list[] = {
{"IBM", "2107900" },
{"IBM", "2145" },
{"Pillar", "Axiom" },
+ {"Intel", "Multi-Flex"},
{NULL, NULL}
};
diff --git a/trunk/drivers/scsi/device_handler/scsi_dh_rdac.c b/trunk/drivers/scsi/device_handler/scsi_dh_rdac.c
index 53664765570a..43b8c51e98d0 100644
--- a/trunk/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/trunk/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -449,28 +449,40 @@ static int mode_select_handle_sense(struct scsi_device *sdev,
unsigned char *sensebuf)
{
struct scsi_sense_hdr sense_hdr;
- int sense, err = SCSI_DH_IO, ret;
+ int err = SCSI_DH_IO, ret;
ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
if (!ret)
goto done;
err = SCSI_DH_OK;
- sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
- sense_hdr.ascq;
- /* If it is retryable failure, submit the c9 inquiry again */
- if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
- sense == 0x62900) {
- /* 0x59136 - Command lock contention
- * 0x[6b]8b02 - Quiesense in progress or achieved
- * 0x62900 - Power On, Reset, or Bus Device Reset
- */
+
+ switch (sense_hdr.sense_key) {
+ case NO_SENSE:
+ case ABORTED_COMMAND:
+ case UNIT_ATTENTION:
err = SCSI_DH_RETRY;
+ break;
+ case NOT_READY:
+ if (sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x01)
+ /* LUN Not Ready and is in the Process of Becoming
+ * Ready
+ */
+ err = SCSI_DH_RETRY;
+ break;
+ case ILLEGAL_REQUEST:
+ if (sense_hdr.asc == 0x91 && sense_hdr.ascq == 0x36)
+ /*
+ * Command Lock contention
+ */
+ err = SCSI_DH_RETRY;
+ break;
+ default:
+ sdev_printk(KERN_INFO, sdev,
+ "MODE_SELECT failed with sense %02x/%02x/%02x.\n",
+ sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq);
}
- if (sense)
- sdev_printk(KERN_INFO, sdev,
- "MODE_SELECT failed with sense 0x%x.\n", sense);
done:
return err;
}
@@ -562,6 +574,12 @@ static int rdac_check_sense(struct scsi_device *sdev,
* Just retry and wait.
*/
return ADD_TO_MLQUEUE;
+ if (sense_hdr->asc == 0xA1 && sense_hdr->ascq == 0x02)
+ /* LUN Not Ready - Quiescense in progress
+ * or has been achieved
+ * Just retry.
+ */
+ return ADD_TO_MLQUEUE;
break;
case ILLEGAL_REQUEST:
if (sense_hdr->asc == 0x94 && sense_hdr->ascq == 0x01) {
@@ -579,6 +597,11 @@ static int rdac_check_sense(struct scsi_device *sdev,
* Power On, Reset, or Bus Device Reset, just retry.
*/
return ADD_TO_MLQUEUE;
+ if (sense_hdr->asc == 0x8b && sense_hdr->ascq == 0x02)
+ /*
+ * Quiescence in progress , just retry.
+ */
+ return ADD_TO_MLQUEUE;
break;
}
/* success just means we do not care what scsi-ml does */
diff --git a/trunk/drivers/scsi/fcoe/fcoe_sw.c b/trunk/drivers/scsi/fcoe/fcoe_sw.c
index da210eba1941..2bbbe3c0cc7b 100644
--- a/trunk/drivers/scsi/fcoe/fcoe_sw.c
+++ b/trunk/drivers/scsi/fcoe/fcoe_sw.c
@@ -133,6 +133,13 @@ static int fcoe_sw_lport_config(struct fc_lport *lp)
/* lport fc_lport related configuration */
fc_lport_config(lp);
+ /* offload related configuration */
+ lp->crc_offload = 0;
+ lp->seq_offload = 0;
+ lp->lro_enabled = 0;
+ lp->lro_xid = 0;
+ lp->lso_max = 0;
+
return 0;
}
@@ -186,7 +193,27 @@ static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
if (fc->real_dev->features & NETIF_F_SG)
lp->sg_supp = 1;
-
+#ifdef NETIF_F_FCOE_CRC
+ if (netdev->features & NETIF_F_FCOE_CRC) {
+ lp->crc_offload = 1;
+ printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+ netdev->name);
+ }
+#endif
+#ifdef NETIF_F_FSO
+ if (netdev->features & NETIF_F_FSO) {
+ lp->seq_offload = 1;
+ lp->lso_max = netdev->gso_max_size;
+ printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+ netdev->name, lp->lso_max);
+ }
+#endif
+ if (netdev->fcoe_ddp_xid) {
+ lp->lro_enabled = 1;
+ lp->lro_xid = netdev->fcoe_ddp_xid;
+ printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+ netdev->name, lp->lro_xid);
+ }
skb_queue_head_init(&fc->fcoe_pending_queue);
fc->fcoe_pending_queue_active = 0;
@@ -346,8 +373,46 @@ static int fcoe_sw_destroy(struct net_device *netdev)
return 0;
}
+/*
+ * fcoe_sw_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp: the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_sw_ddp_setup(struct fc_lport *lp, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
+{
+ struct net_device *n = fcoe_netdev(lp);
+
+ if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+ return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_ddp_done - calls LLD's ddp_done through net_device
+ * @lp: the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_sw_ddp_done(struct fc_lport *lp, u16 xid)
+{
+ struct net_device *n = fcoe_netdev(lp);
+
+ if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+ return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+ return 0;
+}
+
static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
.frame_send = fcoe_xmit,
+ .ddp_setup = fcoe_sw_ddp_setup,
+ .ddp_done = fcoe_sw_ddp_done,
};
/**
diff --git a/trunk/drivers/scsi/fcoe/libfcoe.c b/trunk/drivers/scsi/fcoe/libfcoe.c
index 5548bf3bb58b..0d6f5beb7f9e 100644
--- a/trunk/drivers/scsi/fcoe/libfcoe.c
+++ b/trunk/drivers/scsi/fcoe/libfcoe.c
@@ -423,7 +423,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
/* crc offload */
if (likely(lp->crc_offload)) {
- skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
crc = 0;
@@ -460,7 +460,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->mac_len = elen;
- skb->protocol = htons(ETH_P_802_3);
+ skb->protocol = htons(ETH_P_FCOE);
skb->dev = fc->real_dev;
/* fill up mac and fcoe headers */
@@ -483,6 +483,16 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
hp->fcoe_sof = sof;
+#ifdef NETIF_F_FSO
+ /* fcoe lso, mss is in max_payload which is non-zero for FCP data */
+ if (lp->seq_offload && fr_max_payload(fp)) {
+ skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
+ skb_shinfo(skb)->gso_size = fr_max_payload(fp);
+ } else {
+ skb_shinfo(skb)->gso_type = 0;
+ skb_shinfo(skb)->gso_size = 0;
+ }
+#endif
/* update tx stats: regardless if LLD fails */
stats = lp->dev_stats[smp_processor_id()];
if (stats) {
@@ -623,7 +633,7 @@ int fcoe_percpu_receive_thread(void *arg)
* it's solicited data, in which case, the FCP layer would
* check it during the copy.
*/
- if (lp->crc_offload)
+ if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
else
fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
diff --git a/trunk/drivers/scsi/hosts.c b/trunk/drivers/scsi/hosts.c
index aa670a1d1513..89d41a424b33 100644
--- a/trunk/drivers/scsi/hosts.c
+++ b/trunk/drivers/scsi/hosts.c
@@ -176,7 +176,6 @@ void scsi_remove_host(struct Scsi_Host *shost)
transport_unregister_device(&shost->shost_gendev);
device_unregister(&shost->shost_dev);
device_del(&shost->shost_gendev);
- scsi_proc_hostdir_rm(shost->hostt);
}
EXPORT_SYMBOL(scsi_remove_host);
@@ -270,6 +269,8 @@ static void scsi_host_dev_release(struct device *dev)
struct Scsi_Host *shost = dev_to_shost(dev);
struct device *parent = dev->parent;
+ scsi_proc_hostdir_rm(shost->hostt);
+
if (shost->ehandler)
kthread_stop(shost->ehandler);
if (shost->work_q)
diff --git a/trunk/drivers/scsi/hptiop.c b/trunk/drivers/scsi/hptiop.c
index 34be88d7afa5..af1f0af0c5ac 100644
--- a/trunk/drivers/scsi/hptiop.c
+++ b/trunk/drivers/scsi/hptiop.c
@@ -580,8 +580,7 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
break;
default:
- scp->result = ((DRIVER_INVALID|SUGGEST_ABORT)<<24) |
- (DID_ABORT<<16);
+ scp->result = DRIVER_INVALID << 24 | DID_ABORT << 16;
break;
}
diff --git a/trunk/drivers/scsi/ibmvscsi/ibmvfc.c b/trunk/drivers/scsi/ibmvscsi/ibmvfc.c
index ed1e728763a2..93d1fbe4ee5d 100644
--- a/trunk/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/trunk/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -2767,6 +2767,40 @@ static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
ibmvfc_init_tgt(tgt, job_step);
}
+/* Defined in FC-LS */
+static const struct {
+ int code;
+ int retry;
+ int logged_in;
+} prli_rsp [] = {
+ { 0, 1, 0 },
+ { 1, 0, 1 },
+ { 2, 1, 0 },
+ { 3, 1, 0 },
+ { 4, 0, 0 },
+ { 5, 0, 0 },
+ { 6, 0, 1 },
+ { 7, 0, 0 },
+ { 8, 1, 0 },
+};
+
+/**
+ * ibmvfc_get_prli_rsp - Find PRLI response index
+ * @flags: PRLI response flags
+ *
+ **/
+static int ibmvfc_get_prli_rsp(u16 flags)
+{
+ int i;
+ int code = (flags & 0x0f00) >> 8;
+
+ for (i = 0; i < ARRAY_SIZE(prli_rsp); i++)
+ if (prli_rsp[i].code == code)
+ return i;
+
+ return 0;
+}
+
/**
* ibmvfc_tgt_prli_done - Completion handler for Process Login
* @evt: ibmvfc event struct
@@ -2777,15 +2811,36 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
struct ibmvfc_target *tgt = evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
+ struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
u32 status = rsp->common.status;
+ int index;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Process Login succeeded\n");
- tgt->need_login = 0;
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+ tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n",
+ parms->type, parms->flags, parms->service_parms);
+
+ if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
+ index = ibmvfc_get_prli_rsp(parms->flags);
+ if (prli_rsp[index].logged_in) {
+ if (parms->flags & IBMVFC_PRLI_EST_IMG_PAIR) {
+ tgt->need_login = 0;
+ tgt->ids.roles = 0;
+ if (parms->service_parms & IBMVFC_PRLI_TARGET_FUNC)
+ tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
+ if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC)
+ tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+ } else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ } else if (prli_rsp[index].retry)
+ ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ } else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -2874,7 +2929,6 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
tgt->ids.port_id = tgt->scsi_id;
- tgt->ids.roles = FC_PORT_ROLE_FCP_TARGET;
memcpy(&tgt->service_parms, &rsp->service_parms,
sizeof(tgt->service_parms));
memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
diff --git a/trunk/drivers/scsi/ipr.c b/trunk/drivers/scsi/ipr.c
index 07829009a8be..def473f0a98f 100644
--- a/trunk/drivers/scsi/ipr.c
+++ b/trunk/drivers/scsi/ipr.c
@@ -152,13 +152,13 @@ module_param_named(log_level, ipr_log_level, uint, 0);
MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver");
module_param_named(testmode, ipr_testmode, int, 0);
MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations");
-module_param_named(fastfail, ipr_fastfail, int, 0);
+module_param_named(fastfail, ipr_fastfail, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries");
module_param_named(transop_timeout, ipr_transop_timeout, int, 0);
MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)");
module_param_named(enable_cache, ipr_enable_cache, int, 0);
MODULE_PARM_DESC(enable_cache, "Enable adapter's non-volatile write cache (default: 1)");
-module_param_named(debug, ipr_debug, int, 0);
+module_param_named(debug, ipr_debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)");
module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0);
MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)");
@@ -354,6 +354,8 @@ struct ipr_error_table_t ipr_error_table[] = {
"9076: Configuration error, missing remote IOA"},
{0x06679100, 0, IPR_DEFAULT_LOG_LEVEL,
"4050: Enclosure does not support a required multipath function"},
+ {0x06690000, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4070: Logically bad block written on device"},
{0x06690200, 0, IPR_DEFAULT_LOG_LEVEL,
"9041: Array protection temporarily suspended"},
{0x06698200, 0, IPR_DEFAULT_LOG_LEVEL,
@@ -7147,6 +7149,7 @@ static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg)
ENTER;
free_irq(pdev->irq, ioa_cfg);
+ pci_disable_msi(pdev);
iounmap(ioa_cfg->hdw_dma_regs);
pci_release_regions(pdev);
ipr_free_mem(ioa_cfg);
@@ -7432,6 +7435,11 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
goto out;
}
+ if (!(rc = pci_enable_msi(pdev)))
+ dev_info(&pdev->dev, "MSI enabled\n");
+ else if (ipr_debug)
+ dev_info(&pdev->dev, "Cannot enable MSI\n");
+
dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq);
host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg));
@@ -7574,6 +7582,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
out_scsi_host_put:
scsi_host_put(host);
out_disable:
+ pci_disable_msi(pdev);
pci_disable_device(pdev);
goto out;
}
diff --git a/trunk/drivers/scsi/ipr.h b/trunk/drivers/scsi/ipr.h
index 8f872f816fe4..79a3ae4fb2c7 100644
--- a/trunk/drivers/scsi/ipr.h
+++ b/trunk/drivers/scsi/ipr.h
@@ -37,8 +37,8 @@
/*
* Literals
*/
-#define IPR_DRIVER_VERSION "2.4.1"
-#define IPR_DRIVER_DATE "(April 24, 2007)"
+#define IPR_DRIVER_VERSION "2.4.2"
+#define IPR_DRIVER_DATE "(January 21, 2009)"
/*
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
diff --git a/trunk/drivers/scsi/ips.c b/trunk/drivers/scsi/ips.c
index ef683f0d2b5a..457d76a4cfe5 100644
--- a/trunk/drivers/scsi/ips.c
+++ b/trunk/drivers/scsi/ips.c
@@ -1004,8 +1004,7 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num);
while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
- scb->scsi_cmd->result =
- (DID_RESET << 16) | (SUGGEST_RETRY << 24);
+ scb->scsi_cmd->result = DID_RESET << 16;
scb->scsi_cmd->scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
diff --git a/trunk/drivers/scsi/iscsi_tcp.c b/trunk/drivers/scsi/iscsi_tcp.c
index 23808dfe22ba..b3e5e08e44ab 100644
--- a/trunk/drivers/scsi/iscsi_tcp.c
+++ b/trunk/drivers/scsi/iscsi_tcp.c
@@ -48,13 +48,6 @@ MODULE_AUTHOR("Mike Christie , "
"Alex Aizman ");
MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
-#undef DEBUG_TCP
-
-#ifdef DEBUG_TCP
-#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
-#else
-#define debug_tcp(fmt...)
-#endif
static struct scsi_transport_template *iscsi_sw_tcp_scsi_transport;
static struct scsi_host_template iscsi_sw_tcp_sht;
@@ -63,6 +56,21 @@ static struct iscsi_transport iscsi_sw_tcp_transport;
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+static int iscsi_sw_tcp_dbg;
+module_param_named(debug_iscsi_tcp, iscsi_sw_tcp_dbg, int,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_iscsi_tcp, "Turn on debugging for iscsi_tcp module "
+ "Set to 1 to turn on, and zero to turn off. Default is off.");
+
+#define ISCSI_SW_TCP_DBG(_conn, dbg_fmt, arg...) \
+ do { \
+ if (iscsi_sw_tcp_dbg) \
+ iscsi_conn_printk(KERN_INFO, _conn, \
+ "%s " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
+
+
/**
* iscsi_sw_tcp_recv - TCP receive in sendfile fashion
* @rd_desc: read descriptor
@@ -77,7 +85,7 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
unsigned int consumed, total_consumed = 0;
int status;
- debug_tcp("in %d bytes\n", skb->len - offset);
+ ISCSI_SW_TCP_DBG(conn, "in %d bytes\n", skb->len - offset);
do {
status = 0;
@@ -86,7 +94,8 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
total_consumed += consumed;
} while (consumed != 0 && status != ISCSI_TCP_SKB_DONE);
- debug_tcp("read %d bytes status %d\n", skb->len - offset, status);
+ ISCSI_SW_TCP_DBG(conn, "read %d bytes status %d\n",
+ skb->len - offset, status);
return total_consumed;
}
@@ -131,7 +140,8 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
if ((sk->sk_state == TCP_CLOSE_WAIT ||
sk->sk_state == TCP_CLOSE) &&
!atomic_read(&sk->sk_rmem_alloc)) {
- debug_tcp("iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT\n");
+ ISCSI_SW_TCP_DBG(conn, "iscsi_tcp_state_change: "
+ "TCP_CLOSE|TCP_CLOSE_WAIT\n");
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
}
@@ -155,8 +165,8 @@ static void iscsi_sw_tcp_write_space(struct sock *sk)
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
tcp_sw_conn->old_write_space(sk);
- debug_tcp("iscsi_write_space: cid %d\n", conn->id);
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n");
+ iscsi_conn_queue_work(conn);
}
static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
@@ -283,7 +293,7 @@ static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
}
}
- debug_tcp("xmit %d bytes\n", consumed);
+ ISCSI_SW_TCP_DBG(conn, "xmit %d bytes\n", consumed);
conn->txdata_octets += consumed;
return consumed;
@@ -291,7 +301,7 @@ static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
error:
/* Transmit error. We could initiate error recovery
* here. */
- debug_tcp("Error sending PDU, errno=%d\n", rc);
+ ISCSI_SW_TCP_DBG(conn, "Error sending PDU, errno=%d\n", rc);
iscsi_conn_failure(conn, rc);
return -EIO;
}
@@ -334,9 +344,10 @@ static int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment;
- debug_tcp("Header done. Next segment size %u total_size %u\n",
- tcp_sw_conn->out.segment.size,
- tcp_sw_conn->out.segment.total_size);
+ ISCSI_SW_TCP_DBG(tcp_conn->iscsi_conn,
+ "Header done. Next segment size %u total_size %u\n",
+ tcp_sw_conn->out.segment.size,
+ tcp_sw_conn->out.segment.total_size);
return 0;
}
@@ -346,8 +357,8 @@ static void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr,
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
- conn->hdrdgst_en? ", digest enabled" : "");
+ ISCSI_SW_TCP_DBG(conn, "%s\n", conn->hdrdgst_en ?
+ "digest enabled" : "digest disabled");
/* Clear the data segment - needs to be filled in by the
* caller using iscsi_tcp_send_data_prep() */
@@ -389,9 +400,9 @@ iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
- debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __func__,
- tcp_conn, offset, len,
- conn->datadgst_en? ", digest enabled" : "");
+ ISCSI_SW_TCP_DBG(conn, "offset=%d, datalen=%d %s\n", offset, len,
+ conn->datadgst_en ?
+ "digest enabled" : "digest disabled");
/* Make sure the datalen matches what the caller
said he would send. */
@@ -415,8 +426,8 @@ iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
- debug_tcp("%s(%p, datalen=%d%s)\n", __func__, tcp_conn, len,
- conn->datadgst_en? ", digest enabled" : "");
+ ISCSI_SW_TCP_DBG(conn, "datalen=%zd %s\n", len, conn->datadgst_en ?
+ "digest enabled" : "digest disabled");
/* Make sure the datalen matches what the caller
said he would send. */
@@ -754,8 +765,7 @@ iscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
static struct iscsi_cls_session *
iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
- uint16_t qdepth, uint32_t initial_cmdsn,
- uint32_t *hostno)
+ uint16_t qdepth, uint32_t initial_cmdsn)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
@@ -766,10 +776,11 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
return NULL;
}
- shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, qdepth);
+ shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, 1);
if (!shost)
return NULL;
shost->transportt = iscsi_sw_tcp_scsi_transport;
+ shost->cmd_per_lun = qdepth;
shost->max_lun = iscsi_max_lun;
shost->max_id = 0;
shost->max_channel = 0;
@@ -777,7 +788,6 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
if (iscsi_host_add(shost, NULL))
goto free_host;
- *hostno = shost->host_no;
cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost,
cmds_max,
@@ -813,6 +823,12 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
iscsi_host_free(shost);
}
+static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev)
+{
+ set_bit(QUEUE_FLAG_BIDI, &sdev->request_queue->queue_flags);
+ return 0;
+}
+
static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
{
blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
@@ -833,6 +849,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = {
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler= iscsi_eh_target_reset,
.use_clustering = DISABLE_CLUSTERING,
+ .slave_alloc = iscsi_sw_tcp_slave_alloc,
.slave_configure = iscsi_sw_tcp_slave_configure,
.proc_name = "iscsi_tcp",
.this_id = -1,
diff --git a/trunk/drivers/scsi/libfc/fc_exch.c b/trunk/drivers/scsi/libfc/fc_exch.c
index 505825b6124d..992af05aacf1 100644
--- a/trunk/drivers/scsi/libfc/fc_exch.c
+++ b/trunk/drivers/scsi/libfc/fc_exch.c
@@ -281,7 +281,7 @@ static void fc_exch_release(struct fc_exch *ep)
ep->destructor(&ep->seq, ep->arg);
if (ep->lp->tt.exch_put)
ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
- WARN_ON(!ep->esb_stat & ESB_ST_COMPLETE);
+ WARN_ON(!(ep->esb_stat & ESB_ST_COMPLETE));
mempool_free(ep, mp->ep_pool);
}
}
@@ -489,7 +489,7 @@ static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
struct fc_exch *ep = NULL;
if (mp->max_read) {
- if (fc_frame_is_read(fp)) {
+ if (fc_fcp_is_read(fr_fsp(fp))) {
min = mp->min_xid;
max = mp->max_read;
plast = &mp->last_read;
@@ -1841,6 +1841,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
fc_exch_setup_hdr(ep, fp, ep->f_ctl);
sp->cnt++;
+ fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
if (unlikely(lp->tt.frame_send(lp, fp)))
goto err;
diff --git a/trunk/drivers/scsi/libfc/fc_fcp.c b/trunk/drivers/scsi/libfc/fc_fcp.c
index 2a631d7dbcec..a5725f3b7ce1 100644
--- a/trunk/drivers/scsi/libfc/fc_fcp.c
+++ b/trunk/drivers/scsi/libfc/fc_fcp.c
@@ -259,11 +259,61 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
}
fsp->state &= ~FC_SRB_ABORT_PENDING;
- fsp->io_status = SUGGEST_RETRY << 24;
+ fsp->io_status = 0;
fsp->status_code = FC_ERROR;
fc_fcp_complete_locked(fsp);
}
+/*
+ * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP
+ * transfer for a read I/O indicated by the fc_fcp_pkt.
+ * @fsp: ptr to the fc_fcp_pkt
+ *
+ * This is called in exch_seq_send() when we have a newly allocated
+ * exchange with a valid exchange id to setup ddp.
+ *
+ * returns: none
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
+{
+ struct fc_lport *lp;
+
+ if (!fsp)
+ return;
+
+ lp = fsp->lp;
+ if ((fsp->req_flags & FC_SRB_READ) &&
+ (lp->lro_enabled) && (lp->tt.ddp_setup)) {
+ if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd),
+ scsi_sg_count(fsp->cmd)))
+ fsp->xfer_ddp = xid;
+ }
+}
+EXPORT_SYMBOL(fc_fcp_ddp_setup);
+
+/*
+ * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
+ * DDP related resources for this I/O if it is initialized
+ * as a ddp transfer
+ * @fsp: ptr to the fc_fcp_pkt
+ *
+ * returns: none
+ */
+static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
+{
+ struct fc_lport *lp;
+
+ if (!fsp)
+ return;
+
+ lp = fsp->lp;
+ if (fsp->xfer_ddp && lp->tt.ddp_done) {
+ fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
+ fsp->xfer_ddp = 0;
+ }
+}
+
+
/*
* Receive SCSI data from target.
* Called after receiving solicited data.
@@ -289,6 +339,9 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
len = fr_len(fp) - sizeof(*fh);
buf = fc_frame_payload_get(fp, 0);
+ /* if this I/O is ddped, update xfer len */
+ fc_fcp_ddp_done(fsp);
+
if (offset + len > fsp->data_len) {
/* this should never happen */
if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
@@ -435,7 +488,13 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
* burst length (t_blen) to seq_blen, otherwise set t_blen
* to max FC frame payload previously set in fsp->max_payload.
*/
- t_blen = lp->seq_offload ? seq_blen : fsp->max_payload;
+ t_blen = fsp->max_payload;
+ if (lp->seq_offload) {
+ t_blen = min(seq_blen, (size_t)lp->lso_max);
+ FC_DEBUG_FCP("fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n",
+ fsp, seq_blen, lp->lso_max, t_blen);
+ }
+
WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
if (t_blen > 512)
t_blen &= ~(512 - 1); /* round down to block size */
@@ -744,6 +803,9 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
fsp->scsi_comp_flags = flags;
expected_len = fsp->data_len;
+ /* if ddp, update xfer len */
+ fc_fcp_ddp_done(fsp);
+
if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) {
rp_ex = (void *)(fc_rp + 1);
if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) {
@@ -859,7 +921,7 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
(!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
fsp->status_code = FC_DATA_UNDRUN;
- fsp->io_status = SUGGEST_RETRY << 24;
+ fsp->io_status = 0;
}
}
@@ -1006,7 +1068,7 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
}
memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
- fr_cmd(fp) = fsp->cmd;
+ fr_fsp(fp) = fsp;
rport = fsp->rport;
fsp->max_payload = rport->maxframe_size;
rp = rport->dd_data;
@@ -1267,7 +1329,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
rp = rport->dd_data;
if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
fsp->status_code = FC_HRD_ERROR;
- fsp->io_status = SUGGEST_RETRY << 24;
+ fsp->io_status = 0;
fc_fcp_complete_locked(fsp);
return;
}
@@ -1740,6 +1802,9 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
struct fc_lport *lp;
unsigned long flags;
+ /* release outstanding ddp context */
+ fc_fcp_ddp_done(fsp);
+
fsp->state |= FC_SRB_COMPL;
if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
spin_unlock_bh(&fsp->scsi_pkt_lock);
diff --git a/trunk/drivers/scsi/libfc/fc_lport.c b/trunk/drivers/scsi/libfc/fc_lport.c
index 2ae50a1188e6..7ef44501ecc6 100644
--- a/trunk/drivers/scsi/libfc/fc_lport.c
+++ b/trunk/drivers/scsi/libfc/fc_lport.c
@@ -762,10 +762,11 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
if (remote_wwpn == lport->wwpn) {
FC_DBG("FLOGI from port with same WWPN %llx "
- "possible configuration error\n", remote_wwpn);
+ "possible configuration error\n",
+ (unsigned long long)remote_wwpn);
goto out;
}
- FC_DBG("FLOGI from port WWPN %llx\n", remote_wwpn);
+ FC_DBG("FLOGI from port WWPN %llx\n", (unsigned long long)remote_wwpn);
/*
* XXX what is the right thing to do for FIDs?
diff --git a/trunk/drivers/scsi/libfc/fc_rport.c b/trunk/drivers/scsi/libfc/fc_rport.c
index dae65133a833..0472bb73221e 100644
--- a/trunk/drivers/scsi/libfc/fc_rport.c
+++ b/trunk/drivers/scsi/libfc/fc_rport.c
@@ -988,7 +988,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
switch (rdata->rp_state) {
case RPORT_ST_INIT:
FC_DEBUG_RPORT("incoming PLOGI from %6x wwpn %llx state INIT "
- "- reject\n", sid, wwpn);
+ "- reject\n", sid, (unsigned long long)wwpn);
reject = ELS_RJT_UNSUP;
break;
case RPORT_ST_PLOGI:
diff --git a/trunk/drivers/scsi/libiscsi.c b/trunk/drivers/scsi/libiscsi.c
index 809d32d95c76..dfaa8adf099e 100644
--- a/trunk/drivers/scsi/libiscsi.c
+++ b/trunk/drivers/scsi/libiscsi.c
@@ -38,6 +38,28 @@
#include
#include
+static int iscsi_dbg_lib;
+module_param_named(debug_libiscsi, iscsi_dbg_lib, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_libiscsi, "Turn on debugging for libiscsi module. "
+ "Set to 1 to turn on, and zero to turn off. Default "
+ "is off.");
+
+#define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \
+ do { \
+ if (iscsi_dbg_lib) \
+ iscsi_conn_printk(KERN_INFO, _conn, \
+ "%s " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
+
+#define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \
+ do { \
+ if (iscsi_dbg_lib) \
+ iscsi_session_printk(KERN_INFO, _session, \
+ "%s " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
+
/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
#define SNA32_CHECK 2147483648UL
@@ -54,6 +76,15 @@ static int iscsi_sna_lte(u32 n1, u32 n2)
(n1 > n2 && (n2 - n1 < SNA32_CHECK)));
}
+inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
+{
+ struct Scsi_Host *shost = conn->session->host;
+ struct iscsi_host *ihost = shost_priv(shost);
+
+ queue_work(ihost->workq, &conn->xmitwork);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
+
void
iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
{
@@ -81,8 +112,7 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
if (!list_empty(&session->leadconn->xmitqueue) ||
!list_empty(&session->leadconn->mgmtqueue)) {
if (!(session->tt->caps & CAP_DATA_PATH_OFFLOAD))
- scsi_queue_work(session->host,
- &session->leadconn->xmitwork);
+ iscsi_conn_queue_work(session->leadconn);
}
}
}
@@ -176,10 +206,11 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
ecdb_ahdr->reserved = 0;
memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
- debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
- "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
- cmd->cmd_len, rlen, pad_len, ahslength, task->hdr_len);
-
+ ISCSI_DBG_SESSION(task->conn->session,
+ "iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+ "rlen %d pad_len %d ahs_length %d iscsi_headers_size "
+ "%u\n", cmd->cmd_len, rlen, pad_len, ahslength,
+ task->hdr_len);
return 0;
}
@@ -201,10 +232,11 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
rlen_ahdr->reserved = 0;
rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
- debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
- "rlen_ahdr->ahslength(%d)\n",
- be32_to_cpu(rlen_ahdr->read_length),
- be16_to_cpu(rlen_ahdr->ahslength));
+ ISCSI_DBG_SESSION(task->conn->session,
+ "bidi-in rlen_ahdr->read_length(%d) "
+ "rlen_ahdr->ahslength(%d)\n",
+ be32_to_cpu(rlen_ahdr->read_length),
+ be16_to_cpu(rlen_ahdr->ahslength));
return 0;
}
@@ -335,13 +367,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
list_move_tail(&task->running, &conn->run_list);
conn->scsicmd_pdus_cnt++;
- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
- "bidi_len %d cmdsn %d win %d]\n", scsi_bidi_cmnd(sc) ?
- "bidirectional" : sc->sc_data_direction == DMA_TO_DEVICE ?
- "write" : "read", conn->id, sc, sc->cmnd[0], task->itt,
- scsi_bufflen(sc),
- scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
+ "itt 0x%x len %d bidi_len %d cmdsn %d win %d]\n",
+ scsi_bidi_cmnd(sc) ? "bidirectional" :
+ sc->sc_data_direction == DMA_TO_DEVICE ?
+ "write" : "read", conn->id, sc, sc->cmnd[0],
+ task->itt, scsi_bufflen(sc),
+ scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+ session->cmdsn,
+ session->max_cmdsn - session->exp_cmdsn + 1);
return 0;
}
@@ -483,9 +517,9 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
task->state = ISCSI_TASK_RUNNING;
list_move_tail(&task->running, &conn->mgmt_run_list);
- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
- hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
- task->data_count);
+ ISCSI_DBG_SESSION(session, "mgmtpdu [op 0x%x hdr->itt 0x%x "
+ "datalen %d]\n", hdr->opcode & ISCSI_OPCODE_MASK,
+ hdr->itt, task->data_count);
return 0;
}
@@ -560,7 +594,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
goto free_task;
} else
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
return task;
@@ -637,8 +671,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
memcpy(sc->sense_buffer, data + 2,
min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
- debug_scsi("copied %d bytes of sense\n",
- min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ ISCSI_DBG_SESSION(session, "copied %d bytes of sense\n",
+ min_t(uint16_t, senselen,
+ SCSI_SENSE_BUFFERSIZE));
}
if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
@@ -666,8 +701,8 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
out:
- debug_scsi("done [sc %lx res %d itt 0x%x]\n",
- (long)sc, sc->result, task->itt);
+ ISCSI_DBG_SESSION(session, "done [sc %p res %d itt 0x%x]\n",
+ sc, sc->result, task->itt);
conn->scsirsp_pdus_cnt++;
__iscsi_put_task(task);
@@ -835,8 +870,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
else
itt = ~0U;
- debug_scsi("[op 0x%x cid %d itt 0x%x len %d]\n",
- opcode, conn->id, itt, datalen);
+ ISCSI_DBG_SESSION(session, "[op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, itt, datalen);
if (itt == ~0U) {
iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
@@ -1034,10 +1069,9 @@ struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)
}
EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask);
-void iscsi_session_failure(struct iscsi_cls_session *cls_session,
+void iscsi_session_failure(struct iscsi_session *session,
enum iscsi_err err)
{
- struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn;
struct device *dev;
unsigned long flags;
@@ -1095,10 +1129,10 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
* Check for iSCSI window and take care of CmdSN wrap-around
*/
if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) {
- debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u "
- "CmdSN %u/%u\n", session->exp_cmdsn,
- session->max_cmdsn, session->cmdsn,
- session->queued_cmdsn);
+ ISCSI_DBG_SESSION(session, "iSCSI CmdSN closed. ExpCmdSn "
+ "%u MaxCmdSN %u CmdSN %u/%u\n",
+ session->exp_cmdsn, session->max_cmdsn,
+ session->cmdsn, session->queued_cmdsn);
return -ENOSPC;
}
return 0;
@@ -1133,7 +1167,7 @@ void iscsi_requeue_task(struct iscsi_task *task)
struct iscsi_conn *conn = task->conn;
list_move_tail(&task->running, &conn->requeue);
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
}
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1152,7 +1186,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
spin_lock_bh(&conn->session->lock);
if (unlikely(conn->suspend_tx)) {
- debug_scsi("conn %d Tx suspended!\n", conn->id);
+ ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n");
spin_unlock_bh(&conn->session->lock);
return -ENODATA;
}
@@ -1386,7 +1420,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto prepd_reject;
}
} else
- scsi_queue_work(session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
session->queued_cmdsn++;
spin_unlock(&session->lock);
@@ -1398,7 +1432,8 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
iscsi_complete_command(task);
reject:
spin_unlock(&session->lock);
- debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+ ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
+ sc->cmnd[0], reason);
spin_lock(host->host_lock);
return SCSI_MLQUEUE_TARGET_BUSY;
@@ -1407,7 +1442,8 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
iscsi_complete_command(task);
fault:
spin_unlock(&session->lock);
- debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
+ ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
+ sc->cmnd[0], reason);
if (!scsi_bidi_cmnd(sc))
scsi_set_resid(sc, scsi_bufflen(sc));
else {
@@ -1422,8 +1458,6 @@ EXPORT_SYMBOL_GPL(iscsi_queuecommand);
int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
{
- if (depth > ISCSI_MAX_CMD_PER_LUN)
- depth = ISCSI_MAX_CMD_PER_LUN;
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
return sdev->queue_depth;
}
@@ -1457,8 +1491,10 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc)
spin_lock_bh(&session->lock);
if (session->state == ISCSI_STATE_TERMINATE) {
failed:
- debug_scsi("failing target reset: session terminated "
- "[CID %d age %d]\n", conn->id, session->age);
+ iscsi_session_printk(KERN_INFO, session,
+ "failing target reset: Could not log "
+ "back into target [age %d]\n",
+ session->age);
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
return FAILED;
@@ -1472,7 +1508,7 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc)
*/
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- debug_scsi("iscsi_eh_target_reset wait for relogin\n");
+ ISCSI_DBG_SESSION(session, "wait for relogin\n");
wait_event_interruptible(conn->ehwait,
session->state == ISCSI_STATE_TERMINATE ||
session->state == ISCSI_STATE_LOGGED_IN ||
@@ -1501,7 +1537,7 @@ static void iscsi_tmf_timedout(unsigned long data)
spin_lock(&session->lock);
if (conn->tmf_state == TMF_QUEUED) {
conn->tmf_state = TMF_TIMEDOUT;
- debug_scsi("tmf timedout\n");
+ ISCSI_DBG_SESSION(session, "tmf timedout\n");
/* unblock eh_abort() */
wake_up(&conn->ehwait);
}
@@ -1521,7 +1557,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
spin_unlock_bh(&session->lock);
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
spin_lock_bh(&session->lock);
- debug_scsi("tmf exec failure\n");
+ ISCSI_DBG_SESSION(session, "tmf exec failure\n");
return -EPERM;
}
conn->tmfcmd_pdus_cnt++;
@@ -1529,7 +1565,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
conn->tmf_timer.function = iscsi_tmf_timedout;
conn->tmf_timer.data = (unsigned long)conn;
add_timer(&conn->tmf_timer);
- debug_scsi("tmf set timeout\n");
+ ISCSI_DBG_SESSION(session, "tmf set timeout\n");
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
@@ -1567,22 +1603,27 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
{
struct iscsi_task *task, *tmp;
- if (conn->task && (conn->task->sc->device->lun == lun || lun == -1))
- conn->task = NULL;
+ if (conn->task) {
+ if (lun == -1 ||
+ (conn->task->sc && conn->task->sc->device->lun == lun))
+ conn->task = NULL;
+ }
/* flush pending */
list_for_each_entry_safe(task, tmp, &conn->xmitqueue, running) {
if (lun == task->sc->device->lun || lun == -1) {
- debug_scsi("failing pending sc %p itt 0x%x\n",
- task->sc, task->itt);
+ ISCSI_DBG_SESSION(conn->session,
+ "failing pending sc %p itt 0x%x\n",
+ task->sc, task->itt);
fail_command(conn, task, error << 16);
}
}
list_for_each_entry_safe(task, tmp, &conn->requeue, running) {
if (lun == task->sc->device->lun || lun == -1) {
- debug_scsi("failing requeued sc %p itt 0x%x\n",
- task->sc, task->itt);
+ ISCSI_DBG_SESSION(conn->session,
+ "failing requeued sc %p itt 0x%x\n",
+ task->sc, task->itt);
fail_command(conn, task, error << 16);
}
}
@@ -1590,8 +1631,9 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
/* fail all other running */
list_for_each_entry_safe(task, tmp, &conn->run_list, running) {
if (lun == task->sc->device->lun || lun == -1) {
- debug_scsi("failing in progress sc %p itt 0x%x\n",
- task->sc, task->itt);
+ ISCSI_DBG_SESSION(conn->session,
+ "failing in progress sc %p itt 0x%x\n",
+ task->sc, task->itt);
fail_command(conn, task, error << 16);
}
}
@@ -1599,9 +1641,12 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
void iscsi_suspend_tx(struct iscsi_conn *conn)
{
+ struct Scsi_Host *shost = conn->session->host;
+ struct iscsi_host *ihost = shost_priv(shost);
+
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
- scsi_flush_work(conn->session->host);
+ flush_workqueue(ihost->workq);
}
EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
@@ -1609,7 +1654,7 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
{
clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ iscsi_conn_queue_work(conn);
}
static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
@@ -1622,7 +1667,7 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
cls_session = starget_to_session(scsi_target(scmd->device));
session = cls_session->dd_data;
- debug_scsi("scsi cmd %p timedout\n", scmd);
+ ISCSI_DBG_SESSION(session, "scsi cmd %p timedout\n", scmd);
spin_lock(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
@@ -1662,8 +1707,8 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
rc = BLK_EH_RESET_TIMER;
done:
spin_unlock(&session->lock);
- debug_scsi("return %s\n", rc == BLK_EH_RESET_TIMER ?
- "timer reset" : "nh");
+ ISCSI_DBG_SESSION(session, "return %s\n", rc == BLK_EH_RESET_TIMER ?
+ "timer reset" : "nh");
return rc;
}
@@ -1697,13 +1742,13 @@ static void iscsi_check_transport_timeouts(unsigned long data)
if (time_before_eq(last_recv + recv_timeout, jiffies)) {
/* send a ping to try to provoke some traffic */
- debug_scsi("Sending nopout as ping on conn %p\n", conn);
+ ISCSI_DBG_CONN(conn, "Sending nopout as ping\n");
iscsi_send_nopout(conn, NULL);
next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
} else
next_timeout = last_recv + recv_timeout;
- debug_scsi("Setting next tmo %lu\n", next_timeout);
+ ISCSI_DBG_CONN(conn, "Setting next tmo %lu\n", next_timeout);
mod_timer(&conn->transport_timer, next_timeout);
done:
spin_unlock(&session->lock);
@@ -1740,7 +1785,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
* got the command.
*/
if (!sc->SCp.ptr) {
- debug_scsi("sc never reached iscsi layer or it completed.\n");
+ ISCSI_DBG_SESSION(session, "sc never reached iscsi layer or "
+ "it completed.\n");
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
@@ -1762,11 +1808,13 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
age = session->age;
task = (struct iscsi_task *)sc->SCp.ptr;
- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, task->itt);
+ ISCSI_DBG_SESSION(session, "aborting [sc %p itt 0x%x]\n",
+ sc, task->itt);
/* task completed before time out */
if (!task->sc) {
- debug_scsi("sc completed while abort in progress\n");
+ ISCSI_DBG_SESSION(session, "sc completed while abort in "
+ "progress\n");
goto success;
}
@@ -1815,7 +1863,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
if (!sc->SCp.ptr) {
conn->tmf_state = TMF_INITIAL;
/* task completed before tmf abort response */
- debug_scsi("sc completed while abort in progress\n");
+ ISCSI_DBG_SESSION(session, "sc completed while abort "
+ "in progress\n");
goto success;
}
/* fall through */
@@ -1827,15 +1876,16 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
success:
spin_unlock_bh(&session->lock);
success_unlocked:
- debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, task->itt);
+ ISCSI_DBG_SESSION(session, "abort success [sc %p itt 0x%x]\n",
+ sc, task->itt);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
failed:
spin_unlock_bh(&session->lock);
failed_unlocked:
- debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
- task ? task->itt : 0);
+ ISCSI_DBG_SESSION(session, "abort failed [sc %p itt 0x%x]\n", sc,
+ task ? task->itt : 0);
mutex_unlock(&session->eh_mutex);
return FAILED;
}
@@ -1862,7 +1912,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
+ ISCSI_DBG_SESSION(session, "LU Reset [sc %p lun %u]\n",
+ sc, sc->device->lun);
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
@@ -1916,8 +1967,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
unlock:
spin_unlock_bh(&session->lock);
done:
- debug_scsi("iscsi_eh_device_reset %s\n",
- rc == SUCCESS ? "SUCCESS" : "FAILED");
+ ISCSI_DBG_SESSION(session, "dev reset result = %s\n",
+ rc == SUCCESS ? "SUCCESS" : "FAILED");
mutex_unlock(&session->eh_mutex);
return rc;
}
@@ -1944,7 +1995,7 @@ iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size)
num_arrays++;
q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL);
if (q->pool == NULL)
- goto enomem;
+ return -ENOMEM;
q->queue = kfifo_init((void*)q->pool, max * sizeof(void*),
GFP_KERNEL, NULL);
@@ -1979,8 +2030,7 @@ void iscsi_pool_free(struct iscsi_pool *q)
for (i = 0; i < q->max; i++)
kfree(q->pool[i]);
- if (q->pool)
- kfree(q->pool);
+ kfree(q->pool);
kfree(q->queue);
}
EXPORT_SYMBOL_GPL(iscsi_pool_free);
@@ -1998,6 +2048,9 @@ int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)
if (!shost->can_queue)
shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX;
+ if (!shost->cmd_per_lun)
+ shost->cmd_per_lun = ISCSI_DEF_CMD_PER_LUN;
+
if (!shost->transportt->eh_timed_out)
shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
return scsi_add_host(shost, pdev);
@@ -2008,13 +2061,13 @@ EXPORT_SYMBOL_GPL(iscsi_host_add);
* iscsi_host_alloc - allocate a host and driver data
* @sht: scsi host template
* @dd_data_size: driver host data size
- * @qdepth: default device queue depth
+ * @xmit_can_sleep: bool indicating if LLD will queue IO from a work queue
*
* This should be called by partial offload and software iscsi drivers.
* To access the driver specific memory use the iscsi_host_priv() macro.
*/
struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
- int dd_data_size, uint16_t qdepth)
+ int dd_data_size, bool xmit_can_sleep)
{
struct Scsi_Host *shost;
struct iscsi_host *ihost;
@@ -2022,28 +2075,31 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size);
if (!shost)
return NULL;
+ ihost = shost_priv(shost);
- if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
- if (qdepth != 0)
- printk(KERN_ERR "iscsi: invalid queue depth of %d. "
- "Queue depth must be between 1 and %d.\n",
- qdepth, ISCSI_MAX_CMD_PER_LUN);
- qdepth = ISCSI_DEF_CMD_PER_LUN;
+ if (xmit_can_sleep) {
+ snprintf(ihost->workq_name, sizeof(ihost->workq_name),
+ "iscsi_q_%d", shost->host_no);
+ ihost->workq = create_singlethread_workqueue(ihost->workq_name);
+ if (!ihost->workq)
+ goto free_host;
}
- shost->cmd_per_lun = qdepth;
- ihost = shost_priv(shost);
spin_lock_init(&ihost->lock);
ihost->state = ISCSI_HOST_SETUP;
ihost->num_sessions = 0;
init_waitqueue_head(&ihost->session_removal_wq);
return shost;
+
+free_host:
+ scsi_host_put(shost);
+ return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_host_alloc);
static void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session)
{
- iscsi_session_failure(cls_session, ISCSI_ERR_INVALID_HOST);
+ iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_INVALID_HOST);
}
/**
@@ -2069,6 +2125,8 @@ void iscsi_host_remove(struct Scsi_Host *shost)
flush_signals(current);
scsi_remove_host(shost);
+ if (ihost->workq)
+ destroy_workqueue(ihost->workq);
}
EXPORT_SYMBOL_GPL(iscsi_host_remove);
@@ -2467,14 +2525,16 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
/* handle pending */
list_for_each_entry_safe(task, tmp, &conn->mgmtqueue, running) {
- debug_scsi("flushing pending mgmt task itt 0x%x\n", task->itt);
+ ISCSI_DBG_SESSION(session, "flushing pending mgmt task "
+ "itt 0x%x\n", task->itt);
/* release ref from prep task */
__iscsi_put_task(task);
}
/* handle running */
list_for_each_entry_safe(task, tmp, &conn->mgmt_run_list, running) {
- debug_scsi("flushing running mgmt task itt 0x%x\n", task->itt);
+ ISCSI_DBG_SESSION(session, "flushing running mgmt task "
+ "itt 0x%x\n", task->itt);
/* release ref from prep task */
__iscsi_put_task(task);
}
@@ -2524,7 +2584,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
conn->datadgst_en = 0;
if (session->state == ISCSI_STATE_IN_RECOVERY &&
old_stop_stage != STOP_CONN_RECOVER) {
- debug_scsi("blocking session\n");
+ ISCSI_DBG_SESSION(session, "blocking session\n");
iscsi_block_session(session->cls_session);
}
}
diff --git a/trunk/drivers/scsi/libiscsi_tcp.c b/trunk/drivers/scsi/libiscsi_tcp.c
index e7705d3532c9..91f8ce4d8d08 100644
--- a/trunk/drivers/scsi/libiscsi_tcp.c
+++ b/trunk/drivers/scsi/libiscsi_tcp.c
@@ -49,13 +49,21 @@ MODULE_AUTHOR("Mike Christie , "
"Alex Aizman ");
MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
-#undef DEBUG_TCP
-#ifdef DEBUG_TCP
-#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
-#else
-#define debug_tcp(fmt...)
-#endif
+static int iscsi_dbg_libtcp;
+module_param_named(debug_libiscsi_tcp, iscsi_dbg_libtcp, int,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_libiscsi_tcp, "Turn on debugging for libiscsi_tcp "
+ "module. Set to 1 to turn on, and zero to turn off. Default "
+ "is off.");
+
+#define ISCSI_DBG_TCP(_conn, dbg_fmt, arg...) \
+ do { \
+ if (iscsi_dbg_libtcp) \
+ iscsi_conn_printk(KERN_INFO, _conn, \
+ "%s " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_segment *segment);
@@ -123,18 +131,13 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
if (page_count(sg_page(sg)) >= 1 && !recv)
return;
- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
- segment);
segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
}
void iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
{
- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
-
if (segment->sg_mapped) {
- debug_tcp("iscsi_tcp_segment_unmap valid\n");
kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
segment->sg_mapped = NULL;
segment->data = NULL;
@@ -180,8 +183,9 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
struct scatterlist sg;
unsigned int pad;
- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
- segment->size, recv ? "recv" : "xmit");
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copied %u %u size %u %s\n",
+ segment->copied, copied, segment->size,
+ recv ? "recv" : "xmit");
if (segment->hash && copied) {
/*
* If a segment is kmapd we must unmap it before sending
@@ -214,8 +218,8 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
iscsi_tcp_segment_unmap(segment);
/* Do we have more scatterlist entries? */
- debug_tcp("total copied %u total size %u\n", segment->total_copied,
- segment->total_size);
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "total copied %u total size %u\n",
+ segment->total_copied, segment->total_size);
if (segment->total_copied < segment->total_size) {
/* Proceed to the next entry in the scatterlist. */
iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
@@ -229,7 +233,8 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) {
pad = iscsi_padding(segment->total_copied);
if (pad != 0) {
- debug_tcp("consume %d pad bytes\n", pad);
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn,
+ "consume %d pad bytes\n", pad);
segment->total_size += pad;
segment->size = pad;
segment->data = segment->padbuf;
@@ -278,13 +283,13 @@ iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) {
if (copied == len) {
- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
- len);
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn,
+ "copied %d bytes\n", len);
break;
}
copy = min(len - copied, segment->size - segment->copied);
- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copying %d\n", copy);
memcpy(segment->data + segment->copied, ptr + copied, copy);
copied += copy;
}
@@ -311,7 +316,7 @@ iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
if (memcmp(segment->recv_digest, segment->digest,
segment->digest_len)) {
- debug_scsi("digest mismatch\n");
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "digest mismatch\n");
return 0;
}
@@ -355,12 +360,8 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment,
struct scatterlist *sg;
unsigned int i;
- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
- offset, size);
__iscsi_segment_init(segment, size, done, hash);
for_each_sg(sg_list, sg, sg_count, i) {
- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
- sg->offset);
if (offset < sg->length) {
iscsi_tcp_segment_init_sg(segment, sg, offset);
return 0;
@@ -382,8 +383,9 @@ EXPORT_SYMBOL_GPL(iscsi_segment_seek_sg);
*/
void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
{
- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+ ISCSI_DBG_TCP(tcp_conn->iscsi_conn,
+ "(%s)\n", tcp_conn->iscsi_conn->hdrdgst_en ?
+ "digest enabled" : "digest disabled");
iscsi_segment_init_linear(&tcp_conn->in.segment,
tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
iscsi_tcp_hdr_recv_done, NULL);
@@ -446,7 +448,7 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
- debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
+ ISCSI_DBG_TCP(task->conn, "pending r2t dropped\n");
}
r2t = tcp_task->r2t;
@@ -476,8 +478,8 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
return 0;
if (tcp_task->exp_datasn != datasn) {
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
- __func__, tcp_task->exp_datasn, datasn);
+ ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->datasn(%d)"
+ "\n", tcp_task->exp_datasn, datasn);
return ISCSI_ERR_DATASN;
}
@@ -485,9 +487,9 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
tcp_task->data_offset = be32_to_cpu(rhdr->offset);
if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
- __func__, tcp_task->data_offset,
- tcp_conn->in.datalen, total_in_length);
+ ISCSI_DBG_TCP(conn, "data_offset(%d) + data_len(%d) > "
+ "total_length_in(%d)\n", tcp_task->data_offset,
+ tcp_conn->in.datalen, total_in_length);
return ISCSI_ERR_DATA_OFFSET;
}
@@ -518,8 +520,8 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
}
if (tcp_task->exp_datasn != r2tsn){
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
- __func__, tcp_task->exp_datasn, r2tsn);
+ ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+ tcp_task->exp_datasn, r2tsn);
return ISCSI_ERR_R2TSN;
}
@@ -552,9 +554,9 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
}
if (r2t->data_length > session->max_burst)
- debug_scsi("invalid R2T with data len %u and max burst %u."
- "Attempting to execute request.\n",
- r2t->data_length, session->max_burst);
+ ISCSI_DBG_TCP(conn, "invalid R2T with data len %u and max "
+ "burst %u. Attempting to execute request.\n",
+ r2t->data_length, session->max_burst);
r2t->data_offset = be32_to_cpu(rhdr->data_offset);
if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
@@ -641,8 +643,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
if (rc)
return rc;
- debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
- opcode, ahslen, tcp_conn->in.datalen);
+ ISCSI_DBG_TCP(conn, "opcode 0x%x ahslen %d datalen %d\n",
+ opcode, ahslen, tcp_conn->in.datalen);
switch(opcode) {
case ISCSI_OP_SCSI_DATA_IN:
@@ -674,10 +676,10 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
!(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
rx_hash = tcp_conn->rx_hash;
- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
- "datalen=%d)\n", tcp_conn,
- tcp_task->data_offset,
- tcp_conn->in.datalen);
+ ISCSI_DBG_TCP(conn, "iscsi_tcp_begin_data_in( "
+ "offset=%d, datalen=%d)\n",
+ tcp_task->data_offset,
+ tcp_conn->in.datalen);
rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
sdb->table.sgl,
sdb->table.nents,
@@ -854,10 +856,10 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
unsigned int consumed = 0;
int rc = 0;
- debug_tcp("in %d bytes\n", skb->len - offset);
+ ISCSI_DBG_TCP(conn, "in %d bytes\n", skb->len - offset);
if (unlikely(conn->suspend_rx)) {
- debug_tcp("conn %d Rx suspended!\n", conn->id);
+ ISCSI_DBG_TCP(conn, "Rx suspended!\n");
*status = ISCSI_TCP_SUSPENDED;
return 0;
}
@@ -874,15 +876,16 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
avail = skb_seq_read(consumed, &ptr, &seq);
if (avail == 0) {
- debug_tcp("no more data avail. Consumed %d\n",
- consumed);
+ ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n",
+ consumed);
*status = ISCSI_TCP_SKB_DONE;
skb_abort_seq_read(&seq);
goto skb_done;
}
BUG_ON(segment->copied >= segment->size);
- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+ ISCSI_DBG_TCP(conn, "skb %p ptr=%p avail=%u\n", skb, ptr,
+ avail);
rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
BUG_ON(rc == 0);
consumed += rc;
@@ -895,11 +898,11 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
segment_done:
*status = ISCSI_TCP_SEGMENT_DONE;
- debug_tcp("segment done\n");
+ ISCSI_DBG_TCP(conn, "segment done\n");
rc = segment->done(tcp_conn, segment);
if (rc != 0) {
*status = ISCSI_TCP_CONN_ERR;
- debug_tcp("Error receiving PDU, errno=%d\n", rc);
+ ISCSI_DBG_TCP(conn, "Error receiving PDU, errno=%d\n", rc);
iscsi_conn_failure(conn, rc);
return 0;
}
@@ -929,8 +932,7 @@ int iscsi_tcp_task_init(struct iscsi_task *task)
* mgmt tasks do not have a scatterlist since they come
* in from the iscsi interface.
*/
- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
- task->itt);
+ ISCSI_DBG_TCP(conn, "mtask deq [itt 0x%x]\n", task->itt);
return conn->session->tt->init_pdu(task, 0, task->data_count);
}
@@ -939,9 +941,8 @@ int iscsi_tcp_task_init(struct iscsi_task *task)
tcp_task->exp_datasn = 0;
/* Prepare PDU, optionally w/ immediate data */
- debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
- conn->id, task->itt, task->imm_count,
- task->unsol_r2t.data_length);
+ ISCSI_DBG_TCP(conn, "task deq [itt 0x%x imm %d unsol %d]\n",
+ task->itt, task->imm_count, task->unsol_r2t.data_length);
err = conn->session->tt->init_pdu(task, 0, task->imm_count);
if (err)
@@ -965,7 +966,8 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
r2t = tcp_task->r2t;
/* Continue with this R2T? */
if (r2t->data_length <= r2t->sent) {
- debug_scsi(" done with r2t %p\n", r2t);
+ ISCSI_DBG_TCP(task->conn,
+ " done with r2t %p\n", r2t);
__kfifo_put(tcp_task->r2tpool.queue,
(void *)&tcp_task->r2t,
sizeof(void *));
@@ -1019,7 +1021,7 @@ int iscsi_tcp_task_xmit(struct iscsi_task *task)
r2t = iscsi_tcp_get_curr_r2t(task);
if (r2t == NULL) {
/* Waiting for more R2Ts to arrive. */
- debug_tcp("no R2Ts yet\n");
+ ISCSI_DBG_TCP(conn, "no R2Ts yet\n");
return 0;
}
@@ -1028,9 +1030,9 @@ int iscsi_tcp_task_xmit(struct iscsi_task *task)
return rc;
iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr);
- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
- r2t, r2t->datasn - 1, task->hdr->itt,
- r2t->data_offset + r2t->sent, r2t->data_count);
+ ISCSI_DBG_TCP(conn, "sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+ r2t, r2t->datasn - 1, task->hdr->itt,
+ r2t->data_offset + r2t->sent, r2t->data_count);
rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent,
r2t->data_count);
diff --git a/trunk/drivers/scsi/lpfc/lpfc_debugfs.c b/trunk/drivers/scsi/lpfc/lpfc_debugfs.c
index b615eda361d5..81cdcf46c471 100644
--- a/trunk/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/trunk/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1132,7 +1132,7 @@ lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
}
#undef lpfc_debugfs_op_disc_trc
-static struct file_operations lpfc_debugfs_op_disc_trc = {
+static const struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_disc_trc_open,
.llseek = lpfc_debugfs_lseek,
@@ -1141,7 +1141,7 @@ static struct file_operations lpfc_debugfs_op_disc_trc = {
};
#undef lpfc_debugfs_op_nodelist
-static struct file_operations lpfc_debugfs_op_nodelist = {
+static const struct file_operations lpfc_debugfs_op_nodelist = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_nodelist_open,
.llseek = lpfc_debugfs_lseek,
@@ -1150,7 +1150,7 @@ static struct file_operations lpfc_debugfs_op_nodelist = {
};
#undef lpfc_debugfs_op_hbqinfo
-static struct file_operations lpfc_debugfs_op_hbqinfo = {
+static const struct file_operations lpfc_debugfs_op_hbqinfo = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_hbqinfo_open,
.llseek = lpfc_debugfs_lseek,
@@ -1159,7 +1159,7 @@ static struct file_operations lpfc_debugfs_op_hbqinfo = {
};
#undef lpfc_debugfs_op_dumpHBASlim
-static struct file_operations lpfc_debugfs_op_dumpHBASlim = {
+static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_dumpHBASlim_open,
.llseek = lpfc_debugfs_lseek,
@@ -1168,7 +1168,7 @@ static struct file_operations lpfc_debugfs_op_dumpHBASlim = {
};
#undef lpfc_debugfs_op_dumpHostSlim
-static struct file_operations lpfc_debugfs_op_dumpHostSlim = {
+static const struct file_operations lpfc_debugfs_op_dumpHostSlim = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_dumpHostSlim_open,
.llseek = lpfc_debugfs_lseek,
@@ -1177,7 +1177,7 @@ static struct file_operations lpfc_debugfs_op_dumpHostSlim = {
};
#undef lpfc_debugfs_op_dumpData
-static struct file_operations lpfc_debugfs_op_dumpData = {
+static const struct file_operations lpfc_debugfs_op_dumpData = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_dumpData_open,
.llseek = lpfc_debugfs_lseek,
@@ -1187,7 +1187,7 @@ static struct file_operations lpfc_debugfs_op_dumpData = {
};
#undef lpfc_debugfs_op_dumpDif
-static struct file_operations lpfc_debugfs_op_dumpDif = {
+static const struct file_operations lpfc_debugfs_op_dumpDif = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_dumpDif_open,
.llseek = lpfc_debugfs_lseek,
@@ -1197,7 +1197,7 @@ static struct file_operations lpfc_debugfs_op_dumpDif = {
};
#undef lpfc_debugfs_op_slow_ring_trc
-static struct file_operations lpfc_debugfs_op_slow_ring_trc = {
+static const struct file_operations lpfc_debugfs_op_slow_ring_trc = {
.owner = THIS_MODULE,
.open = lpfc_debugfs_slow_ring_trc_open,
.llseek = lpfc_debugfs_lseek,
diff --git a/trunk/drivers/scsi/lpfc/lpfc_scsi.c b/trunk/drivers/scsi/lpfc/lpfc_scsi.c
index b103b6ed4970..b1bd3fc7bae8 100644
--- a/trunk/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/trunk/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1357,7 +1357,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
0x10, 0x1);
- cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_guard_err_cnt++;
printk(KERN_ERR "BLKGRD: guard_tag error\n");
@@ -1368,7 +1368,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
0x10, 0x3);
- cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_reftag_err_cnt++;
@@ -1380,7 +1380,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
0x10, 0x2);
- cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_apptag_err_cnt++;
diff --git a/trunk/drivers/scsi/mpt2sas/Kconfig b/trunk/drivers/scsi/mpt2sas/Kconfig
new file mode 100644
index 000000000000..4a86855c23b3
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/Kconfig
@@ -0,0 +1,66 @@
+#
+# Kernel configuration file for the MPT2SAS
+#
+# This code is based on drivers/scsi/mpt2sas/Kconfig
+# Copyright (C) 2007-2008 LSI Corporation
+# (mailto:DL-MPTFusionLinux@lsi.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.
+
+# NO WARRANTY
+# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+# solely responsible for determining the appropriateness of using and
+# distributing the Program and assumes all risks associated with its
+# exercise of rights under this Agreement, including but not limited to
+# the risks and costs of program errors, damage to or loss of data,
+# programs or equipment, and unavailability or interruption of operations.
+
+# DISCLAIMER OF LIABILITY
+# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+config SCSI_MPT2SAS
+ tristate "LSI MPT Fusion SAS 2.0 Device Driver"
+ depends on PCI && SCSI
+ select SCSI_SAS_ATTRS
+ ---help---
+ This driver supports PCI-Express SAS 6Gb/s Host Adapters.
+
+config SCSI_MPT2SAS_MAX_SGE
+ int "LSI MPT Fusion Max number of SG Entries (16 - 128)"
+ depends on PCI && SCSI && SCSI_MPT2SAS
+ default "128"
+ range 16 128
+ ---help---
+ This option allows you to specify the maximum number of scatter-
+ gather entries per I/O. The driver default is 128, which matches
+ SAFE_PHYS_SEGMENTS. However, it may decreased down to 16.
+ Decreasing this parameter will reduce memory requirements
+ on a per controller instance.
+
+config SCSI_MPT2SAS_LOGGING
+ bool "LSI MPT Fusion logging facility"
+ depends on PCI && SCSI && SCSI_MPT2SAS
+ ---help---
+ This turns on a logging facility.
diff --git a/trunk/drivers/scsi/mpt2sas/Makefile b/trunk/drivers/scsi/mpt2sas/Makefile
new file mode 100644
index 000000000000..728f0475711d
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/Makefile
@@ -0,0 +1,7 @@
+# mpt2sas makefile
+obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o
+mpt2sas-y += mpt2sas_base.o \
+ mpt2sas_config.o \
+ mpt2sas_scsih.o \
+ mpt2sas_transport.o \
+ mpt2sas_ctl.o
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2.h
new file mode 100644
index 000000000000..7bb2ece8b2e4
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2.h
@@ -0,0 +1,1067 @@
+/*
+ * Copyright (c) 2000-2009 LSI Corporation.
+ *
+ *
+ * Name: mpi2.h
+ * Title: MPI Message independent structures and definitions
+ * including System Interface Register Set and
+ * scatter/gather formats.
+ * Creation Date: June 21, 2006
+ *
+ * mpi2.h Version: 02.00.11
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved ReplyPostHostIndex register to offset 0x6C of the
+ * MPI2_SYSTEM_INTERFACE_REGS and modified the define for
+ * MPI2_REPLY_POST_HOST_INDEX_OFFSET.
+ * Added union of request descriptors.
+ * Added union of reply descriptors.
+ * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_VERSION_02_00.
+ * Fixed the size of the FunctionDependent5 field in the
+ * MPI2_DEFAULT_REPLY structure.
+ * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Removed the MPI-defined Fault Codes and extended the
+ * product specific codes up to 0xEFFF.
+ * Added a sixth key value for the WriteSequence register
+ * and changed the flush value to 0x0.
+ * Added message function codes for Diagnostic Buffer Post
+ * and Diagnsotic Release.
+ * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED
+ * Moved MPI2_VERSION_UNION from mpi2_ioc.h.
+ * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added #defines for marking a reply descriptor as unused.
+ * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved LUN field defines from mpi2_init.h.
+ * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_H
+#define MPI2_H
+
+
+/*****************************************************************************
+*
+* MPI Version Definitions
+*
+*****************************************************************************/
+
+#define MPI2_VERSION_MAJOR (0x02)
+#define MPI2_VERSION_MINOR (0x00)
+#define MPI2_VERSION_MAJOR_MASK (0xFF00)
+#define MPI2_VERSION_MAJOR_SHIFT (8)
+#define MPI2_VERSION_MINOR_MASK (0x00FF)
+#define MPI2_VERSION_MINOR_SHIFT (0)
+#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI2_VERSION_MINOR)
+
+#define MPI2_VERSION_02_00 (0x0200)
+
+/* versioning for this MPI header set */
+#define MPI2_HEADER_VERSION_UNIT (0x0B)
+#define MPI2_HEADER_VERSION_DEV (0x00)
+#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
+#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
+#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF)
+#define MPI2_HEADER_VERSION_DEV_SHIFT (0)
+#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | MPI2_HEADER_VERSION_DEV)
+
+
+/*****************************************************************************
+*
+* IOC State Definitions
+*
+*****************************************************************************/
+
+#define MPI2_IOC_STATE_RESET (0x00000000)
+#define MPI2_IOC_STATE_READY (0x10000000)
+#define MPI2_IOC_STATE_OPERATIONAL (0x20000000)
+#define MPI2_IOC_STATE_FAULT (0x40000000)
+
+#define MPI2_IOC_STATE_MASK (0xF0000000)
+#define MPI2_IOC_STATE_SHIFT (28)
+
+/* Fault state range for prodcut specific codes */
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000)
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF)
+
+
+/*****************************************************************************
+*
+* System Interface Register Definitions
+*
+*****************************************************************************/
+
+typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS
+{
+ U32 Doorbell; /* 0x00 */
+ U32 WriteSequence; /* 0x04 */
+ U32 HostDiagnostic; /* 0x08 */
+ U32 Reserved1; /* 0x0C */
+ U32 DiagRWData; /* 0x10 */
+ U32 DiagRWAddressLow; /* 0x14 */
+ U32 DiagRWAddressHigh; /* 0x18 */
+ U32 Reserved2[5]; /* 0x1C */
+ U32 HostInterruptStatus; /* 0x30 */
+ U32 HostInterruptMask; /* 0x34 */
+ U32 DCRData; /* 0x38 */
+ U32 DCRAddress; /* 0x3C */
+ U32 Reserved3[2]; /* 0x40 */
+ U32 ReplyFreeHostIndex; /* 0x48 */
+ U32 Reserved4[8]; /* 0x4C */
+ U32 ReplyPostHostIndex; /* 0x6C */
+ U32 Reserved5; /* 0x70 */
+ U32 HCBSize; /* 0x74 */
+ U32 HCBAddressLow; /* 0x78 */
+ U32 HCBAddressHigh; /* 0x7C */
+ U32 Reserved6[16]; /* 0x80 */
+ U32 RequestDescriptorPostLow; /* 0xC0 */
+ U32 RequestDescriptorPostHigh; /* 0xC4 */
+ U32 Reserved7[14]; /* 0xC8 */
+} MPI2_SYSTEM_INTERFACE_REGS, MPI2_POINTER PTR_MPI2_SYSTEM_INTERFACE_REGS,
+ Mpi2SystemInterfaceRegs_t, MPI2_POINTER pMpi2SystemInterfaceRegs_t;
+
+/*
+ * Defines for working with the Doorbell register.
+ */
+#define MPI2_DOORBELL_OFFSET (0x00000000)
+
+/* IOC --> System values */
+#define MPI2_DOORBELL_USED (0x08000000)
+#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000)
+#define MPI2_DOORBELL_WHO_INIT_SHIFT (24)
+#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF)
+#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF)
+
+/* System --> IOC values */
+#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000)
+#define MPI2_DOORBELL_FUNCTION_SHIFT (24)
+#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000)
+#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16)
+
+
+/*
+ * Defines for the WriteSequence register
+ */
+#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004)
+#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F)
+#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0)
+#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF)
+#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4)
+#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB)
+#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2)
+#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7)
+#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
+
+/*
+ * Defines for the HostDiagnostic register
+ */
+#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008)
+
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800)
+
+#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400)
+#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200)
+#define MPI2_DIAG_HCB_MODE (0x00000100)
+#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080)
+#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040)
+#define MPI2_DIAG_RESET_HISTORY (0x00000020)
+#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010)
+#define MPI2_DIAG_RESET_ADAPTER (0x00000004)
+#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002)
+
+/*
+ * Offsets for DiagRWData and address
+ */
+#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010)
+#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014)
+#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018)
+
+/*
+ * Defines for the HostInterruptStatus register
+ */
+#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030)
+#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000)
+#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS
+#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000)
+#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008)
+#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001)
+#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS
+
+/*
+ * Defines for the HostInterruptMask register
+ */
+#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034)
+#define MPI2_HIM_RESET_IRQ_MASK (0x40000000)
+#define MPI2_HIM_REPLY_INT_MASK (0x00000008)
+#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK
+#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001)
+#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK
+
+/*
+ * Offsets for DCRData and address
+ */
+#define MPI2_DCR_DATA_OFFSET (0x00000038)
+#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C)
+
+/*
+ * Offset for the Reply Free Queue
+ */
+#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048)
+
+/*
+ * Offset for the Reply Descriptor Post Queue
+ */
+#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
+
+/*
+ * Defines for the HCBSize and address
+ */
+#define MPI2_HCB_SIZE_OFFSET (0x00000074)
+#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000)
+#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001)
+
+#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078)
+#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C)
+
+/*
+ * Offsets for the Request Queue
+ */
+#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0)
+#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4)
+
+
+/*****************************************************************************
+*
+* Message Descriptors
+*
+*****************************************************************************/
+
+/* Request Descriptors */
+
+/* Default Request Descriptor */
+typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR
+{
+ U8 RequestFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U16 LMID; /* 0x04 */
+ U16 DescriptorTypeDependent; /* 0x06 */
+} MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ Mpi2DefaultRequestDescriptor_t, MPI2_POINTER pMpi2DefaultRequestDescriptor_t;
+
+/* defines for the RequestFlags field */
+#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02)
+#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06)
+#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08)
+
+#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01)
+
+
+/* High Priority Request Descriptor */
+typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR
+{
+ U8 RequestFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U16 LMID; /* 0x04 */
+ U16 Reserved1; /* 0x06 */
+} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ Mpi2HighPriorityRequestDescriptor_t,
+ MPI2_POINTER pMpi2HighPriorityRequestDescriptor_t;
+
+
+/* SCSI IO Request Descriptor */
+typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR
+{
+ U8 RequestFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U16 LMID; /* 0x04 */
+ U16 DevHandle; /* 0x06 */
+} MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ Mpi2SCSIIORequestDescriptor_t, MPI2_POINTER pMpi2SCSIIORequestDescriptor_t;
+
+
+/* SCSI Target Request Descriptor */
+typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR
+{
+ U8 RequestFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U16 LMID; /* 0x04 */
+ U16 IoIndex; /* 0x06 */
+} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ Mpi2SCSITargetRequestDescriptor_t,
+ MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t;
+
+/* union of Request Descriptors */
+typedef union _MPI2_REQUEST_DESCRIPTOR_UNION
+{
+ MPI2_DEFAULT_REQUEST_DESCRIPTOR Default;
+ MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority;
+ MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO;
+ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget;
+ U64 Words;
+} MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION,
+ Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t;
+
+
+/* Reply Descriptors */
+
+/* Default Reply Descriptor */
+typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR
+{
+ U8 ReplyFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 DescriptorTypeDependent1; /* 0x02 */
+ U32 DescriptorTypeDependent2; /* 0x04 */
+} MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR,
+ Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t;
+
+/* defines for the ReplyFlags field */
+#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F)
+#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00)
+#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03)
+#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F)
+
+/* values for marking a reply descriptor as unused */
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF)
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF)
+
+/* Address Reply Descriptor */
+typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR
+{
+ U8 ReplyFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U32 ReplyFrameAddress; /* 0x04 */
+} MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR,
+ Mpi2AddressReplyDescriptor_t, MPI2_POINTER pMpi2AddressReplyDescriptor_t;
+
+#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00)
+
+
+/* SCSI IO Success Reply Descriptor */
+typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR
+{
+ U8 ReplyFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U16 TaskTag; /* 0x04 */
+ U16 DevHandle; /* 0x06 */
+} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2SCSIIOSuccessReplyDescriptor_t,
+ MPI2_POINTER pMpi2SCSIIOSuccessReplyDescriptor_t;
+
+
+/* TargetAssist Success Reply Descriptor */
+typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR
+{
+ U8 ReplyFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U16 SMID; /* 0x02 */
+ U8 SequenceNumber; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 IoIndex; /* 0x06 */
+} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2TargetAssistSuccessReplyDescriptor_t,
+ MPI2_POINTER pMpi2TargetAssistSuccessReplyDescriptor_t;
+
+
+/* Target Command Buffer Reply Descriptor */
+typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR
+{
+ U8 ReplyFlags; /* 0x00 */
+ U8 VF_ID; /* 0x01 */
+ U8 VP_ID; /* 0x02 */
+ U8 Flags; /* 0x03 */
+ U16 InitiatorDevHandle; /* 0x04 */
+ U16 IoIndex; /* 0x06 */
+} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ MPI2_POINTER PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ Mpi2TargetCommandBufferReplyDescriptor_t,
+ MPI2_POINTER pMpi2TargetCommandBufferReplyDescriptor_t;
+
+/* defines for Flags field */
+#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F)
+
+
+/* union of Reply Descriptors */
+typedef union _MPI2_REPLY_DESCRIPTORS_UNION
+{
+ MPI2_DEFAULT_REPLY_DESCRIPTOR Default;
+ MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply;
+ MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess;
+ MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess;
+ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
+ U64 Words;
+} MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION,
+ Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t;
+
+
+
+/*****************************************************************************
+*
+* Message Functions
+* 0x80 -> 0x8F reserved for private message use per product
+*
+*
+*****************************************************************************/
+
+#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */
+#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) /* SCSI Task Management */
+#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */
+#define MPI2_FUNCTION_IOC_FACTS (0x03) /* IOC Facts */
+#define MPI2_FUNCTION_CONFIG (0x04) /* Configuration */
+#define MPI2_FUNCTION_PORT_FACTS (0x05) /* Port Facts */
+#define MPI2_FUNCTION_PORT_ENABLE (0x06) /* Port Enable */
+#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) /* Event Notification */
+#define MPI2_FUNCTION_EVENT_ACK (0x08) /* Event Acknowledge */
+#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) /* FW Download */
+#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) /* Target Assist */
+#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) /* Target Status Send */
+#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) /* Target Mode Abort */
+#define MPI2_FUNCTION_FW_UPLOAD (0x12) /* FW Upload */
+#define MPI2_FUNCTION_RAID_ACTION (0x15) /* RAID Action */
+#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) /* SCSI IO RAID Passthrough */
+#define MPI2_FUNCTION_TOOLBOX (0x17) /* Toolbox */
+#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) /* SCSI Enclosure Processor */
+#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) /* SMP Passthrough */
+#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) /* SAS IO Unit Control */
+#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) /* SATA Passthrough */
+#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) /* Diagnostic Buffer Post */
+#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */
+#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */
+#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */
+
+
+
+/* Doorbell functions */
+#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40)
+/* #define MPI2_FUNCTION_IO_UNIT_RESET (0x41) */
+#define MPI2_FUNCTION_HANDSHAKE (0x42)
+
+
+/*****************************************************************************
+*
+* IOC Status Values
+*
+*****************************************************************************/
+
+/* mask for IOCStatus status value */
+#define MPI2_IOCSTATUS_MASK (0x7FFF)
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SUCCESS (0x0000)
+#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001)
+#define MPI2_IOCSTATUS_BUSY (0x0002)
+#define MPI2_IOCSTATUS_INVALID_SGL (0x0003)
+#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004)
+#define MPI2_IOCSTATUS_INVALID_VPID (0x0005)
+#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
+#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007)
+#define MPI2_IOCSTATUS_INVALID_STATE (0x0008)
+#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009)
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
+#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
+#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
+#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
+#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
+#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
+#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
+#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
+#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
+#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
+#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
+#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A)
+#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B)
+#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C)
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D)
+#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E)
+#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F)
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
+#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063)
+#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
+#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
+#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A)
+#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D)
+#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E)
+#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F)
+#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
+#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
+#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0)
+
+
+/****************************************************************************
+* IOCStatus flag to indicate that log info is available
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000)
+
+/****************************************************************************
+* IOCLogInfo Types
+****************************************************************************/
+
+#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000)
+#define MPI2_IOCLOGINFO_TYPE_SHIFT (28)
+#define MPI2_IOCLOGINFO_TYPE_NONE (0x0)
+#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1)
+#define MPI2_IOCLOGINFO_TYPE_FC (0x2)
+#define MPI2_IOCLOGINFO_TYPE_SAS (0x3)
+#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4)
+#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF)
+
+
+/*****************************************************************************
+*
+* Standard Message Structures
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Request Message Header for all request messages
+****************************************************************************/
+
+typedef struct _MPI2_REQUEST_HEADER
+{
+ U16 FunctionDependent1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 FunctionDependent2; /* 0x04 */
+ U8 FunctionDependent3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+} MPI2_REQUEST_HEADER, MPI2_POINTER PTR_MPI2_REQUEST_HEADER,
+ MPI2RequestHeader_t, MPI2_POINTER pMPI2RequestHeader_t;
+
+
+/****************************************************************************
+* Default Reply
+****************************************************************************/
+
+typedef struct _MPI2_DEFAULT_REPLY
+{
+ U16 FunctionDependent1; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 FunctionDependent2; /* 0x04 */
+ U8 FunctionDependent3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U16 FunctionDependent5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_DEFAULT_REPLY, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY,
+ MPI2DefaultReply_t, MPI2_POINTER pMPI2DefaultReply_t;
+
+
+/* common version structure/union used in messages and configuration pages */
+
+typedef struct _MPI2_VERSION_STRUCT
+{
+ U8 Dev; /* 0x00 */
+ U8 Unit; /* 0x01 */
+ U8 Minor; /* 0x02 */
+ U8 Major; /* 0x03 */
+} MPI2_VERSION_STRUCT;
+
+typedef union _MPI2_VERSION_UNION
+{
+ MPI2_VERSION_STRUCT Struct;
+ U32 Word;
+} MPI2_VERSION_UNION;
+
+
+/* LUN field defines, common to many structures */
+#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_LEVEL_1_WORD (0xFF00)
+#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00)
+
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* MPI Simple Element structures
+****************************************************************************/
+
+typedef struct _MPI2_SGE_SIMPLE32
+{
+ U32 FlagsLength;
+ U32 Address;
+} MPI2_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_SGE_SIMPLE32,
+ Mpi2SGESimple32_t, MPI2_POINTER pMpi2SGESimple32_t;
+
+typedef struct _MPI2_SGE_SIMPLE64
+{
+ U32 FlagsLength;
+ U64 Address;
+} MPI2_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_SGE_SIMPLE64,
+ Mpi2SGESimple64_t, MPI2_POINTER pMpi2SGESimple64_t;
+
+typedef struct _MPI2_SGE_SIMPLE_UNION
+{
+ U32 FlagsLength;
+ union
+ {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_SIMPLE_UNION,
+ Mpi2SGESimpleUnion_t, MPI2_POINTER pMpi2SGESimpleUnion_t;
+
+
+/****************************************************************************
+* MPI Chain Element structures
+****************************************************************************/
+
+typedef struct _MPI2_SGE_CHAIN32
+{
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U32 Address;
+} MPI2_SGE_CHAIN32, MPI2_POINTER PTR_MPI2_SGE_CHAIN32,
+ Mpi2SGEChain32_t, MPI2_POINTER pMpi2SGEChain32_t;
+
+typedef struct _MPI2_SGE_CHAIN64
+{
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U64 Address;
+} MPI2_SGE_CHAIN64, MPI2_POINTER PTR_MPI2_SGE_CHAIN64,
+ Mpi2SGEChain64_t, MPI2_POINTER pMpi2SGEChain64_t;
+
+typedef struct _MPI2_SGE_CHAIN_UNION
+{
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ union
+ {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_SGE_CHAIN_UNION,
+ Mpi2SGEChainUnion_t, MPI2_POINTER pMpi2SGEChainUnion_t;
+
+
+/****************************************************************************
+* MPI Transaction Context Element structures
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANSACTION32
+{
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[1];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION32, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION32,
+ Mpi2SGETransaction32_t, MPI2_POINTER pMpi2SGETransaction32_t;
+
+typedef struct _MPI2_SGE_TRANSACTION64
+{
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[2];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION64, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION64,
+ Mpi2SGETransaction64_t, MPI2_POINTER pMpi2SGETransaction64_t;
+
+typedef struct _MPI2_SGE_TRANSACTION96
+{
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[3];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION96, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION96,
+ Mpi2SGETransaction96_t, MPI2_POINTER pMpi2SGETransaction96_t;
+
+typedef struct _MPI2_SGE_TRANSACTION128
+{
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[4];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION128, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION128,
+ Mpi2SGETransaction_t128, MPI2_POINTER pMpi2SGETransaction_t128;
+
+typedef struct _MPI2_SGE_TRANSACTION_UNION
+{
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ union
+ {
+ U32 TransactionContext32[1];
+ U32 TransactionContext64[2];
+ U32 TransactionContext96[3];
+ U32 TransactionContext128[4];
+ } u;
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION_UNION,
+ Mpi2SGETransactionUnion_t, MPI2_POINTER pMpi2SGETransactionUnion_t;
+
+
+/****************************************************************************
+* MPI SGE union for IO SGL's
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_IO_UNION
+{
+ union
+ {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_MPI_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_IO_UNION,
+ Mpi2MpiSGEIOUnion_t, MPI2_POINTER pMpi2MpiSGEIOUnion_t;
+
+
+/****************************************************************************
+* MPI SGE union for SGL's with Simple and Transaction elements
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION
+{
+ union
+ {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_SGE_TRANS_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANS_SIMPLE_UNION,
+ Mpi2SGETransSimpleUnion_t, MPI2_POINTER pMpi2SGETransSimpleUnion_t;
+
+
+/****************************************************************************
+* All MPI SGE types union
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_UNION
+{
+ union
+ {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_MPI_SGE_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_UNION,
+ Mpi2MpiSgeUnion_t, MPI2_POINTER pMpi2MpiSgeUnion_t;
+
+
+/****************************************************************************
+* MPI SGE field definition and masks
+****************************************************************************/
+
+/* Flags field bit definitions */
+
+#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80)
+#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40)
+#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30)
+#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08)
+#define MPI2_SGE_FLAGS_DIRECTION (0x04)
+#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02)
+#define MPI2_SGE_FLAGS_END_OF_LIST (0x01)
+
+#define MPI2_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF)
+#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF)
+
+/* Element Type */
+
+#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00)
+#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10)
+#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30)
+#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30)
+
+/* Address location */
+
+#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00)
+
+/* Direction */
+
+#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00)
+#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04)
+
+/* Address Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02)
+
+/* Context Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02)
+#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04)
+#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06)
+
+#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000)
+#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16)
+
+/****************************************************************************
+* MPI SGE operation Macros
+****************************************************************************/
+
+/* SIMPLE FlagsLength manipulations... */
+#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK)
+#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK)
+
+#define MPI2_SGE_SET_FLAGS_LENGTH(f,l) (MPI2_SGE_SET_FLAGS(f) | MPI2_SGE_LENGTH(l))
+
+#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_SGE_SET_FLAGS_LENGTH(f,l)
+
+/* CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_SGE_SET_FLAGS(f)
+#define MPI2_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_SGE_LENGTH(l)
+
+#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> MPI2_SGE_CHAIN_OFFSET_SHIFT)
+
+
+/*****************************************************************************
+*
+* Fusion-MPT IEEE Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IEEE Simple Element structures
+****************************************************************************/
+
+typedef struct _MPI2_IEEE_SGE_SIMPLE32
+{
+ U32 Address;
+ U32 FlagsLength;
+} MPI2_IEEE_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE32,
+ Mpi2IeeeSgeSimple32_t, MPI2_POINTER pMpi2IeeeSgeSimple32_t;
+
+typedef struct _MPI2_IEEE_SGE_SIMPLE64
+{
+ U64 Address;
+ U32 Length;
+ U16 Reserved1;
+ U8 Reserved2;
+ U8 Flags;
+} MPI2_IEEE_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE64,
+ Mpi2IeeeSgeSimple64_t, MPI2_POINTER pMpi2IeeeSgeSimple64_t;
+
+typedef union _MPI2_IEEE_SGE_SIMPLE_UNION
+{
+ MPI2_IEEE_SGE_SIMPLE32 Simple32;
+ MPI2_IEEE_SGE_SIMPLE64 Simple64;
+} MPI2_IEEE_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE_UNION,
+ Mpi2IeeeSgeSimpleUnion_t, MPI2_POINTER pMpi2IeeeSgeSimpleUnion_t;
+
+
+/****************************************************************************
+* IEEE Chain Element structures
+****************************************************************************/
+
+typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32;
+
+typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64;
+
+typedef union _MPI2_IEEE_SGE_CHAIN_UNION
+{
+ MPI2_IEEE_SGE_CHAIN32 Chain32;
+ MPI2_IEEE_SGE_CHAIN64 Chain64;
+} MPI2_IEEE_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_CHAIN_UNION,
+ Mpi2IeeeSgeChainUnion_t, MPI2_POINTER pMpi2IeeeSgeChainUnion_t;
+
+
+/****************************************************************************
+* All IEEE SGE types union
+****************************************************************************/
+
+typedef struct _MPI2_IEEE_SGE_UNION
+{
+ union
+ {
+ MPI2_IEEE_SGE_SIMPLE_UNION Simple;
+ MPI2_IEEE_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_IEEE_SGE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_UNION,
+ Mpi2IeeeSgeUnion_t, MPI2_POINTER pMpi2IeeeSgeUnion_t;
+
+
+/****************************************************************************
+* IEEE SGE field definitions and masks
+****************************************************************************/
+
+/* Flags field bit definitions */
+
+#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80)
+
+#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF)
+
+/* Element Type */
+
+#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00)
+#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80)
+
+/* Data Location Address Space */
+
+#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03)
+
+
+/****************************************************************************
+* IEEE SGE operation Macros
+****************************************************************************/
+
+/* SIMPLE FlagsLength manipulations... */
+#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) >> MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK)
+
+#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) | MPI2_IEEE32_SGE_LENGTH(l))
+
+#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f,l)
+
+/* CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_IEEE32_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_IEEE32_SGE_SET_FLAGS(f)
+#define MPI2_IEEE32_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_IEEE32_SGE_LENGTH(l)
+
+
+
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI/IEEE Scatter Gather Unions
+*
+*****************************************************************************/
+
+typedef union _MPI2_SIMPLE_SGE_UNION
+{
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+} MPI2_SIMPLE_SGE_UNION, MPI2_POINTER PTR_MPI2_SIMPLE_SGE_UNION,
+ Mpi2SimpleSgeUntion_t, MPI2_POINTER pMpi2SimpleSgeUntion_t;
+
+
+typedef union _MPI2_SGE_IO_UNION
+{
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_SGE_CHAIN_UNION MpiChain;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain;
+} MPI2_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_SGE_IO_UNION,
+ Mpi2SGEIOUnion_t, MPI2_POINTER pMpi2SGEIOUnion_t;
+
+
+/****************************************************************************
+*
+* Values for SGLFlags field, used in many request messages with an SGL
+*
+****************************************************************************/
+
+/* values for MPI SGL Data Location Address Space subfield */
+#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C)
+#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00)
+#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04)
+#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08)
+#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C)
+/* values for SGL Type subfield */
+#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03)
+#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02)
+
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
new file mode 100644
index 000000000000..2f27cf6d6c65
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
@@ -0,0 +1,2151 @@
+/*
+ * Copyright (c) 2000-2009 LSI Corporation.
+ *
+ *
+ * Name: mpi2_cnfg.h
+ * Title: MPI Configuration messages and pages
+ * Creation Date: November 10, 2006
+ *
+ * mpi2_cnfg.h Version: 02.00.10
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags.
+ * Added Manufacturing Page 11.
+ * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE
+ * define.
+ * 06-26-07 02.00.02 Adding generic structure for product-specific
+ * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS.
+ * Rework of BIOS Page 2 configuration page.
+ * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the
+ * forms.
+ * Added configuration pages IOC Page 8 and Driver
+ * Persistent Mapping Page 0.
+ * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated
+ * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1,
+ * RAID Physical Disk Pages 0 and 1, RAID Configuration
+ * Page 0).
+ * Added new value for AccessStatus field of SAS Device
+ * Page 0 (_SATA_NEEDS_INITIALIZATION).
+ * 10-31-07 02.00.04 Added missing SEPDevHandle field to
+ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for
+ * NVDATA.
+ * Modified IOC Page 7 to use masks and added field for
+ * SASBroadcastPrimitiveMasks.
+ * Added MPI2_CONFIG_PAGE_BIOS_4.
+ * Added MPI2_CONFIG_PAGE_LOG_0.
+ * 02-29-08 02.00.06 Modified various names to make them 32-character unique.
+ * Added SAS Device IDs.
+ * Updated Integrated RAID configuration pages including
+ * Manufacturing Page 4, IOC Page 6, and RAID Configuration
+ * Page 0.
+ * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA.
+ * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION.
+ * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING.
+ * Added missing MaxNumRoutedSasAddresses field to
+ * MPI2_CONFIG_PAGE_EXPANDER_0.
+ * Added SAS Port Page 0.
+ * Modified structure layout for
+ * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0.
+ * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use
+ * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array.
+ * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF
+ * to 0x000000FF.
+ * Added two new values for the Physical Disk Coercion Size
+ * bits in the Flags field of Manufacturing Page 4.
+ * Added product-specific Manufacturing pages 16 to 31.
+ * Modified Flags bits for controlling write cache on SATA
+ * drives in IO Unit Page 1.
+ * Added new bit to AdditionalControlFlags of SAS IO Unit
+ * Page 1 to control Invalid Topology Correction.
+ * Added additional defines for RAID Volume Page 0
+ * VolumeStatusFlags field.
+ * Modified meaning of RAID Volume Page 0 VolumeSettings
+ * define for auto-configure of hot-swap drives.
+ * Added SupportedPhysDisks field to RAID Volume Page 1 and
+ * added related defines.
+ * Added PhysDiskAttributes field (and related defines) to
+ * RAID Physical Disk Page 0.
+ * Added MPI2_SAS_PHYINFO_PHY_VACANT define.
+ * Added three new DiscoveryStatus bits for SAS IO Unit
+ * Page 0 and SAS Expander Page 0.
+ * Removed multiplexing information from SAS IO Unit pages.
+ * Added BootDeviceWaitTime field to SAS IO Unit Page 4.
+ * Removed Zone Address Resolved bit from PhyInfo and from
+ * Expander Page 0 Flags field.
+ * Added two new AccessStatus values to SAS Device Page 0
+ * for indicating routing problems. Added 3 reserved words
+ * to this page.
+ * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3.
+ * Inserted missing reserved field into structure for IOC
+ * Page 6.
+ * Added more pending task bits to RAID Volume Page 0
+ * VolumeStatusFlags defines.
+ * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define.
+ * Added a new DiscoveryStatus bit for SAS IO Unit Page 0
+ * and SAS Expander Page 0 to flag a downstream initiator
+ * when in simplified routing mode.
+ * Removed SATA Init Failure defines for DiscoveryStatus
+ * fields of SAS IO Unit Page 0 and SAS Expander Page 0.
+ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define.
+ * Added PortGroups, DmaGroup, and ControlGroup fields to
+ * SAS Device Page 0.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_CNFG_H
+#define MPI2_CNFG_H
+
+/*****************************************************************************
+* Configuration Page Header and defines
+*****************************************************************************/
+
+/* Config Page Header */
+typedef struct _MPI2_CONFIG_PAGE_HEADER
+{
+ U8 PageVersion; /* 0x00 */
+ U8 PageLength; /* 0x01 */
+ U8 PageNumber; /* 0x02 */
+ U8 PageType; /* 0x03 */
+} MPI2_CONFIG_PAGE_HEADER, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER,
+ Mpi2ConfigPageHeader_t, MPI2_POINTER pMpi2ConfigPageHeader_t;
+
+typedef union _MPI2_CONFIG_PAGE_HEADER_UNION
+{
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ U8 Bytes[4];
+ U16 Word16[2];
+ U32 Word32;
+} MPI2_CONFIG_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER_UNION,
+ Mpi2ConfigPageHeaderUnion, MPI2_POINTER pMpi2ConfigPageHeaderUnion;
+
+/* Extended Config Page Header */
+typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER
+{
+ U8 PageVersion; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 PageNumber; /* 0x02 */
+ U8 PageType; /* 0x03 */
+ U16 ExtPageLength; /* 0x04 */
+ U8 ExtPageType; /* 0x06 */
+ U8 Reserved2; /* 0x07 */
+} MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ MPI2_POINTER PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ Mpi2ConfigExtendedPageHeader_t, MPI2_POINTER pMpi2ConfigExtendedPageHeader_t;
+
+typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
+{
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext;
+ U8 Bytes[8];
+ U16 Word16[4];
+ U32 Word32[2];
+} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
+ Mpi2ConfigPageExtendedHeaderUnion, MPI2_POINTER pMpi2ConfigPageExtendedHeaderUnion;
+
+
+/* PageType field values */
+#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00)
+#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10)
+#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI2_CONFIG_PAGEATTR_MASK (0xF0)
+
+#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00)
+#define MPI2_CONFIG_PAGETYPE_IOC (0x01)
+#define MPI2_CONFIG_PAGETYPE_BIOS (0x02)
+#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08)
+#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09)
+#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A)
+#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F)
+#define MPI2_CONFIG_PAGETYPE_MASK (0x0F)
+
+#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF)
+
+
+/* ExtPageType field values */
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13)
+#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14)
+#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15)
+#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16)
+#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
+
+
+/*****************************************************************************
+* PageAddress defines
+*****************************************************************************/
+
+/* RAID Volume PageAddress format */
+#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/* RAID Physical Disk PageAddress format */
+#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000)
+#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000)
+#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000)
+
+#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF)
+#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF)
+
+
+/* SAS Expander PageAddress format */
+#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000)
+
+#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
+
+
+/* SAS Device PageAddress format */
+#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+
+#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/* SAS PHY PageAddress format */
+#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000)
+
+#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF)
+#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF)
+
+
+/* SAS Port PageAddress format */
+#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
+#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
+
+#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF)
+
+
+/* SAS Enclosure PageAddress format */
+#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/* RAID Configuration PageAddress format */
+#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000)
+#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000)
+#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000)
+
+#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF)
+
+
+/* Driver Persistent Mapping PageAddress format */
+#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000)
+
+#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000)
+#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16)
+#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF)
+
+
+/****************************************************************************
+* Configuration messages
+****************************************************************************/
+
+/* Configuration Request Message */
+typedef struct _MPI2_CONFIG_REQUEST
+{
+ U8 Action; /* 0x00 */
+ U8 SGLFlags; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 ExtPageLength; /* 0x04 */
+ U8 ExtPageType; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U32 Reserved2; /* 0x0C */
+ U32 Reserved3; /* 0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */
+ U32 PageAddress; /* 0x18 */
+ MPI2_SGE_IO_UNION PageBufferSGE; /* 0x1C */
+} MPI2_CONFIG_REQUEST, MPI2_POINTER PTR_MPI2_CONFIG_REQUEST,
+ Mpi2ConfigRequest_t, MPI2_POINTER pMpi2ConfigRequest_t;
+
+/* values for the Action field */
+#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00)
+#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02)
+#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04)
+#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05)
+#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06)
+#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07)
+
+/* values for SGLFlags field are in the SGL section of mpi2.h */
+
+
+/* Config Reply Message */
+typedef struct _MPI2_CONFIG_REPLY
+{
+ U8 Action; /* 0x00 */
+ U8 SGLFlags; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 ExtPageLength; /* 0x04 */
+ U8 ExtPageType; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U16 Reserved2; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */
+} MPI2_CONFIG_REPLY, MPI2_POINTER PTR_MPI2_CONFIG_REPLY,
+ Mpi2ConfigReply_t, MPI2_POINTER pMpi2ConfigReply_t;
+
+
+
+/*****************************************************************************
+*
+* C o n f i g u r a t i o n P a g e s
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Manufacturing Config pages
+****************************************************************************/
+
+#define MPI2_MFGPAGE_VENDORID_LSI (0x1000)
+
+/* SAS */
+#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070)
+#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072)
+#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074)
+#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076)
+#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077)
+#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
+#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+
+
+/* Manufacturing Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_0
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 ChipName[16]; /* 0x04 */
+ U8 ChipRevision[8]; /* 0x14 */
+ U8 BoardName[16]; /* 0x1C */
+ U8 BoardAssembly[16]; /* 0x2C */
+ U8 BoardTracerNumber[16]; /* 0x3C */
+} MPI2_CONFIG_PAGE_MAN_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_0,
+ Mpi2ManufacturingPage0_t, MPI2_POINTER pMpi2ManufacturingPage0_t;
+
+#define MPI2_MANUFACTURING0_PAGEVERSION (0x00)
+
+
+/* Manufacturing Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 VPD[256]; /* 0x04 */
+} MPI2_CONFIG_PAGE_MAN_1,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_1,
+ Mpi2ManufacturingPage1_t, MPI2_POINTER pMpi2ManufacturingPage1_t;
+
+#define MPI2_MANUFACTURING1_PAGEVERSION (0x00)
+
+
+typedef struct _MPI2_CHIP_REVISION_ID
+{
+ U16 DeviceID; /* 0x00 */
+ U8 PCIRevisionID; /* 0x02 */
+ U8 Reserved; /* 0x03 */
+} MPI2_CHIP_REVISION_ID, MPI2_POINTER PTR_MPI2_CHIP_REVISION_ID,
+ Mpi2ChipRevisionId_t, MPI2_POINTER pMpi2ChipRevisionId_t;
+
+
+/* Manufacturing Page 2 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS
+#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_2
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */
+ U32 HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 0x08 */
+} MPI2_CONFIG_PAGE_MAN_2,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_2,
+ Mpi2ManufacturingPage2_t, MPI2_POINTER pMpi2ManufacturingPage2_t;
+
+#define MPI2_MANUFACTURING2_PAGEVERSION (0x00)
+
+
+/* Manufacturing Page 3 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_3_INFO_WORDS
+#define MPI2_MAN_PAGE_3_INFO_WORDS (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_3
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */
+ U32 Info[MPI2_MAN_PAGE_3_INFO_WORDS];/* 0x08 */
+} MPI2_CONFIG_PAGE_MAN_3,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_3,
+ Mpi2ManufacturingPage3_t, MPI2_POINTER pMpi2ManufacturingPage3_t;
+
+#define MPI2_MANUFACTURING3_PAGEVERSION (0x00)
+
+
+/* Manufacturing Page 4 */
+
+typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS
+{
+ U8 PowerSaveFlags; /* 0x00 */
+ U8 InternalOperationsSleepTime; /* 0x01 */
+ U8 InternalOperationsRunTime; /* 0x02 */
+ U8 HostIdleTime; /* 0x03 */
+} MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ MPI2_POINTER PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ Mpi2ManPage4PwrSaveSettings_t, MPI2_POINTER pMpi2ManPage4PwrSaveSettings_t;
+
+/* defines for the PowerSaveFlags field */
+#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03)
+#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00)
+#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01)
+#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02)
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_4
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 Flags; /* 0x08 */
+ U8 InquirySize; /* 0x0C */
+ U8 Reserved2; /* 0x0D */
+ U16 Reserved3; /* 0x0E */
+ U8 InquiryData[56]; /* 0x10 */
+ U32 RAID0VolumeSettings; /* 0x48 */
+ U32 RAID1EVolumeSettings; /* 0x4C */
+ U32 RAID1VolumeSettings; /* 0x50 */
+ U32 RAID10VolumeSettings; /* 0x54 */
+ U32 Reserved4; /* 0x58 */
+ U32 Reserved5; /* 0x5C */
+ MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /* 0x60 */
+ U8 MaxOCEDisks; /* 0x64 */
+ U8 ResyncRate; /* 0x65 */
+ U16 DataScrubDuration; /* 0x66 */
+ U8 MaxHotSpares; /* 0x68 */
+ U8 MaxPhysDisksPerVol; /* 0x69 */
+ U8 MaxPhysDisks; /* 0x6A */
+ U8 MaxVolumes; /* 0x6B */
+} MPI2_CONFIG_PAGE_MAN_4,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_4,
+ Mpi2ManufacturingPage4_t, MPI2_POINTER pMpi2ManufacturingPage4_t;
+
+#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A)
+
+/* Manufacturing Page 4 Flags field */
+#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000)
+#define MPI2_MANPAGE4_METADATA_512MB (0x00000000)
+
+#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000)
+#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000)
+#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000)
+
+#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00)
+#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000)
+#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400)
+#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800)
+#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00)
+
+#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300)
+#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000)
+#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100)
+#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200)
+
+#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080)
+#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040)
+#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020)
+#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010)
+#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008)
+#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004)
+#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002)
+#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001)
+
+
+/* Manufacturing Page 5 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES
+#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_MANUFACTURING5_ENTRY
+{
+ U64 WWID; /* 0x00 */
+ U64 DeviceName; /* 0x08 */
+} MPI2_MANUFACTURING5_ENTRY, MPI2_POINTER PTR_MPI2_MANUFACTURING5_ENTRY,
+ Mpi2Manufacturing5Entry_t, MPI2_POINTER pMpi2Manufacturing5Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_5
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 NumPhys; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ U32 Reserved3; /* 0x08 */
+ U32 Reserved4; /* 0x0C */
+ MPI2_MANUFACTURING5_ENTRY Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/* 0x08 */
+} MPI2_CONFIG_PAGE_MAN_5,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_5,
+ Mpi2ManufacturingPage5_t, MPI2_POINTER pMpi2ManufacturingPage5_t;
+
+#define MPI2_MANUFACTURING5_PAGEVERSION (0x03)
+
+
+/* Manufacturing Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_6
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 ProductSpecificInfo;/* 0x04 */
+} MPI2_CONFIG_PAGE_MAN_6,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_6,
+ Mpi2ManufacturingPage6_t, MPI2_POINTER pMpi2ManufacturingPage6_t;
+
+#define MPI2_MANUFACTURING6_PAGEVERSION (0x00)
+
+
+/* Manufacturing Page 7 */
+
+typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO
+{
+ U32 Pinout; /* 0x00 */
+ U8 Connector[16]; /* 0x04 */
+ U8 Location; /* 0x14 */
+ U8 Reserved1; /* 0x15 */
+ U16 Slot; /* 0x16 */
+ U32 Reserved2; /* 0x18 */
+} MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
+ Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t;
+
+/* defines for the Pinout field */
+#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4 (0x00080000)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3 (0x00040000)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2 (0x00020000)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1 (0x00010000)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4 (0x00000800)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3 (0x00000400)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2 (0x00000200)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1 (0x00000100)
+#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x00000002)
+#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN (0x00000001)
+
+/* defines for the Location field */
+#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01)
+#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02)
+#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04)
+#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08)
+#define MPI2_MANPAGE7_LOCATION_AUTO (0x10)
+#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20)
+#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check NumPhys at runtime.
+ */
+#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX
+#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_7
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 Reserved2; /* 0x08 */
+ U32 Flags; /* 0x0C */
+ U8 EnclosureName[16]; /* 0x10 */
+ U8 NumPhys; /* 0x20 */
+ U8 Reserved3; /* 0x21 */
+ U16 Reserved4; /* 0x22 */
+ MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /* 0x24 */
+} MPI2_CONFIG_PAGE_MAN_7,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7,
+ Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t;
+
+#define MPI2_MANUFACTURING7_PAGEVERSION (0x00)
+
+/* defines for the Flags field */
+#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
+
+
+/*
+ * Generic structure to use for product-specific manufacturing pages
+ * (currently Manufacturing Page 8 through Manufacturing Page 31).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_PS
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 ProductSpecificInfo;/* 0x04 */
+} MPI2_CONFIG_PAGE_MAN_PS,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_PS,
+ Mpi2ManufacturingPagePS_t, MPI2_POINTER pMpi2ManufacturingPagePS_t;
+
+#define MPI2_MANUFACTURING8_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING9_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING10_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING11_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING12_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING13_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING14_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING15_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING16_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING17_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING18_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING19_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING20_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING21_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING22_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING23_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING24_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING25_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING26_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING27_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING28_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING29_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING30_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING31_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* IO Unit Config Pages
+****************************************************************************/
+
+/* IO Unit Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U64 UniqueValue; /* 0x04 */
+ MPI2_VERSION_UNION NvdataVersionDefault; /* 0x08 */
+ MPI2_VERSION_UNION NvdataVersionPersistent; /* 0x0A */
+} MPI2_CONFIG_PAGE_IO_UNIT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_0,
+ Mpi2IOUnitPage0_t, MPI2_POINTER pMpi2IOUnitPage0_t;
+
+#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02)
+
+
+/* IO Unit Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Flags; /* 0x04 */
+} MPI2_CONFIG_PAGE_IO_UNIT_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_1,
+ Mpi2IOUnitPage1_t, MPI2_POINTER pMpi2IOUnitPage1_t;
+
+#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04)
+
+/* IO Unit Page 1 Flags defines */
+#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
+#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
+#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
+#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400)
+#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100)
+#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040)
+#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
+#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004)
+#define MPI2_IOUNITPAGE1_MULTI_PATHING (0x00000002)
+#define MPI2_IOUNITPAGE1_SINGLE_PATHING (0x00000000)
+
+
+/* IO Unit Page 3 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
+#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 GPIOCount; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ U16 GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/* 0x08 */
+} MPI2_CONFIG_PAGE_IO_UNIT_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_3,
+ Mpi2IOUnitPage3_t, MPI2_POINTER pMpi2IOUnitPage3_t;
+
+#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01)
+
+/* defines for IO Unit Page 3 GPIOVal field */
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC)
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001)
+
+
+/****************************************************************************
+* IOC Config Pages
+****************************************************************************/
+
+/* IOC Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_0
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 Reserved2; /* 0x08 */
+ U16 VendorID; /* 0x0C */
+ U16 DeviceID; /* 0x0E */
+ U8 RevisionID; /* 0x10 */
+ U8 Reserved3; /* 0x11 */
+ U16 Reserved4; /* 0x12 */
+ U32 ClassCode; /* 0x14 */
+ U16 SubsystemVendorID; /* 0x18 */
+ U16 SubsystemID; /* 0x1A */
+} MPI2_CONFIG_PAGE_IOC_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_0,
+ Mpi2IOCPage0_t, MPI2_POINTER pMpi2IOCPage0_t;
+
+#define MPI2_IOCPAGE0_PAGEVERSION (0x02)
+
+
+/* IOC Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Flags; /* 0x04 */
+ U32 CoalescingTimeout; /* 0x08 */
+ U8 CoalescingDepth; /* 0x0C */
+ U8 PCISlotNum; /* 0x0D */
+ U8 PCIBusNum; /* 0x0E */
+ U8 PCIDomainSegment; /* 0x0F */
+ U32 Reserved1; /* 0x10 */
+ U32 Reserved2; /* 0x14 */
+} MPI2_CONFIG_PAGE_IOC_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_1,
+ Mpi2IOCPage1_t, MPI2_POINTER pMpi2IOCPage1_t;
+
+#define MPI2_IOCPAGE1_PAGEVERSION (0x05)
+
+/* defines for IOC Page 1 Flags field */
+#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001)
+
+#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF)
+
+/* IOC Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_6
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 CapabilitiesFlags; /* 0x04 */
+ U8 MaxDrivesRAID0; /* 0x08 */
+ U8 MaxDrivesRAID1; /* 0x09 */
+ U8 MaxDrivesRAID1E; /* 0x0A */
+ U8 MaxDrivesRAID10; /* 0x0B */
+ U8 MinDrivesRAID0; /* 0x0C */
+ U8 MinDrivesRAID1; /* 0x0D */
+ U8 MinDrivesRAID1E; /* 0x0E */
+ U8 MinDrivesRAID10; /* 0x0F */
+ U32 Reserved1; /* 0x10 */
+ U8 MaxGlobalHotSpares; /* 0x14 */
+ U8 MaxPhysDisks; /* 0x15 */
+ U8 MaxVolumes; /* 0x16 */
+ U8 MaxConfigs; /* 0x17 */
+ U8 MaxOCEDisks; /* 0x18 */
+ U8 Reserved2; /* 0x19 */
+ U16 Reserved3; /* 0x1A */
+ U32 SupportedStripeSizeMapRAID0; /* 0x1C */
+ U32 SupportedStripeSizeMapRAID1E; /* 0x20 */
+ U32 SupportedStripeSizeMapRAID10; /* 0x24 */
+ U32 Reserved4; /* 0x28 */
+ U32 Reserved5; /* 0x2C */
+ U16 DefaultMetadataSize; /* 0x30 */
+ U16 Reserved6; /* 0x32 */
+ U16 MaxBadBlockTableEntries; /* 0x34 */
+ U16 Reserved7; /* 0x36 */
+ U32 IRNvsramVersion; /* 0x38 */
+} MPI2_CONFIG_PAGE_IOC_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_6,
+ Mpi2IOCPage6_t, MPI2_POINTER pMpi2IOCPage6_t;
+
+#define MPI2_IOCPAGE6_PAGEVERSION (0x04)
+
+/* defines for IOC Page 6 CapabilitiesFlags */
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002)
+#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001)
+
+
+/* IOC Page 7 */
+
+#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_7
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/* 0x08 */
+ U16 SASBroadcastPrimitiveMasks; /* 0x18 */
+ U16 Reserved2; /* 0x1A */
+ U32 Reserved3; /* 0x1C */
+} MPI2_CONFIG_PAGE_IOC_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_7,
+ Mpi2IOCPage7_t, MPI2_POINTER pMpi2IOCPage7_t;
+
+#define MPI2_IOCPAGE7_PAGEVERSION (0x01)
+
+
+/* IOC Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_8
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 NumDevsPerEnclosure; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ U16 MaxPersistentEntries; /* 0x08 */
+ U16 MaxNumPhysicalMappedIDs; /* 0x0A */
+ U16 Flags; /* 0x0C */
+ U16 Reserved3; /* 0x0E */
+ U16 IRVolumeMappingFlags; /* 0x10 */
+ U16 Reserved4; /* 0x12 */
+ U32 Reserved5; /* 0x14 */
+} MPI2_CONFIG_PAGE_IOC_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_8,
+ Mpi2IOCPage8_t, MPI2_POINTER pMpi2IOCPage8_t;
+
+#define MPI2_IOCPAGE8_PAGEVERSION (0x00)
+
+/* defines for IOC Page 8 Flags field */
+#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020)
+#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010)
+
+#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E)
+#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002)
+
+#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001)
+#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000)
+
+/* defines for IOC Page 8 IRVolumeMappingFlags */
+#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003)
+#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001)
+
+
+/****************************************************************************
+* BIOS Config Pages
+****************************************************************************/
+
+/* BIOS Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 BiosOptions; /* 0x04 */
+ U32 IOCSettings; /* 0x08 */
+ U32 Reserved1; /* 0x0C */
+ U32 DeviceSettings; /* 0x10 */
+ U16 NumberOfDevices; /* 0x14 */
+ U16 Reserved2; /* 0x16 */
+ U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */
+ U16 IOTimeoutSequential; /* 0x1A */
+ U16 IOTimeoutOther; /* 0x1C */
+ U16 IOTimeoutBlockDevicesRM; /* 0x1E */
+} MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1,
+ Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t;
+
+#define MPI2_BIOSPAGE1_PAGEVERSION (0x04)
+
+/* values for BIOS Page 1 BiosOptions field */
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001)
+
+/* values for BIOS Page 1 IOCSettings field */
+#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000)
+#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0)
+#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040)
+#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030)
+#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010)
+#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020)
+#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030)
+
+#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008)
+
+/* values for BIOS Page 1 DeviceSettings field */
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001)
+
+
+/* BIOS Page 2 */
+
+typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER
+{
+ U32 Reserved1; /* 0x00 */
+ U32 Reserved2; /* 0x04 */
+ U32 Reserved3; /* 0x08 */
+ U32 Reserved4; /* 0x0C */
+ U32 Reserved5; /* 0x10 */
+ U32 Reserved6; /* 0x14 */
+} MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ Mpi2BootDeviceAdapterOrder_t, MPI2_POINTER pMpi2BootDeviceAdapterOrder_t;
+
+typedef struct _MPI2_BOOT_DEVICE_SAS_WWID
+{
+ U64 SASAddress; /* 0x00 */
+ U8 LUN[8]; /* 0x08 */
+ U32 Reserved1; /* 0x10 */
+ U32 Reserved2; /* 0x14 */
+} MPI2_BOOT_DEVICE_SAS_WWID, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_SAS_WWID,
+ Mpi2BootDeviceSasWwid_t, MPI2_POINTER pMpi2BootDeviceSasWwid_t;
+
+typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT
+{
+ U64 EnclosureLogicalID; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ U16 SlotNumber; /* 0x10 */
+ U16 Reserved3; /* 0x12 */
+ U32 Reserved4; /* 0x14 */
+} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ Mpi2BootDeviceEnclosureSlot_t, MPI2_POINTER pMpi2BootDeviceEnclosureSlot_t;
+
+typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME
+{
+ U64 DeviceName; /* 0x00 */
+ U8 LUN[8]; /* 0x08 */
+ U32 Reserved1; /* 0x10 */
+ U32 Reserved2; /* 0x14 */
+} MPI2_BOOT_DEVICE_DEVICE_NAME, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_DEVICE_NAME,
+ Mpi2BootDeviceDeviceName_t, MPI2_POINTER pMpi2BootDeviceDeviceName_t;
+
+typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE
+{
+ MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder;
+ MPI2_BOOT_DEVICE_SAS_WWID SasWwid;
+ MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot;
+ MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName;
+} MPI2_BIOSPAGE2_BOOT_DEVICE, MPI2_POINTER PTR_MPI2_BIOSPAGE2_BOOT_DEVICE,
+ Mpi2BiosPage2BootDevice_t, MPI2_POINTER pMpi2BiosPage2BootDevice_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_2
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 Reserved2; /* 0x08 */
+ U32 Reserved3; /* 0x0C */
+ U32 Reserved4; /* 0x10 */
+ U32 Reserved5; /* 0x14 */
+ U32 Reserved6; /* 0x18 */
+ U8 ReqBootDeviceForm; /* 0x1C */
+ U8 Reserved7; /* 0x1D */
+ U16 Reserved8; /* 0x1E */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /* 0x20 */
+ U8 ReqAltBootDeviceForm; /* 0x38 */
+ U8 Reserved9; /* 0x39 */
+ U16 Reserved10; /* 0x3A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /* 0x3C */
+ U8 CurrentBootDeviceForm; /* 0x58 */
+ U8 Reserved11; /* 0x59 */
+ U16 Reserved12; /* 0x5A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /* 0x58 */
+} MPI2_CONFIG_PAGE_BIOS_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_2,
+ Mpi2BiosPage2_t, MPI2_POINTER pMpi2BiosPage2_t;
+
+#define MPI2_BIOSPAGE2_PAGEVERSION (0x04)
+
+/* values for BIOS Page 2 BootDeviceForm fields */
+#define MPI2_BIOSPAGE2_FORM_MASK (0x0F)
+#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00)
+#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05)
+#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06)
+#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07)
+
+
+/* BIOS Page 3 */
+
+typedef struct _MPI2_ADAPTER_INFO
+{
+ U8 PciBusNumber; /* 0x00 */
+ U8 PciDeviceAndFunctionNumber; /* 0x01 */
+ U16 AdapterFlags; /* 0x02 */
+} MPI2_ADAPTER_INFO, MPI2_POINTER PTR_MPI2_ADAPTER_INFO,
+ Mpi2AdapterInfo_t, MPI2_POINTER pMpi2AdapterInfo_t;
+
+#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001)
+#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002)
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_3
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U32 GlobalFlags; /* 0x04 */
+ U32 BiosVersion; /* 0x08 */
+ MPI2_ADAPTER_INFO AdapterOrder[4]; /* 0x0C */
+ U32 Reserved1; /* 0x1C */
+} MPI2_CONFIG_PAGE_BIOS_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_3,
+ Mpi2BiosPage3_t, MPI2_POINTER pMpi2BiosPage3_t;
+
+#define MPI2_BIOSPAGE3_PAGEVERSION (0x00)
+
+/* values for BIOS Page 3 GlobalFlags */
+#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002)
+#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004)
+#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010)
+
+#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0)
+#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040)
+
+
+/* BIOS Page 4 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES
+#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_BIOS4_ENTRY
+{
+ U64 ReassignmentWWID; /* 0x00 */
+ U64 ReassignmentDeviceName; /* 0x08 */
+} MPI2_BIOS4_ENTRY, MPI2_POINTER PTR_MPI2_BIOS4_ENTRY,
+ Mpi2MBios4Entry_t, MPI2_POINTER pMpi2Bios4Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_4
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 NumPhys; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ MPI2_BIOS4_ENTRY Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /* 0x08 */
+} MPI2_CONFIG_PAGE_BIOS_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_4,
+ Mpi2BiosPage4_t, MPI2_POINTER pMpi2BiosPage4_t;
+
+#define MPI2_BIOSPAGE4_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* RAID Volume Config Pages
+****************************************************************************/
+
+/* RAID Volume Page 0 */
+
+typedef struct _MPI2_RAIDVOL0_PHYS_DISK
+{
+ U8 RAIDSetNum; /* 0x00 */
+ U8 PhysDiskMap; /* 0x01 */
+ U8 PhysDiskNum; /* 0x02 */
+ U8 Reserved; /* 0x03 */
+} MPI2_RAIDVOL0_PHYS_DISK, MPI2_POINTER PTR_MPI2_RAIDVOL0_PHYS_DISK,
+ Mpi2RaidVol0PhysDisk_t, MPI2_POINTER pMpi2RaidVol0PhysDisk_t;
+
+/* defines for the PhysDiskMap field */
+#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAIDVOL0_SETTINGS
+{
+ U16 Settings; /* 0x00 */
+ U8 HotSparePool; /* 0x01 */
+ U8 Reserved; /* 0x02 */
+} MPI2_RAIDVOL0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDVOL0_SETTINGS,
+ Mpi2RaidVol0Settings_t, MPI2_POINTER pMpi2RaidVol0Settings_t;
+
+/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */
+#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01)
+#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02)
+#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04)
+#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08)
+#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10)
+#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20)
+#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40)
+#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80)
+
+/* RAID Volume Page 0 VolumeSettings defines */
+#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008)
+#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004)
+
+#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003)
+#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000)
+#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001)
+#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX
+#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U16 DevHandle; /* 0x04 */
+ U8 VolumeState; /* 0x06 */
+ U8 VolumeType; /* 0x07 */
+ U32 VolumeStatusFlags; /* 0x08 */
+ MPI2_RAIDVOL0_SETTINGS VolumeSettings; /* 0x0C */
+ U64 MaxLBA; /* 0x10 */
+ U32 StripeSize; /* 0x18 */
+ U16 BlockSize; /* 0x1C */
+ U16 Reserved1; /* 0x1E */
+ U8 SupportedPhysDisks; /* 0x20 */
+ U8 ResyncRate; /* 0x21 */
+ U16 DataScrubDuration; /* 0x22 */
+ U8 NumPhysDisks; /* 0x24 */
+ U8 Reserved2; /* 0x25 */
+ U8 Reserved3; /* 0x26 */
+ U8 InactiveStatus; /* 0x27 */
+ MPI2_RAIDVOL0_PHYS_DISK PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /* 0x28 */
+} MPI2_CONFIG_PAGE_RAID_VOL_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_0,
+ Mpi2RaidVolPage0_t, MPI2_POINTER pMpi2RaidVolPage0_t;
+
+#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A)
+
+/* values for RAID VolumeState */
+#define MPI2_RAID_VOL_STATE_MISSING (0x00)
+#define MPI2_RAID_VOL_STATE_FAILED (0x01)
+#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02)
+#define MPI2_RAID_VOL_STATE_ONLINE (0x03)
+#define MPI2_RAID_VOL_STATE_DEGRADED (0x04)
+#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05)
+
+/* values for RAID VolumeType */
+#define MPI2_RAID_VOL_TYPE_RAID0 (0x00)
+#define MPI2_RAID_VOL_TYPE_RAID1E (0x01)
+#define MPI2_RAID_VOL_TYPE_RAID1 (0x02)
+#define MPI2_RAID_VOL_TYPE_RAID10 (0x05)
+#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF)
+
+/* values for RAID Volume Page 0 VolumeStatusFlags field */
+#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004)
+#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001)
+
+/* values for RAID Volume Page 0 SupportedPhysDisks field */
+#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08)
+#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04)
+#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02)
+#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01)
+
+/* values for RAID Volume Page 0 InactiveStatus field */
+#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00)
+#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01)
+#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03)
+#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05)
+#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06)
+
+
+/* RAID Volume Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U16 DevHandle; /* 0x04 */
+ U16 Reserved0; /* 0x06 */
+ U8 GUID[24]; /* 0x08 */
+ U8 Name[16]; /* 0x20 */
+ U64 WWID; /* 0x30 */
+ U32 Reserved1; /* 0x38 */
+ U32 Reserved2; /* 0x3C */
+} MPI2_CONFIG_PAGE_RAID_VOL_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_1,
+ Mpi2RaidVolPage1_t, MPI2_POINTER pMpi2RaidVolPage1_t;
+
+#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03)
+
+
+/****************************************************************************
+* RAID Physical Disk Config Pages
+****************************************************************************/
+
+/* RAID Physical Disk Page 0 */
+
+typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS
+{
+ U16 Reserved1; /* 0x00 */
+ U8 HotSparePool; /* 0x02 */
+ U8 Reserved2; /* 0x03 */
+} MPI2_RAIDPHYSDISK0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_SETTINGS,
+ Mpi2RaidPhysDisk0Settings_t, MPI2_POINTER pMpi2RaidPhysDisk0Settings_t;
+
+/* use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */
+
+typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA
+{
+ U8 VendorID[8]; /* 0x00 */
+ U8 ProductID[16]; /* 0x08 */
+ U8 ProductRevLevel[4]; /* 0x18 */
+ U8 SerialNum[32]; /* 0x1C */
+} MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ Mpi2RaidPhysDisk0InquiryData_t, MPI2_POINTER pMpi2RaidPhysDisk0InquiryData_t;
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U16 DevHandle; /* 0x04 */
+ U8 Reserved1; /* 0x06 */
+ U8 PhysDiskNum; /* 0x07 */
+ MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /* 0x10 */
+ U32 Reserved3; /* 0x4C */
+ U8 PhysDiskState; /* 0x50 */
+ U8 OfflineReason; /* 0x51 */
+ U8 IncompatibleReason; /* 0x52 */
+ U8 PhysDiskAttributes; /* 0x53 */
+ U32 PhysDiskStatusFlags; /* 0x54 */
+ U64 DeviceMaxLBA; /* 0x58 */
+ U64 HostMaxLBA; /* 0x60 */
+ U64 CoercedMaxLBA; /* 0x68 */
+ U16 BlockSize; /* 0x70 */
+ U16 Reserved5; /* 0x72 */
+ U32 Reserved6; /* 0x74 */
+} MPI2_CONFIG_PAGE_RD_PDISK_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_0,
+ Mpi2RaidPhysDiskPage0_t, MPI2_POINTER pMpi2RaidPhysDiskPage0_t;
+
+#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05)
+
+/* PhysDiskState defines */
+#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00)
+#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01)
+#define MPI2_RAID_PD_STATE_OFFLINE (0x02)
+#define MPI2_RAID_PD_STATE_ONLINE (0x03)
+#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04)
+#define MPI2_RAID_PD_STATE_DEGRADED (0x05)
+#define MPI2_RAID_PD_STATE_REBUILDING (0x06)
+#define MPI2_RAID_PD_STATE_OPTIMAL (0x07)
+
+/* OfflineReason defines */
+#define MPI2_PHYSDISK0_ONLINE (0x00)
+#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03)
+#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04)
+#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06)
+#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF)
+
+/* IncompatibleReason defines */
+#define MPI2_PHYSDISK0_COMPATIBLE (0x00)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF)
+
+/* PhysDiskAttributes defines */
+#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08)
+#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04)
+#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02)
+#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01)
+
+/* PhysDiskStatusFlags defines */
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020)
+#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000)
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008)
+#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004)
+#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001)
+
+
+/* RAID Physical Disk Page 1 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength or NumPhysDiskPaths at runtime.
+ */
+#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX
+#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1)
+#endif
+
+typedef struct _MPI2_RAIDPHYSDISK1_PATH
+{
+ U16 DevHandle; /* 0x00 */
+ U16 Reserved1; /* 0x02 */
+ U64 WWID; /* 0x04 */
+ U64 OwnerWWID; /* 0x0C */
+ U8 OwnerIdentifier; /* 0x14 */
+ U8 Reserved2; /* 0x15 */
+ U16 Flags; /* 0x16 */
+} MPI2_RAIDPHYSDISK1_PATH, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK1_PATH,
+ Mpi2RaidPhysDisk1Path_t, MPI2_POINTER pMpi2RaidPhysDisk1Path_t;
+
+/* RAID Physical Disk Page 1 Physical Disk Path Flags field defines */
+#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004)
+#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002)
+#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001)
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
+{
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U8 NumPhysDiskPaths; /* 0x04 */
+ U8 PhysDiskNum; /* 0x05 */
+ U16 Reserved1; /* 0x06 */
+ U32 Reserved2; /* 0x08 */
+ MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/* 0x0C */
+} MPI2_CONFIG_PAGE_RD_PDISK_1,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_1,
+ Mpi2RaidPhysDiskPage1_t, MPI2_POINTER pMpi2RaidPhysDiskPage1_t;
+
+#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* values for fields used by several types of SAS Config Pages
+****************************************************************************/
+
+/* values for NegotiatedLinkRates fields */
+#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0)
+#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4)
+#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F)
+/* link rates used for Negotiated Physical and Logical Link Rate */
+#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
+#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
+#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
+#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08)
+#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09)
+#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A)
+
+
+/* values for AttachedPhyInfo fields */
+#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
+#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
+#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
+
+#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F)
+#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
+#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
+#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
+#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
+#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
+#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
+#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
+#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
+
+
+/* values for PhyInfo fields */
+#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
+#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000)
+#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000)
+#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000)
+#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000)
+
+#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000)
+#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
+#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
+#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
+#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
+#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
+#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
+#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
+#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
+
+#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
+#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
+
+#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00)
+#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8)
+
+#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0)
+#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000)
+#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010)
+#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020)
+
+
+/* values for SAS ProgrammedLinkRate fields */
+#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0)
+#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A)
+
+
+/* values for SAS HwLinkRate fields */
+#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0)
+#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A)
+
+
+
+/****************************************************************************
+* SAS IO Unit Config Pages
+****************************************************************************/
+
+/* SAS IO Unit Page 0 */
+
+typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA
+{
+ U8 Port; /* 0x00 */
+ U8 PortFlags; /* 0x01 */
+ U8 PhyFlags; /* 0x02 */
+ U8 NegotiatedLinkRate; /* 0x03 */
+ U32 ControllerPhyDeviceInfo;/* 0x04 */
+ U16 AttachedDevHandle; /* 0x08 */
+ U16 ControllerDevHandle; /* 0x0A */
+ U32 DiscoveryStatus; /* 0x0C */
+ U32 Reserved; /* 0x10 */
+} MPI2_SAS_IO_UNIT0_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT0_PHY_DATA,
+ Mpi2SasIOUnit0PhyData_t, MPI2_POINTER pMpi2SasIOUnit0PhyData_t;
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT0_PHY_MAX
+#define MPI2_SAS_IOUNIT0_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U8 NumPhys; /* 0x0C */
+ U8 Reserved2; /* 0x0D */
+ U16 Reserved3; /* 0x0E */
+ MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /* 0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ Mpi2SasIOUnitPage0_t, MPI2_POINTER pMpi2SasIOUnitPage0_t;
+
+#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05)
+
+/* values for SAS IO Unit Page 0 PortFlags */
+#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08)
+#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01)
+
+/* values for SAS IO Unit Page 0 PhyFlags */
+#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10)
+#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+
+/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/* see mpi2_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */
+
+/* values for SAS IO Unit Page 0 DiscoveryStatus */
+#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001)
+
+
+/* SAS IO Unit Page 1 */
+
+typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA
+{
+ U8 Port; /* 0x00 */
+ U8 PortFlags; /* 0x01 */
+ U8 PhyFlags; /* 0x02 */
+ U8 MaxMinLinkRate; /* 0x03 */
+ U32 ControllerPhyDeviceInfo; /* 0x04 */
+ U16 MaxTargetPortConnectTime; /* 0x08 */
+ U16 Reserved1; /* 0x0A */
+} MPI2_SAS_IO_UNIT1_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT1_PHY_DATA,
+ Mpi2SasIOUnit1PhyData_t, MPI2_POINTER pMpi2SasIOUnit1PhyData_t;
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT1_PHY_MAX
+#define MPI2_SAS_IOUNIT1_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 ControlFlags; /* 0x08 */
+ U16 SASNarrowMaxQueueDepth; /* 0x0A */
+ U16 AdditionalControlFlags; /* 0x0C */
+ U16 SASWideMaxQueueDepth; /* 0x0E */
+ U8 NumPhys; /* 0x10 */
+ U8 SATAMaxQDepth; /* 0x11 */
+ U8 ReportDeviceMissingDelay; /* 0x12 */
+ U8 IODeviceMissingDelay; /* 0x13 */
+ MPI2_SAS_IO_UNIT1_PHY_DATA PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /* 0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ Mpi2SasIOUnitPage1_t, MPI2_POINTER pMpi2SasIOUnitPage1_t;
+
+#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09)
+
+/* values for SAS IO Unit Page 1 ControlFlags */
+#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
+
+#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600)
+#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2)
+
+#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
+#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
+#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
+#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
+#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001)
+
+/* values for SAS IO Unit Page 1 AdditionalControlFlags */
+#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
+#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
+#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
+#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
+#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
+#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
+#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
+#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
+
+/* defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */
+#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F)
+#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80)
+
+/* values for SAS IO Unit Page 1 PortFlags */
+#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
+
+/* values for SAS IO Unit Page 2 PhyFlags */
+#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10)
+#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+
+/* values for SAS IO Unit Page 0 MaxMinLinkRate */
+#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0)
+#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80)
+#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90)
+#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0)
+#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F)
+#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08)
+#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09)
+#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A)
+
+/* see mpi2_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */
+
+
+/* SAS IO Unit Page 4 */
+
+typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP
+{
+ U8 MaxTargetSpinup; /* 0x00 */
+ U8 SpinupDelay; /* 0x01 */
+ U16 Reserved1; /* 0x02 */
+} MPI2_SAS_IOUNIT4_SPINUP_GROUP, MPI2_POINTER PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP,
+ Mpi2SasIOUnit4SpinupGroup_t, MPI2_POINTER pMpi2SasIOUnit4SpinupGroup_t;
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * four and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT4_PHY_MAX
+#define MPI2_SAS_IOUNIT4_PHY_MAX (4)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ MPI2_SAS_IOUNIT4_SPINUP_GROUP SpinupGroupParameters[4]; /* 0x08 */
+ U32 Reserved1; /* 0x18 */
+ U32 Reserved2; /* 0x1C */
+ U32 Reserved3; /* 0x20 */
+ U8 BootDeviceWaitTime; /* 0x24 */
+ U8 Reserved4; /* 0x25 */
+ U16 Reserved5; /* 0x26 */
+ U8 NumPhys; /* 0x28 */
+ U8 PEInitialSpinupDelay; /* 0x29 */
+ U8 PEReplyDelay; /* 0x2A */
+ U8 Flags; /* 0x2B */
+ U8 PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /* 0x2C */
+} MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ Mpi2SasIOUnitPage4_t, MPI2_POINTER pMpi2SasIOUnitPage4_t;
+
+#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02)
+
+/* defines for Flags field */
+#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01)
+
+/* defines for PHY field */
+#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03)
+
+
+/****************************************************************************
+* SAS Expander Config Pages
+****************************************************************************/
+
+/* SAS Expander Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 PhysicalPort; /* 0x08 */
+ U8 ReportGenLength; /* 0x09 */
+ U16 EnclosureHandle; /* 0x0A */
+ U64 SASAddress; /* 0x0C */
+ U32 DiscoveryStatus; /* 0x14 */
+ U16 DevHandle; /* 0x18 */
+ U16 ParentDevHandle; /* 0x1A */
+ U16 ExpanderChangeCount; /* 0x1C */
+ U16 ExpanderRouteIndexes; /* 0x1E */
+ U8 NumPhys; /* 0x20 */
+ U8 SASLevel; /* 0x21 */
+ U16 Flags; /* 0x22 */
+ U16 STPBusInactivityTimeLimit; /* 0x24 */
+ U16 STPMaxConnectTimeLimit; /* 0x26 */
+ U16 STP_SMP_NexusLossTime; /* 0x28 */
+ U16 MaxNumRoutedSasAddresses; /* 0x2A */
+ U64 ActiveZoneManagerSASAddress;/* 0x2C */
+ U16 ZoneLockInactivityLimit; /* 0x34 */
+ U16 Reserved1; /* 0x36 */
+} MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0,
+ Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t;
+
+#define MPI2_SASEXPANDER0_PAGEVERSION (0x05)
+
+/* values for SAS Expander Page 0 DiscoveryStatus field */
+#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001)
+
+/* values for SAS Expander Page 0 Flags field */
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
+#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
+#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
+#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
+#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
+#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
+#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
+
+
+/* SAS Expander Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 PhysicalPort; /* 0x08 */
+ U8 Reserved1; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U8 NumPhys; /* 0x0C */
+ U8 Phy; /* 0x0D */
+ U16 NumTableEntriesProgrammed; /* 0x0E */
+ U8 ProgrammedLinkRate; /* 0x10 */
+ U8 HwLinkRate; /* 0x11 */
+ U16 AttachedDevHandle; /* 0x12 */
+ U32 PhyInfo; /* 0x14 */
+ U32 AttachedDeviceInfo; /* 0x18 */
+ U16 ExpanderDevHandle; /* 0x1C */
+ U8 ChangeCount; /* 0x1E */
+ U8 NegotiatedLinkRate; /* 0x1F */
+ U8 PhyIdentifier; /* 0x20 */
+ U8 AttachedPhyIdentifier; /* 0x21 */
+ U8 Reserved3; /* 0x22 */
+ U8 DiscoveryInfo; /* 0x23 */
+ U32 AttachedPhyInfo; /* 0x24 */
+ U8 ZoneGroup; /* 0x28 */
+ U8 SelfConfigStatus; /* 0x29 */
+ U16 Reserved4; /* 0x2A */
+} MPI2_CONFIG_PAGE_EXPANDER_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_1,
+ Mpi2ExpanderPage1_t, MPI2_POINTER pMpi2ExpanderPage1_t;
+
+#define MPI2_SASEXPANDER1_PAGEVERSION (0x02)
+
+/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+/* see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines used for the AttachedDeviceInfo field */
+
+/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+/* values for SAS Expander Page 1 DiscoveryInfo field */
+#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
+#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
+#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
+
+
+/****************************************************************************
+* SAS Device Config Pages
+****************************************************************************/
+
+/* SAS Device Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 Slot; /* 0x08 */
+ U16 EnclosureHandle; /* 0x0A */
+ U64 SASAddress; /* 0x0C */
+ U16 ParentDevHandle; /* 0x14 */
+ U8 PhyNum; /* 0x16 */
+ U8 AccessStatus; /* 0x17 */
+ U16 DevHandle; /* 0x18 */
+ U8 AttachedPhyIdentifier; /* 0x1A */
+ U8 ZoneGroup; /* 0x1B */
+ U32 DeviceInfo; /* 0x1C */
+ U16 Flags; /* 0x20 */
+ U8 PhysicalPort; /* 0x22 */
+ U8 MaxPortConnections; /* 0x23 */
+ U64 DeviceName; /* 0x24 */
+ U8 PortGroups; /* 0x2C */
+ U8 DmaGroup; /* 0x2D */
+ U8 ControlGroup; /* 0x2E */
+ U8 Reserved1; /* 0x2F */
+ U32 Reserved2; /* 0x30 */
+ U32 Reserved3; /* 0x34 */
+} MPI2_CONFIG_PAGE_SAS_DEV_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_0,
+ Mpi2SasDevicePage0_t, MPI2_POINTER pMpi2SasDevicePage0_t;
+
+#define MPI2_SASDEVICE0_PAGEVERSION (0x08)
+
+/* values for SAS Device Page 0 AccessStatus field */
+#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04)
+#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05)
+#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06)
+#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07)
+/* specific values for SATA Init failures */
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F)
+
+/* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
+
+/* values for SAS Device Page 0 Flags field */
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200)
+#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010)
+#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008)
+#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
+
+
+/* SAS Device Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U64 SASAddress; /* 0x0C */
+ U32 Reserved2; /* 0x14 */
+ U16 DevHandle; /* 0x18 */
+ U16 Reserved3; /* 0x1A */
+ U8 InitialRegDeviceFIS[20];/* 0x1C */
+} MPI2_CONFIG_PAGE_SAS_DEV_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_1,
+ Mpi2SasDevicePage1_t, MPI2_POINTER pMpi2SasDevicePage1_t;
+
+#define MPI2_SASDEVICE1_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* SAS PHY Config Pages
+****************************************************************************/
+
+/* SAS PHY Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 OwnerDevHandle; /* 0x08 */
+ U16 Reserved1; /* 0x0A */
+ U16 AttachedDevHandle; /* 0x0C */
+ U8 AttachedPhyIdentifier; /* 0x0E */
+ U8 Reserved2; /* 0x0F */
+ U32 AttachedPhyInfo; /* 0x10 */
+ U8 ProgrammedLinkRate; /* 0x14 */
+ U8 HwLinkRate; /* 0x15 */
+ U8 ChangeCount; /* 0x16 */
+ U8 Flags; /* 0x17 */
+ U32 PhyInfo; /* 0x18 */
+ U8 NegotiatedLinkRate; /* 0x1C */
+ U8 Reserved3; /* 0x1D */
+ U16 Reserved4; /* 0x1E */
+} MPI2_CONFIG_PAGE_SAS_PHY_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_0,
+ Mpi2SasPhyPage0_t, MPI2_POINTER pMpi2SasPhyPage0_t;
+
+#define MPI2_SASPHY0_PAGEVERSION (0x03)
+
+/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/* values for SAS PHY Page 0 Flags field */
+#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
+
+/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+
+/* SAS PHY Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 InvalidDwordCount; /* 0x0C */
+ U32 RunningDisparityErrorCount; /* 0x10 */
+ U32 LossDwordSynchCount; /* 0x14 */
+ U32 PhyResetProblemCount; /* 0x18 */
+} MPI2_CONFIG_PAGE_SAS_PHY_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_1,
+ Mpi2SasPhyPage1_t, MPI2_POINTER pMpi2SasPhyPage1_t;
+
+#define MPI2_SASPHY1_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* SAS Port Config Pages
+****************************************************************************/
+
+/* SAS Port Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 PortNumber; /* 0x08 */
+ U8 PhysicalPort; /* 0x09 */
+ U8 PortWidth; /* 0x0A */
+ U8 PhysicalPortWidth; /* 0x0B */
+ U8 ZoneGroup; /* 0x0C */
+ U8 Reserved1; /* 0x0D */
+ U16 Reserved2; /* 0x0E */
+ U64 SASAddress; /* 0x10 */
+ U32 DeviceInfo; /* 0x18 */
+ U32 Reserved3; /* 0x1C */
+ U32 Reserved4; /* 0x20 */
+} MPI2_CONFIG_PAGE_SAS_PORT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PORT_0,
+ Mpi2SasPortPage0_t, MPI2_POINTER pMpi2SasPortPage0_t;
+
+#define MPI2_SASPORT0_PAGEVERSION (0x00)
+
+/* see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */
+
+
+/****************************************************************************
+* SAS Enclosure Config Pages
+****************************************************************************/
+
+/* SAS Enclosure Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U64 EnclosureLogicalID; /* 0x0C */
+ U16 Flags; /* 0x14 */
+ U16 EnclosureHandle; /* 0x16 */
+ U16 NumSlots; /* 0x18 */
+ U16 StartSlot; /* 0x1A */
+ U16 Reserved2; /* 0x1C */
+ U16 SEPDevHandle; /* 0x1E */
+ U32 Reserved3; /* 0x20 */
+ U32 Reserved4; /* 0x24 */
+} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t;
+
+#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03)
+
+/* values for SAS Enclosure Page 0 Flags field */
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005)
+
+
+/****************************************************************************
+* Log Config Page
+****************************************************************************/
+
+/* Log Page 0 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES
+#define MPI2_LOG_0_NUM_LOG_ENTRIES (1)
+#endif
+
+#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_LOG_0_ENTRY
+{
+ U64 TimeStamp; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U16 LogSequence; /* 0x0C */
+ U16 LogEntryQualifier; /* 0x0E */
+ U8 VP_ID; /* 0x10 */
+ U8 VF_ID; /* 0x11 */
+ U16 Reserved2; /* 0x12 */
+ U8 LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/* 0x14 */
+} MPI2_LOG_0_ENTRY, MPI2_POINTER PTR_MPI2_LOG_0_ENTRY,
+ Mpi2Log0Entry_t, MPI2_POINTER pMpi2Log0Entry_t;
+
+/* values for Log Page 0 LogEntry LogEntryQualifier field */
+#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000)
+#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001)
+#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002)
+#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000)
+#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF)
+
+typedef struct _MPI2_CONFIG_PAGE_LOG_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ U16 NumLogEntries; /* 0x10 */
+ U16 Reserved3; /* 0x12 */
+ MPI2_LOG_0_ENTRY LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /* 0x14 */
+} MPI2_CONFIG_PAGE_LOG_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_LOG_0,
+ Mpi2LogPage0_t, MPI2_POINTER pMpi2LogPage0_t;
+
+#define MPI2_LOG_0_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* RAID Config Page
+****************************************************************************/
+
+/* RAID Page 0 */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS
+#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1)
+#endif
+
+typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT
+{
+ U16 ElementFlags; /* 0x00 */
+ U16 VolDevHandle; /* 0x02 */
+ U8 HotSparePool; /* 0x04 */
+ U8 PhysDiskNum; /* 0x05 */
+ U16 PhysDiskDevHandle; /* 0x06 */
+} MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ MPI2_POINTER PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ Mpi2RaidConfig0ConfigElement_t, MPI2_POINTER pMpi2RaidConfig0ConfigElement_t;
+
+/* values for the ElementFlags field */
+#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001)
+#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002)
+#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003)
+
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 NumHotSpares; /* 0x08 */
+ U8 NumPhysDisks; /* 0x09 */
+ U8 NumVolumes; /* 0x0A */
+ U8 ConfigNum; /* 0x0B */
+ U32 Flags; /* 0x0C */
+ U8 ConfigGUID[24]; /* 0x10 */
+ U32 Reserved1; /* 0x28 */
+ U8 NumElements; /* 0x2C */
+ U8 Reserved2; /* 0x2D */
+ U16 Reserved3; /* 0x2E */
+ MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /* 0x30 */
+} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ Mpi2RaidConfigurationPage0_t, MPI2_POINTER pMpi2RaidConfigurationPage0_t;
+
+#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00)
+
+/* values for RAID Configuration Page 0 Flags field */
+#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001)
+
+
+/****************************************************************************
+* Driver Persistent Mapping Config Pages
+****************************************************************************/
+
+/* Driver Persistent Mapping Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY
+{
+ U64 PhysicalIdentifier; /* 0x00 */
+ U16 MappingInformation; /* 0x08 */
+ U16 DeviceIndex; /* 0x0A */
+ U32 PhysicalBitsMapping; /* 0x0C */
+ U32 Reserved1; /* 0x10 */
+} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ Mpi2DriverMap0Entry_t, MPI2_POINTER pMpi2DriverMap0Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /* 0x08 */
+} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ Mpi2DriverMappingPage0_t, MPI2_POINTER pMpi2DriverMappingPage0_t;
+
+#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00)
+
+/* values for Driver Persistent Mapping Page 0 MappingInformation field */
+#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0)
+#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4)
+#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F)
+
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_init.h
new file mode 100644
index 000000000000..f1115f0f0eb2
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_init.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2000-2008 LSI Corporation.
+ *
+ *
+ * Name: mpi2_init.h
+ * Title: MPI SCSI initiator mode messages and structures
+ * Creation Date: June 23, 2006
+ *
+ * mpi2_init.h Version: 02.00.06
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t.
+ * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines.
+ * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention.
+ * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY.
+ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t.
+ * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO
+ * Control field Task Attribute flags.
+ * Moved LUN field defines to mpi2.h becasue they are
+ * common to many structures.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_INIT_H
+#define MPI2_INIT_H
+
+/*****************************************************************************
+*
+* SCSI Initiator Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SCSI IO messages and associated structures
+****************************************************************************/
+
+typedef struct
+{
+ U8 CDB[20]; /* 0x00 */
+ U32 PrimaryReferenceTag; /* 0x14 */
+ U16 PrimaryApplicationTag; /* 0x18 */
+ U16 PrimaryApplicationTagMask; /* 0x1A */
+ U32 TransferLength; /* 0x1C */
+} MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32,
+ Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t;
+
+/* TBD: I don't think this is needed for MPI2/Gen2 */
+#if 0
+typedef struct
+{
+ U8 CDB[16]; /* 0x00 */
+ U32 DataLength; /* 0x10 */
+ U32 PrimaryReferenceTag; /* 0x14 */
+ U16 PrimaryApplicationTag; /* 0x18 */
+ U16 PrimaryApplicationTagMask; /* 0x1A */
+ U32 TransferLength; /* 0x1C */
+} MPI2_SCSI_IO32_CDB_EEDP16, MPI2_POINTER PTR_MPI2_SCSI_IO32_CDB_EEDP16,
+ Mpi2ScsiIo32CdbEedp16_t, MPI2_POINTER pMpi2ScsiIo32CdbEedp16_t;
+#endif
+
+typedef union
+{
+ U8 CDB32[32];
+ MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
+ MPI2_SGE_SIMPLE_UNION SGE;
+} MPI2_SCSI_IO_CDB_UNION, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_UNION,
+ Mpi2ScsiIoCdb_t, MPI2_POINTER pMpi2ScsiIoCdb_t;
+
+/* SCSI IO Request Message */
+typedef struct _MPI2_SCSI_IO_REQUEST
+{
+ U16 DevHandle; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved1; /* 0x04 */
+ U8 Reserved2; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U32 SenseBufferLowAddress; /* 0x0C */
+ U16 SGLFlags; /* 0x10 */
+ U8 SenseBufferLength; /* 0x12 */
+ U8 Reserved4; /* 0x13 */
+ U8 SGLOffset0; /* 0x14 */
+ U8 SGLOffset1; /* 0x15 */
+ U8 SGLOffset2; /* 0x16 */
+ U8 SGLOffset3; /* 0x17 */
+ U32 SkipCount; /* 0x18 */
+ U32 DataLength; /* 0x1C */
+ U32 BidirectionalDataLength; /* 0x20 */
+ U16 IoFlags; /* 0x24 */
+ U16 EEDPFlags; /* 0x26 */
+ U32 EEDPBlockSize; /* 0x28 */
+ U32 SecondaryReferenceTag; /* 0x2C */
+ U16 SecondaryApplicationTag; /* 0x30 */
+ U16 ApplicationTagTranslationMask; /* 0x32 */
+ U8 LUN[8]; /* 0x34 */
+ U32 Control; /* 0x3C */
+ MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */
+ MPI2_SGE_IO_UNION SGL; /* 0x60 */
+} MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST,
+ Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t;
+
+/* SCSI IO MsgFlags bits */
+
+/* MsgFlags for SenseBufferAddressSpace */
+#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C)
+#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00)
+#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C)
+
+/* SCSI IO SGLFlags bits */
+
+/* base values for Data Location Address Space */
+#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C)
+#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C)
+
+/* base values for Type */
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02)
+
+/* shift values for each sub-field */
+#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12)
+#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8)
+#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4)
+#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0)
+
+/* SCSI IO IoFlags bits */
+
+/* Large CDB Address Space */
+#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000)
+#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000)
+
+#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
+#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
+#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400)
+#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200)
+#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
+
+/* SCSI IO EEDPFlags bits */
+
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000)
+
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100)
+
+#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008)
+
+#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007)
+#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001)
+#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003)
+#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004)
+#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007)
+
+/* SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */
+
+/* SCSI IO Control bits */
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000)
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
+
+#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
+#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
+#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000)
+#define MPI2_SCSIIO_CONTROL_READ (0x02000000)
+#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000)
+
+#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800)
+#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11)
+
+#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
+#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
+#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100)
+#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
+#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400)
+
+#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0)
+#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000)
+#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040)
+#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080)
+
+
+/* SCSI IO Error Reply Message */
+typedef struct _MPI2_SCSI_IO_REPLY
+{
+ U16 DevHandle; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved1; /* 0x04 */
+ U8 Reserved2; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U8 SCSIStatus; /* 0x0C */
+ U8 SCSIState; /* 0x0D */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 TransferCount; /* 0x14 */
+ U32 SenseCount; /* 0x18 */
+ U32 ResponseInfo; /* 0x1C */
+ U16 TaskTag; /* 0x20 */
+ U16 Reserved4; /* 0x22 */
+ U32 BidirectionalTransferCount; /* 0x24 */
+ U32 Reserved5; /* 0x28 */
+ U32 Reserved6; /* 0x2C */
+} MPI2_SCSI_IO_REPLY, MPI2_POINTER PTR_MPI2_SCSI_IO_REPLY,
+ Mpi2SCSIIOReply_t, MPI2_POINTER pMpi2SCSIIOReply_t;
+
+/* SCSI IO Reply SCSIStatus values (SAM-4 status codes) */
+
+#define MPI2_SCSI_STATUS_GOOD (0x00)
+#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI2_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI2_SCSI_STATUS_BUSY (0x08)
+#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /* obsolete */
+#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30)
+#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40)
+
+/* SCSI IO Reply SCSIState flags */
+
+#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI2_SCSI_STATE_TERMINATED (0x08)
+#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02)
+#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01)
+
+#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF)
+
+
+/****************************************************************************
+* SCSI Task Management messages
+****************************************************************************/
+
+/* SCSI Task Management Request Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST
+{
+ U16 DevHandle; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Reserved1; /* 0x04 */
+ U8 TaskType; /* 0x05 */
+ U8 Reserved2; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U8 LUN[8]; /* 0x0C */
+ U32 Reserved4[7]; /* 0x14 */
+ U16 TaskMID; /* 0x30 */
+ U16 Reserved5; /* 0x32 */
+} MPI2_SCSI_TASK_MANAGE_REQUEST,
+ MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REQUEST,
+ Mpi2SCSITaskManagementRequest_t,
+ MPI2_POINTER pMpi2SCSITaskManagementRequest_t;
+
+/* TaskType values */
+
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
+#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION (0x0A)
+
+/* MsgFlags bits */
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10)
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01)
+
+
+
+/* SCSI Task Management Reply Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY
+{
+ U16 DevHandle; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 ResponseCode; /* 0x04 */
+ U8 TaskType; /* 0x05 */
+ U8 Reserved1; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U16 Reserved3; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 TerminationCount; /* 0x14 */
+} MPI2_SCSI_TASK_MANAGE_REPLY,
+ MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY,
+ Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t;
+
+/* ResponseCode values */
+
+#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00)
+#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02)
+#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04)
+#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
+#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
+#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
+#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
+
+
+/****************************************************************************
+* SCSI Enclosure Processor messages
+****************************************************************************/
+
+/* SCSI Enclosure Processor Request Message */
+typedef struct _MPI2_SEP_REQUEST
+{
+ U16 DevHandle; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Action; /* 0x04 */
+ U8 Flags; /* 0x05 */
+ U8 Reserved1; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U32 SlotStatus; /* 0x0C */
+ U32 Reserved3; /* 0x10 */
+ U32 Reserved4; /* 0x14 */
+ U32 Reserved5; /* 0x18 */
+ U16 Slot; /* 0x1C */
+ U16 EnclosureHandle; /* 0x1E */
+} MPI2_SEP_REQUEST, MPI2_POINTER PTR_MPI2_SEP_REQUEST,
+ Mpi2SepRequest_t, MPI2_POINTER pMpi2SepRequest_t;
+
+/* Action defines */
+#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00)
+#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01)
+
+/* Flags defines */
+#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00)
+#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01)
+
+/* SlotStatus defines */
+#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000)
+#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001)
+
+
+/* SCSI Enclosure Processor Reply Message */
+typedef struct _MPI2_SEP_REPLY
+{
+ U16 DevHandle; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Action; /* 0x04 */
+ U8 Flags; /* 0x05 */
+ U8 Reserved1; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U16 Reserved3; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 SlotStatus; /* 0x14 */
+ U32 Reserved4; /* 0x18 */
+ U16 Slot; /* 0x1C */
+ U16 EnclosureHandle; /* 0x1E */
+} MPI2_SEP_REPLY, MPI2_POINTER PTR_MPI2_SEP_REPLY,
+ Mpi2SepReply_t, MPI2_POINTER pMpi2SepReply_t;
+
+/* SlotStatus defines */
+#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001)
+
+
+#endif
+
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
new file mode 100644
index 000000000000..8c5d81870c03
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
@@ -0,0 +1,1295 @@
+/*
+ * Copyright (c) 2000-2009 LSI Corporation.
+ *
+ *
+ * Name: mpi2_ioc.h
+ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
+ * Creation Date: October 11, 2006
+ *
+ * mpi2_ioc.h Version: 02.00.10
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to
+ * MaxTargets.
+ * Added TotalImageSize field to FWDownload Request.
+ * Added reserved words to FWUpload Request.
+ * 06-26-07 02.00.02 Added IR Configuration Change List Event.
+ * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit
+ * request and replaced it with
+ * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth.
+ * Replaced the MinReplyQueueDepth field of the IOCFacts
+ * reply with MaxReplyDescriptorPostQueueDepth.
+ * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum
+ * depth for the Reply Descriptor Post Queue.
+ * Added SASAddress field to Initiator Device Table
+ * Overflow Event data.
+ * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING
+ * for SAS Initiator Device Status Change Event data.
+ * Modified Reason Code defines for SAS Topology Change
+ * List Event data, including adding a bit for PHY Vacant
+ * status, and adding a mask for the Reason Code.
+ * Added define for
+ * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING.
+ * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID.
+ * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of
+ * the IOCFacts Reply.
+ * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Moved MPI2_VERSION_UNION to mpi2.h.
+ * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks
+ * instead of enables, and added SASBroadcastPrimitiveMasks
+ * field.
+ * Added Log Entry Added Event and related structure.
+ * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID.
+ * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET.
+ * Added MaxVolumes and MaxPersistentEntries fields to
+ * IOCFacts reply.
+ * Added ProtocalFlags and IOCCapabilities fields to
+ * MPI2_FW_IMAGE_HEADER.
+ * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT.
+ * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to
+ * a U16 (from a U32).
+ * Removed extra 's' from EventMasks name.
+ * 06-27-08 02.00.08 Fixed an offset in a comment.
+ * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST.
+ * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and
+ * renamed MinReplyFrameSize to ReplyFrameSize.
+ * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX.
+ * Added two new RAIDOperation values for Integrated RAID
+ * Operations Status Event data.
+ * Added four new IR Configuration Change List Event data
+ * ReasonCode values.
+ * Added two new ReasonCode defines for SAS Device Status
+ * Change Event data.
+ * Added three new DiscoveryStatus bits for the SAS
+ * Discovery event data.
+ * Added Multiplexing Status Change bit to the PhyStatus
+ * field of the SAS Topology Change List event data.
+ * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY.
+ * BootFlags are now product-specific.
+ * Added defines for the indivdual signature bytes
+ * for MPI2_INIT_IMAGE_FOOTER.
+ * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define.
+ * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR
+ * define.
+ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE
+ * define.
+ * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_IOC_H
+#define MPI2_IOC_H
+
+/*****************************************************************************
+*
+* IOC Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IOCInit message
+****************************************************************************/
+
+/* IOCInit Request message */
+typedef struct _MPI2_IOC_INIT_REQUEST
+{
+ U8 WhoInit; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 MsgVersion; /* 0x0C */
+ U16 HeaderVersion; /* 0x0E */
+ U32 Reserved5; /* 0x10 */
+ U32 Reserved6; /* 0x14 */
+ U16 Reserved7; /* 0x18 */
+ U16 SystemRequestFrameSize; /* 0x1A */
+ U16 ReplyDescriptorPostQueueDepth; /* 0x1C */
+ U16 ReplyFreeQueueDepth; /* 0x1E */
+ U32 SenseBufferAddressHigh; /* 0x20 */
+ U32 SystemReplyAddressHigh; /* 0x24 */
+ U64 SystemRequestFrameBaseAddress; /* 0x28 */
+ U64 ReplyDescriptorPostQueueAddress;/* 0x30 */
+ U64 ReplyFreeQueueAddress; /* 0x38 */
+ U64 TimeStamp; /* 0x40 */
+} MPI2_IOC_INIT_REQUEST, MPI2_POINTER PTR_MPI2_IOC_INIT_REQUEST,
+ Mpi2IOCInitRequest_t, MPI2_POINTER pMpi2IOCInitRequest_t;
+
+/* WhoInit values */
+#define MPI2_WHOINIT_NOT_INITIALIZED (0x00)
+#define MPI2_WHOINIT_SYSTEM_BIOS (0x01)
+#define MPI2_WHOINIT_ROM_BIOS (0x02)
+#define MPI2_WHOINIT_PCI_PEER (0x03)
+#define MPI2_WHOINIT_HOST_DRIVER (0x04)
+#define MPI2_WHOINIT_MANUFACTURER (0x05)
+
+/* MsgVersion */
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0)
+
+/* HeaderVersion */
+#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0)
+
+/* minimum depth for the Reply Descriptor Post Queue */
+#define MPI2_RDPQ_DEPTH_MIN (16)
+
+
+/* IOCInit Reply message */
+typedef struct _MPI2_IOC_INIT_REPLY
+{
+ U8 WhoInit; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_IOC_INIT_REPLY, MPI2_POINTER PTR_MPI2_IOC_INIT_REPLY,
+ Mpi2IOCInitReply_t, MPI2_POINTER pMpi2IOCInitReply_t;
+
+
+/****************************************************************************
+* IOCFacts message
+****************************************************************************/
+
+/* IOCFacts Request message */
+typedef struct _MPI2_IOC_FACTS_REQUEST
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+} MPI2_IOC_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_IOC_FACTS_REQUEST,
+ Mpi2IOCFactsRequest_t, MPI2_POINTER pMpi2IOCFactsRequest_t;
+
+
+/* IOCFacts Reply message */
+typedef struct _MPI2_IOC_FACTS_REPLY
+{
+ U16 MsgVersion; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 HeaderVersion; /* 0x04 */
+ U8 IOCNumber; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U16 IOCExceptions; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U8 MaxChainDepth; /* 0x14 */
+ U8 WhoInit; /* 0x15 */
+ U8 NumberOfPorts; /* 0x16 */
+ U8 Reserved2; /* 0x17 */
+ U16 RequestCredit; /* 0x18 */
+ U16 ProductID; /* 0x1A */
+ U32 IOCCapabilities; /* 0x1C */
+ MPI2_VERSION_UNION FWVersion; /* 0x20 */
+ U16 IOCRequestFrameSize; /* 0x24 */
+ U16 Reserved3; /* 0x26 */
+ U16 MaxInitiators; /* 0x28 */
+ U16 MaxTargets; /* 0x2A */
+ U16 MaxSasExpanders; /* 0x2C */
+ U16 MaxEnclosures; /* 0x2E */
+ U16 ProtocolFlags; /* 0x30 */
+ U16 HighPriorityCredit; /* 0x32 */
+ U16 MaxReplyDescriptorPostQueueDepth; /* 0x34 */
+ U8 ReplyFrameSize; /* 0x36 */
+ U8 MaxVolumes; /* 0x37 */
+ U16 MaxDevHandle; /* 0x38 */
+ U16 MaxPersistentEntries; /* 0x3A */
+ U32 Reserved4; /* 0x3C */
+} MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY,
+ Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t;
+
+/* MsgVersion */
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0)
+
+/* HeaderVersion */
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0)
+
+/* IOCExceptions */
+#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100)
+
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060)
+
+#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010)
+#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008)
+#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004)
+#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002)
+#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001)
+
+/* defines for WhoInit field are after the IOCInit Request */
+
+/* ProductID field uses MPI2_FW_HEADER_PID_ */
+
+/* IOCCapabilities */
+#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000)
+#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000)
+#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800)
+#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100)
+#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080)
+#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040)
+#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010)
+#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008)
+#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
+
+/* ProtocolFlags */
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
+
+
+/****************************************************************************
+* PortFacts message
+****************************************************************************/
+
+/* PortFacts Request message */
+typedef struct _MPI2_PORT_FACTS_REQUEST
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 PortNumber; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+} MPI2_PORT_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_PORT_FACTS_REQUEST,
+ Mpi2PortFactsRequest_t, MPI2_POINTER pMpi2PortFactsRequest_t;
+
+/* PortFacts Reply message */
+typedef struct _MPI2_PORT_FACTS_REPLY
+{
+ U16 Reserved1; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 PortNumber; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U8 Reserved5; /* 0x14 */
+ U8 PortType; /* 0x15 */
+ U16 Reserved6; /* 0x16 */
+ U16 MaxPostedCmdBuffers; /* 0x18 */
+ U16 Reserved7; /* 0x1A */
+} MPI2_PORT_FACTS_REPLY, MPI2_POINTER PTR_MPI2_PORT_FACTS_REPLY,
+ Mpi2PortFactsReply_t, MPI2_POINTER pMpi2PortFactsReply_t;
+
+/* PortType values */
+#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00)
+#define MPI2_PORTFACTS_PORTTYPE_FC (0x10)
+#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31)
+
+
+/****************************************************************************
+* PortEnable message
+****************************************************************************/
+
+/* PortEnable Request message */
+typedef struct _MPI2_PORT_ENABLE_REQUEST
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Reserved2; /* 0x04 */
+ U8 PortFlags; /* 0x05 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+} MPI2_PORT_ENABLE_REQUEST, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REQUEST,
+ Mpi2PortEnableRequest_t, MPI2_POINTER pMpi2PortEnableRequest_t;
+
+
+/* PortEnable Reply message */
+typedef struct _MPI2_PORT_ENABLE_REPLY
+{
+ U16 Reserved1; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Reserved2; /* 0x04 */
+ U8 PortFlags; /* 0x05 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_PORT_ENABLE_REPLY, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REPLY,
+ Mpi2PortEnableReply_t, MPI2_POINTER pMpi2PortEnableReply_t;
+
+
+/****************************************************************************
+* EventNotification message
+****************************************************************************/
+
+/* EventNotification Request message */
+#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U32 Reserved5; /* 0x0C */
+ U32 Reserved6; /* 0x10 */
+ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];/* 0x14 */
+ U16 SASBroadcastPrimitiveMasks; /* 0x24 */
+ U16 Reserved7; /* 0x26 */
+ U32 Reserved8; /* 0x28 */
+} MPI2_EVENT_NOTIFICATION_REQUEST,
+ MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REQUEST,
+ Mpi2EventNotificationRequest_t, MPI2_POINTER pMpi2EventNotificationRequest_t;
+
+
+/* EventNotification Reply message */
+typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
+{
+ U16 EventDataLength; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved1; /* 0x04 */
+ U8 AckRequired; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U16 Reserved3; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U16 Event; /* 0x14 */
+ U16 Reserved4; /* 0x16 */
+ U32 EventContext; /* 0x18 */
+ U32 EventData[1]; /* 0x1C */
+} MPI2_EVENT_NOTIFICATION_REPLY, MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REPLY,
+ Mpi2EventNotificationReply_t, MPI2_POINTER pMpi2EventNotificationReply_t;
+
+/* AckRequired */
+#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00)
+#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01)
+
+/* Event */
+#define MPI2_EVENT_LOG_DATA (0x0001)
+#define MPI2_EVENT_STATE_CHANGE (0x0002)
+#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005)
+#define MPI2_EVENT_EVENT_CHANGE (0x000A)
+#define MPI2_EVENT_TASK_SET_FULL (0x000E)
+#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F)
+#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014)
+#define MPI2_EVENT_SAS_DISCOVERY (0x0016)
+#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017)
+#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018)
+#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019)
+#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C)
+#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D)
+#define MPI2_EVENT_IR_VOLUME (0x001E)
+#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F)
+#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020)
+#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
+
+
+/* Log Entry Added Event data */
+
+/* the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */
+#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED
+{
+ U64 TimeStamp; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U16 LogSequence; /* 0x0C */
+ U16 LogEntryQualifier; /* 0x0E */
+ U8 VP_ID; /* 0x10 */
+ U8 VF_ID; /* 0x11 */
+ U16 Reserved2; /* 0x12 */
+ U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH];/* 0x14 */
+} MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t;
+
+/* Hard Reset Received Event data */
+
+typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
+{
+ U8 Reserved1; /* 0x00 */
+ U8 Port; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+} MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ Mpi2EventDataHardResetReceived_t,
+ MPI2_POINTER pMpi2EventDataHardResetReceived_t;
+
+/* Task Set Full Event data */
+
+typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL
+{
+ U16 DevHandle; /* 0x00 */
+ U16 CurrentDepth; /* 0x02 */
+} MPI2_EVENT_DATA_TASK_SET_FULL, MPI2_POINTER PTR_MPI2_EVENT_DATA_TASK_SET_FULL,
+ Mpi2EventDataTaskSetFull_t, MPI2_POINTER pMpi2EventDataTaskSetFull_t;
+
+
+/* SAS Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE
+{
+ U16 TaskTag; /* 0x00 */
+ U8 ReasonCode; /* 0x02 */
+ U8 Reserved1; /* 0x03 */
+ U8 ASC; /* 0x04 */
+ U8 ASCQ; /* 0x05 */
+ U16 DevHandle; /* 0x06 */
+ U32 Reserved2; /* 0x08 */
+ U64 SASAddress; /* 0x0C */
+ U8 LUN[8]; /* 0x14 */
+} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ Mpi2EventDataSasDeviceStatusChange_t,
+ MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t;
+
+/* SAS Device Status Change Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10)
+
+
+/* Integrated RAID Operation Status Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS
+{
+ U16 VolDevHandle; /* 0x00 */
+ U16 Reserved1; /* 0x02 */
+ U8 RAIDOperation; /* 0x04 */
+ U8 PercentComplete; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ U32 Resereved3; /* 0x08 */
+} MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ Mpi2EventDataIrOperationStatus_t,
+ MPI2_POINTER pMpi2EventDataIrOperationStatus_t;
+
+/* Integrated RAID Operation Status Event data RAIDOperation values */
+#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00)
+#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02)
+#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03)
+#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04)
+
+
+/* Integrated RAID Volume Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_VOLUME
+{
+ U16 VolDevHandle; /* 0x00 */
+ U8 ReasonCode; /* 0x02 */
+ U8 Reserved1; /* 0x03 */
+ U32 NewValue; /* 0x04 */
+ U32 PreviousValue; /* 0x08 */
+} MPI2_EVENT_DATA_IR_VOLUME, MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_VOLUME,
+ Mpi2EventDataIrVolume_t, MPI2_POINTER pMpi2EventDataIrVolume_t;
+
+/* Integrated RAID Volume Event data ReasonCode values */
+#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03)
+
+
+/* Integrated RAID Physical Disk Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ReasonCode; /* 0x02 */
+ U8 PhysDiskNum; /* 0x03 */
+ U16 PhysDiskDevHandle; /* 0x04 */
+ U16 Reserved2; /* 0x06 */
+ U16 Slot; /* 0x08 */
+ U16 EnclosureHandle; /* 0x0A */
+ U32 NewValue; /* 0x0C */
+ U32 PreviousValue; /* 0x10 */
+} MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ Mpi2EventDataIrPhysicalDisk_t, MPI2_POINTER pMpi2EventDataIrPhysicalDisk_t;
+
+/* Integrated RAID Physical Disk Event data ReasonCode values */
+#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03)
+
+
+/* Integrated RAID Configuration Change List Event data */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check NumElements at runtime.
+ */
+#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT
+#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1)
+#endif
+
+typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT
+{
+ U16 ElementFlags; /* 0x00 */
+ U16 VolDevHandle; /* 0x02 */
+ U8 ReasonCode; /* 0x04 */
+ U8 PhysDiskNum; /* 0x05 */
+ U16 PhysDiskDevHandle; /* 0x06 */
+} MPI2_EVENT_IR_CONFIG_ELEMENT, MPI2_POINTER PTR_MPI2_EVENT_IR_CONFIG_ELEMENT,
+ Mpi2EventIrConfigElement_t, MPI2_POINTER pMpi2EventIrConfigElement_t;
+
+/* IR Configuration Change List Event data ElementFlags values */
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002)
+
+/* IR Configuration Change List Event data ReasonCode values */
+#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01)
+#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02)
+#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03)
+#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04)
+#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09)
+
+typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST
+{
+ U8 NumElements; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 Reserved2; /* 0x02 */
+ U8 ConfigNum; /* 0x03 */
+ U32 Flags; /* 0x04 */
+ MPI2_EVENT_IR_CONFIG_ELEMENT ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT]; /* 0x08 */
+} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ Mpi2EventDataIrConfigChangeList_t,
+ MPI2_POINTER pMpi2EventDataIrConfigChangeList_t;
+
+/* IR Configuration Change List Event data Flags values */
+#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001)
+
+
+/* SAS Discovery Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY
+{
+ U8 Flags; /* 0x00 */
+ U8 ReasonCode; /* 0x01 */
+ U8 PhysicalPort; /* 0x02 */
+ U8 Reserved1; /* 0x03 */
+ U32 DiscoveryStatus; /* 0x04 */
+} MPI2_EVENT_DATA_SAS_DISCOVERY,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DISCOVERY,
+ Mpi2EventDataSasDiscovery_t, MPI2_POINTER pMpi2EventDataSasDiscovery_t;
+
+/* SAS Discovery Event data Flags values */
+#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02)
+#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01)
+
+/* SAS Discovery Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02)
+
+/* SAS Discovery Event data DiscoveryStatus values */
+#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400)
+#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001)
+
+
+/* SAS Broadcast Primitive Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE
+{
+ U8 PhyNum; /* 0x00 */
+ U8 Port; /* 0x01 */
+ U8 PortWidth; /* 0x02 */
+ U8 Primitive; /* 0x03 */
+} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ Mpi2EventDataSasBroadcastPrimitive_t,
+ MPI2_POINTER pMpi2EventDataSasBroadcastPrimitive_t;
+
+/* defines for the Primitive field */
+#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01)
+#define MPI2_EVENT_PRIMITIVE_SES (0x02)
+#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03)
+#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
+#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05)
+#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06)
+#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07)
+#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08)
+
+
+/* SAS Initiator Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE
+{
+ U8 ReasonCode; /* 0x00 */
+ U8 PhysicalPort; /* 0x01 */
+ U16 DevHandle; /* 0x02 */
+ U64 SASAddress; /* 0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasInitDevStatusChange_t,
+ MPI2_POINTER pMpi2EventDataSasInitDevStatusChange_t;
+
+/* SAS Initiator Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
+
+
+/* SAS Initiator Device Table Overflow Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW
+{
+ U16 MaxInit; /* 0x00 */
+ U16 CurrentInit; /* 0x02 */
+ U64 SASAddress; /* 0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ Mpi2EventDataSasInitTableOverflow_t,
+ MPI2_POINTER pMpi2EventDataSasInitTableOverflow_t;
+
+
+/* SAS Topology Change List Event data */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check NumEntries at runtime.
+ */
+#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT
+#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1)
+#endif
+
+typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY
+{
+ U16 AttachedDevHandle; /* 0x00 */
+ U8 LinkRate; /* 0x02 */
+ U8 PhyStatus; /* 0x03 */
+} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, MPI2_POINTER PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY,
+ Mpi2EventSasTopoPhyEntry_t, MPI2_POINTER pMpi2EventSasTopoPhyEntry_t;
+
+typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST
+{
+ U16 EnclosureHandle; /* 0x00 */
+ U16 ExpanderDevHandle; /* 0x02 */
+ U8 NumPhys; /* 0x04 */
+ U8 Reserved1; /* 0x05 */
+ U16 Reserved2; /* 0x06 */
+ U8 NumEntries; /* 0x08 */
+ U8 StartPhyNum; /* 0x09 */
+ U8 ExpStatus; /* 0x0A */
+ U8 PhysicalPort; /* 0x0B */
+ MPI2_EVENT_SAS_TOPO_PHY_ENTRY PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /* 0x0C*/
+} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ Mpi2EventDataSasTopologyChangeList_t,
+ MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t;
+
+/* values for the ExpStatus field */
+#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
+#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
+
+/* defines for the LinkRate field */
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0)
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
+
+#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
+#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
+#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
+#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
+#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A)
+
+/* values for the PhyStatus field */
+#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80)
+#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10)
+/* values for the PhyStatus ReasonCode sub-field */
+#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03)
+#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04)
+#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05)
+
+
+/* SAS Enclosure Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE
+{
+ U16 EnclosureHandle; /* 0x00 */
+ U8 ReasonCode; /* 0x02 */
+ U8 PhysicalPort; /* 0x03 */
+ U64 EnclosureLogicalID; /* 0x04 */
+ U16 NumSlots; /* 0x0C */
+ U16 StartSlot; /* 0x0E */
+ U32 PhyBits; /* 0x10 */
+} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasEnclDevStatusChange_t,
+ MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t;
+
+/* SAS Enclosure Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02)
+
+
+/****************************************************************************
+* EventAck message
+****************************************************************************/
+
+/* EventAck Request message */
+typedef struct _MPI2_EVENT_ACK_REQUEST
+{
+ U16 Reserved1; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Event; /* 0x0C */
+ U16 Reserved5; /* 0x0E */
+ U32 EventContext; /* 0x10 */
+} MPI2_EVENT_ACK_REQUEST, MPI2_POINTER PTR_MPI2_EVENT_ACK_REQUEST,
+ Mpi2EventAckRequest_t, MPI2_POINTER pMpi2EventAckRequest_t;
+
+
+/* EventAck Reply message */
+typedef struct _MPI2_EVENT_ACK_REPLY
+{
+ U16 Reserved1; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_EVENT_ACK_REPLY, MPI2_POINTER PTR_MPI2_EVENT_ACK_REPLY,
+ Mpi2EventAckReply_t, MPI2_POINTER pMpi2EventAckReply_t;
+
+
+/****************************************************************************
+* FWDownload message
+****************************************************************************/
+
+/* FWDownload Request message */
+typedef struct _MPI2_FW_DOWNLOAD_REQUEST
+{
+ U8 ImageType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U32 TotalImageSize; /* 0x0C */
+ U32 Reserved5; /* 0x10 */
+ MPI2_MPI_SGE_UNION SGL; /* 0x14 */
+} MPI2_FW_DOWNLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REQUEST,
+ Mpi2FWDownloadRequest, MPI2_POINTER pMpi2FWDownloadRequest;
+
+#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01)
+
+#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01)
+#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02)
+#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+
+/* FWDownload TransactionContext Element */
+typedef struct _MPI2_FW_DOWNLOAD_TCSGE
+{
+ U8 Reserved1; /* 0x00 */
+ U8 ContextSize; /* 0x01 */
+ U8 DetailsLength; /* 0x02 */
+ U8 Flags; /* 0x03 */
+ U32 Reserved2; /* 0x04 */
+ U32 ImageOffset; /* 0x08 */
+ U32 ImageSize; /* 0x0C */
+} MPI2_FW_DOWNLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_TCSGE,
+ Mpi2FWDownloadTCSGE_t, MPI2_POINTER pMpi2FWDownloadTCSGE_t;
+
+/* FWDownload Reply message */
+typedef struct _MPI2_FW_DOWNLOAD_REPLY
+{
+ U8 ImageType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_FW_DOWNLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REPLY,
+ Mpi2FWDownloadReply_t, MPI2_POINTER pMpi2FWDownloadReply_t;
+
+
+/****************************************************************************
+* FWUpload message
+****************************************************************************/
+
+/* FWUpload Request message */
+typedef struct _MPI2_FW_UPLOAD_REQUEST
+{
+ U8 ImageType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U32 Reserved5; /* 0x0C */
+ U32 Reserved6; /* 0x10 */
+ MPI2_MPI_SGE_UNION SGL; /* 0x14 */
+} MPI2_FW_UPLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REQUEST,
+ Mpi2FWUploadRequest_t, MPI2_POINTER pMpi2FWUploadRequest_t;
+
+#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00)
+#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01)
+#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02)
+#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05)
+#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A)
+#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+
+typedef struct _MPI2_FW_UPLOAD_TCSGE
+{
+ U8 Reserved1; /* 0x00 */
+ U8 ContextSize; /* 0x01 */
+ U8 DetailsLength; /* 0x02 */
+ U8 Flags; /* 0x03 */
+ U32 Reserved2; /* 0x04 */
+ U32 ImageOffset; /* 0x08 */
+ U32 ImageSize; /* 0x0C */
+} MPI2_FW_UPLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_UPLOAD_TCSGE,
+ Mpi2FWUploadTCSGE_t, MPI2_POINTER pMpi2FWUploadTCSGE_t;
+
+/* FWUpload Reply message */
+typedef struct _MPI2_FW_UPLOAD_REPLY
+{
+ U8 ImageType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 ActualImageSize; /* 0x14 */
+} MPI2_FW_UPLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REPLY,
+ Mpi2FWUploadReply_t, MPI2_POINTER pMPi2FWUploadReply_t;
+
+
+/* FW Image Header */
+typedef struct _MPI2_FW_IMAGE_HEADER
+{
+ U32 Signature; /* 0x00 */
+ U32 Signature0; /* 0x04 */
+ U32 Signature1; /* 0x08 */
+ U32 Signature2; /* 0x0C */
+ MPI2_VERSION_UNION MPIVersion; /* 0x10 */
+ MPI2_VERSION_UNION FWVersion; /* 0x14 */
+ MPI2_VERSION_UNION NVDATAVersion; /* 0x18 */
+ MPI2_VERSION_UNION PackageVersion; /* 0x1C */
+ U16 VendorID; /* 0x20 */
+ U16 ProductID; /* 0x22 */
+ U16 ProtocolFlags; /* 0x24 */
+ U16 Reserved26; /* 0x26 */
+ U32 IOCCapabilities; /* 0x28 */
+ U32 ImageSize; /* 0x2C */
+ U32 NextImageHeaderOffset; /* 0x30 */
+ U32 Checksum; /* 0x34 */
+ U32 Reserved38; /* 0x38 */
+ U32 Reserved3C; /* 0x3C */
+ U32 Reserved40; /* 0x40 */
+ U32 Reserved44; /* 0x44 */
+ U32 Reserved48; /* 0x48 */
+ U32 Reserved4C; /* 0x4C */
+ U32 Reserved50; /* 0x50 */
+ U32 Reserved54; /* 0x54 */
+ U32 Reserved58; /* 0x58 */
+ U32 Reserved5C; /* 0x5C */
+ U32 Reserved60; /* 0x60 */
+ U32 FirmwareVersionNameWhat; /* 0x64 */
+ U8 FirmwareVersionName[32]; /* 0x68 */
+ U32 VendorNameWhat; /* 0x88 */
+ U8 VendorName[32]; /* 0x8C */
+ U32 PackageNameWhat; /* 0x88 */
+ U8 PackageName[32]; /* 0x8C */
+ U32 ReservedD0; /* 0xD0 */
+ U32 ReservedD4; /* 0xD4 */
+ U32 ReservedD8; /* 0xD8 */
+ U32 ReservedDC; /* 0xDC */
+ U32 ReservedE0; /* 0xE0 */
+ U32 ReservedE4; /* 0xE4 */
+ U32 ReservedE8; /* 0xE8 */
+ U32 ReservedEC; /* 0xEC */
+ U32 ReservedF0; /* 0xF0 */
+ U32 ReservedF4; /* 0xF4 */
+ U32 ReservedF8; /* 0xF8 */
+ U32 ReservedFC; /* 0xFC */
+} MPI2_FW_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_FW_IMAGE_HEADER,
+ Mpi2FWImageHeader_t, MPI2_POINTER pMpi2FWImageHeader_t;
+
+/* Signature field */
+#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00)
+#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000)
+#define MPI2_FW_HEADER_SIGNATURE (0xEA000000)
+
+/* Signature0 field */
+#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04)
+#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A)
+
+/* Signature1 field */
+#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08)
+#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5)
+
+/* Signature2 field */
+#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C)
+#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA)
+
+
+/* defines for using the ProductID field */
+#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000)
+#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000)
+
+#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
+
+#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
+/* SAS */
+#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010)
+
+/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
+
+/* use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */
+
+
+#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C)
+#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30)
+#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64)
+
+#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840)
+
+#define MPI2_FW_HEADER_SIZE (0x100)
+
+
+/* Extended Image Header */
+typedef struct _MPI2_EXT_IMAGE_HEADER
+
+{
+ U8 ImageType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+ U32 Checksum; /* 0x04 */
+ U32 ImageSize; /* 0x08 */
+ U32 NextImageHeaderOffset; /* 0x0C */
+ U32 PackageVersion; /* 0x10 */
+ U32 Reserved3; /* 0x14 */
+ U32 Reserved4; /* 0x18 */
+ U32 Reserved5; /* 0x1C */
+ U8 IdentifyString[32]; /* 0x20 */
+} MPI2_EXT_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_EXT_IMAGE_HEADER,
+ Mpi2ExtImageHeader_t, MPI2_POINTER pMpi2ExtImageHeader_t;
+
+/* useful offsets */
+#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
+#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
+#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C)
+
+#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40)
+
+/* defines for the ImageType field */
+#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI2_EXT_IMAGE_TYPE_FW (0x01)
+#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03)
+#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04)
+#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05)
+#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06)
+#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
+#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08)
+
+#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MEGARAID)
+
+
+
+/* FLASH Layout Extended Image Data */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check RegionsPerLayout at runtime.
+ */
+#ifndef MPI2_FLASH_NUMBER_OF_REGIONS
+#define MPI2_FLASH_NUMBER_OF_REGIONS (1)
+#endif
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check NumberOfLayouts at runtime.
+ */
+#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS
+#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1)
+#endif
+
+typedef struct _MPI2_FLASH_REGION
+{
+ U8 RegionType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+ U32 RegionOffset; /* 0x04 */
+ U32 RegionSize; /* 0x08 */
+ U32 Reserved3; /* 0x0C */
+} MPI2_FLASH_REGION, MPI2_POINTER PTR_MPI2_FLASH_REGION,
+ Mpi2FlashRegion_t, MPI2_POINTER pMpi2FlashRegion_t;
+
+typedef struct _MPI2_FLASH_LAYOUT
+{
+ U32 FlashSize; /* 0x00 */
+ U32 Reserved1; /* 0x04 */
+ U32 Reserved2; /* 0x08 */
+ U32 Reserved3; /* 0x0C */
+ MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS];/* 0x10 */
+} MPI2_FLASH_LAYOUT, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT,
+ Mpi2FlashLayout_t, MPI2_POINTER pMpi2FlashLayout_t;
+
+typedef struct _MPI2_FLASH_LAYOUT_DATA
+{
+ U8 ImageRevision; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 SizeOfRegion; /* 0x02 */
+ U8 Reserved2; /* 0x03 */
+ U16 NumberOfLayouts; /* 0x04 */
+ U16 RegionsPerLayout; /* 0x06 */
+ U16 MinimumSectorAlignment; /* 0x08 */
+ U16 Reserved3; /* 0x0A */
+ U32 Reserved4; /* 0x0C */
+ MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS];/* 0x10 */
+} MPI2_FLASH_LAYOUT_DATA, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT_DATA,
+ Mpi2FlashLayoutData_t, MPI2_POINTER pMpi2FlashLayoutData_t;
+
+/* defines for the RegionType field */
+#define MPI2_FLASH_REGION_UNUSED (0x00)
+#define MPI2_FLASH_REGION_FIRMWARE (0x01)
+#define MPI2_FLASH_REGION_BIOS (0x02)
+#define MPI2_FLASH_REGION_NVDATA (0x03)
+#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05)
+#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06)
+#define MPI2_FLASH_REGION_CONFIG_1 (0x07)
+#define MPI2_FLASH_REGION_CONFIG_2 (0x08)
+#define MPI2_FLASH_REGION_MEGARAID (0x09)
+#define MPI2_FLASH_REGION_INIT (0x0A)
+
+/* ImageRevision */
+#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00)
+
+
+
+/* Supported Devices Extended Image Data */
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check NumberOfDevices at runtime.
+ */
+#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES
+#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1)
+#endif
+
+typedef struct _MPI2_SUPPORTED_DEVICE
+{
+ U16 DeviceID; /* 0x00 */
+ U16 VendorID; /* 0x02 */
+ U16 DeviceIDMask; /* 0x04 */
+ U16 Reserved1; /* 0x06 */
+ U8 LowPCIRev; /* 0x08 */
+ U8 HighPCIRev; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U32 Reserved3; /* 0x0C */
+} MPI2_SUPPORTED_DEVICE, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICE,
+ Mpi2SupportedDevice_t, MPI2_POINTER pMpi2SupportedDevice_t;
+
+typedef struct _MPI2_SUPPORTED_DEVICES_DATA
+{
+ U8 ImageRevision; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 NumberOfDevices; /* 0x02 */
+ U8 Reserved2; /* 0x03 */
+ U32 Reserved3; /* 0x04 */
+ MPI2_SUPPORTED_DEVICE SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES]; /* 0x08 */
+} MPI2_SUPPORTED_DEVICES_DATA, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICES_DATA,
+ Mpi2SupportedDevicesData_t, MPI2_POINTER pMpi2SupportedDevicesData_t;
+
+/* ImageRevision */
+#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00)
+
+
+/* Init Extended Image Data */
+
+typedef struct _MPI2_INIT_IMAGE_FOOTER
+
+{
+ U32 BootFlags; /* 0x00 */
+ U32 ImageSize; /* 0x04 */
+ U32 Signature0; /* 0x08 */
+ U32 Signature1; /* 0x0C */
+ U32 Signature2; /* 0x10 */
+ U32 ResetVector; /* 0x14 */
+} MPI2_INIT_IMAGE_FOOTER, MPI2_POINTER PTR_MPI2_INIT_IMAGE_FOOTER,
+ Mpi2InitImageFooter_t, MPI2_POINTER pMpi2InitImageFooter_t;
+
+/* defines for the BootFlags field */
+#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00)
+
+/* defines for the ImageSize field */
+#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04)
+
+/* defines for the Signature0 field */
+#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08)
+#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA)
+
+/* defines for the Signature1 field */
+#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C)
+#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5)
+
+/* defines for the Signature2 field */
+#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10)
+#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A)
+
+/* Signature fields as individual bytes */
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A)
+
+/* defines for the ResetVector field */
+#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14)
+
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
new file mode 100644
index 000000000000..7134816d9046
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2000-2008 LSI Corporation.
+ *
+ *
+ * Name: mpi2_raid.h
+ * Title: MPI Integrated RAID messages and structures
+ * Creation Date: April 26, 2007
+ *
+ * mpi2_raid.h Version: 02.00.03
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 08-31-07 02.00.01 Modifications to RAID Action request and reply,
+ * including the Actions and ActionData.
+ * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD.
+ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
+ * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
+ * can be sized by the build environment.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_RAID_H
+#define MPI2_RAID_H
+
+/*****************************************************************************
+*
+* Integrated RAID Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* RAID Action messages
+****************************************************************************/
+
+/* ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */
+#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000)
+#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001)
+
+/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+/* ActionDataWord defines for use with MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */
+#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001)
+
+/* ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */
+typedef struct _MPI2_RAID_ACTION_RATE_DATA
+{
+ U8 RateToChange; /* 0x00 */
+ U8 RateOrMode; /* 0x01 */
+ U16 DataScrubDuration; /* 0x02 */
+} MPI2_RAID_ACTION_RATE_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_RATE_DATA,
+ Mpi2RaidActionRateData_t, MPI2_POINTER pMpi2RaidActionRateData_t;
+
+#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00)
+#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01)
+#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02)
+
+/* ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION
+{
+ U8 RAIDFunction; /* 0x00 */
+ U8 Flags; /* 0x01 */
+ U16 Reserved1; /* 0x02 */
+} MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ MPI2_POINTER PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ Mpi2RaidActionStartRaidFunction_t,
+ MPI2_POINTER pMpi2RaidActionStartRaidFunction_t;
+
+/* defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02)
+
+/* defines for the Flags field */
+#define MPI2_RAID_ACTION_START_NEW (0x00)
+#define MPI2_RAID_ACTION_START_RESUME (0x01)
+
+/* ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION
+{
+ U8 RAIDFunction; /* 0x00 */
+ U8 Flags; /* 0x01 */
+ U16 Reserved1; /* 0x02 */
+} MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ MPI2_POINTER PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ Mpi2RaidActionStopRaidFunction_t,
+ MPI2_POINTER pMpi2RaidActionStopRaidFunction_t;
+
+/* defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02)
+
+/* defines for the Flags field */
+#define MPI2_RAID_ACTION_STOP_ABORT (0x00)
+#define MPI2_RAID_ACTION_STOP_PAUSE (0x01)
+
+/* ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */
+typedef struct _MPI2_RAID_ACTION_HOT_SPARE
+{
+ U8 HotSparePool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 DevHandle; /* 0x02 */
+} MPI2_RAID_ACTION_HOT_SPARE, MPI2_POINTER PTR_MPI2_RAID_ACTION_HOT_SPARE,
+ Mpi2RaidActionHotSpare_t, MPI2_POINTER pMpi2RaidActionHotSpare_t;
+
+/* ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */
+typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE
+{
+ U8 Flags; /* 0x00 */
+ U8 DeviceFirmwareUpdateModeTimeout; /* 0x01 */
+ U16 Reserved1; /* 0x02 */
+} MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ MPI2_POINTER PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ Mpi2RaidActionFwUpdateMode_t, MPI2_POINTER pMpi2RaidActionFwUpdateMode_t;
+
+/* ActionDataWord defines for use with MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */
+#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00)
+#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01)
+
+typedef union _MPI2_RAID_ACTION_DATA
+{
+ U32 Word;
+ MPI2_RAID_ACTION_RATE_DATA Rates;
+ MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction;
+ MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction;
+ MPI2_RAID_ACTION_HOT_SPARE HotSpare;
+ MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode;
+} MPI2_RAID_ACTION_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_DATA,
+ Mpi2RaidActionData_t, MPI2_POINTER pMpi2RaidActionData_t;
+
+
+/* RAID Action Request Message */
+typedef struct _MPI2_RAID_ACTION_REQUEST
+{
+ U8 Action; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 VolDevHandle; /* 0x04 */
+ U8 PhysDiskNum; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U32 Reserved3; /* 0x0C */
+ MPI2_RAID_ACTION_DATA ActionDataWord; /* 0x10 */
+ MPI2_SGE_SIMPLE_UNION ActionDataSGE; /* 0x14 */
+} MPI2_RAID_ACTION_REQUEST, MPI2_POINTER PTR_MPI2_RAID_ACTION_REQUEST,
+ Mpi2RaidActionRequest_t, MPI2_POINTER pMpi2RaidActionRequest_t;
+
+/* RAID Action request Action values */
+
+#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01)
+#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02)
+#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03)
+#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04)
+#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05)
+#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A)
+#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B)
+#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F)
+#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11)
+#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15)
+#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17)
+#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18)
+#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19)
+#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C)
+#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D)
+#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E)
+#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20)
+#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21)
+#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22)
+
+
+/* RAID Volume Creation Structure */
+
+/*
+ * The following define can be customized for the targeted product.
+ */
+#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS
+#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1)
+#endif
+
+typedef struct _MPI2_RAID_VOLUME_PHYSDISK
+{
+ U8 RAIDSetNum; /* 0x00 */
+ U8 PhysDiskMap; /* 0x01 */
+ U16 PhysDiskDevHandle; /* 0x02 */
+} MPI2_RAID_VOLUME_PHYSDISK, MPI2_POINTER PTR_MPI2_RAID_VOLUME_PHYSDISK,
+ Mpi2RaidVolumePhysDisk_t, MPI2_POINTER pMpi2RaidVolumePhysDisk_t;
+
+/* defines for the PhysDiskMap field */
+#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT
+{
+ U8 NumPhysDisks; /* 0x00 */
+ U8 VolumeType; /* 0x01 */
+ U16 Reserved1; /* 0x02 */
+ U32 VolumeCreationFlags; /* 0x04 */
+ U32 VolumeSettings; /* 0x08 */
+ U8 Reserved2; /* 0x0C */
+ U8 ResyncRate; /* 0x0D */
+ U16 DataScrubDuration; /* 0x0E */
+ U64 VolumeMaxLBA; /* 0x10 */
+ U32 StripeSize; /* 0x18 */
+ U8 Name[16]; /* 0x1C */
+ MPI2_RAID_VOLUME_PHYSDISK PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS];/* 0x2C */
+} MPI2_RAID_VOLUME_CREATION_STRUCT,
+ MPI2_POINTER PTR_MPI2_RAID_VOLUME_CREATION_STRUCT,
+ Mpi2RaidVolumeCreationStruct_t, MPI2_POINTER pMpi2RaidVolumeCreationStruct_t;
+
+/* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
+
+/* defines for the VolumeCreationFlags field */
+#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
+#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x04)
+#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x02)
+#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x01)
+
+
+/* RAID Online Capacity Expansion Structure */
+
+typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION
+{
+ U32 Flags; /* 0x00 */
+ U16 DevHandle0; /* 0x04 */
+ U16 Reserved1; /* 0x06 */
+ U16 DevHandle1; /* 0x08 */
+ U16 Reserved2; /* 0x0A */
+} MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ MPI2_POINTER PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ Mpi2RaidOnlineCapacityExpansion_t,
+ MPI2_POINTER pMpi2RaidOnlineCapacityExpansion_t;
+
+
+/* RAID Volume Indicator Structure */
+
+typedef struct _MPI2_RAID_VOL_INDICATOR
+{
+ U64 TotalBlocks; /* 0x00 */
+ U64 BlocksRemaining; /* 0x08 */
+ U32 Flags; /* 0x10 */
+} MPI2_RAID_VOL_INDICATOR, MPI2_POINTER PTR_MPI2_RAID_VOL_INDICATOR,
+ Mpi2RaidVolIndicator_t, MPI2_POINTER pMpi2RaidVolIndicator_t;
+
+/* defines for RAID Volume Indicator Flags field */
+#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F)
+#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000)
+#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
+#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002)
+#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003)
+
+
+/* RAID Action Reply ActionData union */
+typedef union _MPI2_RAID_ACTION_REPLY_DATA
+{
+ U32 Word[5];
+ MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator;
+ U16 VolDevHandle;
+ U8 VolumeState;
+ U8 PhysDiskNum;
+} MPI2_RAID_ACTION_REPLY_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY_DATA,
+ Mpi2RaidActionReplyData_t, MPI2_POINTER pMpi2RaidActionReplyData_t;
+
+/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+
+/* RAID Action Reply Message */
+typedef struct _MPI2_RAID_ACTION_REPLY
+{
+ U8 Action; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 VolDevHandle; /* 0x04 */
+ U8 PhysDiskNum; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U16 Reserved3; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ MPI2_RAID_ACTION_REPLY_DATA ActionData; /* 0x14 */
+} MPI2_RAID_ACTION_REPLY, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY,
+ Mpi2RaidActionReply_t, MPI2_POINTER pMpi2RaidActionReply_t;
+
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
new file mode 100644
index 000000000000..8a42b136cf53
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2000-2007 LSI Corporation.
+ *
+ *
+ * Name: mpi2_sas.h
+ * Title: MPI Serial Attached SCSI structures and definitions
+ * Creation Date: February 9, 2007
+ *
+ * mpi2.h Version: 02.00.02
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit
+ * Control Request.
+ * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
+ * Request.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_SAS_H
+#define MPI2_SAS_H
+
+/*
+ * Values for SASStatus.
+ */
+#define MPI2_SASSTATUS_SUCCESS (0x00)
+#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01)
+#define MPI2_SASSTATUS_INVALID_FRAME (0x02)
+#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03)
+#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04)
+#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05)
+#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06)
+#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07)
+#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08)
+#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09)
+#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A)
+#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B)
+#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C)
+#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D)
+#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E)
+#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F)
+#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10)
+#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11)
+#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12)
+#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13)
+#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14)
+
+
+/*
+ * Values for the SAS DeviceInfo field used in SAS Device Status Change Event
+ * data and SAS Configuration pages.
+ */
+#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000)
+#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000)
+#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000)
+#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800)
+#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400)
+#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200)
+#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100)
+#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080)
+#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040)
+#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020)
+#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010)
+#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008)
+
+#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007)
+#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000)
+#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001)
+#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002)
+#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003)
+
+
+/*****************************************************************************
+*
+* SAS Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SMP Passthrough messages
+****************************************************************************/
+
+/* SMP Passthrough Request Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST
+{
+ U8 PassthroughFlags; /* 0x00 */
+ U8 PhysicalPort; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 RequestDataLength; /* 0x04 */
+ U8 SGLFlags; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U32 Reserved2; /* 0x0C */
+ U64 SASAddress; /* 0x10 */
+ U32 Reserved3; /* 0x18 */
+ U32 Reserved4; /* 0x1C */
+ MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */
+} MPI2_SMP_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REQUEST,
+ Mpi2SmpPassthroughRequest_t, MPI2_POINTER pMpi2SmpPassthroughRequest_t;
+
+/* values for PassthroughFlags field */
+#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80)
+
+/* values for SGLFlags field are in the SGL section of mpi2.h */
+
+
+/* SMP Passthrough Reply Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REPLY
+{
+ U8 PassthroughFlags; /* 0x00 */
+ U8 PhysicalPort; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 ResponseDataLength; /* 0x04 */
+ U8 SGLFlags; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U8 Reserved2; /* 0x0C */
+ U8 SASStatus; /* 0x0D */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 Reserved3; /* 0x14 */
+ U8 ResponseData[4]; /* 0x18 */
+} MPI2_SMP_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REPLY,
+ Mpi2SmpPassthroughReply_t, MPI2_POINTER pMpi2SmpPassthroughReply_t;
+
+/* values for PassthroughFlags field */
+#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80)
+
+/* values for SASStatus field are at the top of this file */
+
+
+/****************************************************************************
+* SATA Passthrough messages
+****************************************************************************/
+
+/* SATA Passthrough Request Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST
+{
+ U16 DevHandle; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 PassthroughFlags; /* 0x04 */
+ U8 SGLFlags; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U32 Reserved2; /* 0x0C */
+ U32 Reserved3; /* 0x10 */
+ U32 Reserved4; /* 0x14 */
+ U32 DataLength; /* 0x18 */
+ U8 CommandFIS[20]; /* 0x1C */
+ MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */
+} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
+ Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t;
+
+/* values for PassthroughFlags field */
+#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001)
+
+/* values for SGLFlags field are in the SGL section of mpi2.h */
+
+
+/* SATA Passthrough Reply Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REPLY
+{
+ U16 DevHandle; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 PassthroughFlags; /* 0x04 */
+ U8 SGLFlags; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U8 Reserved2; /* 0x0C */
+ U8 SASStatus; /* 0x0D */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U8 StatusFIS[20]; /* 0x14 */
+ U32 StatusControlRegisters; /* 0x28 */
+ U32 TransferCount; /* 0x2C */
+} MPI2_SATA_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REPLY,
+ Mpi2SataPassthroughReply_t, MPI2_POINTER pMpi2SataPassthroughReply_t;
+
+/* values for SASStatus field are at the top of this file */
+
+
+/****************************************************************************
+* SAS IO Unit Control messages
+****************************************************************************/
+
+/* SAS IO Unit Control Request Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST
+{
+ U8 Operation; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 DevHandle; /* 0x04 */
+ U8 IOCParameter; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U8 PhyNum; /* 0x0E */
+ U8 PrimFlags; /* 0x0F */
+ U32 Primitive; /* 0x10 */
+ U8 LookupMethod; /* 0x14 */
+ U8 Reserved5; /* 0x15 */
+ U16 SlotNumber; /* 0x16 */
+ U64 LookupAddress; /* 0x18 */
+ U32 IOCParameterValue; /* 0x20 */
+ U32 Reserved7; /* 0x24 */
+ U32 Reserved8; /* 0x28 */
+} MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ Mpi2SasIoUnitControlRequest_t, MPI2_POINTER pMpi2SasIoUnitControlRequest_t;
+
+/* values for the Operation field */
+#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02)
+#define MPI2_SAS_OP_PHY_LINK_RESET (0x06)
+#define MPI2_SAS_OP_PHY_HARD_RESET (0x07)
+#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08)
+#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A)
+#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B)
+#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C)
+#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D)
+#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E)
+#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F)
+#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80)
+
+/* values for the PrimFlags field */
+#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08)
+#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02)
+#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01)
+
+/* values for the LookupMethod field */
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+
+
+/* SAS IO Unit Control Reply Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY
+{
+ U8 Operation; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 DevHandle; /* 0x04 */
+ U8 IOCParameter; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ Mpi2SasIoUnitControlReply_t, MPI2_POINTER pMpi2SasIoUnitControlReply_t;
+
+
+#endif
+
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
new file mode 100644
index 000000000000..2ff4e936bd39
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2000-2008 LSI Corporation.
+ *
+ *
+ * Name: mpi2_tool.h
+ * Title: MPI diagnostic tool structures and definitions
+ * Creation Date: March 26, 2007
+ *
+ * mpi2_tool.h Version: 02.00.02
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release
+ * structures and defines.
+ * 02-29-08 02.00.02 Modified various names to make them 32-character unique.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_TOOL_H
+#define MPI2_TOOL_H
+
+/*****************************************************************************
+*
+* Toolbox Messages
+*
+*****************************************************************************/
+
+/* defines for the Tools */
+#define MPI2_TOOLBOX_CLEAN_TOOL (0x00)
+#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01)
+#define MPI2_TOOLBOX_BEACON_TOOL (0x05)
+
+/****************************************************************************
+* Toolbox reply
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_REPLY
+{
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_TOOLBOX_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_REPLY,
+ Mpi2ToolboxReply_t, MPI2_POINTER pMpi2ToolboxReply_t;
+
+
+/****************************************************************************
+* Toolbox Clean Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST
+{
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U32 Flags; /* 0x0C */
+ } MPI2_TOOLBOX_CLEAN_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_CLEAN_REQUEST,
+ Mpi2ToolboxCleanRequest_t, MPI2_POINTER pMpi2ToolboxCleanRequest_t;
+
+/* values for the Flags field */
+#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000)
+#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000)
+#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000)
+#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000)
+#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000)
+#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000)
+#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000)
+#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004)
+#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002)
+#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001)
+
+
+/****************************************************************************
+* Toolbox Memory Move request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST
+{
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ MPI2_SGE_SIMPLE_UNION SGL; /* 0x0C */
+} MPI2_TOOLBOX_MEM_MOVE_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST,
+ Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t;
+
+
+/****************************************************************************
+* Toolbox Beacon Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_BEACON_REQUEST
+{
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U8 Reserved5; /* 0x0C */
+ U8 PhysicalPort; /* 0x0D */
+ U8 Reserved6; /* 0x0E */
+ U8 Flags; /* 0x0F */
+} MPI2_TOOLBOX_BEACON_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_BEACON_REQUEST,
+ Mpi2ToolboxBeaconRequest_t, MPI2_POINTER pMpi2ToolboxBeaconRequest_t;
+
+/* values for the Flags field */
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00)
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01)
+
+
+/*****************************************************************************
+*
+* Diagnostic Buffer Messages
+*
+*****************************************************************************/
+
+
+/****************************************************************************
+* Diagnostic Buffer Post request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
+{
+ U8 Reserved1; /* 0x00 */
+ U8 BufferType; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U64 BufferAddress; /* 0x0C */
+ U32 BufferLength; /* 0x14 */
+ U32 Reserved5; /* 0x18 */
+ U32 Reserved6; /* 0x1C */
+ U32 Flags; /* 0x20 */
+ U32 ProductSpecific[23]; /* 0x24 */
+} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
+ Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t;
+
+/* values for the BufferType field */
+#define MPI2_DIAG_BUF_TYPE_TRACE (0x00)
+#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01)
+/* count of the number of buffer types */
+#define MPI2_DIAG_BUF_TYPE_COUNT (0x02)
+
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REPLY
+{
+ U8 Reserved1; /* 0x00 */
+ U8 BufferType; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 TransferLength; /* 0x14 */
+} MPI2_DIAG_BUFFER_POST_REPLY, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REPLY,
+ Mpi2DiagBufferPostReply_t, MPI2_POINTER pMpi2DiagBufferPostReply_t;
+
+
+/****************************************************************************
+* Diagnostic Release request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REQUEST
+{
+ U8 Reserved1; /* 0x00 */
+ U8 BufferType; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+} MPI2_DIAG_RELEASE_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REQUEST,
+ Mpi2DiagReleaseRequest_t, MPI2_POINTER pMpi2DiagReleaseRequest_t;
+
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REPLY
+{
+ U8 Reserved1; /* 0x00 */
+ U8 BufferType; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_DIAG_RELEASE_REPLY, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REPLY,
+ Mpi2DiagReleaseReply_t, MPI2_POINTER pMpi2DiagReleaseReply_t;
+
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_type.h
new file mode 100644
index 000000000000..cfde017bf16e
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpi/mpi2_type.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2000-2007 LSI Corporation.
+ *
+ *
+ * Name: mpi2_type.h
+ * Title: MPI basic type definitions
+ * Creation Date: August 16, 2006
+ *
+ * mpi2_type.h Version: 02.00.00
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_TYPE_H
+#define MPI2_TYPE_H
+
+
+/*******************************************************************************
+ * Define MPI2_POINTER if it hasn't already been defined. By default
+ * MPI2_POINTER is defined to be a near pointer. MPI2_POINTER can be defined as
+ * a far pointer by defining MPI2_POINTER as "far *" before this header file is
+ * included.
+ */
+#ifndef MPI2_POINTER
+#define MPI2_POINTER *
+#endif
+
+/* the basic types may have already been included by mpi_type.h */
+#ifndef MPI_TYPE_H
+/*****************************************************************************
+*
+* Basic Types
+*
+*****************************************************************************/
+
+typedef u8 U8;
+typedef __le16 U16;
+typedef __le32 U32;
+typedef __le64 U64 __attribute__((aligned(4)));
+
+/*****************************************************************************
+*
+* Pointer Types
+*
+*****************************************************************************/
+
+typedef U8 *PU8;
+typedef U16 *PU16;
+typedef U32 *PU32;
+typedef U64 *PU64;
+
+#endif
+
+#endif
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_base.c b/trunk/drivers/scsi/mpt2sas/mpt2sas_base.c
new file mode 100644
index 000000000000..52427a8324f5
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -0,0 +1,3435 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_base.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "mpt2sas_base.h"
+
+static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS];
+
+#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
+#define MPT2SAS_MAX_REQUEST_QUEUE 500 /* maximum controller queue depth */
+
+static int max_queue_depth = -1;
+module_param(max_queue_depth, int, 0);
+MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
+
+static int max_sgl_entries = -1;
+module_param(max_sgl_entries, int, 0);
+MODULE_PARM_DESC(max_sgl_entries, " max sg entries ");
+
+static int msix_disable = -1;
+module_param(msix_disable, int, 0);
+MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
+
+/**
+ * _base_fault_reset_work - workq handling ioc fault conditions
+ * @work: input argument, used to derive ioc
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+static void
+_base_fault_reset_work(struct work_struct *work)
+{
+ struct MPT2SAS_ADAPTER *ioc =
+ container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work);
+ unsigned long flags;
+ u32 doorbell;
+ int rc;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->ioc_reset_in_progress)
+ goto rearm_timer;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ doorbell = mpt2sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ printk(MPT2SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name,
+ __func__, (rc == 0) ? "success" : "failed");
+ doorbell = mpt2sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+ mpt2sas_base_fault_info(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ }
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ rearm_timer:
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _base_sas_ioc_info - verbose translation of the ioc status
+ * @ioc: pointer to scsi command object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @request_hdr: request mf
+ *
+ * Return nothing.
+ */
+static void
+_base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
+ MPI2RequestHeader_t *request_hdr)
+{
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ char *desc = NULL;
+ u16 frame_sz;
+ char *func_str = NULL;
+
+ /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */
+ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return;
+
+ switch (ioc_status) {
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_BUSY:
+ desc = "busy";
+ break;
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ desc = "invalid sgl";
+ break;
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ desc = "internal error";
+ break;
+ case MPI2_IOCSTATUS_INVALID_VPID:
+ desc = "invalid vpid";
+ break;
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ desc = "insufficient resources";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ desc = "invalid field";
+ break;
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ desc = "invalid state";
+ break;
+ case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
+ desc = "op state not supported";
+ break;
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION:
+ desc = "config invalid action";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE:
+ desc = "config invalid type";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE:
+ desc = "config invalid page";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_DATA:
+ desc = "config invalid data";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS:
+ desc = "config no defaults";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT:
+ desc = "config cant commit";
+ break;
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ break;
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc = "eedp app tag error";
+ break;
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX:
+ desc = "target invalid io index";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ABORTED:
+ desc = "target aborted";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE:
+ desc = "target no conn retryable";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONNECTION:
+ desc = "target no connection";
+ break;
+ case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
+ desc = "target xfer count mismatch";
+ break;
+ case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
+ desc = "target data offset error";
+ break;
+ case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
+ desc = "target too much write data";
+ break;
+ case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT:
+ desc = "target iu too short";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT:
+ desc = "target ack nak timeout";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED:
+ desc = "target nak received";
+ break;
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED:
+ desc = "smp request failed";
+ break;
+ case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN:
+ desc = "smp data overrun";
+ break;
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED:
+ desc = "diagnostic released";
+ break;
+ default:
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ switch (request_hdr->Function) {
+ case MPI2_FUNCTION_CONFIG:
+ frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size;
+ func_str = "config_page";
+ break;
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t);
+ func_str = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t);
+ func_str = "sas_iounit_ctl";
+ break;
+ case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
+ frame_sz = sizeof(Mpi2SepRequest_t);
+ func_str = "enclosure";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ frame_sz = sizeof(Mpi2IOCInitRequest_t);
+ func_str = "ioc_init";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ frame_sz = sizeof(Mpi2PortEnableRequest_t);
+ func_str = "port_enable";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size;
+ func_str = "smp_passthru";
+ break;
+ default:
+ frame_sz = 32;
+ func_str = "unknown";
+ break;
+ }
+
+ printk(MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p),"
+ " (%s)\n", ioc->name, desc, ioc_status, request_hdr, func_str);
+
+ _debug_dump_mf(request_hdr, frame_sz/4);
+}
+
+/**
+ * _base_display_event_data - verbose translation of firmware asyn events
+ * @ioc: pointer to scsi command object
+ * @mpi_reply: reply mf payload returned from firmware
+ *
+ * Return nothing.
+ */
+static void
+_base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ char *desc = NULL;
+ u16 event;
+
+ if (!(ioc->logging_level & MPT_DEBUG_EVENTS))
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ switch (event) {
+ case MPI2_EVENT_LOG_DATA:
+ desc = "Log Data";
+ break;
+ case MPI2_EVENT_STATE_CHANGE:
+ desc = "Status Change";
+ break;
+ case MPI2_EVENT_HARD_RESET_RECEIVED:
+ desc = "Hard Reset Received";
+ break;
+ case MPI2_EVENT_EVENT_CHANGE:
+ desc = "Event Change";
+ break;
+ case MPI2_EVENT_TASK_SET_FULL:
+ desc = "Task Set Full";
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ desc = "Device Status Change";
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ desc = "IR Operation Status";
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ desc = "Discovery";
+ break;
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ desc = "SAS Broadcast Primitive";
+ break;
+ case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
+ desc = "SAS Init Device Status Change";
+ break;
+ case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW:
+ desc = "SAS Init Table Overflow";
+ break;
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ desc = "SAS Topology Change List";
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ desc = "SAS Enclosure Device Status Change";
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ desc = "IR Volume";
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ desc = "IR Physical Disk";
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ desc = "IR Configuration Change List";
+ break;
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ desc = "Log Entry Added";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, desc);
+}
+#endif
+
+/**
+ * _base_sas_log_info - verbose translation of firmware log info
+ * @ioc: pointer to scsi command object
+ * @log_info: log info
+ *
+ * Return nothing.
+ */
+static void
+_base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
+{
+ union loginfo_type {
+ u32 loginfo;
+ struct {
+ u32 subcode:16;
+ u32 code:8;
+ u32 originator:4;
+ u32 bus_type:4;
+ } dw;
+ };
+ union loginfo_type sas_loginfo;
+ char *originator_str = NULL;
+
+ sas_loginfo.loginfo = log_info;
+ if (sas_loginfo.dw.bus_type != 3 /*SAS*/)
+ return;
+
+ /* eat the loginfos associated with task aborts */
+ if (ioc->ignore_loginfos && (log_info == 30050000 || log_info ==
+ 0x31140000 || log_info == 0x31130000))
+ return;
+
+ switch (sas_loginfo.dw.originator) {
+ case 0:
+ originator_str = "IOP";
+ break;
+ case 1:
+ originator_str = "PL";
+ break;
+ case 2:
+ originator_str = "IR";
+ break;
+ }
+
+ printk(MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), "
+ "code(0x%02x), sub_code(0x%04x)\n", ioc->name, log_info,
+ originator_str, sas_loginfo.dw.code,
+ sas_loginfo.dw.subcode);
+}
+
+/**
+ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: pointer to scsi command object
+ * @fault_code: fault code
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
+{
+ printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
+ ioc->name, fault_code);
+}
+
+/**
+ * _base_display_reply_info -
+ * @ioc: pointer to scsi command object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return nothing.
+ */
+static void
+_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ u16 ioc_status;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if ((ioc_status & MPI2_IOCSTATUS_MASK) &&
+ (ioc->logging_level & MPT_DEBUG_REPLY)) {
+ _base_sas_ioc_info(ioc , mpi_reply,
+ mpt2sas_base_get_msg_frame(ioc, smid));
+ }
+#endif
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ _base_sas_log_info(ioc, le32_to_cpu(mpi_reply->IOCLogInfo));
+}
+
+/**
+ * mpt2sas_base_done - base internal command completion routine
+ * @ioc: pointer to scsi command object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
+ return;
+
+ if (ioc->base_cmds.status == MPT2_CMD_NOT_USED)
+ return;
+
+ ioc->base_cmds.status |= MPT2_CMD_COMPLETE;
+ if (mpi_reply) {
+ ioc->base_cmds.status |= MPT2_CMD_REPLY_VALID;
+ memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ }
+ ioc->base_cmds.status &= ~MPT2_CMD_PENDING;
+ complete(&ioc->base_cmds.done);
+}
+
+/**
+ * _base_async_event - main callback handler for firmware asyn events
+ * @ioc: pointer to scsi command object
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return nothing.
+ */
+static void
+_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+ Mpi2EventAckRequest_t *ack_request;
+ u16 smid;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (!mpi_reply)
+ return;
+ if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return;
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ _base_display_event_data(ioc, mpi_reply);
+#endif
+ if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED))
+ goto out;
+ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ goto out;
+ }
+
+ ack_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t));
+ ack_request->Function = MPI2_FUNCTION_EVENT_ACK;
+ ack_request->Event = mpi_reply->Event;
+ ack_request->EventContext = mpi_reply->EventContext;
+ ack_request->VF_ID = VF_ID;
+ mpt2sas_base_put_smid_default(ioc, smid, VF_ID);
+
+ out:
+
+ /* scsih callback handler */
+ mpt2sas_scsih_event_callback(ioc, VF_ID, reply);
+
+ /* ctl callback handler */
+ mpt2sas_ctl_event_callback(ioc, VF_ID, reply);
+}
+
+/**
+ * _base_mask_interrupts - disable interrupts
+ * @ioc: pointer to scsi command object
+ *
+ * Disabling ResetIRQ, Reply and Doorbell Interrupts
+ *
+ * Return nothing.
+ */
+static void
+_base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ ioc->mask_interrupts = 1;
+ him_register = readl(&ioc->chip->HostInterruptMask);
+ him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ readl(&ioc->chip->HostInterruptMask);
+}
+
+/**
+ * _base_unmask_interrupts - enable interrupts
+ * @ioc: pointer to scsi command object
+ *
+ * Enabling only Reply Interrupts
+ *
+ * Return nothing.
+ */
+static void
+_base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ writel(0, &ioc->chip->HostInterruptStatus);
+ him_register = readl(&ioc->chip->HostInterruptMask);
+ him_register &= ~MPI2_HIM_RIM;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ ioc->mask_interrupts = 0;
+}
+
+/**
+ * _base_interrupt - MPT adapter (IOC) specific interrupt handler.
+ * @irq: irq number (not used)
+ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
+ * @r: pt_regs pointer (not used)
+ *
+ * Return IRQ_HANDLE if processed, else IRQ_NONE.
+ */
+static irqreturn_t
+_base_interrupt(int irq, void *bus_id)
+{
+ u32 post_index, post_index_next, completed_cmds;
+ u8 request_desript_type;
+ u16 smid;
+ u8 cb_idx;
+ u32 reply;
+ u8 VF_ID;
+ int i;
+ struct MPT2SAS_ADAPTER *ioc = bus_id;
+
+ if (ioc->mask_interrupts)
+ return IRQ_NONE;
+
+ post_index = ioc->reply_post_host_index;
+ request_desript_type = ioc->reply_post_free[post_index].
+ Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ return IRQ_NONE;
+
+ completed_cmds = 0;
+ do {
+ if (ioc->reply_post_free[post_index].Words == ~0ULL)
+ goto out;
+ reply = 0;
+ cb_idx = 0xFF;
+ smid = le16_to_cpu(ioc->reply_post_free[post_index].
+ Default.DescriptorTypeDependent1);
+ VF_ID = ioc->reply_post_free[post_index].
+ Default.VF_ID;
+ if (request_desript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
+ reply = le32_to_cpu(ioc->reply_post_free[post_index].
+ AddressReply.ReplyFrameAddress);
+ } else if (request_desript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER)
+ goto next;
+ else if (request_desript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS)
+ goto next;
+ if (smid)
+ cb_idx = ioc->scsi_lookup[smid - 1].cb_idx;
+ if (smid && cb_idx != 0xFF) {
+ mpt_callbacks[cb_idx](ioc, smid, VF_ID, reply);
+ if (reply)
+ _base_display_reply_info(ioc, smid, VF_ID,
+ reply);
+ mpt2sas_base_free_smid(ioc, smid);
+ }
+ if (!smid)
+ _base_async_event(ioc, VF_ID, reply);
+
+ /* reply free queue handling */
+ if (reply) {
+ ioc->reply_free_host_index =
+ (ioc->reply_free_host_index ==
+ (ioc->reply_free_queue_depth - 1)) ?
+ 0 : ioc->reply_free_host_index + 1;
+ ioc->reply_free[ioc->reply_free_host_index] =
+ cpu_to_le32(reply);
+ writel(ioc->reply_free_host_index,
+ &ioc->chip->ReplyFreeHostIndex);
+ wmb();
+ }
+
+ next:
+ post_index_next = (post_index == (ioc->reply_post_queue_depth -
+ 1)) ? 0 : post_index + 1;
+ request_desript_type =
+ ioc->reply_post_free[post_index_next].Default.ReplyFlags
+ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ completed_cmds++;
+ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ goto out;
+ post_index = post_index_next;
+ } while (1);
+
+ out:
+
+ if (!completed_cmds)
+ return IRQ_NONE;
+
+ /* reply post descriptor handling */
+ post_index_next = ioc->reply_post_host_index;
+ for (i = 0 ; i < completed_cmds; i++) {
+ post_index = post_index_next;
+ /* poison the reply post descriptor */
+ ioc->reply_post_free[post_index_next].Words = ~0ULL;
+ post_index_next = (post_index ==
+ (ioc->reply_post_queue_depth - 1))
+ ? 0 : post_index + 1;
+ }
+ ioc->reply_post_host_index = post_index_next;
+ writel(post_index_next, &ioc->chip->ReplyPostHostIndex);
+ wmb();
+ return IRQ_HANDLED;
+}
+
+/**
+ * mpt2sas_base_release_callback_handler - clear interupt callback handler
+ * @cb_idx: callback index
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_release_callback_handler(u8 cb_idx)
+{
+ mpt_callbacks[cb_idx] = NULL;
+}
+
+/**
+ * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler
+ * @cb_func: callback function
+ *
+ * Returns cb_func.
+ */
+u8
+mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func)
+{
+ u8 cb_idx;
+
+ for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--)
+ if (mpt_callbacks[cb_idx] == NULL)
+ break;
+
+ mpt_callbacks[cb_idx] = cb_func;
+ return cb_idx;
+}
+
+/**
+ * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_initialize_callback_handler(void)
+{
+ u8 cb_idx;
+
+ for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++)
+ mpt2sas_base_release_callback_handler(cb_idx);
+}
+
+/**
+ * mpt2sas_base_build_zero_len_sge - build zero length sg entry
+ * @ioc: per adapter object
+ * @paddr: virtual address for SGE
+ *
+ * Create a zero length scatter gather entry to insure the IOCs hardware has
+ * something to use if the target device goes brain dead and tries
+ * to send data even when none is asked for.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr)
+{
+ u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST |
+ MPI2_SGE_FLAGS_SIMPLE_ELEMENT) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ ioc->base_add_sg_single(paddr, flags_length, -1);
+}
+
+/**
+ * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * Return nothing.
+ */
+static void
+_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple32_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le32(dma_addr);
+}
+
+
+/**
+ * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * Return nothing.
+ */
+static void
+_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple64_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le64(dma_addr);
+}
+
+#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10))
+
+/**
+ * _base_config_dma_addressing - set dma addressing
+ * @ioc: per adapter object
+ * @pdev: PCI device struct
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev)
+{
+ struct sysinfo s;
+ char *desc = NULL;
+
+ if (sizeof(dma_addr_t) > 4) {
+ const uint64_t required_mask =
+ dma_get_required_mask(&pdev->dev);
+ if ((required_mask > DMA_32BIT_MASK) && !pci_set_dma_mask(pdev,
+ DMA_64BIT_MASK) && !pci_set_consistent_dma_mask(pdev,
+ DMA_64BIT_MASK)) {
+ ioc->base_add_sg_single = &_base_add_sg_single_64;
+ ioc->sge_size = sizeof(Mpi2SGESimple64_t);
+ desc = "64";
+ goto out;
+ }
+ }
+
+ if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK)
+ && !pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
+ ioc->base_add_sg_single = &_base_add_sg_single_32;
+ ioc->sge_size = sizeof(Mpi2SGESimple32_t);
+ desc = "32";
+ } else
+ return -ENODEV;
+
+ out:
+ si_meminfo(&s);
+ printk(MPT2SAS_INFO_FMT "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, "
+ "total mem (%ld kB)\n", ioc->name, desc, convert_to_kb(s.totalram));
+
+ return 0;
+}
+
+/**
+ * _base_save_msix_table - backup msix vector table
+ * @ioc: per adapter object
+ *
+ * This address an errata where diag reset clears out the table
+ */
+static void
+_base_save_msix_table(struct MPT2SAS_ADAPTER *ioc)
+{
+ int i;
+
+ if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
+ return;
+
+ for (i = 0; i < ioc->msix_vector_count; i++)
+ ioc->msix_table_backup[i] = ioc->msix_table[i];
+}
+
+/**
+ * _base_restore_msix_table - this restores the msix vector table
+ * @ioc: per adapter object
+ *
+ */
+static void
+_base_restore_msix_table(struct MPT2SAS_ADAPTER *ioc)
+{
+ int i;
+
+ if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
+ return;
+
+ for (i = 0; i < ioc->msix_vector_count; i++)
+ ioc->msix_table[i] = ioc->msix_table_backup[i];
+}
+
+/**
+ * _base_check_enable_msix - checks MSIX capabable.
+ * @ioc: per adapter object
+ *
+ * Check to see if card is capable of MSIX, and set number
+ * of avaliable msix vectors
+ */
+static int
+_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
+{
+ int base;
+ u16 message_control;
+ u32 msix_table_offset;
+
+ base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
+ if (!base) {
+ dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
+ "supported\n", ioc->name));
+ return -EINVAL;
+ }
+
+ /* get msix vector count */
+ pci_read_config_word(ioc->pdev, base + 2, &message_control);
+ ioc->msix_vector_count = (message_control & 0x3FF) + 1;
+
+ /* get msix table */
+ pci_read_config_dword(ioc->pdev, base + 4, &msix_table_offset);
+ msix_table_offset &= 0xFFFFFFF8;
+ ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset);
+
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, "
+ "vector_count(%d), table_offset(0x%08x), table(%p)\n", ioc->name,
+ ioc->msix_vector_count, msix_table_offset, ioc->msix_table));
+ return 0;
+}
+
+/**
+ * _base_disable_msix - disables msix
+ * @ioc: per adapter object
+ *
+ */
+static void
+_base_disable_msix(struct MPT2SAS_ADAPTER *ioc)
+{
+ if (ioc->msix_enable) {
+ pci_disable_msix(ioc->pdev);
+ kfree(ioc->msix_table_backup);
+ ioc->msix_table_backup = NULL;
+ ioc->msix_enable = 0;
+ }
+}
+
+/**
+ * _base_enable_msix - enables msix, failback to io_apic
+ * @ioc: per adapter object
+ *
+ */
+static int
+_base_enable_msix(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct msix_entry entries;
+ int r;
+ u8 try_msix = 0;
+
+ if (msix_disable == -1 || msix_disable == 0)
+ try_msix = 1;
+
+ if (!try_msix)
+ goto try_ioapic;
+
+ if (_base_check_enable_msix(ioc) != 0)
+ goto try_ioapic;
+
+ ioc->msix_table_backup = kcalloc(ioc->msix_vector_count,
+ sizeof(u32), GFP_KERNEL);
+ if (!ioc->msix_table_backup) {
+ dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for "
+ "msix_table_backup failed!!!\n", ioc->name));
+ goto try_ioapic;
+ }
+
+ memset(&entries, 0, sizeof(struct msix_entry));
+ r = pci_enable_msix(ioc->pdev, &entries, 1);
+ if (r) {
+ dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "pci_enable_msix "
+ "failed (r=%d) !!!\n", ioc->name, r));
+ goto try_ioapic;
+ }
+
+ r = request_irq(entries.vector, _base_interrupt, IRQF_SHARED,
+ ioc->name, ioc);
+ if (r) {
+ dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "unable to allocate "
+ "interrupt %d !!!\n", ioc->name, entries.vector));
+ pci_disable_msix(ioc->pdev);
+ goto try_ioapic;
+ }
+
+ ioc->pci_irq = entries.vector;
+ ioc->msix_enable = 1;
+ return 0;
+
+/* failback to io_apic interrupt routing */
+ try_ioapic:
+
+ r = request_irq(ioc->pdev->irq, _base_interrupt, IRQF_SHARED,
+ ioc->name, ioc);
+ if (r) {
+ printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n",
+ ioc->name, ioc->pdev->irq);
+ r = -EBUSY;
+ goto out_fail;
+ }
+
+ ioc->pci_irq = ioc->pdev->irq;
+ return 0;
+
+ out_fail:
+ return r;
+}
+
+/**
+ * mpt2sas_base_map_resources - map in controller resources (io/irq/memap)
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+ u32 memap_sz;
+ u32 pio_sz;
+ int i, r = 0;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n",
+ ioc->name, __func__));
+
+ ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ if (pci_enable_device_mem(pdev)) {
+ printk(MPT2SAS_WARN_FMT "pci_enable_device_mem: "
+ "failed\n", ioc->name);
+ return -ENODEV;
+ }
+
+
+ if (pci_request_selected_regions(pdev, ioc->bars,
+ MPT2SAS_DRIVER_NAME)) {
+ printk(MPT2SAS_WARN_FMT "pci_request_selected_regions: "
+ "failed\n", ioc->name);
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+ pci_set_master(pdev);
+
+ if (_base_config_dma_addressing(ioc, pdev) != 0) {
+ printk(MPT2SAS_WARN_FMT "no suitable DMA mask for %s\n",
+ ioc->name, pci_name(pdev));
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+ for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) {
+ if (pio_sz)
+ continue;
+ ioc->pio_chip = pci_resource_start(pdev, i);
+ pio_sz = pci_resource_len(pdev, i);
+ } else {
+ if (memap_sz)
+ continue;
+ ioc->chip_phys = pci_resource_start(pdev, i);
+ memap_sz = pci_resource_len(pdev, i);
+ ioc->chip = ioremap(ioc->chip_phys, memap_sz);
+ if (ioc->chip == NULL) {
+ printk(MPT2SAS_ERR_FMT "unable to map adapter "
+ "memory!\n", ioc->name);
+ r = -EINVAL;
+ goto out_fail;
+ }
+ }
+ }
+
+ pci_set_drvdata(pdev, ioc->shost);
+ _base_mask_interrupts(ioc);
+ r = _base_enable_msix(ioc);
+ if (r)
+ goto out_fail;
+
+ printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n",
+ ioc->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
+ "IO-APIC enabled"), ioc->pci_irq);
+ printk(MPT2SAS_INFO_FMT "iomem(0x%lx), mapped(0x%p), size(%d)\n",
+ ioc->name, ioc->chip_phys, ioc->chip, memap_sz);
+ printk(MPT2SAS_INFO_FMT "ioport(0x%lx), size(%d)\n",
+ ioc->name, ioc->pio_chip, pio_sz);
+
+ return 0;
+
+ out_fail:
+ if (ioc->chip_phys)
+ iounmap(ioc->chip);
+ ioc->chip_phys = 0;
+ ioc->pci_irq = -1;
+ pci_release_selected_regions(ioc->pdev, ioc->bars);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ return r;
+}
+
+/**
+ * mpt2sas_base_get_msg_frame_dma - obtain request mf pointer phys addr
+ * @ioc: per adapter object
+ * @smid: system request message index(smid zero is invalid)
+ *
+ * Returns phys pointer to message frame.
+ */
+dma_addr_t
+mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->request_dma + (smid * ioc->request_sz);
+}
+
+/**
+ * mpt2sas_base_get_msg_frame - obtain request mf pointer
+ * @ioc: per adapter object
+ * @smid: system request message index(smid zero is invalid)
+ *
+ * Returns virt pointer to message frame.
+ */
+void *
+mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->request + (smid * ioc->request_sz));
+}
+
+/**
+ * mpt2sas_base_get_sense_buffer - obtain a sense buffer assigned to a mf request
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns virt pointer to sense buffer.
+ */
+void *
+mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
+}
+
+/**
+ * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer assigned to a mf request
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns phys pointer to sense buffer.
+ */
+dma_addr_t
+mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE);
+}
+
+/**
+ * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address
+ * @ioc: per adapter object
+ * @phys_addr: lower 32 physical addr of the reply
+ *
+ * Converts 32bit lower physical addr into a virt address.
+ */
+void *
+mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+ return ioc->reply + (phys_addr - (u32)ioc->reply_dma);
+}
+
+/**
+ * mpt2sas_base_get_smid - obtain a free smid
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ *
+ * Returns smid (zero is invalid)
+ */
+u16
+mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx)
+{
+ unsigned long flags;
+ struct request_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ printk(MPT2SAS_ERR_FMT "%s: smid not available\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ request = list_entry(ioc->free_list.next,
+ struct request_tracker, tracker_list);
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+
+/**
+ * mpt2sas_base_free_smid - put smid back on free_list
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ ioc->scsi_lookup[smid - 1].cb_idx = 0xFF;
+ list_add_tail(&ioc->scsi_lookup[smid - 1].tracker_list,
+ &ioc->free_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ /*
+ * See _wait_for_commands_to_complete() call with regards to this code.
+ */
+ if (ioc->shost_recovery && ioc->pending_io_count) {
+ if (ioc->pending_io_count == 1)
+ wake_up(&ioc->reset_wq);
+ ioc->pending_io_count--;
+ }
+}
+
+/**
+ * _base_writeq - 64 bit write to MMIO
+ * @ioc: per adapter object
+ * @b: data payload
+ * @addr: address in MMIO space
+ * @writeq_lock: spin lock
+ *
+ * Glue for handling an atomic 64 bit word to MMIO. This special handling takes
+ * care of 32 bit environment where its not quarenteed to send the entire word
+ * in one transfer.
+ */
+#ifndef writeq
+static inline void _base_writeq(__u64 b, volatile void __iomem *addr,
+ spinlock_t *writeq_lock)
+{
+ unsigned long flags;
+ __u64 data_out = cpu_to_le64(b);
+
+ spin_lock_irqsave(writeq_lock, flags);
+ writel((u32)(data_out), addr);
+ writel((u32)(data_out >> 32), (addr + 4));
+ spin_unlock_irqrestore(writeq_lock, flags);
+}
+#else
+static inline void _base_writeq(__u64 b, volatile void __iomem *addr,
+ spinlock_t *writeq_lock)
+{
+ writeq(cpu_to_le64(b), addr);
+}
+#endif
+
+/**
+ * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @vf_id: virtual function id
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id,
+ u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+
+ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.SCSIIO.VF_ID = vf_id;
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+
+/**
+ * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @vf_id: virtual function id
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ u8 vf_id)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.HighPriority.RequestFlags =
+ MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ descriptor.HighPriority.VF_ID = vf_id;
+ descriptor.HighPriority.SMID = cpu_to_le16(smid);
+ descriptor.HighPriority.LMID = 0;
+ descriptor.HighPriority.Reserved1 = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt2sas_base_put_smid_default - Default, primarily used for config pages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @vf_id: virtual function id
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ descriptor.Default.VF_ID = vf_id;
+ descriptor.Default.SMID = cpu_to_le16(smid);
+ descriptor.Default.LMID = 0;
+ descriptor.Default.DescriptorTypeDependent = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @vf_id: virtual function id
+ * @io_index: value used to track the IO
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ u8 vf_id, u16 io_index)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.SCSITarget.RequestFlags =
+ MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET;
+ descriptor.SCSITarget.VF_ID = vf_id;
+ descriptor.SCSITarget.SMID = cpu_to_le16(smid);
+ descriptor.SCSITarget.LMID = 0;
+ descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index);
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_display_ioc_capabilities - Disply IOC's capabilities.
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
+{
+ int i = 0;
+ char desc[16];
+ u8 revision;
+ u32 iounit_pg1_flags;
+
+ pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
+ strncpy(desc, ioc->manu_pg0.ChipName, 16);
+ printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "
+ "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n",
+ ioc->name, desc,
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF,
+ revision,
+ (ioc->bios_pg3.BiosVersion & 0xFF000000) >> 24,
+ (ioc->bios_pg3.BiosVersion & 0x00FF0000) >> 16,
+ (ioc->bios_pg3.BiosVersion & 0x0000FF00) >> 8,
+ ioc->bios_pg3.BiosVersion & 0x000000FF);
+
+ printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name);
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
+ printk("Initiator");
+ i++;
+ }
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) {
+ printk("%sTarget", i ? "," : "");
+ i++;
+ }
+
+ i = 0;
+ printk("), ");
+ printk("Capabilities=(");
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
+ printk("Raid");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
+ printk("%sTLR", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) {
+ printk("%sMulticast", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) {
+ printk("%sBIDI Target", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) {
+ printk("%sEEDP", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
+ printk("%sSnapshot Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
+ printk("%sDiag Trace Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
+ printk("%sTask Set Full", i ? "," : "");
+ i++;
+ }
+
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) {
+ printk("%sNCQ", i ? "," : "");
+ i++;
+ }
+
+ printk(")\n");
+}
+
+/**
+ * _base_static_config_pages - static start of day config pages
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_static_config_pages(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ u32 iounit_pg1_flags;
+
+ mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0);
+ mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
+ mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
+ mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
+ mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0);
+ mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
+ _base_display_ioc_capabilities(ioc);
+
+ /*
+ * Enable task_set_full handling in iounit_pg1 when the
+ * facts capabilities indicate that its supported.
+ */
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if ((ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING))
+ iounit_pg1_flags &=
+ ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ else
+ iounit_pg1_flags |=
+ MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
+ mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, ioc->iounit_pg1);
+}
+
+/**
+ * _base_release_memory_pools - release memory
+ * @ioc: per adapter object
+ *
+ * Free memory allocated from _base_allocate_memory_pools.
+ *
+ * Return nothing.
+ */
+static void
+_base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc)
+{
+ dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ if (ioc->request) {
+ pci_free_consistent(ioc->pdev, ioc->request_dma_sz,
+ ioc->request, ioc->request_dma);
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "request_pool(0x%p)"
+ ": free\n", ioc->name, ioc->request));
+ ioc->request = NULL;
+ }
+
+ if (ioc->sense) {
+ pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma);
+ if (ioc->sense_dma_pool)
+ pci_pool_destroy(ioc->sense_dma_pool);
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_pool(0x%p)"
+ ": free\n", ioc->name, ioc->sense));
+ ioc->sense = NULL;
+ }
+
+ if (ioc->reply) {
+ pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma);
+ if (ioc->reply_dma_pool)
+ pci_pool_destroy(ioc->reply_dma_pool);
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_pool(0x%p)"
+ ": free\n", ioc->name, ioc->reply));
+ ioc->reply = NULL;
+ }
+
+ if (ioc->reply_free) {
+ pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free,
+ ioc->reply_free_dma);
+ if (ioc->reply_free_dma_pool)
+ pci_pool_destroy(ioc->reply_free_dma_pool);
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_pool"
+ "(0x%p): free\n", ioc->name, ioc->reply_free));
+ ioc->reply_free = NULL;
+ }
+
+ if (ioc->reply_post_free) {
+ pci_pool_free(ioc->reply_post_free_dma_pool,
+ ioc->reply_post_free, ioc->reply_post_free_dma);
+ if (ioc->reply_post_free_dma_pool)
+ pci_pool_destroy(ioc->reply_post_free_dma_pool);
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "reply_post_free_pool(0x%p): free\n", ioc->name,
+ ioc->reply_post_free));
+ ioc->reply_post_free = NULL;
+ }
+
+ if (ioc->config_page) {
+ dexitprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "config_page(0x%p): free\n", ioc->name,
+ ioc->config_page));
+ pci_free_consistent(ioc->pdev, ioc->config_page_sz,
+ ioc->config_page, ioc->config_page_dma);
+ }
+
+ kfree(ioc->scsi_lookup);
+}
+
+
+/**
+ * _base_allocate_memory_pools - allocate start of day memory pools
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 success, anything else error
+ */
+static int
+_base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2IOCFactsReply_t *facts;
+ u32 queue_size, queue_diff;
+ u16 max_sge_elements;
+ u16 num_of_reply_frames;
+ u16 chains_needed_per_io;
+ u32 sz, total_sz;
+ u16 i;
+ u32 retry_sz;
+ u16 max_request_credit;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ retry_sz = 0;
+ facts = &ioc->facts;
+
+ /* command line tunables for max sgl entries */
+ if (max_sgl_entries != -1) {
+ ioc->shost->sg_tablesize = (max_sgl_entries <
+ MPT2SAS_SG_DEPTH) ? max_sgl_entries :
+ MPT2SAS_SG_DEPTH;
+ } else {
+ ioc->shost->sg_tablesize = MPT2SAS_SG_DEPTH;
+ }
+
+ /* command line tunables for max controller queue depth */
+ if (max_queue_depth != -1) {
+ max_request_credit = (max_queue_depth < facts->RequestCredit)
+ ? max_queue_depth : facts->RequestCredit;
+ } else {
+ max_request_credit = (facts->RequestCredit >
+ MPT2SAS_MAX_REQUEST_QUEUE) ? MPT2SAS_MAX_REQUEST_QUEUE :
+ facts->RequestCredit;
+ }
+ ioc->request_depth = max_request_credit;
+
+ /* request frame size */
+ ioc->request_sz = facts->IOCRequestFrameSize * 4;
+
+ /* reply frame size */
+ ioc->reply_sz = facts->ReplyFrameSize * 4;
+
+ retry_allocation:
+ total_sz = 0;
+ /* calculate number of sg elements left over in the 1st frame */
+ max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) -
+ sizeof(Mpi2SGEIOUnion_t)) + ioc->sge_size);
+ ioc->max_sges_in_main_message = max_sge_elements/ioc->sge_size;
+
+ /* now do the same for a chain buffer */
+ max_sge_elements = ioc->request_sz - ioc->sge_size;
+ ioc->max_sges_in_chain_message = max_sge_elements/ioc->sge_size;
+
+ ioc->chain_offset_value_for_main_message =
+ ((sizeof(Mpi2SCSIIORequest_t) - sizeof(Mpi2SGEIOUnion_t)) +
+ (ioc->max_sges_in_chain_message * ioc->sge_size)) / 4;
+
+ /*
+ * MPT2SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE
+ */
+ chains_needed_per_io = ((ioc->shost->sg_tablesize -
+ ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message)
+ + 1;
+ if (chains_needed_per_io > facts->MaxChainDepth) {
+ chains_needed_per_io = facts->MaxChainDepth;
+ ioc->shost->sg_tablesize = min_t(u16,
+ ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message
+ * chains_needed_per_io), ioc->shost->sg_tablesize);
+ }
+ ioc->chains_needed_per_io = chains_needed_per_io;
+
+ /* reply free queue sizing - taking into account for events */
+ num_of_reply_frames = ioc->request_depth + 32;
+
+ /* number of replies frames can't be a multiple of 16 */
+ /* decrease number of reply frames by 1 */
+ if (!(num_of_reply_frames % 16))
+ num_of_reply_frames--;
+
+ /* calculate number of reply free queue entries
+ * (must be multiple of 16)
+ */
+
+ /* (we know reply_free_queue_depth is not a multiple of 16) */
+ queue_size = num_of_reply_frames;
+ queue_size += 16 - (queue_size % 16);
+ ioc->reply_free_queue_depth = queue_size;
+
+ /* reply descriptor post queue sizing */
+ /* this size should be the number of request frames + number of reply
+ * frames
+ */
+
+ queue_size = ioc->request_depth + num_of_reply_frames + 1;
+ /* round up to 16 byte boundary */
+ if (queue_size % 16)
+ queue_size += 16 - (queue_size % 16);
+
+ /* check against IOC maximum reply post queue depth */
+ if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) {
+ queue_diff = queue_size -
+ facts->MaxReplyDescriptorPostQueueDepth;
+
+ /* round queue_diff up to multiple of 16 */
+ if (queue_diff % 16)
+ queue_diff += 16 - (queue_diff % 16);
+
+ /* adjust request_depth, reply_free_queue_depth,
+ * and queue_size
+ */
+ ioc->request_depth -= queue_diff;
+ ioc->reply_free_queue_depth -= queue_diff;
+ queue_size -= queue_diff;
+ }
+ ioc->reply_post_queue_depth = queue_size;
+
+ /* max scsi host queue depth */
+ ioc->shost->can_queue = ioc->request_depth - INTERNAL_CMDS_COUNT;
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host queue: depth"
+ "(%d)\n", ioc->name, ioc->shost->can_queue));
+
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: "
+ "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), "
+ "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message,
+ ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize,
+ ioc->chains_needed_per_io));
+
+ /* contiguous pool for request and chains, 16 byte align, one extra "
+ * "frame for smid=0
+ */
+ ioc->chain_depth = ioc->chains_needed_per_io * ioc->request_depth;
+ sz = ((ioc->request_depth + 1 + ioc->chain_depth) * ioc->request_sz);
+
+ ioc->request_dma_sz = sz;
+ ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma);
+ if (!ioc->request) {
+ printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent "
+ "failed: req_depth(%d), chains_per_io(%d), frame_sz(%d), "
+ "total(%d kB)\n", ioc->name, ioc->request_depth,
+ ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
+ if (ioc->request_depth < MPT2SAS_SAS_QUEUE_DEPTH)
+ goto out;
+ retry_sz += 64;
+ ioc->request_depth = max_request_credit - retry_sz;
+ goto retry_allocation;
+ }
+
+ if (retry_sz)
+ printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent "
+ "succeed: req_depth(%d), chains_per_io(%d), frame_sz(%d), "
+ "total(%d kb)\n", ioc->name, ioc->request_depth,
+ ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
+
+ ioc->chain = ioc->request + ((ioc->request_depth + 1) *
+ ioc->request_sz);
+ ioc->chain_dma = ioc->request_dma + ((ioc->request_depth + 1) *
+ ioc->request_sz);
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): "
+ "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name,
+ ioc->request, ioc->request_depth, ioc->request_sz,
+ ((ioc->request_depth + 1) * ioc->request_sz)/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool(0x%p): depth"
+ "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain,
+ ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth *
+ ioc->request_sz))/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n",
+ ioc->name, (unsigned long long) ioc->request_dma));
+ total_sz += sz;
+
+ ioc->scsi_lookup = kcalloc(ioc->request_depth,
+ sizeof(struct request_tracker), GFP_KERNEL);
+ if (!ioc->scsi_lookup) {
+ printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n",
+ ioc->name);
+ goto out;
+ }
+
+ /* initialize some bits */
+ for (i = 0; i < ioc->request_depth; i++)
+ ioc->scsi_lookup[i].smid = i + 1;
+
+ /* sense buffers, 4 byte align */
+ sz = ioc->request_depth * SCSI_SENSE_BUFFERSIZE;
+ ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4,
+ 0);
+ if (!ioc->sense_dma_pool) {
+ printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL,
+ &ioc->sense_dma);
+ if (!ioc->sense) {
+ printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "sense pool(0x%p): depth(%d), element_size(%d), pool_size"
+ "(%d kB)\n", ioc->name, ioc->sense, ioc->request_depth,
+ SCSI_SENSE_BUFFERSIZE, sz/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->sense_dma));
+ total_sz += sz;
+
+ /* reply pool, 4 byte align */
+ sz = ioc->reply_free_queue_depth * ioc->reply_sz;
+ ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4,
+ 0);
+ if (!ioc->reply_dma_pool) {
+ printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL,
+ &ioc->reply_dma);
+ if (!ioc->reply) {
+ printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply pool(0x%p): depth"
+ "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->reply,
+ ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_dma(0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->reply_dma));
+ total_sz += sz;
+
+ /* reply free queue, 16 byte align */
+ sz = ioc->reply_free_queue_depth * 4;
+ ioc->reply_free_dma_pool = pci_pool_create("reply_free pool",
+ ioc->pdev, sz, 16, 0);
+ if (!ioc->reply_free_dma_pool) {
+ printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_create "
+ "failed\n", ioc->name);
+ goto out;
+ }
+ ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL,
+ &ioc->reply_free_dma);
+ if (!ioc->reply_free) {
+ printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_alloc "
+ "failed\n", ioc->name);
+ goto out;
+ }
+ memset(ioc->reply_free, 0, sz);
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free pool(0x%p): "
+ "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name,
+ ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_dma"
+ "(0x%llx)\n", ioc->name, (unsigned long long)ioc->reply_free_dma));
+ total_sz += sz;
+
+ /* reply post queue, 16 byte align */
+ sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t);
+ ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
+ ioc->pdev, sz, 16, 0);
+ if (!ioc->reply_post_free_dma_pool) {
+ printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_create "
+ "failed\n", ioc->name);
+ goto out;
+ }
+ ioc->reply_post_free = pci_pool_alloc(ioc->reply_post_free_dma_pool ,
+ GFP_KERNEL, &ioc->reply_post_free_dma);
+ if (!ioc->reply_post_free) {
+ printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_alloc "
+ "failed\n", ioc->name);
+ goto out;
+ }
+ memset(ioc->reply_post_free, 0, sz);
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply post free pool"
+ "(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n",
+ ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth, 8,
+ sz/1024));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_post_free_dma = "
+ "(0x%llx)\n", ioc->name, (unsigned long long)
+ ioc->reply_post_free_dma));
+ total_sz += sz;
+
+ ioc->config_page_sz = 512;
+ ioc->config_page = pci_alloc_consistent(ioc->pdev,
+ ioc->config_page_sz, &ioc->config_page_dma);
+ if (!ioc->config_page) {
+ printk(MPT2SAS_ERR_FMT "config page: pci_pool_alloc "
+ "failed\n", ioc->name);
+ goto out;
+ }
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config page(0x%p): size"
+ "(%d)\n", ioc->name, ioc->config_page, ioc->config_page_sz));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config_page_dma"
+ "(0x%llx)\n", ioc->name, (unsigned long long)ioc->config_page_dma));
+ total_sz += ioc->config_page_sz;
+
+ printk(MPT2SAS_INFO_FMT "Allocated physical memory: size(%d kB)\n",
+ ioc->name, total_sz/1024);
+ printk(MPT2SAS_INFO_FMT "Current Controller Queue Depth(%d), "
+ "Max Controller Queue Depth(%d)\n",
+ ioc->name, ioc->shost->can_queue, facts->RequestCredit);
+ printk(MPT2SAS_INFO_FMT "Scatter Gather Elements per IO(%d)\n",
+ ioc->name, ioc->shost->sg_tablesize);
+ return 0;
+
+ out:
+ _base_release_memory_pools(ioc);
+ return -ENOMEM;
+}
+
+
+/**
+ * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @cooked: Request raw or cooked IOC state
+ *
+ * Returns all IOC Doorbell register bits if cooked==0, else just the
+ * Doorbell bits in MPI_IOC_STATE_MASK.
+ */
+u32
+mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked)
+{
+ u32 s, sc;
+
+ s = readl(&ioc->chip->Doorbell);
+ sc = s & MPI2_IOC_STATE_MASK;
+ return cooked ? sc : s;
+}
+
+/**
+ * _base_wait_on_iocstate - waiting on a particular ioc state
+ * @ioc_state: controller state { READY, OPERATIONAL, or RESET }
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_on_iocstate(struct MPT2SAS_ADAPTER *ioc, u32 ioc_state, int timeout,
+ int sleep_flag)
+{
+ u32 count, cntdn;
+ u32 current_state;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ current_state = mpt2sas_base_get_iocstate(ioc, 1);
+ if (current_state == ioc_state)
+ return 0;
+ if (count && current_state == MPI2_IOC_STATE_FAULT)
+ break;
+ if (sleep_flag == CAN_SLEEP)
+ msleep(1);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ return current_state;
+}
+
+/**
+ * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by
+ * a write to the doorbell)
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
+ */
+static int
+_base_wait_for_doorbell_int(struct MPT2SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 int_status;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ int_status = readl(&ioc->chip->HostInterruptStatus);
+ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "successfull count(%d), timeout(%d)\n", ioc->name,
+ __func__, count, timeout));
+ return 0;
+ }
+ if (sleep_flag == CAN_SLEEP)
+ msleep(1);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
+ "int_status(%x)!\n", ioc->name, __func__, count, int_status);
+ return -EFAULT;
+}
+
+/**
+ * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell.
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to
+ * doorbell.
+ */
+static int
+_base_wait_for_doorbell_ack(struct MPT2SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 int_status;
+ u32 doorbell;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ int_status = readl(&ioc->chip->HostInterruptStatus);
+ if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) {
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "successfull count(%d), timeout(%d)\n", ioc->name,
+ __func__, count, timeout));
+ return 0;
+ } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ doorbell = readl(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_FAULT) {
+ mpt2sas_base_fault_info(ioc , doorbell);
+ return -EFAULT;
+ }
+ } else if (int_status == 0xFFFFFFFF)
+ goto out;
+
+ if (sleep_flag == CAN_SLEEP)
+ msleep(1);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ out:
+ printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
+ "int_status(%x)!\n", ioc->name, __func__, count, int_status);
+ return -EFAULT;
+}
+
+/**
+ * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int
+_base_wait_for_doorbell_not_used(struct MPT2SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 doorbell_reg;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ doorbell_reg = readl(&ioc->chip->Doorbell);
+ if (!(doorbell_reg & MPI2_DOORBELL_USED)) {
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "successfull count(%d), timeout(%d)\n", ioc->name,
+ __func__, count, timeout));
+ return 0;
+ }
+ if (sleep_flag == CAN_SLEEP)
+ msleep(1);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
+ "doorbell_reg(%x)!\n", ioc->name, __func__, count, doorbell_reg);
+ return -EFAULT;
+}
+
+/**
+ * _base_send_ioc_reset - send doorbell reset
+ * @ioc: per adapter object
+ * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_reset(struct MPT2SAS_ADAPTER *ioc, u8 reset_type, int timeout,
+ int sleep_flag)
+{
+ u32 ioc_state;
+ int r = 0;
+
+ if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) {
+ printk(MPT2SAS_ERR_FMT "%s: unknown reset_type\n",
+ ioc->name, __func__);
+ return -EFAULT;
+ }
+
+ if (!(ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY))
+ return -EFAULT;
+
+ printk(MPT2SAS_INFO_FMT "sending message unit reset !!\n", ioc->name);
+
+ writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT,
+ &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) {
+ r = -EFAULT;
+ goto out;
+ }
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY,
+ timeout, sleep_flag);
+ if (ioc_state) {
+ printk(MPT2SAS_ERR_FMT "%s: failed going to ready state "
+ " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
+ r = -EFAULT;
+ goto out;
+ }
+ out:
+ printk(MPT2SAS_INFO_FMT "message unit reset: %s\n",
+ ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
+ return r;
+}
+
+/**
+ * _base_handshake_req_reply_wait - send request thru doorbell interface
+ * @ioc: per adapter object
+ * @request_bytes: request length
+ * @request: pointer having request payload
+ * @reply_bytes: reply length
+ * @reply: pointer to reply payload
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes,
+ u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag)
+{
+ MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply;
+ int i;
+ u8 failed;
+ u16 dummy;
+ u32 *mfp;
+
+ /* make sure doorbell is not in use */
+ if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) {
+ printk(MPT2SAS_ERR_FMT "doorbell is in use "
+ " (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* clear pending doorbell interrupts from previous state changes */
+ if (readl(&ioc->chip->HostInterruptStatus) &
+ MPI2_HIS_IOC2SYS_DB_STATUS)
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ /* send message to ioc */
+ writel(((MPI2_FUNCTION_HANDSHAKE<chip->Doorbell);
+
+ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
+ printk(MPT2SAS_ERR_FMT "doorbell handshake "
+ "int failed (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) {
+ printk(MPT2SAS_ERR_FMT "doorbell handshake "
+ "ack failed (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* send message 32-bits at a time */
+ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) {
+ writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag)))
+ failed = 1;
+ }
+
+ if (failed) {
+ printk(MPT2SAS_ERR_FMT "doorbell handshake "
+ "sending request failed (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* now wait for the reply */
+ if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) {
+ printk(MPT2SAS_ERR_FMT "doorbell handshake "
+ "int failed (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* read the first two 16-bits, it gives the total length of the reply */
+ reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
+ printk(MPT2SAS_ERR_FMT "doorbell handshake "
+ "int failed (line=%d)\n", ioc->name, __LINE__);
+ return -EFAULT;
+ }
+ reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ for (i = 2; i < default_reply->MsgLength * 2; i++) {
+ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
+ printk(MPT2SAS_ERR_FMT "doorbell "
+ "handshake int failed (line=%d)\n", ioc->name,
+ __LINE__);
+ return -EFAULT;
+ }
+ if (i >= reply_bytes/2) /* overflow case */
+ dummy = readl(&ioc->chip->Doorbell);
+ else
+ reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ }
+
+ _base_wait_for_doorbell_int(ioc, 5, sleep_flag);
+ if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) {
+ dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "doorbell is in use "
+ " (line=%d)\n", ioc->name, __LINE__));
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ mfp = (u32 *)reply;
+ printk(KERN_DEBUG "\toffset:data\n");
+ for (i = 0; i < reply_bytes/4; i++)
+ printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+ return 0;
+}
+
+/**
+ * mpt2sas_base_sas_iounit_control - send sas iounit control to FW
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SAS IO Unit Control Request message allows the host to perform low-level
+ * operations, such as resets on the PHYs of the IO Unit, also allows the host
+ * to obtain the IOC assigned device handles for a device if it has other
+ * identifying information about the device, in addition allows the host to
+ * remove IOC resources associated with the device.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply,
+ Mpi2SasIoUnitControlRequest_t *mpi_request)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ u8 issue_reset;
+ int rc;
+ void *request;
+ u16 wait_state_count;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ printk(MPT2SAS_ERR_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ printk(MPT2SAS_INFO_FMT "%s: waiting for "
+ "operational state(count=%d)\n", ioc->name,
+ __func__, wait_state_count);
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT2_CMD_PENDING;
+ request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t));
+ if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
+ ioc->ioc_link_reset_in_progress = 1;
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) &&
+ ioc->ioc_link_reset_in_progress)
+ ioc->ioc_link_reset_in_progress = 0;
+ if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SasIoUnitControlRequest_t)/4);
+ if (!(ioc->base_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SasIoUnitControlReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t));
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+
+/**
+ * mpt2sas_base_scsi_enclosure_processor - sending request to sep device
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SCSI Enclosure Processor request message causes the IOC to
+ * communicate with SES devices to control LED status signals.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ u8 issue_reset;
+ int rc;
+ void *request;
+ u16 wait_state_count;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ printk(MPT2SAS_ERR_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ printk(MPT2SAS_INFO_FMT "%s: waiting for "
+ "operational state(count=%d)\n", ioc->name,
+ __func__, wait_state_count);
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT2_CMD_PENDING;
+ request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SepRequest_t)/4);
+ if (!(ioc->base_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SepReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SepReply_t));
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _base_get_port_facts - obtain port facts reply and save in ioc
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag)
+{
+ Mpi2PortFactsRequest_t mpi_request;
+ Mpi2PortFactsReply_t mpi_reply, *pfacts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ mpi_reply_sz = sizeof(Mpi2PortFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2PortFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_PORT_FACTS;
+ mpi_request.PortNumber = port;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
+
+ if (r != 0) {
+ printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ pfacts = &ioc->pfacts[port];
+ memset(pfacts, 0, sizeof(Mpi2PortFactsReply_t));
+ pfacts->PortNumber = mpi_reply.PortNumber;
+ pfacts->VP_ID = mpi_reply.VP_ID;
+ pfacts->VF_ID = mpi_reply.VF_ID;
+ pfacts->MaxPostedCmdBuffers =
+ le16_to_cpu(mpi_reply.MaxPostedCmdBuffers);
+
+ return 0;
+}
+
+/**
+ * _base_get_ioc_facts - obtain ioc facts reply and save in ioc
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2IOCFactsRequest_t mpi_request;
+ Mpi2IOCFactsReply_t mpi_reply, *facts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_IOC_FACTS;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
+
+ if (r != 0) {
+ printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ facts = &ioc->facts;
+ memset(facts, 0, sizeof(Mpi2IOCFactsReply_t));
+ facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion);
+ facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion);
+ facts->VP_ID = mpi_reply.VP_ID;
+ facts->VF_ID = mpi_reply.VF_ID;
+ facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions);
+ facts->MaxChainDepth = mpi_reply.MaxChainDepth;
+ facts->WhoInit = mpi_reply.WhoInit;
+ facts->NumberOfPorts = mpi_reply.NumberOfPorts;
+ facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit);
+ facts->MaxReplyDescriptorPostQueueDepth =
+ le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth);
+ facts->ProductID = le16_to_cpu(mpi_reply.ProductID);
+ facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities);
+ if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
+ ioc->ir_firmware = 1;
+ facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
+ facts->IOCRequestFrameSize =
+ le16_to_cpu(mpi_reply.IOCRequestFrameSize);
+ facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators);
+ facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets);
+ ioc->shost->max_id = -1;
+ facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders);
+ facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures);
+ facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags);
+ facts->HighPriorityCredit =
+ le16_to_cpu(mpi_reply.HighPriorityCredit);
+ facts->ReplyFrameSize = mpi_reply.ReplyFrameSize;
+ facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle);
+
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hba queue depth(%d), "
+ "max chains per io(%d)\n", ioc->name, facts->RequestCredit,
+ facts->MaxChainDepth));
+ dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request frame size(%d), "
+ "reply frame size(%d)\n", ioc->name,
+ facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4));
+ return 0;
+}
+
+/**
+ * _base_send_ioc_init - send ioc_init to firmware
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag)
+{
+ Mpi2IOCInitRequest_t mpi_request;
+ Mpi2IOCInitReply_t mpi_reply;
+ int r;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_IOC_INIT;
+ mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER;
+ mpi_request.VF_ID = VF_ID;
+ mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION);
+ mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
+
+ /* In MPI Revision I (0xA), the SystemReplyFrameSize(offset 0x18) was
+ * removed and made reserved. For those with older firmware will need
+ * this fix. It was decided that the Reply and Request frame sizes are
+ * the same.
+ */
+ if ((ioc->facts.HeaderVersion >> 8) < 0xA) {
+ mpi_request.Reserved7 = cpu_to_le16(ioc->reply_sz);
+/* mpi_request.SystemReplyFrameSize =
+ * cpu_to_le16(ioc->reply_sz);
+ */
+ }
+
+ mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4);
+ mpi_request.ReplyDescriptorPostQueueDepth =
+ cpu_to_le16(ioc->reply_post_queue_depth);
+ mpi_request.ReplyFreeQueueDepth =
+ cpu_to_le16(ioc->reply_free_queue_depth);
+
+#if BITS_PER_LONG > 32
+ mpi_request.SenseBufferAddressHigh =
+ cpu_to_le32(ioc->sense_dma >> 32);
+ mpi_request.SystemReplyAddressHigh =
+ cpu_to_le32(ioc->reply_dma >> 32);
+ mpi_request.SystemRequestFrameBaseAddress =
+ cpu_to_le64(ioc->request_dma);
+ mpi_request.ReplyFreeQueueAddress =
+ cpu_to_le64(ioc->reply_free_dma);
+ mpi_request.ReplyDescriptorPostQueueAddress =
+ cpu_to_le64(ioc->reply_post_free_dma);
+#else
+ mpi_request.SystemRequestFrameBaseAddress =
+ cpu_to_le32(ioc->request_dma);
+ mpi_request.ReplyFreeQueueAddress =
+ cpu_to_le32(ioc->reply_free_dma);
+ mpi_request.ReplyDescriptorPostQueueAddress =
+ cpu_to_le32(ioc->reply_post_free_dma);
+#endif
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ u32 *mfp;
+ int i;
+
+ mfp = (u32 *)&mpi_request;
+ printk(KERN_DEBUG "\toffset:data\n");
+ for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++)
+ printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+
+ r = _base_handshake_req_reply_wait(ioc,
+ sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request,
+ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10,
+ sleep_flag);
+
+ if (r != 0) {
+ printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS ||
+ mpi_reply.IOCLogInfo) {
+ printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__);
+ r = -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * _base_send_port_enable - send port_enable(discovery stuff) to firmware
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag)
+{
+ Mpi2PortEnableRequest_t *mpi_request;
+ u32 ioc_state;
+ unsigned long timeleft;
+ int r = 0;
+ u16 smid;
+
+ printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
+
+ if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
+ printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ ioc->base_cmds.status = MPT2_CMD_PENDING;
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+ mpi_request->VF_ID = VF_ID;
+
+ mpt2sas_base_put_smid_default(ioc, smid, VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+ 300*HZ);
+ if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2PortEnableRequest_t)/4);
+ if (ioc->base_cmds.status & MPT2_CMD_RESET)
+ r = -EFAULT;
+ else
+ r = -ETIME;
+ goto out;
+ } else
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n",
+ ioc->name, __func__));
+
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL,
+ 60, sleep_flag);
+ if (ioc_state) {
+ printk(MPT2SAS_ERR_FMT "%s: failed going to operational state "
+ " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
+ r = -EFAULT;
+ }
+ out:
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ printk(MPT2SAS_INFO_FMT "port enable: %s\n",
+ ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
+ return r;
+}
+
+/**
+ * _base_unmask_events - turn on notification for this event
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The mask is stored in ioc->event_masks.
+ */
+static void
+_base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event)
+{
+ u32 desired_event;
+
+ if (event >= 128)
+ return;
+
+ desired_event = (1 << (event % 32));
+
+ if (event < 32)
+ ioc->event_masks[0] &= ~desired_event;
+ else if (event < 64)
+ ioc->event_masks[1] &= ~desired_event;
+ else if (event < 96)
+ ioc->event_masks[2] &= ~desired_event;
+ else if (event < 128)
+ ioc->event_masks[3] &= ~desired_event;
+}
+
+/**
+ * _base_event_notification - send event notification
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag)
+{
+ Mpi2EventNotificationRequest_t *mpi_request;
+ unsigned long timeleft;
+ u16 smid;
+ int r = 0;
+ int i;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
+ printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+ ioc->base_cmds.status = MPT2_CMD_PENDING;
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ mpi_request->VF_ID = VF_ID;
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mpi_request->EventMasks[i] =
+ le32_to_cpu(ioc->event_masks[i]);
+ mpt2sas_base_put_smid_default(ioc, smid, VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
+ if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2EventNotificationRequest_t)/4);
+ if (ioc->base_cmds.status & MPT2_CMD_RESET)
+ r = -EFAULT;
+ else
+ r = -ETIME;
+ } else
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n",
+ ioc->name, __func__));
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+ return r;
+}
+
+/**
+ * mpt2sas_base_validate_event_type - validating event types
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * This will turn on firmware event notification when application
+ * ask for that event. We don't mask events that are already enabled.
+ */
+void
+mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type)
+{
+ int i, j;
+ u32 event_mask, desired_event;
+ u8 send_update_to_fw;
+
+ for (i = 0, send_update_to_fw = 0; i <
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) {
+ event_mask = ~event_type[i];
+ desired_event = 1;
+ for (j = 0; j < 32; j++) {
+ if (!(event_mask & desired_event) &&
+ (ioc->event_masks[i] & desired_event)) {
+ ioc->event_masks[i] &= ~desired_event;
+ send_update_to_fw = 1;
+ }
+ desired_event = (desired_event << 1);
+ }
+ }
+
+ if (!send_update_to_fw)
+ return;
+
+ mutex_lock(&ioc->base_cmds.mutex);
+ _base_event_notification(ioc, 0, CAN_SLEEP);
+ mutex_unlock(&ioc->base_cmds.mutex);
+}
+
+/**
+ * _base_diag_reset - the "big hammer" start of day reset
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
+{
+ u32 host_diagnostic;
+ u32 ioc_state;
+ u32 count;
+ u32 hcb_size;
+
+ printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name);
+
+ _base_save_msix_table(ioc);
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "clear interrupts\n",
+ ioc->name));
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ count = 0;
+ do {
+ /* Write magic sequence to WriteSequence register
+ * Loop until in diagnostic mode
+ */
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "write magic "
+ "sequence\n", ioc->name));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
+
+ /* wait 100 msec */
+ if (sleep_flag == CAN_SLEEP)
+ msleep(100);
+ else
+ mdelay(100);
+
+ if (count++ > 20)
+ goto out;
+
+ host_diagnostic = readl(&ioc->chip->HostDiagnostic);
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "wrote magic "
+ "sequence: count(%d), host_diagnostic(0x%08x)\n",
+ ioc->name, count, host_diagnostic));
+
+ } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
+
+ hcb_size = readl(&ioc->chip->HCBSize);
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "diag reset: issued\n",
+ ioc->name));
+ writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
+ &ioc->chip->HostDiagnostic);
+
+ /* don't access any registers for 50 milliseconds */
+ msleep(50);
+
+ /* 300 second max wait */
+ for (count = 0; count < 3000000 ; count++) {
+
+ host_diagnostic = readl(&ioc->chip->HostDiagnostic);
+
+ if (host_diagnostic == 0xFFFFFFFF)
+ goto out;
+ if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
+ break;
+
+ /* wait 100 msec */
+ if (sleep_flag == CAN_SLEEP)
+ msleep(1);
+ else
+ mdelay(1);
+ }
+
+ if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter "
+ "assuming the HCB Address points to good F/W\n",
+ ioc->name));
+ host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
+ host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
+ writel(host_diagnostic, &ioc->chip->HostDiagnostic);
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "re-enable the HCDW\n", ioc->name));
+ writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE,
+ &ioc->chip->HCBSize);
+ }
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter\n",
+ ioc->name));
+ writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET,
+ &ioc->chip->HostDiagnostic);
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "disable writes to the "
+ "diagnostic register\n", ioc->name));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+
+ drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "Wait for FW to go to the "
+ "READY state\n", ioc->name));
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20,
+ sleep_flag);
+ if (ioc_state) {
+ printk(MPT2SAS_ERR_FMT "%s: failed going to ready state "
+ " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
+ goto out;
+ }
+
+ _base_restore_msix_table(ioc);
+ printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name);
+ return 0;
+
+ out:
+ printk(MPT2SAS_ERR_FMT "diag reset: FAILED\n", ioc->name);
+ return -EFAULT;
+}
+
+/**
+ * _base_make_ioc_ready - put controller in READY state
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type)
+{
+ u32 ioc_state;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: ioc_state(0x%08x)\n",
+ ioc->name, __func__, ioc_state));
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY)
+ return 0;
+
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell "
+ "active!\n", ioc->name));
+ goto issue_diag_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt2sas_base_fault_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ goto issue_diag_reset;
+ }
+
+ if (type == FORCE_BIG_HAMMER)
+ goto issue_diag_reset;
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
+ if (!(_base_send_ioc_reset(ioc,
+ MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP)))
+ return 0;
+
+ issue_diag_reset:
+ return _base_diag_reset(ioc, CAN_SLEEP);
+}
+
+/**
+ * _base_make_ioc_operational - put controller in OPERATIONAL state
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ int sleep_flag)
+{
+ int r, i;
+ unsigned long flags;
+ u32 reply_address;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ /* initialize the scsi lookup free list */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ INIT_LIST_HEAD(&ioc->free_list);
+ for (i = 0; i < ioc->request_depth; i++) {
+ ioc->scsi_lookup[i].cb_idx = 0xFF;
+ list_add_tail(&ioc->scsi_lookup[i].tracker_list,
+ &ioc->free_list);
+ }
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ /* initialize Reply Free Queue */
+ for (i = 0, reply_address = (u32)ioc->reply_dma ;
+ i < ioc->reply_free_queue_depth ; i++, reply_address +=
+ ioc->reply_sz)
+ ioc->reply_free[i] = cpu_to_le32(reply_address);
+
+ /* initialize Reply Post Free Queue */
+ for (i = 0; i < ioc->reply_post_queue_depth; i++)
+ ioc->reply_post_free[i].Words = ~0ULL;
+
+ r = _base_send_ioc_init(ioc, VF_ID, sleep_flag);
+ if (r)
+ return r;
+
+ /* initialize the index's */
+ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1;
+ ioc->reply_post_host_index = 0;
+ writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex);
+ writel(0, &ioc->chip->ReplyPostHostIndex);
+
+ _base_unmask_interrupts(ioc);
+ r = _base_event_notification(ioc, VF_ID, sleep_flag);
+ if (r)
+ return r;
+
+ if (sleep_flag == CAN_SLEEP)
+ _base_static_config_pages(ioc);
+
+ r = _base_send_port_enable(ioc, VF_ID, sleep_flag);
+ if (r)
+ return r;
+
+ return r;
+}
+
+/**
+ * mpt2sas_base_free_resources - free resources controller resources (io/irq/memap)
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+
+ dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ _base_mask_interrupts(ioc);
+ _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ if (ioc->pci_irq) {
+ synchronize_irq(pdev->irq);
+ free_irq(ioc->pci_irq, ioc);
+ }
+ _base_disable_msix(ioc);
+ if (ioc->chip_phys)
+ iounmap(ioc->chip);
+ ioc->pci_irq = -1;
+ ioc->chip_phys = 0;
+ pci_release_selected_regions(ioc->pdev, ioc->bars);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ return;
+}
+
+/**
+ * mpt2sas_base_attach - attach controller instance
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
+{
+ int r, i;
+ unsigned long flags;
+
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ r = mpt2sas_base_map_resources(ioc);
+ if (r)
+ return r;
+
+ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ if (r)
+ goto out_free_resources;
+
+ r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ r = _base_allocate_memory_pools(ioc, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ init_waitqueue_head(&ioc->reset_wq);
+
+ /* base internal command bits */
+ mutex_init(&ioc->base_cmds.mutex);
+ init_completion(&ioc->base_cmds.done);
+ ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->base_cmds.status = MPT2_CMD_NOT_USED;
+
+ /* transport internal command bits */
+ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_init(&ioc->transport_cmds.mutex);
+ init_completion(&ioc->transport_cmds.done);
+
+ /* task management internal command bits */
+ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_init(&ioc->tm_cmds.mutex);
+ init_completion(&ioc->tm_cmds.done);
+
+ /* config page internal command bits */
+ ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_init(&ioc->config_cmds.mutex);
+ init_completion(&ioc->config_cmds.done);
+
+ /* ctl module internal command bits */
+ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_init(&ioc->ctl_cmds.mutex);
+ init_completion(&ioc->ctl_cmds.done);
+
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ ioc->event_masks[i] = -1;
+
+ /* here we enable the events we care about */
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS);
+ _base_unmask_events(ioc, MPI2_EVENT_TASK_SET_FULL);
+ _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
+
+ ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts,
+ sizeof(Mpi2PortFactsReply_t), GFP_KERNEL);
+ if (!ioc->pfacts)
+ goto out_free_resources;
+
+ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) {
+ r = _base_get_port_facts(ioc, i, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+ }
+ r = _base_make_ioc_operational(ioc, 0, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ /* initialize fault polling */
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
+ snprintf(ioc->fault_reset_work_q_name,
+ sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id);
+ ioc->fault_reset_work_q =
+ create_singlethread_workqueue(ioc->fault_reset_work_q_name);
+ if (!ioc->fault_reset_work_q) {
+ printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n",
+ ioc->name, __func__, __LINE__);
+ goto out_free_resources;
+ }
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ return 0;
+
+ out_free_resources:
+
+ ioc->remove_host = 1;
+ mpt2sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->pfacts);
+ ioc->ctl_cmds.reply = NULL;
+ ioc->base_cmds.reply = NULL;
+ ioc->tm_cmds.reply = NULL;
+ ioc->transport_cmds.reply = NULL;
+ ioc->config_cmds.reply = NULL;
+ ioc->pfacts = NULL;
+ return r;
+}
+
+
+/**
+ * mpt2sas_base_detach - remove controller instance
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ wq = ioc->fault_reset_work_q;
+ ioc->fault_reset_work_q = NULL;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (!cancel_delayed_work(&ioc->fault_reset_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+
+ mpt2sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ kfree(ioc->pfacts);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+}
+
+/**
+ * _base_reset_handler - reset callback handler (for base)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
+ * MPT2_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+static void
+_base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
+{
+ switch (reset_phase) {
+ case MPT2_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT2_IOC_AFTER_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->transport_cmds.status & MPT2_CMD_PENDING) {
+ ioc->transport_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid);
+ complete(&ioc->transport_cmds.done);
+ }
+ if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
+ ioc->base_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid);
+ complete(&ioc->base_cmds.done);
+ }
+ if (ioc->config_cmds.status & MPT2_CMD_PENDING) {
+ ioc->config_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid);
+ complete(&ioc->config_cmds.done);
+ }
+ break;
+ case MPT2_IOC_DONE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+ break;
+ }
+ mpt2sas_scsih_reset_handler(ioc, reset_phase);
+ mpt2sas_ctl_reset_handler(ioc, reset_phase);
+}
+
+/**
+ * _wait_for_commands_to_complete - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * This function waiting(3s) for all pending commands to complete
+ * prior to putting controller in reset.
+ */
+static void
+_wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
+{
+ u32 ioc_state;
+ unsigned long flags;
+ u16 i;
+
+ ioc->pending_io_count = 0;
+ if (sleep_flag != CAN_SLEEP)
+ return;
+
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL)
+ return;
+
+ /* pending command count */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ for (i = 0; i < ioc->request_depth; i++)
+ if (ioc->scsi_lookup[i].cb_idx != 0xFF)
+ ioc->pending_io_count++;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ if (!ioc->pending_io_count)
+ return;
+
+ /* wait for pending commands to complete */
+ wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 3 * HZ);
+}
+
+/**
+ * mpt2sas_base_hard_reset_handler - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type)
+{
+ int r, i;
+ unsigned long flags;
+
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ printk(MPT2SAS_ERR_FMT "%s: busy\n",
+ ioc->name, __func__);
+ return -EBUSY;
+ }
+ ioc->ioc_reset_in_progress = 1;
+ ioc->shost_recovery = 1;
+ if (ioc->shost->shost_state == SHOST_RUNNING) {
+ /* set back to SHOST_RUNNING in mpt2sas_scsih.c */
+ scsi_host_set_state(ioc->shost, SHOST_RECOVERY);
+ printk(MPT2SAS_INFO_FMT "putting controller into "
+ "SHOST_RECOVERY\n", ioc->name);
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ _base_reset_handler(ioc, MPT2_IOC_PRE_RESET);
+ _wait_for_commands_to_complete(ioc, sleep_flag);
+ _base_mask_interrupts(ioc);
+ r = _base_make_ioc_ready(ioc, sleep_flag, type);
+ if (r)
+ goto out;
+ _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET);
+ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++)
+ r = _base_make_ioc_operational(ioc, ioc->pfacts[i].VF_ID,
+ sleep_flag);
+ if (!r)
+ _base_reset_handler(ioc, MPT2_IOC_DONE_RESET);
+ out:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: %s\n",
+ ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED")));
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->ioc_reset_in_progress = 0;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ return r;
+}
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_base.h b/trunk/drivers/scsi/mpt2sas/mpt2sas_base.h
new file mode 100644
index 000000000000..6945ff4d382e
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -0,0 +1,779 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_base.h
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT2SAS_BASE_H_INCLUDED
+#define MPT2SAS_BASE_H_INCLUDED
+
+#include "mpi/mpi2_type.h"
+#include "mpi/mpi2.h"
+#include "mpi/mpi2_ioc.h"
+#include "mpi/mpi2_cnfg.h"
+#include "mpi/mpi2_init.h"
+#include "mpi/mpi2_raid.h"
+#include "mpi/mpi2_tool.h"
+#include "mpi/mpi2_sas.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "mpt2sas_debug.h"
+
+/* driver versioning info */
+#define MPT2SAS_DRIVER_NAME "mpt2sas"
+#define MPT2SAS_AUTHOR "LSI Corporation "
+#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
+#define MPT2SAS_DRIVER_VERSION "00.100.11.16"
+#define MPT2SAS_MAJOR_VERSION 00
+#define MPT2SAS_MINOR_VERSION 100
+#define MPT2SAS_BUILD_VERSION 11
+#define MPT2SAS_RELEASE_VERSION 16
+
+/*
+ * Set MPT2SAS_SG_DEPTH value based on user input.
+ */
+#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE
+#if CONFIG_SCSI_MPT2SAS_MAX_SGE < 16
+#define MPT2SAS_SG_DEPTH 16
+#elif CONFIG_SCSI_MPT2SAS_MAX_SGE > 128
+#define MPT2SAS_SG_DEPTH 128
+#else
+#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE
+#endif
+#else
+#define MPT2SAS_SG_DEPTH 128 /* MAX_HW_SEGMENTS */
+#endif
+
+
+/*
+ * Generic Defines
+ */
+#define MPT2SAS_SATA_QUEUE_DEPTH 32
+#define MPT2SAS_SAS_QUEUE_DEPTH 254
+#define MPT2SAS_RAID_QUEUE_DEPTH 128
+
+#define MPT_NAME_LENGTH 32 /* generic length of strings */
+#define MPT_STRING_LENGTH 64
+
+#define MPT_MAX_CALLBACKS 16
+
+#define CAN_SLEEP 1
+#define NO_SLEEP 0
+
+#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */
+
+#define MPI2_HIM_MASK 0xFFFFFFFF /* mask every bit*/
+
+#define MPT2SAS_INVALID_DEVICE_HANDLE 0xFFFF
+
+
+/*
+ * reset phases
+ */
+#define MPT2_IOC_PRE_RESET 1 /* prior to host reset */
+#define MPT2_IOC_AFTER_RESET 2 /* just after host reset */
+#define MPT2_IOC_DONE_RESET 3 /* links re-initialized */
+
+/*
+ * logging format
+ */
+#define MPT2SAS_FMT "%s: "
+#define MPT2SAS_DEBUG_FMT KERN_DEBUG MPT2SAS_FMT
+#define MPT2SAS_INFO_FMT KERN_INFO MPT2SAS_FMT
+#define MPT2SAS_NOTE_FMT KERN_NOTICE MPT2SAS_FMT
+#define MPT2SAS_WARN_FMT KERN_WARNING MPT2SAS_FMT
+#define MPT2SAS_ERR_FMT KERN_ERR MPT2SAS_FMT
+
+/*
+ * per target private data
+ */
+#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
+#define MPT_TARGET_FLAGS_VOLUME 0x02
+#define MPT_TARGET_FLAGS_DELETED 0x04
+
+/**
+ * struct MPT2SAS_TARGET - starget private hostdata
+ * @starget: starget object
+ * @sas_address: target sas address
+ * @handle: device handle
+ * @num_luns: number luns
+ * @flags: MPT_TARGET_FLAGS_XXX flags
+ * @deleted: target flaged for deletion
+ * @tm_busy: target is busy with TM request.
+ */
+struct MPT2SAS_TARGET {
+ struct scsi_target *starget;
+ u64 sas_address;
+ u16 handle;
+ int num_luns;
+ u32 flags;
+ u8 deleted;
+ u8 tm_busy;
+};
+
+/*
+ * per device private data
+ */
+#define MPT_DEVICE_FLAGS_INIT 0x01
+#define MPT_DEVICE_TLR_ON 0x02
+
+/**
+ * struct MPT2SAS_DEVICE - sdev private hostdata
+ * @sas_target: starget private hostdata
+ * @lun: lun number
+ * @flags: MPT_DEVICE_XXX flags
+ * @configured_lun: lun is configured
+ * @block: device is in SDEV_BLOCK state
+ * @tlr_snoop_check: flag used in determining whether to disable TLR
+ */
+struct MPT2SAS_DEVICE {
+ struct MPT2SAS_TARGET *sas_target;
+ unsigned int lun;
+ u32 flags;
+ u8 configured_lun;
+ u8 block;
+ u8 tlr_snoop_check;
+};
+
+#define MPT2_CMD_NOT_USED 0x8000 /* free */
+#define MPT2_CMD_COMPLETE 0x0001 /* completed */
+#define MPT2_CMD_PENDING 0x0002 /* pending */
+#define MPT2_CMD_REPLY_VALID 0x0004 /* reply is valid */
+#define MPT2_CMD_RESET 0x0008 /* host reset dropped the command */
+
+/**
+ * struct _internal_cmd - internal commands struct
+ * @mutex: mutex
+ * @done: completion
+ * @reply: reply message pointer
+ * @status: MPT2_CMD_XXX status
+ * @smid: system message id
+ */
+struct _internal_cmd {
+ struct mutex mutex;
+ struct completion done;
+ void *reply;
+ u16 status;
+ u16 smid;
+};
+
+/*
+ * SAS Topology Structures
+ */
+
+/**
+ * struct _sas_device - attached device information
+ * @list: sas device list
+ * @starget: starget object
+ * @sas_address: device sas address
+ * @device_name: retrieved from the SAS IDENTIFY frame.
+ * @handle: device handle
+ * @parent_handle: handle to parent device
+ * @enclosure_handle: enclosure handle
+ * @enclosure_logical_id: enclosure logical identifier
+ * @volume_handle: volume handle (valid when hidden raid member)
+ * @volume_wwid: volume unique identifier
+ * @device_info: bitfield provides detailed info about the device
+ * @id: target id
+ * @channel: target channel
+ * @slot: number number
+ * @hidden_raid_component: set to 1 when this is a raid member
+ * @responding: used in _scsih_sas_device_mark_responding
+ */
+struct _sas_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ u64 sas_address;
+ u64 device_name;
+ u16 handle;
+ u16 parent_handle;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u16 volume_handle;
+ u64 volume_wwid;
+ u32 device_info;
+ int id;
+ int channel;
+ u16 slot;
+ u8 hidden_raid_component;
+ u8 responding;
+};
+
+/**
+ * struct _raid_device - raid volume link list
+ * @list: sas device list
+ * @starget: starget object
+ * @sdev: scsi device struct (volumes are single lun)
+ * @wwid: unique identifier for the volume
+ * @handle: device handle
+ * @id: target id
+ * @channel: target channel
+ * @volume_type: the raid level
+ * @device_info: bitfield provides detailed info about the hidden components
+ * @num_pds: number of hidden raid components
+ * @responding: used in _scsih_raid_device_mark_responding
+ */
+struct _raid_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ struct scsi_device *sdev;
+ u64 wwid;
+ u16 handle;
+ int id;
+ int channel;
+ u8 volume_type;
+ u32 device_info;
+ u8 num_pds;
+ u8 responding;
+};
+
+/**
+ * struct _boot_device - boot device info
+ * @is_raid: flag to indicate whether this is volume
+ * @device: holds pointer for either struct _sas_device or
+ * struct _raid_device
+ */
+struct _boot_device {
+ u8 is_raid;
+ void *device;
+};
+
+/**
+ * struct _sas_port - wide/narrow sas port information
+ * @port_list: list of ports belonging to expander
+ * @handle: device handle for this port
+ * @sas_address: sas address of this port
+ * @num_phys: number of phys belonging to this port
+ * @remote_identify: attached device identification
+ * @rphy: sas transport rphy object
+ * @port: sas transport wide/narrow port object
+ * @phy_list: _sas_phy list objects belonging to this port
+ */
+struct _sas_port {
+ struct list_head port_list;
+ u16 handle;
+ u64 sas_address;
+ u8 num_phys;
+ struct sas_identify remote_identify;
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct list_head phy_list;
+};
+
+/**
+ * struct _sas_phy - phy information
+ * @port_siblings: list of phys belonging to a port
+ * @identify: phy identification
+ * @remote_identify: attached device identification
+ * @phy: sas transport phy object
+ * @phy_id: unique phy id
+ * @handle: device handle for this phy
+ * @attached_handle: device handle for attached device
+ */
+struct _sas_phy {
+ struct list_head port_siblings;
+ struct sas_identify identify;
+ struct sas_identify remote_identify;
+ struct sas_phy *phy;
+ u8 phy_id;
+ u16 handle;
+ u16 attached_handle;
+};
+
+/**
+ * struct _sas_node - sas_host/expander information
+ * @list: list of expanders
+ * @parent_dev: parent device class
+ * @num_phys: number phys belonging to this sas_host/expander
+ * @sas_address: sas address of this sas_host/expander
+ * @handle: handle for this sas_host/expander
+ * @parent_handle: parent handle
+ * @enclosure_handle: handle for this a member of an enclosure
+ * @device_info: bitwise defining capabilities of this sas_host/expander
+ * @responding: used in _scsih_expander_device_mark_responding
+ * @phy: a list of phys that make up this sas_host/expander
+ * @sas_port_list: list of ports attached to this sas_host/expander
+ */
+struct _sas_node {
+ struct list_head list;
+ struct device *parent_dev;
+ u8 num_phys;
+ u64 sas_address;
+ u16 handle;
+ u16 parent_handle;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u8 responding;
+ struct _sas_phy *phy;
+ struct list_head sas_port_list;
+};
+
+/**
+ * enum reset_type - reset state
+ * @FORCE_BIG_HAMMER: issue diagnostic reset
+ * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer
+ */
+enum reset_type {
+ FORCE_BIG_HAMMER,
+ SOFT_RESET,
+};
+
+/**
+ * struct request_tracker - firmware request tracker
+ * @smid: system message id
+ * @scmd: scsi request pointer
+ * @cb_idx: callback index
+ * @chain_list: list of chains associated to this IO
+ * @tracker_list: list of free request (ioc->free_list)
+ */
+struct request_tracker {
+ u16 smid;
+ struct scsi_cmnd *scmd;
+ u8 cb_idx;
+ struct list_head tracker_list;
+};
+
+typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
+
+/**
+ * struct MPT2SAS_ADAPTER - per adapter struct
+ * @list: ioc_list
+ * @shost: shost object
+ * @id: unique adapter id
+ * @pci_irq: irq number
+ * @name: generic ioc string
+ * @tmp_string: tmp string used for logging
+ * @pdev: pci pdev object
+ * @chip: memory mapped register space
+ * @chip_phys: physical addrss prior to mapping
+ * @pio_chip: I/O mapped register space
+ * @logging_level: see mpt2sas_debug.h
+ * @ir_firmware: IR firmware present
+ * @bars: bitmask of BAR's that must be configured
+ * @mask_interrupts: ignore interrupt
+ * @fault_reset_work_q_name: fw fault work queue
+ * @fault_reset_work_q: ""
+ * @fault_reset_work: ""
+ * @firmware_event_name: fw event work queue
+ * @firmware_event_thread: ""
+ * @fw_events_off: flag to turn off fw event handling
+ * @fw_event_lock:
+ * @fw_event_list: list of fw events
+ * @aen_event_read_flag: event log was read
+ * @broadcast_aen_busy: broadcast aen waiting to be serviced
+ * @ioc_reset_in_progress: host reset in progress
+ * @ioc_reset_in_progress_lock:
+ * @ioc_link_reset_in_progress: phy/hard reset in progress
+ * @ignore_loginfos: ignore loginfos during task managment
+ * @remove_host: flag for when driver unloads, to avoid sending dev resets
+ * @wait_for_port_enable_to_complete:
+ * @msix_enable: flag indicating msix is enabled
+ * @msix_vector_count: number msix vectors
+ * @msix_table: virt address to the msix table
+ * @msix_table_backup: backup msix table
+ * @scsi_io_cb_idx: shost generated commands
+ * @tm_cb_idx: task management commands
+ * @transport_cb_idx: transport internal commands
+ * @ctl_cb_idx: clt internal commands
+ * @base_cb_idx: base internal commands
+ * @config_cb_idx: base internal commands
+ * @base_cmds:
+ * @transport_cmds:
+ * @tm_cmds:
+ * @ctl_cmds:
+ * @config_cmds:
+ * @base_add_sg_single: handler for either 32/64 bit sgl's
+ * @event_type: bits indicating which events to log
+ * @event_context: unique id for each logged event
+ * @event_log: event log pointer
+ * @event_masks: events that are masked
+ * @facts: static facts data
+ * @pfacts: static port facts data
+ * @manu_pg0: static manufacturing page 0
+ * @bios_pg2: static bios page 2
+ * @bios_pg3: static bios page 3
+ * @ioc_pg8: static ioc page 8
+ * @iounit_pg0: static iounit page 0
+ * @iounit_pg1: static iounit page 1
+ * @sas_hba: sas host object
+ * @sas_expander_list: expander object list
+ * @sas_node_lock:
+ * @sas_device_list: sas device object list
+ * @sas_device_init_list: sas device object list (used only at init time)
+ * @sas_device_lock:
+ * @io_missing_delay: time for IO completed by fw when PDR enabled
+ * @device_missing_delay: time for device missing by fw when PDR enabled
+ * @config_page_sz: config page size
+ * @config_page: reserve memory for config page payload
+ * @config_page_dma:
+ * @sge_size: sg element size for either 32/64 bit
+ * @request_depth: hba request queue depth
+ * @request_sz: per request frame size
+ * @request: pool of request frames
+ * @request_dma:
+ * @request_dma_sz:
+ * @scsi_lookup: firmware request tracker list
+ * @scsi_lookup_lock:
+ * @free_list: free list of request
+ * @chain: pool of chains
+ * @pending_io_count:
+ * @reset_wq:
+ * @chain_dma:
+ * @max_sges_in_main_message: number sg elements in main message
+ * @max_sges_in_chain_message: number sg elements per chain
+ * @chains_needed_per_io: max chains per io
+ * @chain_offset_value_for_main_message: location 1st sg in main
+ * @chain_depth: total chains allocated
+ * @sense: pool of sense
+ * @sense_dma:
+ * @sense_dma_pool:
+ * @reply_depth: hba reply queue depth:
+ * @reply_sz: per reply frame size:
+ * @reply: pool of replys:
+ * @reply_dma:
+ * @reply_dma_pool:
+ * @reply_free_queue_depth: reply free depth
+ * @reply_free: pool for reply free queue (32 bit addr)
+ * @reply_free_dma:
+ * @reply_free_dma_pool:
+ * @reply_free_host_index: tail index in pool to insert free replys
+ * @reply_post_queue_depth: reply post queue depth
+ * @reply_post_free: pool for reply post (64bit descriptor)
+ * @reply_post_free_dma:
+ * @reply_post_free_dma_pool:
+ * @reply_post_host_index: head index in the pool where FW completes IO
+ */
+struct MPT2SAS_ADAPTER {
+ struct list_head list;
+ struct Scsi_Host *shost;
+ u8 id;
+ u32 pci_irq;
+ char name[MPT_NAME_LENGTH];
+ char tmp_string[MPT_STRING_LENGTH];
+ struct pci_dev *pdev;
+ Mpi2SystemInterfaceRegs_t __iomem *chip;
+ unsigned long chip_phys;
+ unsigned long pio_chip;
+ int logging_level;
+ u8 ir_firmware;
+ int bars;
+ u8 mask_interrupts;
+
+ /* fw fault handler */
+ char fault_reset_work_q_name[20];
+ struct workqueue_struct *fault_reset_work_q;
+ struct delayed_work fault_reset_work;
+
+ /* fw event handler */
+ char firmware_event_name[20];
+ struct workqueue_struct *firmware_event_thread;
+ u8 fw_events_off;
+ spinlock_t fw_event_lock;
+ struct list_head fw_event_list;
+
+ /* misc flags */
+ int aen_event_read_flag;
+ u8 broadcast_aen_busy;
+ u8 ioc_reset_in_progress;
+ u8 shost_recovery;
+ spinlock_t ioc_reset_in_progress_lock;
+ u8 ioc_link_reset_in_progress;
+ u8 ignore_loginfos;
+ u8 remove_host;
+ u8 wait_for_port_enable_to_complete;
+
+ u8 msix_enable;
+ u16 msix_vector_count;
+ u32 *msix_table;
+ u32 *msix_table_backup;
+
+ /* internal commands, callback index */
+ u8 scsi_io_cb_idx;
+ u8 tm_cb_idx;
+ u8 transport_cb_idx;
+ u8 ctl_cb_idx;
+ u8 base_cb_idx;
+ u8 config_cb_idx;
+ struct _internal_cmd base_cmds;
+ struct _internal_cmd transport_cmds;
+ struct _internal_cmd tm_cmds;
+ struct _internal_cmd ctl_cmds;
+ struct _internal_cmd config_cmds;
+
+ MPT_ADD_SGE base_add_sg_single;
+
+ /* event log */
+ u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+ u32 event_context;
+ void *event_log;
+ u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+
+ /* static config pages */
+ Mpi2IOCFactsReply_t facts;
+ Mpi2PortFactsReply_t *pfacts;
+ Mpi2ManufacturingPage0_t manu_pg0;
+ Mpi2BiosPage2_t bios_pg2;
+ Mpi2BiosPage3_t bios_pg3;
+ Mpi2IOCPage8_t ioc_pg8;
+ Mpi2IOUnitPage0_t iounit_pg0;
+ Mpi2IOUnitPage1_t iounit_pg1;
+
+ struct _boot_device req_boot_device;
+ struct _boot_device req_alt_boot_device;
+ struct _boot_device current_boot_device;
+
+ /* sas hba, expander, and device list */
+ struct _sas_node sas_hba;
+ struct list_head sas_expander_list;
+ spinlock_t sas_node_lock;
+ struct list_head sas_device_list;
+ struct list_head sas_device_init_list;
+ spinlock_t sas_device_lock;
+ struct list_head raid_device_list;
+ spinlock_t raid_device_lock;
+ u8 io_missing_delay;
+ u16 device_missing_delay;
+ int sas_id;
+
+ /* config page */
+ u16 config_page_sz;
+ void *config_page;
+ dma_addr_t config_page_dma;
+
+ /* request */
+ u16 sge_size;
+ u16 request_depth;
+ u16 request_sz;
+ u8 *request;
+ dma_addr_t request_dma;
+ u32 request_dma_sz;
+ struct request_tracker *scsi_lookup;
+ spinlock_t scsi_lookup_lock;
+ struct list_head free_list;
+ int pending_io_count;
+ wait_queue_head_t reset_wq;
+
+ /* chain */
+ u8 *chain;
+ dma_addr_t chain_dma;
+ u16 max_sges_in_main_message;
+ u16 max_sges_in_chain_message;
+ u16 chains_needed_per_io;
+ u16 chain_offset_value_for_main_message;
+ u16 chain_depth;
+
+ /* sense */
+ u8 *sense;
+ dma_addr_t sense_dma;
+ struct dma_pool *sense_dma_pool;
+
+ /* reply */
+ u16 reply_sz;
+ u8 *reply;
+ dma_addr_t reply_dma;
+ struct dma_pool *reply_dma_pool;
+
+ /* reply free queue */
+ u16 reply_free_queue_depth;
+ u32 *reply_free;
+ dma_addr_t reply_free_dma;
+ struct dma_pool *reply_free_dma_pool;
+ u32 reply_free_host_index;
+
+ /* reply post queue */
+ u16 reply_post_queue_depth;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free;
+ dma_addr_t reply_post_free_dma;
+ struct dma_pool *reply_post_free_dma_pool;
+ u32 reply_post_host_index;
+
+ /* diag buffer support */
+ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT];
+ dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT];
+ u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23];
+ u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
+};
+
+typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID,
+ u32 reply);
+
+
+/* base shared API */
+extern struct list_head mpt2sas_ioc_list;
+
+int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc);
+void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc);
+int mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc);
+void mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc);
+int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type);
+
+void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid);
+void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid);
+void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr);
+dma_addr_t mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid);
+dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid);
+
+u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx);
+void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid);
+void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id,
+ u16 handle);
+void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id);
+void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ u8 vf_id, u16 io_index);
+void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id);
+void mpt2sas_base_initialize_callback_handler(void);
+u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func);
+void mpt2sas_base_release_callback_handler(u8 cb_idx);
+
+void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply);
+void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr);
+
+u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked);
+
+void mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code);
+int mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply, Mpi2SasIoUnitControlRequest_t
+ *mpi_request);
+int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
+void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type);
+
+/* scsih shared API */
+void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
+ u8 type, u16 smid_task, ulong timeout);
+void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle);
+void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle);
+struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc,
+ u16 handle);
+struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER
+ *ioc, u64 sas_address);
+struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address(
+ struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
+
+void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply);
+void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase);
+
+/* config shared API */
+void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply);
+int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys);
+int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page);
+int mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage2_t *config_page);
+int mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page);
+int mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage0_t *config_page);
+int mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle);
+int mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle);
+int mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz);
+int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t config_page);
+int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
+int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOCPage8_t *config_page);
+int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle);
+int mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, u16 handle);
+int mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle);
+int mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number);
+int mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number);
+int mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u32 handle);
+int mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 *num_pds);
+int mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, u32 handle, u16 sz);
+int mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
+ u32 form_specific);
+int mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle);
+int mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle,
+ u64 *wwid);
+
+/* ctl shared API */
+extern struct device_attribute *mpt2sas_host_attrs[];
+extern struct device_attribute *mpt2sas_dev_attrs[];
+void mpt2sas_ctl_init(void);
+void mpt2sas_ctl_exit(void);
+void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply);
+void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase);
+void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply);
+void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply);
+
+/* transport shared API */
+void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply);
+struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc,
+ u16 handle, u16 parent_handle);
+void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
+ u16 parent_handle);
+int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
+int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
+void mpt2sas_transport_update_phy_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+ u16 attached_handle, u8 phy_number, u8 link_rate);
+extern struct sas_function_template mpt2sas_transport_functions;
+extern struct scsi_transport_template *mpt2sas_transport_template;
+
+#endif /* MPT2SAS_BASE_H_INCLUDED */
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_config.c b/trunk/drivers/scsi/mpt2sas/mpt2sas_config.c
new file mode 100644
index 000000000000..58cfb97846f7
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_config.c
@@ -0,0 +1,1873 @@
+/*
+ * This module provides common API for accessing firmware configuration pages
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_base.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "mpt2sas_base.h"
+
+/* local definitions */
+
+/* Timeout for config page request (in seconds) */
+#define MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT 15
+
+/* Common sgl flags for READING a config page. */
+#define MPT2_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT)
+
+/* Common sgl flags for WRITING a config page. */
+#define MPT2_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \
+ << MPI2_SGE_FLAGS_SHIFT)
+
+/**
+ * struct config_request - obtain dma memory via routine
+ * @config_page_sz: size
+ * @config_page: virt pointer
+ * @config_page_dma: phys pointer
+ *
+ */
+struct config_request{
+ u16 config_page_sz;
+ void *config_page;
+ dma_addr_t config_page_dma;
+};
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _config_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpfull when debugging issues
+ * in this module.
+ */
+static void
+_config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ return;
+
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) {
+ case MPI2_CONFIG_PAGETYPE_IO_UNIT:
+ desc = "io_unit";
+ break;
+ case MPI2_CONFIG_PAGETYPE_IOC:
+ desc = "ioc";
+ break;
+ case MPI2_CONFIG_PAGETYPE_BIOS:
+ desc = "bios";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_VOLUME:
+ desc = "raid_volume";
+ break;
+ case MPI2_CONFIG_PAGETYPE_MANUFACTURING:
+ desc = "manufaucturing";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK:
+ desc = "physdisk";
+ break;
+ case MPI2_CONFIG_PAGETYPE_EXTENDED:
+ switch (mpi_request->ExtPageType) {
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT:
+ desc = "sas_io_unit";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER:
+ desc = "sas_expander";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE:
+ desc = "sas_device";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY:
+ desc = "sas_phy";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_LOG:
+ desc = "log";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE:
+ desc = "enclosure";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG:
+ desc = "raid_config";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING:
+ desc = "driver_mappping";
+ break;
+ }
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ printk(MPT2SAS_DEBUG_FMT "%s: %s(%d), action(%d), form(0x%08x), "
+ "smid(%d)\n", ioc->name, calling_function_name, desc,
+ mpi_request->Header.PageNumber, mpi_request->Action,
+ le32_to_cpu(mpi_request->PageAddress), smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ printk(MPT2SAS_DEBUG_FMT
+ "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+}
+#endif
+
+/**
+ * mpt2sas_config_done - config page completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using _config_request.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->config_cmds.status == MPT2_CMD_NOT_USED)
+ return;
+ if (ioc->config_cmds.smid != smid)
+ return;
+ ioc->config_cmds.status |= MPT2_CMD_COMPLETE;
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ ioc->config_cmds.status |= MPT2_CMD_REPLY_VALID;
+ memcpy(ioc->config_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ }
+ ioc->config_cmds.status &= ~MPT2_CMD_PENDING;
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ _config_display_some_debug(ioc, smid, "config_done", mpi_reply);
+#endif
+ complete(&ioc->config_cmds.done);
+}
+
+/**
+ * _config_request - main routine for sending config page requests
+ * @ioc: per adapter object
+ * @mpi_request: request message frame
+ * @mpi_reply: reply mf payload returned from firmware
+ * @timeout: timeout in seconds
+ * Context: sleep, the calling function needs to acquire the config_cmds.mutex
+ *
+ * A generic API for config page requests to firmware.
+ *
+ * The ioc->config_cmds.status flag should be MPT2_CMD_NOT_USED before calling
+ * this API.
+ *
+ * The callback index is set inside `ioc->config_cb_idx.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
+ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ Mpi2ConfigRequest_t *config_request;
+ int r;
+ u8 retry_count;
+ u8 issue_reset;
+ u16 wait_state_count;
+
+ if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+ retry_count = 0;
+
+ retry_config:
+ wait_state_count = 0;
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT) {
+ printk(MPT2SAS_ERR_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
+ return -EFAULT;
+ }
+ ssleep(1);
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ printk(MPT2SAS_INFO_FMT "%s: waiting for "
+ "operational state(count=%d)\n", ioc->name,
+ __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
+ return -EAGAIN;
+ }
+
+ r = 0;
+ memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t));
+ ioc->config_cmds.status = MPT2_CMD_PENDING;
+ config_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->config_cmds.smid = smid;
+ memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t));
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ _config_display_some_debug(ioc, smid, "config_request", NULL);
+#endif
+ mpt2sas_base_put_smid_default(ioc, smid, config_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->config_cmds.done,
+ timeout*HZ);
+ if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2ConfigRequest_t)/4);
+ if (!(ioc->config_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+ if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->config_cmds.reply,
+ sizeof(Mpi2ConfigReply_t));
+ if (retry_count)
+ printk(MPT2SAS_INFO_FMT "%s: retry completed!!\n",
+ ioc->name, __func__);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
+ return r;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
+ if (!retry_count) {
+ printk(MPT2SAS_INFO_FMT "%s: attempting retry\n",
+ ioc->name, __func__);
+ retry_count++;
+ goto retry_config;
+ }
+ return -EFAULT;
+}
+
+/**
+ * _config_alloc_config_dma_memory - obtain physical memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper for obtaining dma-able memory for config page request.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ int r = 0;
+
+ mem->config_page = pci_alloc_consistent(ioc->pdev, mem->config_page_sz,
+ &mem->config_page_dma);
+ if (!mem->config_page)
+ r = -ENOMEM;
+ return r;
+}
+
+/**
+ * _config_free_config_dma_memory - wrapper to free the memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static void
+_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ pci_free_consistent(ioc->pdev, mem->config_page_sz, mem->config_page,
+ mem->config_page_dma);
+}
+
+/**
+ * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2ManufacturingPage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2ManufacturingPage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_bios_pg2 - obtain bios page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2BiosPage2_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 2;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2BiosPage2_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_bios_pg3 - obtain bios page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2BiosPage3_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2BiosPage3_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2IOUnitPage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2IOUnitPage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2IOUnitPage1_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2IOUnitPage1_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_set_iounit_pg1 - set iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+
+ memset(mem.config_page, 0, mem.config_page_sz);
+ memcpy(mem.config_page, &config_page,
+ sizeof(Mpi2IOUnitPage1_t));
+
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2IOCPage8_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ mpi_request.Header.PageNumber = 8;
+ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2IOCPage8_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2SasDevicePage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasDevicePage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2SasDevicePage1_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION;
+ mpi_request.Header.PageNumber = 1;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasDevicePage1_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host
+ * @ioc: per adapter object
+ * @num_phys: pointer returned with the number of phys
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+ u16 ioc_status;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t config_page;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply.Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply.ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply.ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ memcpy(&config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasIOUnitPage0_t)));
+ *num_phys = config_page.NumPhys;
+ }
+ }
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sz);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, sz, mem.config_page_sz));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sz);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, sz, mem.config_page_sz));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_expander_pg0 - obtain expander page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2ExpanderPage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2ExpanderPage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_expander_pg1 - obtain expander page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number,
+ u16 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2ExpanderPage1_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
+ (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2ExpanderPage1_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2SasEnclosurePage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasEnclosurePage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_phy_pg0 - obtain phy page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2SasPhyPage0_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasPhyPage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_phy_pg1 - obtain phy page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2SasPhyPage1_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply->ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2SasPhyPage1_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(config_page, 0, sizeof(Mpi2RaidVolPage1_t));
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2RaidVolPage1_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume
+ * @ioc: per adapter object
+ * @handle: volume handle
+ * @num_pds: returns pds count
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+ u8 *num_pds)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2RaidVolPage0_t *config_page;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ struct config_request mem;
+ u16 ioc_status;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ *num_pds = 0;
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply.Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply.Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply.Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ config_page = mem.config_page;
+ *num_pds = config_page->NumPhysDisks;
+ }
+ }
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
+ u32 handle, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ memset(config_page, 0, sz);
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, sz, mem.config_page_sz));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE
+ * @form_specific: specific to the form
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
+ u32 form_specific)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ struct config_request mem;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ memset(config_page, 0, sizeof(Mpi2RaidPhysDiskPage0_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | form_specific);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply->Header.PageType;
+ mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
+ mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (!r)
+ memcpy(config_page, mem.config_page,
+ min_t(u16, mem.config_page_sz,
+ sizeof(Mpi2RaidPhysDiskPage0_t)));
+
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_volume_handle - returns volume handle for give hidden raid components
+ * @ioc: per adapter object
+ * @pd_handle: phys disk handle
+ * @volume_handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle)
+{
+ Mpi2RaidConfigurationPage0_t *config_page;
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2ConfigReply_t mpi_reply;
+ int r, i;
+ struct config_request mem;
+ u16 ioc_status;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ *volume_handle = 0;
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG;
+ mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
+ mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
+ mpi_request.Header.PageType = mpi_reply.Header.PageType;
+ mpi_request.ExtPageLength = mpi_reply.ExtPageLength;
+ mpi_request.ExtPageType = mpi_reply.ExtPageType;
+ mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4;
+ if (mem.config_page_sz > ioc->config_page_sz) {
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r)
+ goto out;
+ } else {
+ mem.config_page_dma = ioc->config_page_dma;
+ mem.config_page = ioc->config_page;
+ }
+ ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
+ mem.config_page_dma);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r)
+ goto out;
+
+ r = -1;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto done;
+ config_page = mem.config_page;
+ for (i = 0; i < config_page->NumElements; i++) {
+ if ((config_page->ConfigElement[i].ElementFlags &
+ MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) !=
+ MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT)
+ continue;
+ if (config_page->ConfigElement[i].PhysDiskDevHandle ==
+ pd_handle) {
+ *volume_handle = le16_to_cpu(config_page->
+ ConfigElement[i].VolDevHandle);
+ r = 0;
+ goto done;
+ }
+ }
+
+ done:
+ if (mem.config_page_sz > ioc->config_page_sz)
+ _config_free_config_dma_memory(ioc, &mem);
+
+ out:
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return r;
+}
+
+/**
+ * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle
+ * @ioc: per adapter object
+ * @volume_handle: volume handle
+ * @wwid: volume wwid
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle,
+ u64 *wwid)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2RaidVolPage1_t raid_vol_pg1;
+
+ *wwid = 0;
+ if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE,
+ volume_handle))) {
+ *wwid = le64_to_cpu(raid_vol_pg1.WWID);
+ return 0;
+ } else
+ return -1;
+}
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.c
new file mode 100644
index 000000000000..2d4f85c9d7a1
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -0,0 +1,2516 @@
+/*
+ * Management Module Support for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "mpt2sas_base.h"
+#include "mpt2sas_ctl.h"
+
+static struct fasync_struct *async_queue;
+static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);
+
+/**
+ * enum block_state - blocking state
+ * @NON_BLOCKING: non blocking
+ * @BLOCKING: blocking
+ *
+ * These states are for ioctls that need to wait for a response
+ * from firmware, so they probably require sleep.
+ */
+enum block_state {
+ NON_BLOCKING,
+ BLOCKING,
+};
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _ctl_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpfull when debugging issues
+ * in this module.
+ */
+static void
+_ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ if (!(ioc->logging_level & MPT_DEBUG_IOCTL))
+ return;
+
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "scsi_io, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ desc = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ desc = "ioc_init";
+ break;
+ case MPI2_FUNCTION_IOC_FACTS:
+ desc = "ioc_facts";
+ break;
+ case MPI2_FUNCTION_CONFIG:
+ {
+ Mpi2ConfigRequest_t *config_request =
+ (Mpi2ConfigRequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "config, type(0x%02x), ext_type(0x%02x), number(%d)",
+ (config_request->Header.PageType &
+ MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType,
+ config_request->Header.PageNumber);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_PORT_FACTS:
+ desc = "port_facts";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ desc = "port_enable";
+ break;
+ case MPI2_FUNCTION_EVENT_NOTIFICATION:
+ desc = "event_notification";
+ break;
+ case MPI2_FUNCTION_FW_DOWNLOAD:
+ desc = "fw_download";
+ break;
+ case MPI2_FUNCTION_FW_UPLOAD:
+ desc = "fw_upload";
+ break;
+ case MPI2_FUNCTION_RAID_ACTION:
+ desc = "raid_action";
+ break;
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "raid_pass, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ desc = "sas_iounit_cntl";
+ break;
+ case MPI2_FUNCTION_SATA_PASSTHROUGH:
+ desc = "sata_pass";
+ break;
+ case MPI2_FUNCTION_DIAG_BUFFER_POST:
+ desc = "diag_buffer_post";
+ break;
+ case MPI2_FUNCTION_DIAG_RELEASE:
+ desc = "diag_release";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ desc = "smp_passthrough";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ printk(MPT2SAS_DEBUG_FMT "%s: %s, smid(%d)\n",
+ ioc->name, calling_function_name, desc, smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ printk(MPT2SAS_DEBUG_FMT
+ "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ Mpi2SCSIIOReply_t *scsi_reply =
+ (Mpi2SCSIIOReply_t *)mpi_reply;
+ if (scsi_reply->SCSIState || scsi_reply->SCSIStatus)
+ printk(MPT2SAS_DEBUG_FMT
+ "\tscsi_state(0x%02x), scsi_status"
+ "(0x%02x)\n", ioc->name,
+ scsi_reply->SCSIState,
+ scsi_reply->SCSIStatus);
+ }
+}
+#endif
+
+/**
+ * mpt2sas_ctl_done - ctl module completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using ioc->ctl_cb_idx.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED)
+ return;
+ if (ioc->ctl_cmds.smid != smid)
+ return;
+ ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE;
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->ctl_cmds.status |= MPT2_CMD_REPLY_VALID;
+ }
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply);
+#endif
+ ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING;
+ complete(&ioc->ctl_cmds.done);
+}
+
+/**
+ * _ctl_check_event_type - determines when an event needs logging
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The bitmask in ioc->event_type[] indicates which events should be
+ * be saved in the driver event_log. This bitmask is set by application.
+ *
+ * Returns 1 when event should be captured, or zero means no match.
+ */
+static int
+_ctl_check_event_type(struct MPT2SAS_ADAPTER *ioc, u16 event)
+{
+ u16 i;
+ u32 desired_event;
+
+ if (event >= 128 || !event || !ioc->event_log)
+ return 0;
+
+ desired_event = (1 << (event % 32));
+ if (!desired_event)
+ desired_event = 1;
+ i = event / 32;
+ return desired_event & ioc->event_type[i];
+}
+
+/**
+ * mpt2sas_ctl_add_to_event_log - add event
+ * @ioc: per adapter object
+ * @mpi_reply: reply message frame
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ struct MPT2_IOCTL_EVENTS *event_log;
+ u16 event;
+ int i;
+ u32 sz, event_data_sz;
+ u8 send_aen = 0;
+
+ if (!ioc->event_log)
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ if (_ctl_check_event_type(ioc, event)) {
+
+ /* insert entry into circular event_log */
+ i = ioc->event_context % MPT2SAS_CTL_EVENT_LOG_SIZE;
+ event_log = ioc->event_log;
+ event_log[i].event = event;
+ event_log[i].context = ioc->event_context++;
+
+ event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4;
+ sz = min_t(u32, event_data_sz, MPT2_EVENT_DATA_SIZE);
+ memset(event_log[i].data, 0, MPT2_EVENT_DATA_SIZE);
+ memcpy(event_log[i].data, mpi_reply->EventData, sz);
+ send_aen = 1;
+ }
+
+ /* This aen_event_read_flag flag is set until the
+ * application has read the event log.
+ * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify.
+ */
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED ||
+ (send_aen && !ioc->aen_event_read_flag)) {
+ ioc->aen_event_read_flag = 1;
+ wake_up_interruptible(&ctl_poll_wait);
+ if (async_queue)
+ kill_fasync(&async_queue, SIGIO, POLL_IN);
+ }
+}
+
+/**
+ * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ mpt2sas_ctl_add_to_event_log(ioc, mpi_reply);
+}
+
+/**
+ * _ctl_verify_adapter - validates ioc_number passed from application
+ * @ioc: per adapter object
+ * @iocpp: The ioc pointer is returned in this.
+ *
+ * Return (-1) means error, else ioc_number.
+ */
+static int
+_ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp)
+{
+ struct MPT2SAS_ADAPTER *ioc;
+
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
+ if (ioc->id != ioc_number)
+ continue;
+ *iocpp = ioc;
+ return ioc_number;
+ }
+ *iocpp = NULL;
+ return -1;
+}
+
+/**
+ * mpt2sas_ctl_reset_handler - reset callback handler (for ctl)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
+ * MPT2_IOC_DONE_RESET
+ */
+void
+mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
+{
+ switch (reset_phase) {
+ case MPT2_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT2_IOC_AFTER_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->ctl_cmds.status & MPT2_CMD_PENDING) {
+ ioc->ctl_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid);
+ complete(&ioc->ctl_cmds.done);
+ }
+ break;
+ case MPT2_IOC_DONE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+ break;
+ }
+}
+
+/**
+ * _ctl_fasync -
+ * @fd -
+ * @filep -
+ * @mode -
+ *
+ * Called when application request fasyn callback handler.
+ */
+static int
+_ctl_fasync(int fd, struct file *filep, int mode)
+{
+ return fasync_helper(fd, filep, mode, &async_queue);
+}
+
+/**
+ * _ctl_release -
+ * @inode -
+ * @filep -
+ *
+ * Called when application releases the fasyn callback handler.
+ */
+static int
+_ctl_release(struct inode *inode, struct file *filep)
+{
+ return fasync_helper(-1, filep, 0, &async_queue);
+}
+
+/**
+ * _ctl_poll -
+ * @file -
+ * @wait -
+ *
+ */
+static unsigned int
+_ctl_poll(struct file *filep, poll_table *wait)
+{
+ struct MPT2SAS_ADAPTER *ioc;
+
+ poll_wait(filep, &ctl_poll_wait, wait);
+
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
+ if (ioc->aen_event_read_flag)
+ return POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_do_task_abort - assign an active smid to the abort_task
+ * @ioc: per adapter object
+ * @karg - (struct mpt2_ioctl_command)
+ * @tm_request - pointer to mf from user space
+ *
+ * Returns 0 when an smid if found, else fail.
+ * during failure, the reply frame is filled.
+ */
+static int
+_ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
+ Mpi2SCSITaskManagementRequest_t *tm_request)
+{
+ u8 found = 0;
+ u16 i;
+ u16 handle;
+ struct scsi_cmnd *scmd;
+ struct MPT2SAS_DEVICE *priv_data;
+ unsigned long flags;
+ Mpi2SCSITaskManagementReply_t *tm_reply;
+ u32 sz;
+ u32 lun;
+
+ lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
+
+ handle = le16_to_cpu(tm_request->DevHandle);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ for (i = ioc->request_depth; i && !found; i--) {
+ scmd = ioc->scsi_lookup[i - 1].scmd;
+ if (scmd == NULL || scmd->device == NULL ||
+ scmd->device->hostdata == NULL)
+ continue;
+ if (lun != scmd->device->lun)
+ continue;
+ priv_data = scmd->device->hostdata;
+ if (priv_data->sas_target == NULL)
+ continue;
+ if (priv_data->sas_target->handle != handle)
+ continue;
+ tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid);
+ found = 1;
+ }
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ if (!found) {
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
+ "DevHandle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
+ tm_request->DevHandle, lun));
+ tm_reply = ioc->ctl_cmds.reply;
+ tm_reply->DevHandle = tm_request->DevHandle;
+ tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ tm_reply->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+ tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
+ tm_reply->VP_ID = tm_request->VP_ID;
+ tm_reply->VF_ID = tm_request->VF_ID;
+ sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz))
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ return 1;
+ }
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
+ "DevHandle(0x%04x), lun(%d), smid(%d)\n", ioc->name,
+ tm_request->DevHandle, lun, tm_request->TaskMID));
+ return 0;
+}
+
+/**
+ * _ctl_do_mpt_command - main handler for MPT2COMMAND opcode
+ * @ioc: per adapter object
+ * @karg - (struct mpt2_ioctl_command)
+ * @mf - pointer to mf in user space
+ * @state - NON_BLOCKING or BLOCKING
+ */
+static long
+_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
+ struct mpt2_ioctl_command karg, void __user *mf, enum block_state state)
+{
+ MPI2RequestHeader_t *mpi_request;
+ MPI2DefaultReply_t *mpi_reply;
+ u32 ioc_state;
+ u16 ioc_status;
+ u16 smid;
+ unsigned long timeout, timeleft;
+ u8 issue_reset;
+ u32 sz;
+ void *psge;
+ void *priv_sense = NULL;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ size_t data_out_sz = 0;
+ void *data_in = NULL;
+ dma_addr_t data_in_dma;
+ size_t data_in_sz = 0;
+ u32 sgl_flags;
+ long ret;
+ u16 wait_state_count;
+
+ issue_reset = 0;
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ printk(MPT2SAS_ERR_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ ret = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
+ printk(MPT2SAS_INFO_FMT "%s: waiting for "
+ "operational state(count=%d)\n", ioc->name,
+ __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = 0;
+ ioc->ctl_cmds.status = MPT2_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+ data_out_sz = karg.data_out_size;
+ data_in_sz = karg.data_in_size;
+
+ /* copy in request message frame from user */
+ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__,
+ __func__);
+ ret = -EFAULT;
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ if (!mpi_request->FunctionDependent1 ||
+ mpi_request->FunctionDependent1 >
+ cpu_to_le16(ioc->facts.MaxDevHandle)) {
+ ret = -EINVAL;
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ /* obtain dma-able memory for data transfer */
+ if (data_out_sz) /* WRITE */ {
+ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz,
+ &data_out_dma);
+ if (!data_out) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ if (copy_from_user(data_out, karg.data_out_buf_ptr,
+ data_out_sz)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -EFAULT;
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ if (data_in_sz) /* READ */ {
+ data_in = pci_alloc_consistent(ioc->pdev, data_in_sz,
+ &data_in_dma);
+ if (!data_in) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ /* add scatter gather elements */
+ psge = (void *)mpi_request + (karg.data_sge_offset*4);
+
+ if (!data_out_sz && !data_in_sz) {
+ mpt2sas_base_build_zero_len_sge(ioc, psge);
+ } else if (data_out_sz && data_in_sz) {
+ /* WRITE sgel first */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size;
+
+ /* READ sgel last */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ } else if (data_out_sz) /* WRITE */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+ } else if (data_in_sz) /* READ */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ }
+
+ /* send command to firmware */
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL);
+#endif
+
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsiio_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+ scsiio_request->SenseBufferLowAddress =
+ (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid);
+ memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE);
+ mpt2sas_base_put_smid_scsi_io(ioc, smid, 0,
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)mpi_request;
+
+ if (tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ if (_ctl_do_task_abort(ioc, &karg, tm_request))
+ goto out;
+ }
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ mpt2sas_base_put_smid_hi_priority(ioc, smid,
+ mpi_request->VF_ID);
+ break;
+ }
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ {
+ Mpi2SmpPassthroughRequest_t *smp_request =
+ (Mpi2SmpPassthroughRequest_t *)mpi_request;
+ u8 *data;
+
+ /* ioc determines which port to use */
+ smp_request->PhysicalPort = 0xFF;
+ if (smp_request->PassthroughFlags &
+ MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
+ data = (u8 *)&smp_request->SGL;
+ else
+ data = data_out;
+
+ if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ {
+ Mpi2SasIoUnitControlRequest_t *sasiounit_request =
+ (Mpi2SasIoUnitControlRequest_t *)mpi_request;
+
+ if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET
+ || sasiounit_request->Operation ==
+ MPI2_SAS_OP_PHY_LINK_RESET) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ break;
+ }
+ default:
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ break;
+ }
+
+ if (karg.timeout < MPT2_IOCTL_DEFAULT_TIMEOUT)
+ timeout = MPT2_IOCTL_DEFAULT_TIMEOUT;
+ else
+ timeout = karg.timeout;
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ timeout*HZ);
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)mpi_request;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) &&
+ ioc->ioc_link_reset_in_progress) {
+ ioc->ioc_link_reset_in_progress = 0;
+ ioc->ignore_loginfos = 0;
+ }
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request, karg.data_sge_offset);
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT &&
+ (ioc->logging_level & MPT_DEBUG_TM)) {
+ Mpi2SCSITaskManagementReply_t *tm_reply =
+ (Mpi2SCSITaskManagementReply_t *)mpi_reply;
+
+ printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: "
+ "IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
+ "TerminationCount(0x%08x)\n", ioc->name,
+ tm_reply->IOCStatus, tm_reply->IOCLogInfo,
+ tm_reply->TerminationCount);
+ }
+#endif
+ /* copy out xdata to user */
+ if (data_in_sz) {
+ if (copy_to_user(karg.data_in_buf_ptr, data_in,
+ data_in_sz)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out reply message frame to user */
+ if (karg.max_reply_bytes) {
+ sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out sense to user */
+ if (karg.max_sense_bytes && (mpi_request->Function ==
+ MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
+ if (copy_to_user(karg.sense_data_ptr, priv_sense, sz)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ issue_host_reset:
+ if (issue_reset) {
+ if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ printk(MPT2SAS_INFO_FMT "issue target reset: handle "
+ "= (0x%04x)\n", ioc->name,
+ mpi_request->FunctionDependent1);
+ mutex_lock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_issue_tm(ioc,
+ mpi_request->FunctionDependent1, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10);
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+ } else
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ }
+
+ out:
+
+ /* free memory associated with sg buffers */
+ if (data_in)
+ pci_free_consistent(ioc->pdev, data_in_sz, data_in,
+ data_in_dma);
+
+ if (data_out)
+ pci_free_consistent(ioc->pdev, data_out_sz, data_out,
+ data_out_dma);
+
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+ return ret;
+}
+
+/**
+ * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_getiocinfo(void __user *arg)
+{
+ struct mpt2_ioctl_iocinfo karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ u8 revision;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ memset(&karg, 0 , sizeof(karg));
+ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
+ if (ioc->pfacts)
+ karg.port_number = ioc->pfacts[0].PortNumber;
+ pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
+ karg.hw_rev = revision;
+ karg.pci_id = ioc->pdev->device;
+ karg.subsystem_device = ioc->pdev->subsystem_device;
+ karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
+ karg.pci_information.u.bits.bus = ioc->pdev->bus->number;
+ karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn);
+ karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
+ karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
+ karg.firmware_version = ioc->facts.FWVersion.Word;
+ strncpy(karg.driver_version, MPT2SAS_DRIVER_VERSION,
+ MPT2_IOCTL_VERSION_LENGTH);
+ karg.driver_version[MPT2_IOCTL_VERSION_LENGTH - 1] = '\0';
+ karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventquery(void __user *arg)
+{
+ struct mpt2_ioctl_eventquery karg;
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE;
+ memcpy(karg.event_types, ioc->event_type,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventenable(void __user *arg)
+{
+ struct mpt2_ioctl_eventenable karg;
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ if (ioc->event_log)
+ return 0;
+ memcpy(ioc->event_type, karg.event_types,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+ mpt2sas_base_validate_event_type(ioc, ioc->event_type);
+
+ /* initialize event_log */
+ ioc->event_context = 0;
+ ioc->aen_event_read_flag = 0;
+ ioc->event_log = kcalloc(MPT2SAS_CTL_EVENT_LOG_SIZE,
+ sizeof(struct MPT2_IOCTL_EVENTS), GFP_KERNEL);
+ if (!ioc->event_log) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventreport(void __user *arg)
+{
+ struct mpt2_ioctl_eventreport karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ u32 number_bytes, max_events, max;
+ struct mpt2_ioctl_eventreport __user *uarg = arg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ number_bytes = karg.hdr.max_data_size -
+ sizeof(struct mpt2_ioctl_header);
+ max_events = number_bytes/sizeof(struct MPT2_IOCTL_EVENTS);
+ max = min_t(u32, MPT2SAS_CTL_EVENT_LOG_SIZE, max_events);
+
+ /* If fewer than 1 event is requested, there must have
+ * been some type of error.
+ */
+ if (!max || !ioc->event_log)
+ return -ENODATA;
+
+ number_bytes = max * sizeof(struct MPT2_IOCTL_EVENTS);
+ if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ /* reset flag so SIGIO can restart */
+ ioc->aen_event_read_flag = 0;
+ return 0;
+}
+
+/**
+ * _ctl_do_reset - main handler for MPT2HARDRESET opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_do_reset(void __user *arg)
+{
+ struct mpt2_ioctl_diag_reset karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ int retval;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ printk(MPT2SAS_INFO_FMT "host reset: %s\n",
+ ioc->name, ((!retval) ? "SUCCESS" : "FAILED"));
+ return 0;
+}
+
+/**
+ * _ctl_btdh_search_sas_device - searching for sas device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_sas_device(struct MPT2SAS_ADAPTER *ioc,
+ struct mpt2_ioctl_btdh_mapping *btdh)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->sas_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == sas_device->handle) {
+ btdh->bus = sas_device->channel;
+ btdh->id = sas_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == sas_device->channel && btdh->id ==
+ sas_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = sas_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_search_raid_device - searching for raid device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc,
+ struct mpt2_ioctl_btdh_mapping *btdh)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->raid_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == raid_device->handle) {
+ btdh->bus = raid_device->channel;
+ btdh->id = raid_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == raid_device->channel && btdh->id ==
+ raid_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = raid_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_btdh_mapping(void __user *arg)
+{
+ struct mpt2_ioctl_btdh_mapping karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ int rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ rc = _ctl_btdh_search_sas_device(ioc, &karg);
+ if (!rc)
+ _ctl_btdh_search_raid_device(ioc, &karg);
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_diag_capability - return diag buffer capability
+ * @ioc: per adapter object
+ * @buffer_type: specifies either TRACE or SNAPSHOT
+ *
+ * returns 1 when diag buffer support is enabled in firmware
+ */
+static u8
+_ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type)
+{
+ u8 rc = 0;
+
+ switch (buffer_type) {
+ case MPI2_DIAG_BUF_TYPE_TRACE:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
+ rc = 1;
+ break;
+ case MPI2_DIAG_BUF_TYPE_SNAPSHOT:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * _ctl_diag_register - application register with driver
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+static long
+_ctl_diag_register(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_register karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ int rc, i;
+ void *request_data = NULL;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz = 0;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ u8 buffer_type;
+ unsigned long timeleft;
+ u16 smid;
+ u16 ioc_status;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.buffer_type;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED) {
+ printk(MPT2SAS_ERR_FMT "%s: already has a registered "
+ "buffer for buffer_type(0x%02x)\n", ioc->name, __func__,
+ buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.requested_buffer_size % 4) {
+ printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size "
+ "is not 4 byte aligned\n", ioc->name, __func__);
+ return -EINVAL;
+ }
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT2_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ request_data = ioc->diag_buffer[buffer_type];
+ request_data_sz = karg.requested_buffer_size;
+ ioc->unique_id[buffer_type] = karg.unique_id;
+ ioc->diag_buffer_status[buffer_type] = 0;
+ memcpy(ioc->product_specific[buffer_type], karg.product_specific,
+ MPT2_PRODUCT_SPECIFIC_DWORDS);
+ ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags;
+
+ if (request_data) {
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) {
+ pci_free_consistent(ioc->pdev,
+ ioc->diag_buffer_sz[buffer_type],
+ request_data, request_data_dma);
+ request_data = NULL;
+ }
+ }
+
+ if (request_data == NULL) {
+ ioc->diag_buffer_sz[buffer_type] = 0;
+ ioc->diag_buffer_dma[buffer_type] = 0;
+ request_data = pci_alloc_consistent(
+ ioc->pdev, request_data_sz, &request_data_dma);
+ if (request_data == NULL) {
+ printk(MPT2SAS_ERR_FMT "%s: failed allocating memory"
+ " for diag buffers, requested size(%d)\n",
+ ioc->name, __func__, request_data_sz);
+ mpt2sas_base_free_smid(ioc, smid);
+ return -ENOMEM;
+ }
+ ioc->diag_buffer[buffer_type] = request_data;
+ ioc->diag_buffer_sz[buffer_type] = request_data_sz;
+ ioc->diag_buffer_dma[buffer_type] = request_data_dma;
+ }
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = karg.buffer_type;
+ mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags);
+ mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
+ mpi_request->BufferLength = cpu_to_le32(request_data_sz);
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), "
+ "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data,
+ (unsigned long long)request_data_dma, mpi_request->BufferLength));
+
+ for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT2_DIAG_BUFFER_IS_REGISTERED;
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
+ "log_info(0x%08x)\n", ioc->name, __func__,
+ ioc_status, mpi_reply->IOCLogInfo);
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ out:
+
+ if (rc && request_data)
+ pci_free_consistent(ioc->pdev, request_data_sz,
+ request_data, request_data_dma);
+
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _ctl_diag_unregister - application unregister with driver
+ * @arg - user space buffer containing ioctl content
+ *
+ * This will allow the driver to cleanup any memory allocated for diag
+ * messages and to free up any resources.
+ */
+static long
+_ctl_diag_unregister(void __user *arg)
+{
+ struct mpt2_diag_unregister karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ void *request_data;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
+ "registered\n", ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_RELEASED) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) has not been "
+ "released\n", ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
+ "registered\n", ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ request_data_sz = ioc->diag_buffer_sz[buffer_type];
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ pci_free_consistent(ioc->pdev, request_data_sz,
+ request_data, request_data_dma);
+ ioc->diag_buffer[buffer_type] = NULL;
+ ioc->diag_buffer_status[buffer_type] = 0;
+ return 0;
+}
+
+/**
+ * _ctl_diag_query - query relevant info associated with diag buffers
+ * @arg - user space buffer containing ioctl content
+ *
+ * The application will send only buffer_type and unique_id. Driver will
+ * inspect unique_id first, if valid, fill in all the info. If unique_id is
+ * 0x00, the driver will return info specified by Buffer Type.
+ */
+static long
+_ctl_diag_query(void __user *arg)
+{
+ struct mpt2_diag_query karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ void *request_data;
+ int i;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ karg.application_flags = 0;
+ buffer_type = karg.buffer_type;
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
+ "registered\n", ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id & 0xffffff00) {
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
+ "registered\n", ioc->name, __func__,
+ karg.unique_id);
+ return -EINVAL;
+ }
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED)
+ karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED |
+ MPT2_APP_FLAGS_BUFFER_VALID);
+ else
+ karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED |
+ MPT2_APP_FLAGS_BUFFER_VALID |
+ MPT2_APP_FLAGS_FW_BUFFER_ACCESS);
+
+ for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
+ karg.product_specific[i] =
+ ioc->product_specific[buffer_type][i];
+
+ karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type];
+ karg.driver_added_buffer_size = 0;
+ karg.unique_id = ioc->unique_id[buffer_type];
+ karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type];
+
+ if (copy_to_user(arg, &karg, sizeof(struct mpt2_diag_query))) {
+ printk(MPT2SAS_ERR_FMT "%s: unable to write mpt2_diag_query "
+ "data @ %p\n", ioc->name, __func__, arg);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_diag_release - request to send Diag Release Message to firmware
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwritting information in the buffer.
+ */
+static long
+_ctl_diag_release(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_release karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ void *request_data;
+ int rc;
+ Mpi2DiagReleaseRequest_t *mpi_request;
+ Mpi2DiagReleaseReply_t *mpi_reply;
+ u8 buffer_type;
+ unsigned long timeleft;
+ u16 smid;
+ u16 ioc_status;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
+ "registered\n", ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
+ "registered\n", ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_RELEASED) {
+ printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
+ "is already released\n", ioc->name, __func__,
+ buffer_type);
+ return 0;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+
+ if (!request_data) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT2_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE;
+ mpi_request->BufferType = buffer_type;
+
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagReleaseRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT2_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
+ "log_info(0x%08x)\n", ioc->name, __func__,
+ ioc_status, mpi_reply->IOCLogInfo);
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ out:
+
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _ctl_diag_read_buffer - request for copy of the diag buffer
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ */
+static long
+_ctl_diag_read_buffer(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_read_buffer karg;
+ struct mpt2_diag_read_buffer __user *uarg = arg;
+ struct MPT2SAS_ADAPTER *ioc;
+ void *request_data, *diag_data;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ int rc, i;
+ u8 buffer_type;
+ unsigned long timeleft;
+ u16 smid;
+ u16 ioc_status;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
+ "registered\n", ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
+ printk(MPT2SAS_ERR_FMT "%s: either the starting_offset "
+ "or bytes_to_read are not 4 byte aligned\n", ioc->name,
+ __func__);
+ return -EINVAL;
+ }
+
+ diag_data = (void *)(request_data + karg.starting_offset);
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(%p), "
+ "offset(%d), sz(%d)\n", ioc->name, __func__,
+ diag_data, karg.starting_offset, karg.bytes_to_read));
+
+ if (copy_to_user((void __user *)uarg->diagnostic_data,
+ diag_data, karg.bytes_to_read)) {
+ printk(MPT2SAS_ERR_FMT "%s: Unable to write "
+ "mpt_diag_read_buffer_t data @ %p\n", ioc->name,
+ __func__, diag_data);
+ return -EFAULT;
+ }
+
+ if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0)
+ return 0;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: Reregister "
+ "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type));
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT2_DIAG_BUFFER_IS_RELEASED) == 0) {
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "buffer_type(0x%02x) is still registered\n", ioc->name,
+ __func__, buffer_type));
+ return 0;
+ }
+ /* Get a free request frame and save the message context.
+ */
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT2_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = buffer_type;
+ mpi_request->BufferLength =
+ cpu_to_le32(ioc->diag_buffer_sz[buffer_type]);
+ mpi_request->BufferAddress =
+ cpu_to_le64(ioc->diag_buffer_dma[buffer_type]);
+ for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+
+ mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
+ printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT2_DIAG_BUFFER_IS_REGISTERED;
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
+ "log_info(0x%08x)\n", ioc->name, __func__,
+ ioc_status, mpi_reply->IOCLogInfo);
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ out:
+
+ ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _ctl_ioctl_main - main ioctl entry point
+ * @file - (struct file)
+ * @cmd - ioctl opcode
+ * @arg -
+ */
+static long
+_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg)
+{
+ enum block_state state;
+ long ret = -EINVAL;
+ unsigned long flags;
+
+ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING :
+ BLOCKING;
+
+ switch (cmd) {
+ case MPT2IOCINFO:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo))
+ ret = _ctl_getiocinfo(arg);
+ break;
+ case MPT2COMMAND:
+ {
+ struct mpt2_ioctl_command karg;
+ struct mpt2_ioctl_command __user *uarg;
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 ||
+ !ioc)
+ return -ENODEV;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
+ flags);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) {
+ uarg = arg;
+ ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf, state);
+ }
+ break;
+ }
+ case MPT2EVENTQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery))
+ ret = _ctl_eventquery(arg);
+ break;
+ case MPT2EVENTENABLE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable))
+ ret = _ctl_eventenable(arg);
+ break;
+ case MPT2EVENTREPORT:
+ ret = _ctl_eventreport(arg);
+ break;
+ case MPT2HARDRESET:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset))
+ ret = _ctl_do_reset(arg);
+ break;
+ case MPT2BTDHMAPPING:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping))
+ ret = _ctl_btdh_mapping(arg);
+ break;
+ case MPT2DIAGREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register))
+ ret = _ctl_diag_register(arg, state);
+ break;
+ case MPT2DIAGUNREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister))
+ ret = _ctl_diag_unregister(arg);
+ break;
+ case MPT2DIAGQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query))
+ ret = _ctl_diag_query(arg);
+ break;
+ case MPT2DIAGRELEASE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release))
+ ret = _ctl_diag_release(arg, state);
+ break;
+ case MPT2DIAGREADBUFFER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer))
+ ret = _ctl_diag_read_buffer(arg, state);
+ break;
+ default:
+ {
+ struct mpt2_ioctl_command karg;
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 ||
+ !ioc)
+ return -ENODEV;
+
+ dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd));
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * _ctl_ioctl - main ioctl entry point (unlocked)
+ * @file - (struct file)
+ * @cmd - ioctl opcode
+ * @arg -
+ */
+static long
+_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg);
+ unlock_kernel();
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+/**
+ * _ctl_compat_mpt_command - convert 32bit pointers to 64bit.
+ * @file - (struct file)
+ * @cmd - ioctl opcode
+ * @arg - (struct mpt2_ioctl_command32)
+ *
+ * MPT2COMMAND32 - Handle 32bit applications running on 64bit os.
+ */
+static long
+_ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg)
+{
+ struct mpt2_ioctl_command32 karg32;
+ struct mpt2_ioctl_command32 __user *uarg;
+ struct mpt2_ioctl_command karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ enum block_state state;
+ unsigned long flags;
+
+ if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32))
+ return -EINVAL;
+
+ uarg = (struct mpt2_ioctl_command32 __user *) arg;
+
+ if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
+ flags);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ memset(&karg, 0, sizeof(struct mpt2_ioctl_command));
+ karg.hdr.ioc_number = karg32.hdr.ioc_number;
+ karg.hdr.port_number = karg32.hdr.port_number;
+ karg.hdr.max_data_size = karg32.hdr.max_data_size;
+ karg.timeout = karg32.timeout;
+ karg.max_reply_bytes = karg32.max_reply_bytes;
+ karg.data_in_size = karg32.data_in_size;
+ karg.data_out_size = karg32.data_out_size;
+ karg.max_sense_bytes = karg32.max_sense_bytes;
+ karg.data_sge_offset = karg32.data_sge_offset;
+ memcpy(&karg.reply_frame_buf_ptr, &karg32.reply_frame_buf_ptr,
+ sizeof(uint32_t));
+ memcpy(&karg.data_in_buf_ptr, &karg32.data_in_buf_ptr,
+ sizeof(uint32_t));
+ memcpy(&karg.data_out_buf_ptr, &karg32.data_out_buf_ptr,
+ sizeof(uint32_t));
+ memcpy(&karg.sense_data_ptr, &karg32.sense_data_ptr,
+ sizeof(uint32_t));
+ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
+ return _ctl_do_mpt_command(ioc, karg, &uarg->mf, state);
+}
+
+/**
+ * _ctl_ioctl_compat - main ioctl entry point (compat)
+ * @file -
+ * @cmd -
+ * @arg -
+ *
+ * This routine handles 32 bit applications in 64bit os.
+ */
+static long
+_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ if (cmd == MPT2COMMAND32)
+ ret = _ctl_compat_mpt_command(file, cmd, arg);
+ else
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg);
+ unlock_kernel();
+ return ret;
+}
+#endif
+
+/* scsi host attributes */
+
+/**
+ * _ctl_version_fw_show - firmware version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF);
+}
+static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL);
+
+/**
+ * _ctl_version_bios_show - bios version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (version & 0xFF000000) >> 24,
+ (version & 0x00FF0000) >> 16,
+ (version & 0x0000FF00) >> 8,
+ version & 0x000000FF);
+}
+static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL);
+
+/**
+ * _ctl_version_mpi_show - MPI (message passing interface) version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n",
+ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8);
+}
+static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL);
+
+/**
+ * _ctl_version_product_show - product name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_product_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName);
+}
+static DEVICE_ATTR(version_product, S_IRUGO,
+ _ctl_version_product_show, NULL);
+
+/**
+ * _ctl_version_nvdata_persistent_show - ndvata persistent version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_nvdata_persistent_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02xh\n",
+ le16_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word));
+}
+static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO,
+ _ctl_version_nvdata_persistent_show, NULL);
+
+/**
+ * _ctl_version_nvdata_default_show - nvdata default version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_nvdata_default_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02xh\n",
+ le16_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word));
+}
+static DEVICE_ATTR(version_nvdata_default, S_IRUGO,
+ _ctl_version_nvdata_default_show, NULL);
+
+/**
+ * _ctl_board_name_show - board name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_name_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName);
+}
+static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL);
+
+/**
+ * _ctl_board_assembly_show - board assembly name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly);
+}
+static DEVICE_ATTR(board_assembly, S_IRUGO,
+ _ctl_board_assembly_show, NULL);
+
+/**
+ * _ctl_board_tracer_show - board tracer number
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber);
+}
+static DEVICE_ATTR(board_tracer, S_IRUGO,
+ _ctl_board_tracer_show, NULL);
+
+/**
+ * _ctl_io_delay_show - io missing delay
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay);
+}
+static DEVICE_ATTR(io_delay, S_IRUGO,
+ _ctl_io_delay_show, NULL);
+
+/**
+ * _ctl_device_delay_show - device missing delay
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay);
+}
+static DEVICE_ATTR(device_delay, S_IRUGO,
+ _ctl_device_delay_show, NULL);
+
+/**
+ * _ctl_fw_queue_depth_show - global credits
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is firmware queue depth limit
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit);
+}
+static DEVICE_ATTR(fw_queue_depth, S_IRUGO,
+ _ctl_fw_queue_depth_show, NULL);
+
+/**
+ * _ctl_sas_address_show - sas address
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the controller sas address
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)ioc->sas_hba.sas_address);
+}
+static DEVICE_ATTR(host_sas_address, S_IRUGO,
+ _ctl_host_sas_address_show, NULL);
+
+/**
+ * _ctl_logging_level_show - logging level
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level);
+}
+static ssize_t
+_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%x", &val) != 1)
+ return -EINVAL;
+
+ ioc->logging_level = val;
+ printk(MPT2SAS_INFO_FMT "logging_level=%08xh\n", ioc->name,
+ ioc->logging_level);
+ return strlen(buf);
+}
+static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
+ _ctl_logging_level_show, _ctl_logging_level_store);
+
+struct device_attribute *mpt2sas_host_attrs[] = {
+ &dev_attr_version_fw,
+ &dev_attr_version_bios,
+ &dev_attr_version_mpi,
+ &dev_attr_version_product,
+ &dev_attr_version_nvdata_persistent,
+ &dev_attr_version_nvdata_default,
+ &dev_attr_board_name,
+ &dev_attr_board_assembly,
+ &dev_attr_board_tracer,
+ &dev_attr_io_delay,
+ &dev_attr_device_delay,
+ &dev_attr_logging_level,
+ &dev_attr_fw_queue_depth,
+ &dev_attr_host_sas_address,
+ NULL,
+};
+
+/* device attributes */
+
+/**
+ * _ctl_device_sas_address_show - sas address
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the sas address for the target
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)sas_device_priv_data->sas_target->sas_address);
+}
+static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL);
+
+/**
+ * _ctl_device_handle_show - device handle
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the firmware assigned device handle
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_handle_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+ sas_device_priv_data->sas_target->handle);
+}
+static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL);
+
+struct device_attribute *mpt2sas_dev_attrs[] = {
+ &dev_attr_sas_address,
+ &dev_attr_sas_device_handle,
+ NULL,
+};
+
+static const struct file_operations ctl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = _ctl_ioctl,
+ .release = _ctl_release,
+ .poll = _ctl_poll,
+ .fasync = _ctl_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = _ctl_ioctl_compat,
+#endif
+};
+
+static struct miscdevice ctl_dev = {
+ .minor = MPT2SAS_MINOR,
+ .name = MPT2SAS_DEV_NAME,
+ .fops = &ctl_fops,
+};
+
+/**
+ * mpt2sas_ctl_init - main entry point for ctl.
+ *
+ */
+void
+mpt2sas_ctl_init(void)
+{
+ async_queue = NULL;
+ if (misc_register(&ctl_dev) < 0)
+ printk(KERN_ERR "%s can't register misc device [minor=%d]\n",
+ MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR);
+
+ init_waitqueue_head(&ctl_poll_wait);
+}
+
+/**
+ * mpt2sas_ctl_exit - exit point for ctl
+ *
+ */
+void
+mpt2sas_ctl_exit(void)
+{
+ struct MPT2SAS_ADAPTER *ioc;
+ int i;
+
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
+
+ /* free memory associated to diag buffers */
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!ioc->diag_buffer[i])
+ continue;
+ pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i],
+ ioc->diag_buffer[i], ioc->diag_buffer_dma[i]);
+ ioc->diag_buffer[i] = NULL;
+ ioc->diag_buffer_status[i] = 0;
+ }
+
+ kfree(ioc->event_log);
+ }
+ misc_deregister(&ctl_dev);
+}
+
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.h
new file mode 100644
index 000000000000..dbb6c0cf8889
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_ctl.h
@@ -0,0 +1,416 @@
+/*
+ * Management Module Support for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT2SAS_CTL_H_INCLUDED
+#define MPT2SAS_CTL_H_INCLUDED
+
+#ifdef __KERNEL__
+#include
+#endif
+
+#define MPT2SAS_DEV_NAME "mpt2ctl"
+#define MPT2_MAGIC_NUMBER 'm'
+#define MPT2_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */
+
+/**
+ * IOCTL opcodes
+ */
+#define MPT2IOCINFO _IOWR(MPT2_MAGIC_NUMBER, 17, \
+ struct mpt2_ioctl_iocinfo)
+#define MPT2COMMAND _IOWR(MPT2_MAGIC_NUMBER, 20, \
+ struct mpt2_ioctl_command)
+#ifdef CONFIG_COMPAT
+#define MPT2COMMAND32 _IOWR(MPT2_MAGIC_NUMBER, 20, \
+ struct mpt2_ioctl_command32)
+#endif
+#define MPT2EVENTQUERY _IOWR(MPT2_MAGIC_NUMBER, 21, \
+ struct mpt2_ioctl_eventquery)
+#define MPT2EVENTENABLE _IOWR(MPT2_MAGIC_NUMBER, 22, \
+ struct mpt2_ioctl_eventenable)
+#define MPT2EVENTREPORT _IOWR(MPT2_MAGIC_NUMBER, 23, \
+ struct mpt2_ioctl_eventreport)
+#define MPT2HARDRESET _IOWR(MPT2_MAGIC_NUMBER, 24, \
+ struct mpt2_ioctl_diag_reset)
+#define MPT2BTDHMAPPING _IOWR(MPT2_MAGIC_NUMBER, 31, \
+ struct mpt2_ioctl_btdh_mapping)
+
+/* diag buffer support */
+#define MPT2DIAGREGISTER _IOWR(MPT2_MAGIC_NUMBER, 26, \
+ struct mpt2_diag_register)
+#define MPT2DIAGRELEASE _IOWR(MPT2_MAGIC_NUMBER, 27, \
+ struct mpt2_diag_release)
+#define MPT2DIAGUNREGISTER _IOWR(MPT2_MAGIC_NUMBER, 28, \
+ struct mpt2_diag_unregister)
+#define MPT2DIAGQUERY _IOWR(MPT2_MAGIC_NUMBER, 29, \
+ struct mpt2_diag_query)
+#define MPT2DIAGREADBUFFER _IOWR(MPT2_MAGIC_NUMBER, 30, \
+ struct mpt2_diag_read_buffer)
+
+/**
+ * struct mpt2_ioctl_header - main header structure
+ * @ioc_number - IOC unit number
+ * @port_number - IOC port number
+ * @max_data_size - maximum number bytes to transfer on read
+ */
+struct mpt2_ioctl_header {
+ uint32_t ioc_number;
+ uint32_t port_number;
+ uint32_t max_data_size;
+};
+
+/**
+ * struct mpt2_ioctl_diag_reset - diagnostic reset
+ * @hdr - generic header
+ */
+struct mpt2_ioctl_diag_reset {
+ struct mpt2_ioctl_header hdr;
+};
+
+
+/**
+ * struct mpt2_ioctl_pci_info - pci device info
+ * @device - pci device id
+ * @function - pci function id
+ * @bus - pci bus id
+ * @segment_id - pci segment id
+ */
+struct mpt2_ioctl_pci_info {
+ union {
+ struct {
+ uint32_t device:5;
+ uint32_t function:3;
+ uint32_t bus:24;
+ } bits;
+ uint32_t word;
+ } u;
+ uint32_t segment_id;
+};
+
+
+#define MPT2_IOCTL_INTERFACE_SCSI (0x00)
+#define MPT2_IOCTL_INTERFACE_FC (0x01)
+#define MPT2_IOCTL_INTERFACE_FC_IP (0x02)
+#define MPT2_IOCTL_INTERFACE_SAS (0x03)
+#define MPT2_IOCTL_INTERFACE_SAS2 (0x04)
+#define MPT2_IOCTL_VERSION_LENGTH (32)
+
+/**
+ * struct mpt2_ioctl_iocinfo - generic controller info
+ * @hdr - generic header
+ * @adapter_type - type of adapter (spi, fc, sas)
+ * @port_number - port number
+ * @pci_id - PCI Id
+ * @hw_rev - hardware revision
+ * @sub_system_device - PCI subsystem Device ID
+ * @sub_system_vendor - PCI subsystem Vendor ID
+ * @rsvd0 - reserved
+ * @firmware_version - firmware version
+ * @bios_version - BIOS version
+ * @driver_version - driver version - 32 ASCII characters
+ * @rsvd1 - reserved
+ * @scsi_id - scsi id of adapter 0
+ * @rsvd2 - reserved
+ * @pci_information - pci info (2nd revision)
+ */
+struct mpt2_ioctl_iocinfo {
+ struct mpt2_ioctl_header hdr;
+ uint32_t adapter_type;
+ uint32_t port_number;
+ uint32_t pci_id;
+ uint32_t hw_rev;
+ uint32_t subsystem_device;
+ uint32_t subsystem_vendor;
+ uint32_t rsvd0;
+ uint32_t firmware_version;
+ uint32_t bios_version;
+ uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH];
+ uint8_t rsvd1;
+ uint8_t scsi_id;
+ uint16_t rsvd2;
+ struct mpt2_ioctl_pci_info pci_information;
+};
+
+
+/* number of event log entries */
+#define MPT2SAS_CTL_EVENT_LOG_SIZE (50)
+
+/**
+ * struct mpt2_ioctl_eventquery - query event count and type
+ * @hdr - generic header
+ * @event_entries - number of events returned by get_event_report
+ * @rsvd - reserved
+ * @event_types - type of events currently being captured
+ */
+struct mpt2_ioctl_eventquery {
+ struct mpt2_ioctl_header hdr;
+ uint16_t event_entries;
+ uint16_t rsvd;
+ uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+};
+
+/**
+ * struct mpt2_ioctl_eventenable - enable/disable event capturing
+ * @hdr - generic header
+ * @event_types - toggle off/on type of events to be captured
+ */
+struct mpt2_ioctl_eventenable {
+ struct mpt2_ioctl_header hdr;
+ uint32_t event_types[4];
+};
+
+#define MPT2_EVENT_DATA_SIZE (192)
+/**
+ * struct MPT2_IOCTL_EVENTS -
+ * @event - the event that was reported
+ * @context - unique value for each event assigned by driver
+ * @data - event data returned in fw reply message
+ */
+struct MPT2_IOCTL_EVENTS {
+ uint32_t event;
+ uint32_t context;
+ uint8_t data[MPT2_EVENT_DATA_SIZE];
+};
+
+/**
+ * struct mpt2_ioctl_eventreport - returing event log
+ * @hdr - generic header
+ * @event_data - (see struct MPT2_IOCTL_EVENTS)
+ */
+struct mpt2_ioctl_eventreport {
+ struct mpt2_ioctl_header hdr;
+ struct MPT2_IOCTL_EVENTS event_data[1];
+};
+
+/**
+ * struct mpt2_ioctl_command - generic mpt firmware passthru ioclt
+ * @hdr - generic header
+ * @timeout - command timeout in seconds. (if zero then use driver default
+ * value).
+ * @reply_frame_buf_ptr - reply location
+ * @data_in_buf_ptr - destination for read
+ * @data_out_buf_ptr - data source for write
+ * @sense_data_ptr - sense data location
+ * @max_reply_bytes - maximum number of reply bytes to be sent to app.
+ * @data_in_size - number bytes for data transfer in (read)
+ * @data_out_size - number bytes for data transfer out (write)
+ * @max_sense_bytes - maximum number of bytes for auto sense buffers
+ * @data_sge_offset - offset in words from the start of the request message to
+ * the first SGL
+ * @mf[1];
+ */
+struct mpt2_ioctl_command {
+ struct mpt2_ioctl_header hdr;
+ uint32_t timeout;
+ void __user *reply_frame_buf_ptr;
+ void __user *data_in_buf_ptr;
+ void __user *data_out_buf_ptr;
+ void __user *sense_data_ptr;
+ uint32_t max_reply_bytes;
+ uint32_t data_in_size;
+ uint32_t data_out_size;
+ uint32_t max_sense_bytes;
+ uint32_t data_sge_offset;
+ uint8_t mf[1];
+};
+
+#ifdef CONFIG_COMPAT
+struct mpt2_ioctl_command32 {
+ struct mpt2_ioctl_header hdr;
+ uint32_t timeout;
+ uint32_t reply_frame_buf_ptr;
+ uint32_t data_in_buf_ptr;
+ uint32_t data_out_buf_ptr;
+ uint32_t sense_data_ptr;
+ uint32_t max_reply_bytes;
+ uint32_t data_in_size;
+ uint32_t data_out_size;
+ uint32_t max_sense_bytes;
+ uint32_t data_sge_offset;
+ uint8_t mf[1];
+};
+#endif
+
+/**
+ * struct mpt2_ioctl_btdh_mapping - mapping info
+ * @hdr - generic header
+ * @id - target device identification number
+ * @bus - SCSI bus number that the target device exists on
+ * @handle - device handle for the target device
+ * @rsvd - reserved
+ *
+ * To obtain a bus/id the application sets
+ * handle to valid handle, and bus/id to 0xFFFF.
+ *
+ * To obtain the device handle the application sets
+ * bus/id valid value, and the handle to 0xFFFF.
+ */
+struct mpt2_ioctl_btdh_mapping {
+ struct mpt2_ioctl_header hdr;
+ uint32_t id;
+ uint32_t bus;
+ uint16_t handle;
+ uint16_t rsvd;
+};
+
+
+/* status bits for ioc->diag_buffer_status */
+#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01)
+#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02)
+
+/* application flags for mpt2_diag_register, mpt2_diag_query */
+#define MPT2_APP_FLAGS_APP_OWNED (0x0001)
+#define MPT2_APP_FLAGS_BUFFER_VALID (0x0002)
+#define MPT2_APP_FLAGS_FW_BUFFER_ACCESS (0x0004)
+
+/* flags for mpt2_diag_read_buffer */
+#define MPT2_FLAGS_REREGISTER (0x0001)
+
+#define MPT2_PRODUCT_SPECIFIC_DWORDS 23
+
+/**
+ * struct mpt2_diag_register - application register with driver
+ * @hdr - generic header
+ * @reserved -
+ * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @application_flags - misc flags
+ * @diagnostic_flags - specifies flags affecting command processing
+ * @product_specific - product specific information
+ * @requested_buffer_size - buffers size in bytes
+ * @unique_id - tag specified by application that is used to signal ownership
+ * of the buffer.
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+struct mpt2_diag_register {
+ struct mpt2_ioctl_header hdr;
+ uint8_t reserved;
+ uint8_t buffer_type;
+ uint16_t application_flags;
+ uint32_t diagnostic_flags;
+ uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t requested_buffer_size;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt2_diag_unregister - application unregister with driver
+ * @hdr - generic header
+ * @unique_id - tag uniquely identifies the buffer to be unregistered
+ *
+ * This will allow the driver to cleanup any memory allocated for diag
+ * messages and to free up any resources.
+ */
+struct mpt2_diag_unregister {
+ struct mpt2_ioctl_header hdr;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt2_diag_query - query relevant info associated with diag buffers
+ * @hdr - generic header
+ * @reserved -
+ * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @application_flags - misc flags
+ * @diagnostic_flags - specifies flags affecting command processing
+ * @product_specific - product specific information
+ * @total_buffer_size - diag buffer size in bytes
+ * @driver_added_buffer_size - size of extra space appended to end of buffer
+ * @unique_id - unique id associated with this buffer.
+ *
+ * The application will send only buffer_type and unique_id. Driver will
+ * inspect unique_id first, if valid, fill in all the info. If unique_id is
+ * 0x00, the driver will return info specified by Buffer Type.
+ */
+struct mpt2_diag_query {
+ struct mpt2_ioctl_header hdr;
+ uint8_t reserved;
+ uint8_t buffer_type;
+ uint16_t application_flags;
+ uint32_t diagnostic_flags;
+ uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t total_buffer_size;
+ uint32_t driver_added_buffer_size;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt2_diag_release - request to send Diag Release Message to firmware
+ * @hdr - generic header
+ * @unique_id - tag uniquely identifies the buffer to be released
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwritting information in the buffer.
+ */
+struct mpt2_diag_release {
+ struct mpt2_ioctl_header hdr;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt2_diag_read_buffer - request for copy of the diag buffer
+ * @hdr - generic header
+ * @status -
+ * @reserved -
+ * @flags - misc flags
+ * @starting_offset - starting offset within drivers buffer where to start
+ * reading data at into the specified application buffer
+ * @bytes_to_read - number of bytes to copy from the drivers buffer into the
+ * application buffer starting at starting_offset.
+ * @unique_id - unique id associated with this buffer.
+ * @diagnostic_data - data payload
+ */
+struct mpt2_diag_read_buffer {
+ struct mpt2_ioctl_header hdr;
+ uint8_t status;
+ uint8_t reserved;
+ uint16_t flags;
+ uint32_t starting_offset;
+ uint32_t bytes_to_read;
+ uint32_t unique_id;
+ uint32_t diagnostic_data[1];
+};
+
+#endif /* MPT2SAS_CTL_H_INCLUDED */
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_debug.h b/trunk/drivers/scsi/mpt2sas/mpt2sas_debug.h
new file mode 100644
index 000000000000..ad325096e842
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_debug.h
@@ -0,0 +1,181 @@
+/*
+ * Logging Support for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT2SAS_DEBUG_H_INCLUDED
+#define MPT2SAS_DEBUG_H_INCLUDED
+
+#define MPT_DEBUG 0x00000001
+#define MPT_DEBUG_MSG_FRAME 0x00000002
+#define MPT_DEBUG_SG 0x00000004
+#define MPT_DEBUG_EVENTS 0x00000008
+#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010
+#define MPT_DEBUG_INIT 0x00000020
+#define MPT_DEBUG_EXIT 0x00000040
+#define MPT_DEBUG_FAIL 0x00000080
+#define MPT_DEBUG_TM 0x00000100
+#define MPT_DEBUG_REPLY 0x00000200
+#define MPT_DEBUG_HANDSHAKE 0x00000400
+#define MPT_DEBUG_CONFIG 0x00000800
+#define MPT_DEBUG_DL 0x00001000
+#define MPT_DEBUG_RESET 0x00002000
+#define MPT_DEBUG_SCSI 0x00004000
+#define MPT_DEBUG_IOCTL 0x00008000
+#define MPT_DEBUG_CSMISAS 0x00010000
+#define MPT_DEBUG_SAS 0x00020000
+#define MPT_DEBUG_TRANSPORT 0x00040000
+#define MPT_DEBUG_TASK_SET_FULL 0x00080000
+
+#define MPT_DEBUG_TARGET_MODE 0x00100000
+
+
+/*
+ * CONFIG_SCSI_MPT2SAS_LOGGING - enabled in Kconfig
+ */
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \
+{ \
+ if (IOC->logging_level & BITS) \
+ CMD; \
+}
+#else
+#define MPT_CHECK_LOGGING(IOC, CMD, BITS)
+#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */
+
+
+/*
+ * debug macros
+ */
+
+#define dprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG)
+
+#define dsgprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG)
+
+#define devtprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS)
+
+#define dewtprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK)
+
+#define dinitprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT)
+
+#define dexitprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT)
+
+#define dfailprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL)
+
+#define dtmprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM)
+
+#define dreplyprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY)
+
+#define dhsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE)
+
+#define dcprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG)
+
+#define ddlprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL)
+
+#define drsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET)
+
+#define dsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI)
+
+#define dctlprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL)
+
+#define dcsmisasprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CSMISAS)
+
+#define dsasprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS)
+
+#define dsastransport(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE)
+
+#define dmfprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME)
+
+#define dtsfprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL)
+
+#define dtransportprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT)
+
+#define dTMprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TARGET_MODE)
+
+/* inline functions for dumping debug data*/
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _debug_dump_mf - print message frame contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_mf(void *mpi_request, int sz)
+{
+ int i;
+ u32 *mfp = (u32 *)mpi_request;
+
+ printk(KERN_INFO "mf:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ printk("\n\t");
+ printk("%08x ", le32_to_cpu(mfp[i]));
+ }
+ printk("\n");
+}
+#else
+#define _debug_dump_mf(mpi_request, sz)
+#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */
+
+#endif /* MPT2SAS_DEBUG_H_INCLUDED */
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/trunk/drivers/scsi/mpt2sas/mpt2sas_scsih.c
new file mode 100644
index 000000000000..0c463c483c02
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -0,0 +1,5687 @@
+/*
+ * Scsi Host Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "mpt2sas_base.h"
+
+MODULE_AUTHOR(MPT2SAS_AUTHOR);
+MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MPT2SAS_DRIVER_VERSION);
+
+#define RAID_CHANNEL 1
+
+/* forward proto's */
+static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander);
+static void _firmware_event_work(struct work_struct *work);
+
+/* global parameters */
+LIST_HEAD(mpt2sas_ioc_list);
+
+/* local parameters */
+static u8 scsi_io_cb_idx = -1;
+static u8 tm_cb_idx = -1;
+static u8 ctl_cb_idx = -1;
+static u8 base_cb_idx = -1;
+static u8 transport_cb_idx = -1;
+static u8 config_cb_idx = -1;
+static int mpt_ids;
+
+/* command line options */
+static u32 logging_level;
+MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info "
+ "(default=0)");
+
+/* scsi-mid layer global parmeter is max_report_luns, which is 511 */
+#define MPT2SAS_MAX_LUN (16895)
+static int max_lun = MPT2SAS_MAX_LUN;
+module_param(max_lun, int, 0);
+MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
+
+/**
+ * struct sense_info - common structure for obtaining sense keys
+ * @skey: sense key
+ * @asc: additional sense code
+ * @ascq: additional sense code qualifier
+ */
+struct sense_info {
+ u8 skey;
+ u8 asc;
+ u8 ascq;
+};
+
+
+#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF)
+/**
+ * struct fw_event_work - firmware event struct
+ * @list: link list framework
+ * @work: work object (ioc->fault_reset_work_q)
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @host_reset_handling: handling events during host reset
+ * @ignore: flag meaning this event has been marked to ignore
+ * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h
+ * @event_data: reply event data payload follows
+ *
+ * This object stored on ioc->fw_event_list.
+ */
+struct fw_event_work {
+ struct list_head list;
+ struct delayed_work work;
+ struct MPT2SAS_ADAPTER *ioc;
+ u8 VF_ID;
+ u8 host_reset_handling;
+ u8 ignore;
+ u16 event;
+ void *event_data;
+};
+
+/**
+ * struct _scsi_io_transfer - scsi io transfer
+ * @handle: sas device handle (assigned by firmware)
+ * @is_raid: flag set for hidden raid components
+ * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE,
+ * @data_length: data transfer length
+ * @data_dma: dma pointer to data
+ * @sense: sense data
+ * @lun: lun number
+ * @cdb_length: cdb length
+ * @cdb: cdb contents
+ * @valid_reply: flag set for reply message
+ * @timeout: timeout for this command
+ * @sense_length: sense length
+ * @ioc_status: ioc status
+ * @scsi_state: scsi state
+ * @scsi_status: scsi staus
+ * @log_info: log information
+ * @transfer_length: data length transfer when there is a reply message
+ *
+ * Used for sending internal scsi commands to devices within this module.
+ * Refer to _scsi_send_scsi_io().
+ */
+struct _scsi_io_transfer {
+ u16 handle;
+ u8 is_raid;
+ enum dma_data_direction dir;
+ u32 data_length;
+ dma_addr_t data_dma;
+ u8 sense[SCSI_SENSE_BUFFERSIZE];
+ u32 lun;
+ u8 cdb_length;
+ u8 cdb[32];
+ u8 timeout;
+ u8 valid_reply;
+ /* the following bits are only valid when 'valid_reply = 1' */
+ u32 sense_length;
+ u16 ioc_status;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ u32 transfer_length;
+};
+
+/*
+ * The pci device ids are defined in mpi/mpi2_cnfg.h.
+ */
+static struct pci_device_id scsih_pci_table[] = {
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Falcon ~ 2008*/
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Liberator ~ 2108 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, scsih_pci_table);
+
+/**
+ * scsih_set_debug_level - global setting of ioc->logging_level.
+ *
+ * Note: The logging levels are defined in mpt2sas_debug.h.
+ */
+static int
+scsih_set_debug_level(const char *val, struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level);
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
+ ioc->logging_level = logging_level;
+ return 0;
+}
+module_param_call(logging_level, scsih_set_debug_level, param_get_int,
+ &logging_level, 0644);
+
+/**
+ * _scsih_srch_boot_sas_address - search based on sas_address
+ * @sas_address: sas address
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_sas_address(u64 sas_address,
+ Mpi2BootDeviceSasWwid_t *boot_device)
+{
+ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_device_name - search based on device name
+ * @device_name: device name specified in INDENTIFY fram
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_device_name(u64 device_name,
+ Mpi2BootDeviceDeviceName_t *boot_device)
+{
+ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot
+ * @enclosure_logical_id: enclosure logical id
+ * @slot_number: slot number
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
+ Mpi2BootDeviceEnclosureSlot_t *boot_device)
+{
+ return (enclosure_logical_id == le64_to_cpu(boot_device->
+ EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
+ SlotNumber)) ? 1 : 0;
+}
+
+/**
+ * _scsih_is_boot_device - search for matching boot device.
+ * @sas_address: sas address
+ * @device_name: device name specified in INDENTIFY fram
+ * @enclosure_logical_id: enclosure logical id
+ * @slot_number: slot number
+ * @form: specifies boot device form
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static int
+_scsih_is_boot_device(u64 sas_address, u64 device_name,
+ u64 enclosure_logical_id, u16 slot, u8 form,
+ Mpi2BiosPage2BootDevice_t *boot_device)
+{
+ int rc = 0;
+
+ switch (form) {
+ case MPI2_BIOSPAGE2_FORM_SAS_WWID:
+ if (!sas_address)
+ break;
+ rc = _scsih_srch_boot_sas_address(
+ sas_address, &boot_device->SasWwid);
+ break;
+ case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT:
+ if (!enclosure_logical_id)
+ break;
+ rc = _scsih_srch_boot_encl_slot(
+ enclosure_logical_id,
+ slot, &boot_device->EnclosureSlot);
+ break;
+ case MPI2_BIOSPAGE2_FORM_DEVICE_NAME:
+ if (!device_name)
+ break;
+ rc = _scsih_srch_boot_device_name(
+ device_name, &boot_device->DeviceName);
+ break;
+ case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * _scsih_determine_boot_device - determine boot device.
+ * @ioc: per adapter object
+ * @device: either sas_device or raid_device object
+ * @is_raid: [flag] 1 = raid object, 0 = sas object
+ *
+ * Determines whether this device should be first reported device to
+ * to scsi-ml or sas transport, this purpose is for persistant boot device.
+ * There are primary, alternate, and current entries in bios page 2. The order
+ * priority is primary, alternate, then current. This routine saves
+ * the corresponding device object and is_raid flag in the ioc object.
+ * The saved data to be used later in _scsih_probe_boot_devices().
+ */
+static void
+_scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,
+ void *device, u8 is_raid)
+{
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ u64 sas_address;
+ u64 device_name;
+ u64 enclosure_logical_id;
+ u16 slot;
+
+ /* only process this function when driver loads */
+ if (!ioc->wait_for_port_enable_to_complete)
+ return;
+
+ if (!is_raid) {
+ sas_device = device;
+ sas_address = sas_device->sas_address;
+ device_name = sas_device->device_name;
+ enclosure_logical_id = sas_device->enclosure_logical_id;
+ slot = sas_device->slot;
+ } else {
+ raid_device = device;
+ sas_address = raid_device->wwid;
+ device_name = 0;
+ enclosure_logical_id = 0;
+ slot = 0;
+ }
+
+ if (!ioc->req_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedBootDevice)) {
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "%s: req_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->req_boot_device.device = device;
+ ioc->req_boot_device.is_raid = is_raid;
+ }
+ }
+
+ if (!ioc->req_alt_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqAltBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedAltBootDevice)) {
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "%s: req_alt_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->req_alt_boot_device.device = device;
+ ioc->req_alt_boot_device.is_raid = is_raid;
+ }
+ }
+
+ if (!ioc->current_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.CurrentBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.CurrentBootDevice)) {
+ dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "%s: current_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->current_boot_device.device = device;
+ ioc->current_boot_device.is_raid = is_raid;
+ }
+ }
+}
+
+/**
+ * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+struct _sas_device *
+mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ struct _sas_device *sas_device, *r;
+
+ r = NULL;
+ /* check the sas_device_init_list */
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list,
+ list) {
+ if (sas_device->sas_address != sas_address)
+ continue;
+ r = sas_device;
+ goto out;
+ }
+
+ /* then check the sas_device_list */
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->sas_address != sas_address)
+ continue;
+ r = sas_device;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * _scsih_sas_device_find_by_handle - sas device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+static struct _sas_device *
+_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device, *r;
+
+ r = NULL;
+ if (ioc->wait_for_port_enable_to_complete) {
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list,
+ list) {
+ if (sas_device->handle != handle)
+ continue;
+ r = sas_device;
+ goto out;
+ }
+ } else {
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->handle != handle)
+ continue;
+ r = sas_device;
+ goto out;
+ }
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_sas_device_remove - remove sas_device from list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Removing object and freeing associated memory from the ioc->sas_device_list.
+ */
+static void
+_scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_del(&sas_device->list);
+ memset(sas_device, 0, sizeof(struct _sas_device));
+ kfree(sas_device);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_sas_device_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object to the ioc->sas_device_list.
+ */
+static void
+_scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+ u16 handle, parent_handle;
+ u64 sas_address;
+
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
+ "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
+ sas_device->handle, (unsigned long long)sas_device->sas_address));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_add_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ handle = sas_device->handle;
+ parent_handle = sas_device->parent_handle;
+ sas_address = sas_device->sas_address;
+ if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ mpt2sas_transport_port_remove(ioc, sas_address, parent_handle);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+}
+
+/**
+ * _scsih_sas_device_init_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object at driver load time to the ioc->sas_device_init_list.
+ */
+static void
+_scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
+ "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
+ sas_device->handle, (unsigned long long)sas_device->sas_address));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ _scsih_determine_boot_device(ioc, sas_device, 0);
+}
+
+/**
+ * mpt2sas_scsih_expander_find_by_handle - expander device search
+ * @ioc: per adapter object
+ * @handle: expander handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for expander device based on handle, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->handle != handle)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_id - raid device search
+ * @ioc: per adapter object
+ * @id: sas device target id
+ * @channel: sas device channel
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on target id, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_id(struct MPT2SAS_ADAPTER *ioc, int id, int channel)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->id == id && raid_device->channel == channel) {
+ r = raid_device;
+ goto out;
+ }
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_handle - raid device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on handle, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->handle != handle)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_wwid - raid device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on wwid, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_wwid(struct MPT2SAS_ADAPTER *ioc, u64 wwid)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid != wwid)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_add - add raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ * This is added to the raid_device_list link list.
+ */
+static void
+_scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
+ "(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__,
+ raid_device->handle, (unsigned long long)raid_device->wwid));
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_add_tail(&raid_device->list, &ioc->raid_device_list);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_raid_device_remove - delete raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ * This is removed from the raid_device_list link list.
+ */
+static void
+_scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_del(&raid_device->list);
+ memset(raid_device, 0, sizeof(struct _raid_device));
+ kfree(raid_device);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * mpt2sas_scsih_expander_find_by_sas_address - expander device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * This searches for expander device based on sas_address, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * _scsih_expander_node_add - insert expander device to the list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Adding new object to the ioc->sas_expander_list.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_expander_node_add(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_add_tail(&sas_expander->list, &ioc->sas_expander_list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_is_end_device - determines if device is an end device
+ * @device_info: bitfield providing information about the device.
+ * Context: none
+ *
+ * Returns 1 if end device.
+ */
+static int
+_scsih_is_end_device(u32 device_info)
+{
+ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
+ ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * _scsih_scsi_lookup_get - returns scmd entry
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * Returns the smid stored scmd pointer.
+ */
+static struct scsi_cmnd *
+_scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+ struct scsi_cmnd *scmd;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ scmd = ioc->scsi_lookup[smid - 1].scmd;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return scmd;
+}
+
+/**
+ * mptscsih_getclear_scsi_lookup - returns scmd entry
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * Returns the smid stored scmd pointer, as well as clearing the scmd pointer.
+ */
+static struct scsi_cmnd *
+_scsih_scsi_lookup_getclear(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+ struct scsi_cmnd *scmd;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ scmd = ioc->scsi_lookup[smid - 1].scmd;
+ ioc->scsi_lookup[smid - 1].scmd = NULL;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return scmd;
+}
+
+/**
+ * _scsih_scsi_lookup_set - updates scmd entry in lookup
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @scmd: pointer to scsi command object
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will save scmd pointer in the scsi_lookup array.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_scsi_lookup_set(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+ struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ ioc->scsi_lookup[smid - 1].scmd = scmd;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_scmd - scmd lookup
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @scmd: pointer to scsi command object
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a scmd pointer in the scsi_lookup array,
+ * returning the revelent smid. A returned value of zero means invalid.
+ */
+static u16
+_scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd
+ *scmd)
+{
+ u16 smid;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ smid = 0;
+ for (i = 0; i < ioc->request_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd == scmd) {
+ smid = i + 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_target - search for matching channel:id
+ * @ioc: per adapter object
+ * @id: target id
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id,
+ int channel)
+{
+ u8 found;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ found = 0;
+ for (i = 0 ; i < ioc->request_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd &&
+ (ioc->scsi_lookup[i].scmd->device->id == id &&
+ ioc->scsi_lookup[i].scmd->device->channel == channel)) {
+ found = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return found;
+}
+
+/**
+ * _scsih_get_chain_buffer_dma - obtain block of chains (dma address)
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns phys pointer to chain buffer.
+ */
+static dma_addr_t
+_scsih_get_chain_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->chain_dma + ((smid - 1) * (ioc->request_sz *
+ ioc->chains_needed_per_io));
+}
+
+/**
+ * _scsih_get_chain_buffer - obtain block of chains assigned to a mf request
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns virt pointer to chain buffer.
+ */
+static void *
+_scsih_get_chain_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->chain + ((smid - 1) * (ioc->request_sz *
+ ioc->chains_needed_per_io)));
+}
+
+/**
+ * _scsih_build_scatter_gather - main sg creation routine
+ * @ioc: per adapter object
+ * @scmd: scsi command
+ * @smid: system request message index
+ * Context: none.
+ *
+ * The main routine that builds scatter gather table from a given
+ * scsi request sent via the .queuecommand main handler.
+ *
+ * Returns 0 success, anything else error
+ */
+static int
+_scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid)
+{
+ Mpi2SCSIIORequest_t *mpi_request;
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_offset;
+ u32 chain_length;
+ u32 chain_flags;
+ u32 sges_left;
+ u32 sges_in_segment;
+ u32 sgl_flags;
+ u32 sgl_flags_last_element;
+ u32 sgl_flags_end_buffer;
+
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+
+ /* init scatter gather flags */
+ sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT;
+ if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC;
+ sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT)
+ << MPI2_SGE_FLAGS_SHIFT;
+ sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST)
+ << MPI2_SGE_FLAGS_SHIFT;
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = scsi_dma_map(scmd);
+ if (!sges_left) {
+ sdev_printk(KERN_ERR, scmd->device, "pci_map_sg"
+ " failed: request for %d bytes!\n", scsi_bufflen(scmd));
+ return -ENOMEM;
+ }
+
+ sg_local = &mpi_request->SGL;
+ sges_in_segment = ioc->max_sges_in_main_message;
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) +
+ (sges_in_segment * ioc->sge_size))/4;
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment) {
+ if (sges_in_segment == 1)
+ ioc->base_add_sg_single(sg_local,
+ sgl_flags_last_element | sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ /* initializing the chain flags and pointers */
+ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT;
+ chain = _scsih_get_chain_buffer(ioc, smid);
+ chain_dma = _scsih_get_chain_buffer_dma(ioc, smid);
+ do {
+ sges_in_segment = (sges_left <=
+ ioc->max_sges_in_chain_message) ? sges_left :
+ ioc->max_sges_in_chain_message;
+ chain_offset = (sges_left == sges_in_segment) ?
+ 0 : (sges_in_segment * ioc->sge_size)/4;
+ chain_length = sges_in_segment * ioc->sge_size;
+ if (chain_offset) {
+ chain_offset = chain_offset <<
+ MPI2_SGE_CHAIN_OFFSET_SHIFT;
+ chain_length += ioc->sge_size;
+ }
+ ioc->base_add_sg_single(sg_local, chain_flags | chain_offset |
+ chain_length, chain_dma);
+ sg_local = chain;
+ if (!chain_offset)
+ goto fill_in_last_segment;
+
+ /* fill in chain segments */
+ while (sges_in_segment) {
+ if (sges_in_segment == 1)
+ ioc->base_add_sg_single(sg_local,
+ sgl_flags_last_element |
+ sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_dma += ioc->request_sz;
+ chain += ioc->request_sz;
+ } while (1);
+
+
+ fill_in_last_segment:
+
+ /* fill the last segment */
+ while (sges_left) {
+ if (sges_left == 1)
+ ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * scsih_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ *
+ * Returns queue depth.
+ */
+static int
+scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ struct Scsi_Host *shost = sdev->host;
+ int max_depth;
+ int tag_type;
+
+ max_depth = shost->can_queue;
+ if (!sdev->tagged_supported)
+ max_depth = 1;
+ if (qdepth > max_depth)
+ qdepth = max_depth;
+ tag_type = (qdepth == 1) ? 0 : MSG_SIMPLE_TAG;
+ scsi_adjust_queue_depth(sdev, tag_type, qdepth);
+
+ if (sdev->inquiry_len > 7)
+ sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), "
+ "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
+ sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags,
+ sdev->ordered_tags, sdev->scsi_level,
+ (sdev->inquiry[7] & 2) >> 1);
+
+ return sdev->queue_depth;
+}
+
+/**
+ * scsih_change_queue_depth - changing device queue tag type
+ * @sdev: scsi device struct
+ * @tag_type: requested tag type
+ *
+ * Returns queue tag type.
+ */
+static int
+scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag_type);
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+
+ return tag_type;
+}
+
+/**
+ * scsih_target_alloc - target add routine
+ * @starget: scsi target struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct sas_rphy *rphy;
+
+ sas_target_priv_data = kzalloc(sizeof(struct scsi_target), GFP_KERNEL);
+ if (!sas_target_priv_data)
+ return -ENOMEM;
+
+ starget->hostdata = sas_target_priv_data;
+ sas_target_priv_data->starget = starget;
+ sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE;
+
+ /* RAID volumes */
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ sas_target_priv_data->handle = raid_device->handle;
+ sas_target_priv_data->sas_address = raid_device->wwid;
+ sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
+ raid_device->starget = starget;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return 0;
+ }
+
+ /* sas/sata devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ rphy = dev_to_rphy(starget->dev.parent);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+
+ if (sas_device) {
+ sas_target_priv_data->handle = sas_device->handle;
+ sas_target_priv_data->sas_address = sas_device->sas_address;
+ sas_device->starget = starget;
+ sas_device->id = starget->id;
+ sas_device->channel = starget->channel;
+ if (sas_device->hidden_raid_component)
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_RAID_COMPONENT;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return 0;
+}
+
+/**
+ * scsih_target_destroy - target destroy routine
+ * @starget: scsi target struct
+ *
+ * Returns nothing.
+ */
+static void
+scsih_target_destroy(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct sas_rphy *rphy;
+
+ sas_target_priv_data = starget->hostdata;
+ if (!sas_target_priv_data)
+ return;
+
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ raid_device->starget = NULL;
+ raid_device->sdev = NULL;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ goto out;
+ }
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ rphy = dev_to_rphy(starget->dev.parent);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (sas_device)
+ sas_device->starget = NULL;
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ out:
+ kfree(sas_target_priv_data);
+ starget->hostdata = NULL;
+}
+
+/**
+ * scsih_slave_alloc - device add routine
+ * @sdev: scsi device struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_slave_alloc(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT2SAS_ADAPTER *ioc;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
+ if (!sas_device_priv_data)
+ return -ENOMEM;
+
+ sas_device_priv_data->lun = sdev->lun;
+ sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns++;
+ sas_device_priv_data->sas_target = sas_target_priv_data;
+ sdev->hostdata = sas_device_priv_data;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
+ sdev->no_uld_attach = 1;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc,
+ starget->id, starget->channel);
+ if (raid_device)
+ raid_device->sdev = sdev; /* raid is single lun */
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ } else {
+ /* set TLR bit for SSP devices */
+ if (!(ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TLR))
+ goto out;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device && sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET)
+ sas_device_priv_data->flags |= MPT_DEVICE_TLR_ON;
+ }
+
+ out:
+ return 0;
+}
+
+/**
+ * scsih_slave_destroy - device destroy routine
+ * @sdev: scsi device struct
+ *
+ * Returns nothing.
+ */
+static void
+scsih_slave_destroy(struct scsi_device *sdev)
+{
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+
+ if (!sdev->hostdata)
+ return;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns--;
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/**
+ * scsih_display_sata_capabilities - sata capabilities
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * @sdev: scsi device struct
+ */
+static void
+scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device, struct scsi_device *sdev)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ u16 flags;
+ u32 device_info;
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, sas_device->handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ flags = le16_to_cpu(sas_device_pg0.Flags);
+ device_info = le16_to_cpu(sas_device_pg0.DeviceInfo);
+
+ sdev_printk(KERN_INFO, sdev,
+ "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), "
+ "sw_preserve(%s)\n",
+ (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" :
+ "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n");
+}
+
+/**
+ * _scsih_get_volume_capabilities - volume capabilities
+ * @ioc: per adapter object
+ * @sas_device: the raid_device object
+ */
+static void
+_scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ Mpi2RaidVolPage0_t *vol_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 sz;
+ u8 num_pds;
+
+ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,
+ &num_pds)) || !num_pds) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ raid_device->num_pds = num_pds;
+ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds *
+ sizeof(Mpi2RaidVol0PhysDisk_t));
+ vol_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!vol_pg0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ kfree(vol_pg0);
+ return;
+ }
+
+ raid_device->volume_type = vol_pg0->VolumeType;
+
+ /* figure out what the underlying devices are by
+ * obtaining the device_info bits for the 1st device
+ */
+ if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
+ vol_pg0->PhysDisk[0].PhysDiskNum))) {
+ if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ le16_to_cpu(pd_pg0.DevHandle)))) {
+ raid_device->device_info =
+ le32_to_cpu(sas_device_pg0.DeviceInfo);
+ }
+ }
+
+ kfree(vol_pg0);
+}
+
+/**
+ * scsih_slave_configure - device configure routine.
+ * @sdev: scsi device struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_slave_configure(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int qdepth;
+ u8 ssp_target = 0;
+ char *ds = "";
+ char *r_level = "";
+
+ qdepth = 1;
+ sas_device_priv_data = sdev->hostdata;
+ sas_device_priv_data->configured_lun = 1;
+ sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+
+ /* raid volume handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) {
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc,
+ sas_target_priv_data->handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (!raid_device) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+
+ _scsih_get_volume_capabilities(ioc, raid_device);
+
+ /* RAID Queue Depth Support
+ * IS volume = underlying qdepth of drive type, either
+ * MPT2SAS_SAS_QUEUE_DEPTH or MPT2SAS_SATA_QUEUE_DEPTH
+ * IM/IME/R10 = 128 (MPT2SAS_RAID_QUEUE_DEPTH)
+ */
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = MPT2SAS_SAS_QUEUE_DEPTH;
+ ds = "SSP";
+ } else {
+ qdepth = MPT2SAS_SATA_QUEUE_DEPTH;
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ else
+ ds = "STP";
+ }
+
+ switch (raid_device->volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ r_level = "RAID0";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID1E";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID1";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID10";
+ break;
+ case MPI2_RAID_VOL_TYPE_UNKNOWN:
+ default:
+ qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAIDX";
+ break;
+ }
+
+ sdev_printk(KERN_INFO, sdev, "%s: "
+ "handle(0x%04x), wwid(0x%016llx), pd_count(%d), type(%s)\n",
+ r_level, raid_device->handle,
+ (unsigned long long)raid_device->wwid,
+ raid_device->num_pds, ds);
+ scsih_change_queue_depth(sdev, qdepth);
+ return 0;
+ }
+
+ /* non-raid handling */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device) {
+ if (sas_target_priv_data->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ mpt2sas_config_get_volume_handle(ioc,
+ sas_device->handle, &sas_device->volume_handle);
+ mpt2sas_config_get_volume_wwid(ioc,
+ sas_device->volume_handle,
+ &sas_device->volume_wwid);
+ }
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = MPT2SAS_SAS_QUEUE_DEPTH;
+ ssp_target = 1;
+ ds = "SSP";
+ } else {
+ qdepth = MPT2SAS_SATA_QUEUE_DEPTH;
+ if (sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_STP_TARGET)
+ ds = "STP";
+ else if (sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ }
+
+ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), "
+ "sas_addr(0x%016llx), device_name(0x%016llx)\n",
+ ds, sas_device->handle,
+ (unsigned long long)sas_device->sas_address,
+ (unsigned long long)sas_device->device_name);
+ sdev_printk(KERN_INFO, sdev, "%s: "
+ "enclosure_logical_id(0x%016llx), slot(%d)\n", ds,
+ (unsigned long long) sas_device->enclosure_logical_id,
+ sas_device->slot);
+
+ if (!ssp_target)
+ scsih_display_sata_capabilities(ioc, sas_device, sdev);
+ }
+
+ scsih_change_queue_depth(sdev, qdepth);
+
+ if (ssp_target)
+ sas_read_port_mode_page(sdev);
+ return 0;
+}
+
+/**
+ * scsih_bios_param - fetch head, sector, cylinder info for a disk
+ * @sdev: scsi device struct
+ * @bdev: pointer to block device context
+ * @capacity: device size (in 512 byte sectors)
+ * @params: three element array to place output:
+ * params[0] number of heads (max 255)
+ * params[1] number of sectors (max 63)
+ * params[2] number of cylinders
+ *
+ * Return nothing.
+ */
+static int
+scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int params[])
+{
+ int heads;
+ int sectors;
+ sector_t cylinders;
+ ulong dummy;
+
+ heads = 64;
+ sectors = 32;
+
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+
+ /*
+ * Handle extended translation size for logical drives
+ * > 1Gb
+ */
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+ }
+
+ /* return result */
+ params[0] = heads;
+ params[1] = sectors;
+ params[2] = cylinders;
+
+ return 0;
+}
+
+/**
+ * _scsih_response_code - translation of device response code
+ * @ioc: per adapter object
+ * @response_code: response code returned by the device
+ *
+ * Return nothing.
+ */
+static void
+_scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
+{
+ char *desc;
+
+ switch (response_code) {
+ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
+ desc = "invalid lun";
+ break;
+ case 0xA:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ printk(MPT2SAS_WARN_FMT "response_code(0x%01x): %s\n",
+ ioc->name, response_code, desc);
+}
+
+/**
+ * scsih_tm_done - tm completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using scsih_issue_tm.
+ *
+ * Return nothing.
+ */
+static void
+scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED)
+ return;
+ if (ioc->tm_cmds.smid != smid)
+ return;
+ ioc->tm_cmds.status |= MPT2_CMD_COMPLETE;
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->tm_cmds.status |= MPT2_CMD_REPLY_VALID;
+ }
+ ioc->tm_cmds.status &= ~MPT2_CMD_PENDING;
+ complete(&ioc->tm_cmds.done);
+}
+
+/**
+ * mpt2sas_scsih_set_tm_flag - set per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 1;
+ skip = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ }
+}
+
+/**
+ * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 0;
+ skip = 1;
+ ioc->ignore_loginfos = 0;
+ }
+ }
+}
+
+/**
+ * mpt2sas_scsih_issue_tm - main routine for sending tm requests
+ * @ioc: per adapter struct
+ * @device_handle: device handle
+ * @lun: lun number
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
+ * @smid_task: smid assigned to the task
+ * @timeout: timeout in seconds
+ * Context: The calling function needs to acquire the tm_cmds.mutex
+ *
+ * A generic API for sending task management requests to firmware.
+ *
+ * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling
+ * this API.
+ *
+ * The callback index is set inside `ioc->tm_cb_idx`.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
+ u8 type, u16 smid_task, ulong timeout)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+ u16 smid = 0;
+ u32 ioc_state;
+ unsigned long timeleft;
+ u8 VF_ID = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED ||
+ ioc->shost_recovery) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell "
+ "active!\n", ioc->name));
+ goto issue_host_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt2sas_base_fault_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ goto issue_host_reset;
+ }
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->tm_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return;
+ }
+
+ dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x),"
+ " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid));
+ ioc->tm_cmds.status = MPT2_CMD_PENDING;
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->tm_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = type;
+ mpi_request->TaskMID = cpu_to_le16(smid_task);
+ int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
+ mpt2sas_scsih_set_tm_flag(ioc, handle);
+ mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID);
+ timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
+ mpt2sas_scsih_clear_tm_flag(ioc, handle);
+ if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SCSITaskManagementRequest_t)/4);
+ if (!(ioc->tm_cmds.status & MPT2_CMD_RESET))
+ goto issue_host_reset;
+ }
+
+ if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) {
+ mpi_reply = ioc->tm_cmds.reply;
+ dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "complete tm: "
+ "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+ if (ioc->logging_level & MPT_DEBUG_TM)
+ _scsih_response_code(ioc, mpi_reply->ResponseCode);
+ }
+ return;
+ issue_host_reset:
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER);
+}
+
+/**
+ * scsih_abort - eh threads main abort routine
+ * @sdev: scsi device struct
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_abort(struct scsi_cmnd *scmd)
+{
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ u16 smid;
+ u16 handle;
+ int r;
+ struct scsi_cmnd *scmd_lookup;
+
+ printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n",
+ ioc->name, scmd);
+ scsi_print_command(scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+ ioc->name, scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* search for the command */
+ smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd);
+ if (!smid) {
+ scmd->result = DID_RESET << 16;
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components and volumes this is not supported */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT ||
+ sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ handle = sas_device_priv_data->sas_target->handle;
+ mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30);
+
+ /* sanity check - see whether command actually completed */
+ scmd_lookup = _scsih_scsi_lookup_get(ioc, smid);
+ if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number))
+ r = FAILED;
+ else
+ r = SUCCESS;
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ out:
+ printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n",
+ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
+
+
+/**
+ * scsih_dev_reset - eh threads main device reset routine
+ * @sdev: scsi device struct
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle;
+ int r;
+
+ printk(MPT2SAS_INFO_FMT "attempting target reset! scmd(%p)\n",
+ ioc->name, scmd);
+ scsi_print_command(scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+ ioc->name, scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc,
+ sas_device_priv_data->sas_target->handle);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_issue_tm(ioc, handle, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30);
+
+ /*
+ * sanity check see whether all commands to this target been
+ * completed
+ */
+ if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id,
+ scmd->device->channel))
+ r = FAILED;
+ else
+ r = SUCCESS;
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ out:
+ printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n",
+ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
+
+/**
+ * scsih_abort - eh threads main host reset routine
+ * @sdev: scsi device struct
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_host_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ int r, retval;
+
+ printk(MPT2SAS_INFO_FMT "attempting host reset! scmd(%p)\n",
+ ioc->name, scmd);
+ scsi_print_command(scmd);
+
+ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ r = (retval < 0) ? FAILED : SUCCESS;
+ printk(MPT2SAS_INFO_FMT "host reset: %s scmd(%p)\n",
+ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return r;
+}
+
+/**
+ * _scsih_fw_event_add - insert and queue up fw_event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * This adds the firmware event object into link list, then queues it up to
+ * be processed from user context.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ unsigned long flags;
+
+ if (ioc->firmware_event_thread == NULL)
+ return;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_add_tail(&fw_event->list, &ioc->fw_event_list);
+ INIT_DELAYED_WORK(&fw_event->work, _firmware_event_work);
+ queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, 1);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_fw_event_free - delete fw_event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * This removes firmware event object from link list, frees associated memory.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
+ *fw_event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_del(&fw_event->list);
+ kfree(fw_event->event_data);
+ kfree(fw_event);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_fw_event_add - requeue an event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
+ *fw_event, unsigned long delay)
+{
+ unsigned long flags;
+ if (ioc->firmware_event_thread == NULL)
+ return;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, delay);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_fw_event_off - turn flag off preventing event handling
+ * @ioc: per adapter object
+ *
+ * Used to prevent handling of firmware events during adapter reset
+ * driver unload.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ ioc->fw_events_off = 1;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+}
+
+/**
+ * _scsih_fw_event_on - turn flag on allowing firmware event handling
+ * @ioc: per adapter object
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ ioc->fw_events_off = 0;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_ublock_io_device - set the device state to SDEV_RUNNING
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During device pull we need to appropiately set the sdev state.
+ */
+static void
+_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (!sas_device_priv_data->block)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
+ MPT2SAS_INFO_FMT "SDEV_RUNNING: "
+ "handle(0x%04x)\n", ioc->name, handle));
+ sas_device_priv_data->block = 0;
+ scsi_device_set_state(sdev, SDEV_RUNNING);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_device - set the device state to SDEV_BLOCK
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During device pull we need to appropiately set the sdev state.
+ */
+static void
+_scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->block)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
+ MPT2SAS_INFO_FMT "SDEV_BLOCK: "
+ "handle(0x%04x)\n", ioc->name, handle));
+ sas_device_priv_data->block = 1;
+ scsi_device_set_state(sdev, SDEV_BLOCK);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_to_children_attached_to_ex
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * attached to this expander. This function called when expander is
+ * pulled.
+ */
+static void
+_scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt2sas_port;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_sibling;
+ unsigned long flags;
+
+ if (!sas_expander)
+ return;
+
+ list_for_each_entry(mpt2sas_port,
+ &sas_expander->sas_port_list, port_list) {
+ if (mpt2sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device =
+ mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ mpt2sas_port->remote_identify.sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ continue;
+ _scsih_block_io_device(ioc, sas_device->handle);
+ }
+ }
+
+ list_for_each_entry(mpt2sas_port,
+ &sas_expander->sas_port_list, port_list) {
+
+ if (mpt2sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+ mpt2sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ expander_sibling =
+ mpt2sas_scsih_expander_find_by_sas_address(
+ ioc, mpt2sas_port->remote_identify.sas_address);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ _scsih_block_io_to_children_attached_to_ex(ioc,
+ expander_sibling);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_to_children_attached_directly
+ * @ioc: per adapter object
+ * @event_data: topology change event data
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * direct attached during device pull.
+ */
+static void
+_scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 phy_number;
+
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
+ _scsih_block_io_device(ioc, handle);
+ }
+}
+
+/**
+ * _scsih_check_topo_delete_events - sanity check on topo events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ *
+ * This routine added to better handle cable breaker.
+ *
+ * This handles the case where driver recieves multiple expander
+ * add and delete events in a single shot. When there is a delete event
+ * the routine will void any pending add events waiting in the event queue.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventDataSasTopologyChangeList_t *local_event_data;
+ u16 expander_handle;
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+
+ expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+ if (expander_handle < ioc->sas_hba.num_phys) {
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+ return;
+ }
+
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING
+ || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+ expander_handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
+ } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+
+ if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+ return;
+
+ /* mark ignore flag for pending events */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
+ if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+ fw_event->ignore)
+ continue;
+ local_event_data = fw_event->event_data;
+ if (local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_ADDED ||
+ local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
+ if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
+ expander_handle) {
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "setting ignoring flag\n", ioc->name));
+ fw_event->ignore = 1;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_queue_rescan - queue a topology rescan from user context
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ if (ioc->wait_for_port_enable_to_complete)
+ return;
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * _scsih_flush_running_cmds - completing outstanding commands.
+ * @ioc: per adapter object
+ *
+ * The flushing out of all pending scmd commands following host reset,
+ * where all IO is dropped to the floor.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct scsi_cmnd *scmd;
+ u16 smid;
+ u16 count = 0;
+
+ for (smid = 1; smid <= ioc->request_depth; smid++) {
+ scmd = _scsih_scsi_lookup_getclear(ioc, smid);
+ if (!scmd)
+ continue;
+ count++;
+ mpt2sas_base_free_smid(ioc, smid);
+ scsi_dma_unmap(scmd);
+ scmd->result = DID_RESET << 16;
+ scmd->scsi_done(scmd);
+ }
+ dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "completing %d cmds\n",
+ ioc->name, count));
+}
+
+/**
+ * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
+ * MPT2_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
+{
+ switch (reset_phase) {
+ case MPT2_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+ _scsih_fw_event_off(ioc);
+ break;
+ case MPT2_IOC_AFTER_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
+ ioc->tm_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
+ complete(&ioc->tm_cmds.done);
+ }
+ _scsih_fw_event_on(ioc);
+ _scsih_flush_running_cmds(ioc);
+ break;
+ case MPT2_IOC_DONE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+ _scsih_queue_rescan(ioc);
+ break;
+ }
+}
+
+/**
+ * scsih_qcmd - main scsi request entry point
+ * @scmd: pointer to scsi command object
+ * @done: function pointer to be invoked on completion
+ *
+ * The callback index is set inside `ioc->scsi_io_cb_idx`.
+ *
+ * Returns 0 on success. If there's a failure, return either:
+ * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or
+ * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
+ */
+static int
+scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
+{
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ Mpi2SCSIIORequest_t *mpi_request;
+ u32 mpi_control;
+ u16 smid;
+ unsigned long flags;
+
+ scmd->scsi_done = done;
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ if (!sas_target_priv_data || sas_target_priv_data->handle ==
+ MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
+ /* see if we are busy with task managment stuff */
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (sas_target_priv_data->tm_busy ||
+ ioc->shost_recovery || ioc->ioc_link_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_READ;
+ else if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_WRITE;
+ else
+ mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
+
+ /* set tags */
+ if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) {
+ if (scmd->device->tagged_supported) {
+ if (scmd->device->ordered_tags)
+ mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
+ else
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ } else
+/* MPI Revision I (UNIT = 0xA) - removed MPI2_SCSIIO_CONTROL_UNTAGGED */
+/* mpi_control |= MPI2_SCSIIO_CONTROL_UNTAGGED;
+ */
+ mpi_control |= (0x500);
+
+ } else
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+
+ if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON))
+ mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->scsi_io_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ goto out;
+ }
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
+ else
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ mpi_request->DevHandle =
+ cpu_to_le16(sas_device_priv_data->sas_target->handle);
+ mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ mpi_request->Control = cpu_to_le32(mpi_control);
+ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len);
+ mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
+ mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
+ mpi_request->SenseBufferLowAddress =
+ (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
+ mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
+ MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
+
+ int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *)
+ mpi_request->LUN);
+ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
+
+ if (!mpi_request->DataLength) {
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request->SGL);
+ } else {
+ if (_scsih_build_scatter_gather(ioc, scmd, smid)) {
+ mpt2sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ _scsih_scsi_lookup_set(ioc, smid, scmd);
+ mpt2sas_base_put_smid_scsi_io(ioc, smid, 0,
+ sas_device_priv_data->sas_target->handle);
+ return 0;
+
+ out:
+ return SCSI_MLQUEUE_HOST_BUSY;
+}
+
+/**
+ * _scsih_normalize_sense - normalize descriptor and fixed format sense data
+ * @sense_buffer: sense data returned by target
+ * @data: normalized skey/asc/ascq
+ *
+ * Return nothing.
+ */
+static void
+_scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
+{
+ if ((sense_buffer[0] & 0x7F) >= 0x72) {
+ /* descriptor format */
+ data->skey = sense_buffer[1] & 0x0F;
+ data->asc = sense_buffer[2];
+ data->ascq = sense_buffer[3];
+ } else {
+ /* fixed format */
+ data->skey = sense_buffer[2] & 0x0F;
+ data->asc = sense_buffer[12];
+ data->ascq = sense_buffer[13];
+ }
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @mpi_reply: reply mf payload returned from firmware
+ *
+ * scsi_status - SCSI Status code returned from target device
+ * scsi_state - state info associated with SCSI_IO determined by ioc
+ * ioc_status - ioc supplied status info
+ *
+ * Return nothing.
+ */
+static void
+_scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ Mpi2SCSIIOReply_t *mpi_reply, u16 smid)
+{
+ u32 response_info;
+ u8 *response_bytes;
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ u8 scsi_state = mpi_reply->SCSIState;
+ u8 scsi_status = mpi_reply->SCSIStatus;
+ char *desc_ioc_state = NULL;
+ char *desc_scsi_status = NULL;
+ char *desc_scsi_state = ioc->tmp_string;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_SUCCESS:
+ desc_ioc_state = "success";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc_ioc_state = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ desc_ioc_state = "scsi recovered error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ desc_ioc_state = "scsi invalid dev handle";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ desc_ioc_state = "scsi device not there";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ desc_ioc_state = "scsi data overrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ desc_ioc_state = "scsi data underrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ desc_ioc_state = "scsi io data error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ desc_ioc_state = "scsi protocol error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ desc_ioc_state = "scsi task terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ desc_ioc_state = "scsi residual mismatch";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ desc_ioc_state = "scsi task mgmt failed";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ desc_ioc_state = "scsi ioc terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ desc_ioc_state = "scsi ext terminated";
+ break;
+ default:
+ desc_ioc_state = "unknown";
+ break;
+ }
+
+ switch (scsi_status) {
+ case MPI2_SCSI_STATUS_GOOD:
+ desc_scsi_status = "good";
+ break;
+ case MPI2_SCSI_STATUS_CHECK_CONDITION:
+ desc_scsi_status = "check condition";
+ break;
+ case MPI2_SCSI_STATUS_CONDITION_MET:
+ desc_scsi_status = "condition met";
+ break;
+ case MPI2_SCSI_STATUS_BUSY:
+ desc_scsi_status = "busy";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE:
+ desc_scsi_status = "intermediate";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET:
+ desc_scsi_status = "intermediate condmet";
+ break;
+ case MPI2_SCSI_STATUS_RESERVATION_CONFLICT:
+ desc_scsi_status = "reservation conflict";
+ break;
+ case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
+ desc_scsi_status = "command terminated";
+ break;
+ case MPI2_SCSI_STATUS_TASK_SET_FULL:
+ desc_scsi_status = "task set full";
+ break;
+ case MPI2_SCSI_STATUS_ACA_ACTIVE:
+ desc_scsi_status = "aca active";
+ break;
+ case MPI2_SCSI_STATUS_TASK_ABORTED:
+ desc_scsi_status = "task aborted";
+ break;
+ default:
+ desc_scsi_status = "unknown";
+ break;
+ }
+
+ desc_scsi_state[0] = '\0';
+ if (!scsi_state)
+ desc_scsi_state = " ";
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ strcat(desc_scsi_state, "response info ");
+ if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ strcat(desc_scsi_state, "state terminated ");
+ if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS)
+ strcat(desc_scsi_state, "no status ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
+ strcat(desc_scsi_state, "autosense failed ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)
+ strcat(desc_scsi_state, "autosense valid ");
+
+ scsi_print_command(scmd);
+ printk(MPT2SAS_WARN_FMT "\tdev handle(0x%04x), "
+ "ioc_status(%s)(0x%04x), smid(%d)\n", ioc->name,
+ le16_to_cpu(mpi_reply->DevHandle), desc_ioc_state,
+ ioc_status, smid);
+ printk(MPT2SAS_WARN_FMT "\trequest_len(%d), underflow(%d), "
+ "resid(%d)\n", ioc->name, scsi_bufflen(scmd), scmd->underflow,
+ scsi_get_resid(scmd));
+ printk(MPT2SAS_WARN_FMT "\ttag(%d), transfer_count(%d), "
+ "sc->result(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->TaskTag),
+ le32_to_cpu(mpi_reply->TransferCount), scmd->result);
+ printk(MPT2SAS_WARN_FMT "\tscsi_status(%s)(0x%02x), "
+ "scsi_state(%s)(0x%02x)\n", ioc->name, desc_scsi_status,
+ scsi_status, desc_scsi_state, scsi_state);
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: "
+ "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey,
+ data.asc, data.ascq);
+ }
+
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
+ response_info = le32_to_cpu(mpi_reply->ResponseInfo);
+ response_bytes = (u8 *)&response_info;
+ _scsih_response_code(ioc, response_bytes[3]);
+ }
+}
+#endif
+
+/**
+ * _scsih_smart_predicted_fault - illuminate Fault LED
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+static void
+_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SepReply_t mpi_reply;
+ Mpi2SepRequest_t mpi_request;
+ struct scsi_target *starget;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ Mpi2EventNotificationReply_t *event_reply;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data;
+ struct _sas_device *sas_device;
+ ssize_t sz;
+ unsigned long flags;
+
+ /* only handle non-raid devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
+ ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+ starget_printk(KERN_WARNING, starget, "predicted fault\n");
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) {
+ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
+ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
+ mpi_request.SlotStatus =
+ MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT;
+ mpi_request.DevHandle = cpu_to_le16(handle);
+ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS;
+ if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply,
+ &mpi_request)) != 0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "enclosure_processor: ioc_status (0x%04x), "
+ "loginfo(0x%08x)\n", ioc->name,
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo)));
+ return;
+ }
+ }
+
+ /* insert into event log */
+ sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
+ sizeof(Mpi2EventDataSasDeviceStatusChange_t);
+ event_reply = kzalloc(sz, GFP_KERNEL);
+ if (!event_reply) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ event_reply->Event =
+ cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ event_reply->MsgLength = sz/4;
+ event_reply->EventDataLength =
+ cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4);
+ event_data = (Mpi2EventDataSasDeviceStatusChange_t *)
+ event_reply->EventData;
+ event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA;
+ event_data->ASC = 0x5D;
+ event_data->DevHandle = cpu_to_le16(handle);
+ event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
+ mpt2sas_ctl_add_to_event_log(ioc, event_reply);
+ kfree(event_reply);
+}
+
+/**
+ * scsih_io_done - scsi request callback
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when using scsih_qcmd.
+ *
+ * Return nothing.
+ */
+static void
+scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+{
+ Mpi2SCSIIORequest_t *mpi_request;
+ Mpi2SCSIIOReply_t *mpi_reply;
+ struct scsi_cmnd *scmd;
+ u16 ioc_status;
+ u32 xfer_cnt;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ u32 response_code;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ scmd = _scsih_scsi_lookup_getclear(ioc, smid);
+ if (scmd == NULL)
+ return;
+
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+
+ if (mpi_reply == NULL) {
+ scmd->result = DID_OK << 16;
+ goto out;
+ }
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ sas_device_priv_data->sas_target->deleted) {
+ scmd->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+
+ /* turning off TLR */
+ if (!sas_device_priv_data->tlr_snoop_check) {
+ sas_device_priv_data->tlr_snoop_check++;
+ if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) {
+ response_code = (le32_to_cpu(mpi_reply->ResponseInfo)
+ >> 24);
+ if (response_code ==
+ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
+ sas_device_priv_data->flags &=
+ ~MPT_DEVICE_TLR_ON;
+ }
+ }
+
+ xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
+ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ else
+ log_info = 0;
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ scsi_state = mpi_reply->SCSIState;
+ scsi_status = mpi_reply->SCSIStatus;
+
+ if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
+ (scsi_status == MPI2_SCSI_STATUS_BUSY ||
+ scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT ||
+ scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) {
+ ioc_status = MPI2_IOCSTATUS_SUCCESS;
+ }
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ const void *sense_data = mpt2sas_base_get_sense_buffer(ioc,
+ smid);
+ memcpy(scmd->sense_buffer, sense_data,
+ le32_to_cpu(mpi_reply->SenseCount));
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ /* failure prediction threshold exceeded */
+ if (data.asc == 0x5D)
+ _scsih_smart_predicted_fault(ioc,
+ le16_to_cpu(mpi_reply->DevHandle));
+ }
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_BUSY:
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ scmd->result = SAM_STAT_BUSY;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ scmd->result = DID_NO_CONNECT << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ if (sas_device_priv_data->block) {
+ scmd->result = (DID_BUS_BUSY << 16);
+ break;
+ }
+
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else
+ scmd->result = (DID_OK << 16) | scsi_status;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ scmd->result = (DID_OK << 16) | scsi_status;
+
+ if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID))
+ break;
+
+ if (xfer_cnt < scmd->underflow) {
+ if (scsi_status == SAM_STAT_BUSY)
+ scmd->result = SAM_STAT_BUSY;
+ else
+ scmd->result = DID_SOFT_ERROR << 16;
+ } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
+ mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID;
+ mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION;
+ scmd->result = (DRIVER_SENSE << 24) |
+ SAM_STAT_CHECK_CONDITION;
+ scmd->sense_buffer[0] = 0x70;
+ scmd->sense_buffer[2] = ILLEGAL_REQUEST;
+ scmd->sense_buffer[12] = 0x20;
+ scmd->sense_buffer[13] = 0;
+ }
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ scsi_set_resid(scmd, 0);
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SUCCESS:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ default:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+
+ }
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY))
+ _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid);
+#endif
+
+ out:
+ scsi_dma_unmap(scmd);
+ scmd->scsi_done(scmd);
+}
+
+/**
+ * _scsih_link_change - process phy link changes
+ * @ioc: per adapter object
+ * @handle: phy handle
+ * @attached_handle: valid for devices attached to link
+ * @phy_number: phy number
+ * @link_rate: new link rate
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 attached_handle,
+ u8 phy_number, u8 link_rate)
+{
+ mpt2sas_transport_update_phy_link_change(ioc, handle, attached_handle,
+ phy_number, link_rate);
+}
+
+/**
+ * _scsih_sas_host_refresh - refreshing sas host object contents
+ * @ioc: per adapter object
+ * @update: update link information
+ * Context: user
+ *
+ * During port enable, fw will send topology events for every device. Its
+ * possible that the handles may change from the previous setting, so this
+ * code keeping handles updating if changed.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
+{
+ u16 sz;
+ u16 ioc_status;
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+
+ dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "updating handles for sas_host(0x%016llx)\n",
+ ioc->name, (unsigned long long)ioc->sas_hba.sas_address));
+
+ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys
+ * sizeof(Mpi2SasIOUnit0PhyData_t));
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+ if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ ioc->sas_hba.phy[i].handle =
+ le16_to_cpu(sas_iounit_pg0->PhyData[i].
+ ControllerDevHandle);
+ if (update)
+ _scsih_link_change(ioc,
+ ioc->sas_hba.phy[i].handle,
+ le16_to_cpu(sas_iounit_pg0->PhyData[i].
+ AttachedDevHandle), i,
+ sas_iounit_pg0->PhyData[i].
+ NegotiatedLinkRate >> 4);
+ }
+ }
+
+ out:
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_sas_host_add - create sas host object
+ * @ioc: per adapter object
+ *
+ * Creating host side data object, stored in ioc->sas_hba
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
+{
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ u16 ioc_status;
+ u16 sz;
+ u16 device_missing_delay;
+
+ mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys);
+ if (!ioc->sas_hba.num_phys) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ /* sas_iounit page 0 */
+ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit0PhyData_t));
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ ioc->io_missing_delay =
+ le16_to_cpu(sas_iounit_pg1->IODeviceMissingDelay);
+ device_missing_delay =
+ le16_to_cpu(sas_iounit_pg1->ReportDeviceMissingDelay);
+ if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ ioc->device_missing_delay = (device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ ioc->device_missing_delay = device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+
+ ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev;
+ ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!ioc->sas_hba.phy) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ i))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->sas_hba.phy[i].handle =
+ le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle);
+ ioc->sas_hba.phy[i].phy_id = i;
+ mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
+ phy_pg0, ioc->sas_hba.parent_dev);
+ }
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle);
+ ioc->sas_hba.enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ printk(MPT2SAS_INFO_FMT "host_add: handle(0x%04x), "
+ "sas_addr(0x%016llx), phys(%d)\n", ioc->name, ioc->sas_hba.handle,
+ (unsigned long long) ioc->sas_hba.sas_address,
+ ioc->sas_hba.num_phys) ;
+
+ if (ioc->sas_hba.enclosure_handle) {
+ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_pg0,
+ MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ ioc->sas_hba.enclosure_handle))) {
+ ioc->sas_hba.enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_expander_add - creating expander object
+ * @ioc: per adapter object
+ * @handle: expander handle
+ *
+ * Creating expander object, stored in ioc->sas_expander_list.
+ *
+ * Return 0 for success, else error.
+ */
+static int
+_scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ExpanderPage1_t expander_pg1;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ u32 ioc_status;
+ u16 parent_handle;
+ __le64 sas_address;
+ int i;
+ unsigned long flags;
+ struct _sas_port *mpt2sas_port;
+ int rc = 0;
+
+ if (!handle)
+ return -1;
+
+ if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* handle out of order topology events */
+ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
+ if (parent_handle >= ioc->sas_hba.num_phys) {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (!sas_expander) {
+ rc = _scsih_expander_add(ioc, parent_handle);
+ if (rc != 0)
+ return rc;
+ }
+ }
+
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (sas_expander)
+ return 0;
+
+ sas_expander = kzalloc(sizeof(struct _sas_node),
+ GFP_KERNEL);
+ if (!sas_expander) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ sas_expander->handle = handle;
+ sas_expander->num_phys = expander_pg0.NumPhys;
+ sas_expander->parent_handle = parent_handle;
+ sas_expander->enclosure_handle =
+ le16_to_cpu(expander_pg0.EnclosureHandle);
+ sas_expander->sas_address = sas_address;
+
+ printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
+ " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
+ handle, sas_expander->parent_handle, (unsigned long long)
+ sas_expander->sas_address, sas_expander->num_phys);
+
+ if (!sas_expander->num_phys)
+ goto out_fail;
+ sas_expander->phy = kcalloc(sas_expander->num_phys,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!sas_expander->phy) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+
+ INIT_LIST_HEAD(&sas_expander->sas_port_list);
+ mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
+ sas_expander->parent_handle);
+ if (!mpt2sas_port) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->parent_dev = &mpt2sas_port->rphy->dev;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply,
+ &expander_pg1, i, handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ continue;
+ }
+ sas_expander->phy[i].handle = handle;
+ sas_expander->phy[i].phy_id = i;
+ mpt2sas_transport_add_expander_phy(ioc, &sas_expander->phy[i],
+ expander_pg1, sas_expander->parent_dev);
+ }
+
+ if (sas_expander->enclosure_handle) {
+ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ sas_expander->enclosure_handle))) {
+ sas_expander->enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+ }
+
+ _scsih_expander_node_add(ioc, sas_expander);
+ return 0;
+
+ out_fail:
+
+ if (sas_expander)
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+ return rc;
+}
+
+/**
+ * _scsih_expander_remove - removing expander object
+ * @ioc: per adapter object
+ * @handle: expander handle
+ *
+ * Return nothing.
+ */
+static void
+_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ _scsih_expander_node_remove(ioc, sas_expander);
+}
+
+/**
+ * _scsih_add_device - creating sas device object
+ * @ioc: per adapter object
+ * @handle: sas device handle
+ * @phy_num: phy number end device attached to
+ * @is_pd: is this hidden raid component
+ *
+ * Creating end device object, stored in ioc->sas_device_list.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ struct _sas_device *sas_device;
+ u32 ioc_status;
+ __le64 sas_address;
+ u32 device_info;
+ unsigned long flags;
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* check if device is present */
+ if (!(le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ printk(MPT2SAS_ERR_FMT "Flags = 0x%04x\n",
+ ioc->name, le16_to_cpu(sas_device_pg0.Flags));
+ return -1;
+ }
+
+ /* check if there were any issus with discovery */
+ if (sas_device_pg0.AccessStatus ==
+ MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n",
+ ioc->name, sas_device_pg0.AccessStatus);
+ return -1;
+ }
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (sas_device) {
+ _scsih_ublock_io_device(ioc, handle);
+ return 0;
+ }
+
+ sas_device = kzalloc(sizeof(struct _sas_device),
+ GFP_KERNEL);
+ if (!sas_device) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ sas_device->handle = handle;
+ sas_device->parent_handle =
+ le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ sas_device->enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ sas_device->slot =
+ le16_to_cpu(sas_device_pg0.Slot);
+ sas_device->device_info = device_info;
+ sas_device->sas_address = sas_address;
+ sas_device->hidden_raid_component = is_pd;
+
+ /* get enclosure_logical_id */
+ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, &enclosure_pg0,
+ MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ sas_device->enclosure_handle))) {
+ sas_device->enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+
+ /* get device name */
+ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
+
+ if (ioc->wait_for_port_enable_to_complete)
+ _scsih_sas_device_init_add(ioc, sas_device);
+ else
+ _scsih_sas_device_add(ioc, sas_device);
+
+ return 0;
+}
+
+/**
+ * _scsih_remove_device - removing sas device object
+ * @ioc: per adapter object
+ * @handle: sas device handle
+ *
+ * Return nothing.
+ */
+static void
+_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ Mpi2SasIoUnitControlReply_t mpi_reply;
+ Mpi2SasIoUnitControlRequest_t mpi_request;
+ u16 device_handle;
+
+ /* lookup sas_device */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle"
+ "(0x%04x)\n", ioc->name, __func__, handle));
+
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->remove_host)
+ goto out;
+
+ /* Target Reset to flush out all the outstanding IO */
+ device_handle = (sas_device->hidden_raid_component) ?
+ sas_device->volume_handle : handle;
+ if (device_handle) {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: "
+ "handle(0x%04x)\n", ioc->name, device_handle));
+ mutex_lock(&ioc->tm_cmds.mutex);
+ mpt2sas_scsih_issue_tm(ioc, device_handle, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10);
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset "
+ "done: handle(0x%04x)\n", ioc->name, device_handle));
+ }
+
+ /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle"
+ "(0x%04x)\n", ioc->name, handle));
+ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE;
+ mpi_request.DevHandle = handle;
+ mpi_request.VF_ID = 0;
+ if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply,
+ &mpi_request)) != 0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ }
+
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status"
+ "(0x%04x), loginfo(0x%08x)\n", ioc->name,
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo)));
+
+ out:
+ mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
+ sas_device->parent_handle);
+
+ printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
+ "(0x%016llx)\n", ioc->name, sas_device->handle,
+ (unsigned long long) sas_device->sas_address);
+ _scsih_sas_device_remove(ioc, sas_device);
+
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle"
+ "(0x%04x)\n", ioc->name, __func__, handle));
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_sas_topology_change_event_debug - debug for topology event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 phy_number;
+ char *status_str = NULL;
+ char link_rate[25];
+
+ switch (event_data->ExpStatus) {
+ case MPI2_EVENT_SAS_TOPO_ES_ADDED:
+ status_str = "add";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+ status_str = "responding";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ printk(MPT2SAS_DEBUG_FMT "sas topology change: (%s)\n",
+ ioc->name, status_str);
+ printk(KERN_DEBUG "\thandle(0x%04x), enclosure_handle(0x%04x) "
+ "start_phy(%02d), count(%d)\n",
+ le16_to_cpu(event_data->ExpanderDevHandle),
+ le16_to_cpu(event_data->EnclosureHandle),
+ event_data->StartPhyNum, event_data->NumEntries);
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+ snprintf(link_rate, 25, ": add, link(0x%02x)",
+ (event_data->PHY[i].LinkRate >> 4));
+ status_str = link_rate;
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+ status_str = ": remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
+ status_str = ": remove_delay";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+ snprintf(link_rate, 25, ": link(0x%02x)",
+ (event_data->PHY[i].LinkRate >> 4));
+ status_str = link_rate;
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
+ status_str = ": responding";
+ break;
+ default:
+ status_str = ": unknown";
+ break;
+ }
+ printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n",
+ phy_number, handle, status_str);
+ }
+}
+#endif
+
+/**
+ * _scsih_sas_topology_change_event - handle topology changes
+ * @ioc: per adapter object
+ * @VF_ID:
+ * @event_data: event data payload
+ * fw_event:
+ * Context: user.
+ *
+ */
+static void
+_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataSasTopologyChangeList_t *event_data,
+ struct fw_event_work *fw_event)
+{
+ int i;
+ u16 parent_handle, handle;
+ u16 reason_code;
+ u8 phy_number;
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+ u8 link_rate_;
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_topology_change_event_debug(ioc, event_data);
+#endif
+
+ if (!ioc->sas_hba.num_phys)
+ _scsih_sas_host_add(ioc);
+ else
+ _scsih_sas_host_refresh(ioc, 0);
+
+ if (fw_event->ignore) {
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander "
+ "event\n", ioc->name));
+ return;
+ }
+
+ parent_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+
+ /* handle expander add */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED)
+ if (_scsih_expander_add(ioc, parent_handle) != 0)
+ return;
+
+ /* handle siblings events */
+ for (i = 0; i < event_data->NumEntries; i++) {
+ if (fw_event->ignore) {
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring "
+ "expander event\n", ioc->name));
+ return;
+ }
+ if (event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT)
+ continue;
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ link_rate_ = event_data->PHY[i].LinkRate >> 4;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+ if (!parent_handle) {
+ if (phy_number < ioc->sas_hba.num_phys)
+ _scsih_link_change(ioc,
+ ioc->sas_hba.phy[phy_number].handle,
+ handle, phy_number, link_rate_);
+ } else {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander =
+ mpt2sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock,
+ flags);
+ if (sas_expander) {
+ if (phy_number < sas_expander->num_phys)
+ _scsih_link_change(ioc,
+ sas_expander->
+ phy[phy_number].handle,
+ handle, phy_number,
+ link_rate_);
+ }
+ }
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
+ if (link_rate_ >= MPI2_SAS_NEG_LINK_RATE_1_5)
+ _scsih_ublock_io_device(ioc, handle);
+ }
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
+ if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
+ break;
+ _scsih_add_device(ioc, handle, phy_number, 0);
+ }
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+ _scsih_remove_device(ioc, handle);
+ break;
+ }
+ }
+
+ /* handle expander removal */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+ _scsih_expander_remove(ioc, parent_handle);
+
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_sas_device_status_change_event_debug - debug for device event
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataSasDeviceStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
+ reason_str = "smart data";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
+ reason_str = "unsupported device discovered";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
+ reason_str = "internal device reset";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
+ reason_str = "internal task abort set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
+ reason_str = "internal clear task set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
+ reason_str = "internal query task";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE:
+ reason_str = "sata init failure";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+ reason_str = "internal device reset complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
+ reason_str = "internal async notification";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ printk(MPT2SAS_DEBUG_FMT "device status change: (%s)\n"
+ "\thandle(0x%04x), sas address(0x%016llx)", ioc->name,
+ reason_str, le16_to_cpu(event_data->DevHandle),
+ (unsigned long long)le64_to_cpu(event_data->SASAddress));
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA)
+ printk(MPT2SAS_DEBUG_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name,
+ event_data->ASC, event_data->ASCQ);
+ printk(KERN_INFO "\n");
+}
+#endif
+
+/**
+ * _scsih_sas_device_status_change_event - handle device status change
+ * @ioc: per adapter object
+ * @VF_ID:
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataSasDeviceStatusChange_t *event_data)
+{
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_device_status_change_event_debug(ioc, event_data);
+#endif
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_ENCL_RC_ADDED:
+ reason_str = "enclosure add";
+ break;
+ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING:
+ reason_str = "enclosure remove";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+
+ printk(MPT2SAS_DEBUG_FMT "enclosure status change: (%s)\n"
+ "\thandle(0x%04x), enclosure logical id(0x%016llx)"
+ " number slots(%d)\n", ioc->name, reason_str,
+ le16_to_cpu(event_data->EnclosureHandle),
+ (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID),
+ le16_to_cpu(event_data->StartSlot));
+}
+#endif
+
+/**
+ * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events
+ * @ioc: per adapter object
+ * @VF_ID:
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc,
+ u8 VF_ID, Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_enclosure_dev_status_change_event_debug(ioc,
+ event_data);
+#endif
+}
+
+/**
+ * _scsih_sas_broadcast_primative_event - handle broadcast events
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataSasBroadcastPrimitive_t *event_data)
+{
+ struct scsi_cmnd *scmd;
+ u16 smid, handle;
+ u32 lun;
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ u32 termination_count;
+ u32 query_count;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: "
+ "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
+ event_data->PortWidth));
+
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ termination_count = 0;
+ query_count = 0;
+ mpi_reply = ioc->tm_cmds.reply;
+ for (smid = 1; smid <= ioc->request_depth; smid++) {
+ scmd = _scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
+ continue;
+ /* skip hidden raid components */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ continue;
+ /* skip volumes */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_VOLUME)
+ continue;
+
+ handle = sas_device_priv_data->sas_target->handle;
+ lun = sas_device_priv_data->lun;
+ query_count++;
+
+ mpt2sas_scsih_issue_tm(ioc, handle, lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
+ termination_count += le32_to_cpu(mpi_reply->TerminationCount);
+
+ if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
+ (mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
+ mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))
+ continue;
+
+ mpt2sas_scsih_issue_tm(ioc, handle, lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, smid, 30);
+ termination_count += le32_to_cpu(mpi_reply->TerminationCount);
+ }
+ ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+ ioc->broadcast_aen_busy = 0;
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+ "%s - exit, query_count = %d termination_count = %d\n",
+ ioc->name, __func__, query_count, termination_count));
+}
+
+/**
+ * _scsih_sas_discovery_event - handle discovery events
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataSasDiscovery_t *event_data)
+{
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
+ printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name,
+ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
+ "start" : "stop");
+ if (event_data->DiscoveryStatus)
+ printk(MPT2SAS_DEBUG_FMT ", discovery_status(0x%08x)",
+ ioc->name, le32_to_cpu(event_data->DiscoveryStatus));
+ printk("\n");
+ }
+#endif
+
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED &&
+ !ioc->sas_hba.num_phys)
+ _scsih_sas_host_add(ioc);
+}
+
+/**
+ * _scsih_reprobe_lun - reprobing lun
+ * @sdev: scsi device struct
+ * @no_uld_attach: sdev->no_uld_attach flag setting
+ *
+ **/
+static void
+_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
+{
+ int rc;
+
+ sdev->no_uld_attach = no_uld_attach ? 1 : 0;
+ sdev_printk(KERN_INFO, sdev, "%s raid component\n",
+ sdev->no_uld_attach ? "hidding" : "exposing");
+ rc = scsi_device_reprobe(sdev);
+}
+
+/**
+ * _scsih_reprobe_target - reprobing target
+ * @starget: scsi target struct
+ * @no_uld_attach: sdev->no_uld_attach flag setting
+ *
+ * Note: no_uld_attach flag determines whether the disk device is attached
+ * to block layer. A value of `1` means to not attach.
+ **/
+static void
+_scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach)
+{
+ struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata;
+
+ if (no_uld_attach)
+ sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
+ else
+ sas_target_priv_data->flags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+
+ starget_for_each_device(starget, no_uld_attach ? (void *)1 : NULL,
+ _scsih_reprobe_lun);
+}
+/**
+ * _scsih_sas_volume_add - add new volume
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ u64 wwid;
+ u16 handle = le16_to_cpu(element->VolDevHandle);
+ int rc;
+
+#if 0 /* RAID_HACKS */
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ return;
+#endif
+
+ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ printk(MPT2SAS_ERR_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (raid_device)
+ return;
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ printk(MPT2SAS_ERR_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ if (!ioc->wait_for_port_enable_to_complete) {
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else
+ _scsih_determine_boot_device(ioc, raid_device, 1);
+}
+
+/**
+ * _scsih_sas_volume_delete - delete volume
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _raid_device *raid_device;
+ u16 handle = le16_to_cpu(element->VolDevHandle);
+ unsigned long flags;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+
+#if 0 /* RAID_HACKS */
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ return;
+#endif
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (!raid_device)
+ return;
+ if (raid_device->starget) {
+ sas_target_priv_data = raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ _scsih_raid_device_remove(ioc, raid_device);
+}
+
+/**
+ * _scsih_sas_pd_expose - expose pd component to /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* exposing raid component */
+ sas_device->volume_handle = 0;
+ sas_device->volume_wwid = 0;
+ sas_device->hidden_raid_component = 0;
+ _scsih_reprobe_target(sas_device->starget, 0);
+}
+
+/**
+ * _scsih_sas_pd_hide - hide pd component from /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* hiding raid component */
+ mpt2sas_config_get_volume_handle(ioc, handle,
+ &sas_device->volume_handle);
+ mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle,
+ &sas_device->volume_wwid);
+ sas_device->hidden_raid_component = 1;
+ _scsih_reprobe_target(sas_device->starget, 1);
+}
+
+/**
+ * _scsih_sas_pd_delete - delete pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+ _scsih_remove_device(ioc, handle);
+}
+
+/**
+ * _scsih_sas_pd_add - remove pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ sas_device->hidden_raid_component = 1;
+ else
+ _scsih_add_device(ioc, handle, 0, 1);
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ u8 element_type;
+ int i;
+ char *reason_str = NULL, *element_str = NULL;
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+
+ printk(MPT2SAS_DEBUG_FMT "raid config change: (%s), elements(%d)\n",
+ ioc->name, (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ?
+ "foreign" : "native", event_data->NumElements);
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ reason_str = "add";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ reason_str = "remove";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE:
+ reason_str = "no change";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ reason_str = "hide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ reason_str = "unhide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ reason_str = "volume_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ reason_str = "volume_deleted";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ reason_str = "pd_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ reason_str = "pd_deleted";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ element_type = le16_to_cpu(element->ElementFlags) &
+ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK;
+ switch (element_type) {
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT:
+ element_str = "volume";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT:
+ element_str = "phys disk";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT:
+ element_str = "hot spare";
+ break;
+ default:
+ element_str = "unknown element";
+ break;
+ }
+ printk(KERN_DEBUG "\t(%s:%s), vol handle(0x%04x), "
+ "pd handle(0x%04x), pd num(0x%02x)\n", element_str,
+ reason_str, le16_to_cpu(element->VolDevHandle),
+ le16_to_cpu(element->PhysDiskDevHandle),
+ element->PhysDiskNum);
+ }
+}
+#endif
+
+/**
+ * _scsih_sas_ir_config_change_event - handle ir configuration change events
+ * @ioc: per adapter object
+ * @VF_ID:
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_ir_config_change_event_debug(ioc, event_data);
+
+#endif
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ _scsih_sas_volume_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ _scsih_sas_volume_delete(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ _scsih_sas_pd_hide(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ _scsih_sas_pd_expose(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ _scsih_sas_pd_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ _scsih_sas_pd_delete(ioc, element);
+ break;
+ }
+ }
+}
+
+/**
+ * _scsih_sas_ir_volume_event - IR volume event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataIrVolume_t *event_data)
+{
+ u64 wwid;
+ unsigned long flags;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u32 state;
+ int rc;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->VolDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), "
+ "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
+ le32_to_cpu(event_data->PreviousValue), state));
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ switch (state) {
+ case MPI2_RAID_VOL_STATE_MISSING:
+ case MPI2_RAID_VOL_STATE_FAILED:
+ if (!raid_device)
+ break;
+ if (raid_device->starget) {
+ sas_target_priv_data = raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ _scsih_raid_device_remove(ioc, raid_device);
+ break;
+
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+ if (raid_device)
+ break;
+
+ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ printk(MPT2SAS_ERR_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ printk(MPT2SAS_ERR_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ break;
+
+ case MPI2_RAID_VOL_STATE_INITIALIZING:
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_sas_ir_physical_disk_event - PD event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataIrPhysicalDisk_t *event_data)
+{
+ u16 handle;
+ u32 state;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->PhysDiskDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+
+ dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), "
+ "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
+ le32_to_cpu(event_data->PreviousValue), state));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ switch (state) {
+#if 0
+ case MPI2_RAID_PD_STATE_OFFLINE:
+ if (sas_device)
+ _scsih_remove_device(ioc, handle);
+ break;
+#endif
+ case MPI2_RAID_PD_STATE_ONLINE:
+ case MPI2_RAID_PD_STATE_DEGRADED:
+ case MPI2_RAID_PD_STATE_REBUILDING:
+ case MPI2_RAID_PD_STATE_OPTIMAL:
+ if (sas_device)
+ sas_device->hidden_raid_component = 1;
+ else
+ _scsih_add_device(ioc, handle, 0, 1);
+ break;
+
+ case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
+ case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
+ case MPI2_RAID_PD_STATE_HOT_SPARE:
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+/**
+ * _scsih_sas_ir_operation_status_event_debug - debug for IR op event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2EventDataIrOperationStatus_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->RAIDOperation) {
+ case MPI2_EVENT_IR_RAIDOP_RESYNC:
+ reason_str = "resync";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION:
+ reason_str = "online capacity expansion";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
+ reason_str = "consistency check";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+
+ printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
+ "\thandle(0x%04x), percent complete(%d)\n",
+ ioc->name, reason_str,
+ le16_to_cpu(event_data->VolDevHandle),
+ event_data->PercentComplete);
+}
+#endif
+
+/**
+ * _scsih_sas_ir_operation_status_event - handle RAID operation events
+ * @ioc: per adapter object
+ * @VF_ID:
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataIrOperationStatus_t *event_data)
+{
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_ir_operation_status_event_debug(ioc, event_data);
+#endif
+}
+
+/**
+ * _scsih_task_set_full - handle task set full
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Throttle back qdepth.
+ */
+static void
+_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
+ Mpi2EventDataTaskSetFull_t *event_data)
+{
+ unsigned long flags;
+ struct _sas_device *sas_device;
+ static struct _raid_device *raid_device;
+ struct scsi_device *sdev;
+ int depth;
+ u16 current_depth;
+ u16 handle;
+ int id, channel;
+ u64 sas_address;
+
+ current_depth = le16_to_cpu(event_data->CurrentDepth);
+ handle = le16_to_cpu(event_data->DevHandle);
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ id = sas_device->id;
+ channel = sas_device->channel;
+ sas_address = sas_device->sas_address;
+
+ /* if hidden raid component, then change to volume characteristics */
+ if (sas_device->hidden_raid_component && sas_device->volume_handle) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(
+ ioc, sas_device->volume_handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (raid_device) {
+ id = raid_device->id;
+ channel = raid_device->channel;
+ handle = raid_device->handle;
+ sas_address = raid_device->wwid;
+ }
+ }
+
+ if (ioc->logging_level & MPT_DEBUG_TASK_SET_FULL)
+ starget_printk(KERN_DEBUG, sas_device->starget, "task set "
+ "full: handle(0x%04x), sas_addr(0x%016llx), depth(%d)\n",
+ handle, (unsigned long long)sas_address, current_depth);
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (sdev->id == id && sdev->channel == channel) {
+ if (current_depth > sdev->queue_depth) {
+ if (ioc->logging_level &
+ MPT_DEBUG_TASK_SET_FULL)
+ sdev_printk(KERN_INFO, sdev, "strange "
+ "observation, the queue depth is"
+ " (%d) meanwhile fw queue depth "
+ "is (%d)\n", sdev->queue_depth,
+ current_depth);
+ continue;
+ }
+ depth = scsi_track_queue_full(sdev,
+ current_depth - 1);
+ if (depth > 0)
+ sdev_printk(KERN_INFO, sdev, "Queue depth "
+ "reduced to (%d)\n", depth);
+ else if (depth < 0)
+ sdev_printk(KERN_INFO, sdev, "Tagged Command "
+ "Queueing is being disabled\n");
+ else if (depth == 0)
+ if (ioc->logging_level &
+ MPT_DEBUG_TASK_SET_FULL)
+ sdev_printk(KERN_INFO, sdev,
+ "Queue depth not changed yet\n");
+ }
+ }
+}
+
+/**
+ * _scsih_mark_responding_sas_device - mark a sas_devices as responding
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @slot: enclosure slot id
+ * @handle: device handle
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsi_remove_unresponsive_sas_devices.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
+ u16 slot, u16 handle)
+{
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->sas_address == sas_address &&
+ sas_device->slot == slot && sas_device->starget) {
+ sas_device->responding = 1;
+ starget_printk(KERN_INFO, sas_device->starget,
+ "handle(0x%04x), sas_addr(0x%016llx), enclosure "
+ "logical id(0x%016llx), slot(%d)\n", handle,
+ (unsigned long long)sas_device->sas_address,
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
+ if (sas_device->handle == handle)
+ goto out;
+ printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
+ sas_device->handle);
+ sas_device->handle = handle;
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->handle = handle;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_sas_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ __le64 sas_address;
+ u16 handle;
+ u32 device_info;
+ u16 slot;
+
+ printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+
+ if (list_empty(&ioc->sas_device_list))
+ return;
+
+ handle = 0xFFFF;
+ while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+ handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ handle = le16_to_cpu(sas_device_pg0.DevHandle);
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ continue;
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ slot = le16_to_cpu(sas_device_pg0.Slot);
+ _scsih_mark_responding_sas_device(ioc, sas_address, slot,
+ handle);
+ }
+}
+
+/**
+ * _scsih_mark_responding_raid_device - mark a raid_device as responding
+ * @ioc: per adapter object
+ * @wwid: world wide identifier for raid volume
+ * @handle: device handle
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsi_remove_unresponsive_raid_devices.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,
+ u16 handle)
+{
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid == wwid && raid_device->starget) {
+ raid_device->responding = 1;
+ starget_printk(KERN_INFO, raid_device->starget,
+ "handle(0x%04x), wwid(0x%016llx)\n", handle,
+ (unsigned long long)raid_device->wwid);
+ if (raid_device->handle == handle)
+ goto out;
+ printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
+ raid_device->handle);
+ raid_device->handle = handle;
+ starget = raid_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->handle = handle;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_raid_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t volume_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+
+ printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+
+ if (list_empty(&ioc->raid_device_list))
+ return;
+
+ handle = 0xFFFF;
+ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ handle = le16_to_cpu(volume_pg1.DevHandle);
+ _scsih_mark_responding_raid_device(ioc,
+ le64_to_cpu(volume_pg1.WWID), handle);
+ }
+}
+
+/**
+ * _scsih_mark_responding_expander - mark a expander as responding
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @handle:
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsi_remove_unresponsive_expanders.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
+ u16 handle)
+{
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address == sas_address) {
+ sas_expander->responding = 1;
+ if (sas_expander->handle != handle) {
+ printk(KERN_INFO "old handle(0x%04x)\n",
+ sas_expander->handle);
+ sas_expander->handle = handle;
+ }
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_expanders -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ __le64 sas_address;
+ u16 handle;
+
+ printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
+
+ if (list_empty(&ioc->sas_expander_list))
+ return;
+
+ handle = 0xFFFF;
+ while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+
+ handle = le16_to_cpu(expander_pg0.DevHandle);
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+ printk(KERN_INFO "\texpander present: handle(0x%04x), "
+ "sas_addr(0x%016llx)\n", handle,
+ (unsigned long long)sas_address);
+ _scsih_mark_responding_expander(ioc, sas_address, handle);
+ }
+
+}
+
+/**
+ * _scsih_remove_unresponding_devices - removing unresponding devices
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *sas_device_next;
+ struct _sas_node *sas_expander, *sas_expander_next;
+ struct _raid_device *raid_device, *raid_device_next;
+ unsigned long flags;
+
+ _scsih_search_responding_sas_devices(ioc);
+ _scsih_search_responding_raid_devices(ioc);
+ _scsih_search_responding_expanders(ioc);
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->shost_recovery = 0;
+ if (ioc->shost->shost_state == SHOST_RECOVERY) {
+ printk(MPT2SAS_INFO_FMT "putting controller into "
+ "SHOST_RUNNING\n", ioc->name);
+ scsi_host_set_state(ioc->shost, SHOST_RUNNING);
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_list, list) {
+ if (sas_device->responding) {
+ sas_device->responding = 0;
+ continue;
+ }
+ if (sas_device->starget)
+ starget_printk(KERN_INFO, sas_device->starget,
+ "removing: handle(0x%04x), sas_addr(0x%016llx), "
+ "enclosure logical id(0x%016llx), slot(%d)\n",
+ sas_device->handle,
+ (unsigned long long)sas_device->sas_address,
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
+ _scsih_remove_device(ioc, sas_device->handle);
+ }
+
+ list_for_each_entry_safe(raid_device, raid_device_next,
+ &ioc->raid_device_list, list) {
+ if (raid_device->responding) {
+ raid_device->responding = 0;
+ continue;
+ }
+ if (raid_device->starget) {
+ starget_printk(KERN_INFO, raid_device->starget,
+ "removing: handle(0x%04x), wwid(0x%016llx)\n",
+ raid_device->handle,
+ (unsigned long long)raid_device->wwid);
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+
+ list_for_each_entry_safe(sas_expander, sas_expander_next,
+ &ioc->sas_expander_list, list) {
+ if (sas_expander->responding) {
+ sas_expander->responding = 0;
+ continue;
+ }
+ printk("\tremoving expander: handle(0x%04x), "
+ " sas_addr(0x%016llx)\n", sas_expander->handle,
+ (unsigned long long)sas_expander->sas_address);
+ _scsih_expander_remove(ioc, sas_expander->handle);
+ }
+}
+
+/**
+ * _firmware_event_work - delayed task for processing firmware events
+ * @ioc: per adapter object
+ * @work: equal to the fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_firmware_event_work(struct work_struct *work)
+{
+ struct fw_event_work *fw_event = container_of(work,
+ struct fw_event_work, work.work);
+ unsigned long flags;
+ struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
+
+ /* This is invoked by calling _scsih_queue_rescan(). */
+ if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) {
+ _scsih_fw_event_free(ioc, fw_event);
+ _scsih_sas_host_refresh(ioc, 1);
+ _scsih_remove_unresponding_devices(ioc);
+ return;
+ }
+
+ /* the queue is being flushed so ignore this event */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ if (ioc->fw_events_off || ioc->remove_host) {
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ _scsih_fw_event_free(ioc, fw_event);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery) {
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ _scsih_fw_event_requeue(ioc, fw_event, 1000);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ switch (fw_event->event) {
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_sas_topology_change_event(ioc, fw_event->VF_ID,
+ fw_event->event_data, fw_event);
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ _scsih_sas_device_status_change_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ _scsih_sas_discovery_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ _scsih_sas_broadcast_primative_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ _scsih_sas_enclosure_dev_status_change_event(ioc,
+ fw_event->VF_ID, fw_event->event_data);
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ _scsih_sas_ir_config_change_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ _scsih_sas_ir_volume_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ _scsih_sas_ir_physical_disk_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ _scsih_sas_ir_operation_status_event(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_TASK_SET_FULL:
+ _scsih_task_set_full(ioc, fw_event->VF_ID,
+ fw_event->event_data);
+ break;
+ }
+ _scsih_fw_event_free(ioc, fw_event);
+}
+
+/**
+ * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @VF_ID: virtual function id
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventNotificationReply_t *mpi_reply;
+ unsigned long flags;
+ u16 event;
+
+ /* events turned off due to host reset or driver unloading */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ if (ioc->fw_events_off || ioc->remove_host) {
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ event = le16_to_cpu(mpi_reply->Event);
+
+ switch (event) {
+ /* handle these */
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ {
+ Mpi2EventDataSasBroadcastPrimitive_t *baen_data =
+ (Mpi2EventDataSasBroadcastPrimitive_t *)
+ mpi_reply->EventData;
+
+ if (baen_data->Primitive !=
+ MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT ||
+ ioc->broadcast_aen_busy)
+ return;
+ ioc->broadcast_aen_busy = 1;
+ break;
+ }
+
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_check_topo_delete_events(ioc,
+ (Mpi2EventDataSasTopologyChangeList_t *)
+ mpi_reply->EventData);
+ break;
+
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ case MPI2_EVENT_SAS_DISCOVERY:
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_IR_VOLUME:
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ case MPI2_EVENT_TASK_SET_FULL:
+ break;
+
+ default: /* ignore the rest */
+ return;
+ }
+
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+ fw_event->event_data =
+ kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC);
+ if (!fw_event->event_data) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ kfree(fw_event);
+ return;
+ }
+
+ memcpy(fw_event->event_data, mpi_reply->EventData,
+ mpi_reply->EventDataLength*4);
+ fw_event->ioc = ioc;
+ fw_event->VF_ID = VF_ID;
+ fw_event->event = event;
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/* shost template */
+static struct scsi_host_template scsih_driver_template = {
+ .module = THIS_MODULE,
+ .name = "Fusion MPT SAS Host",
+ .proc_name = MPT2SAS_DRIVER_NAME,
+ .queuecommand = scsih_qcmd,
+ .target_alloc = scsih_target_alloc,
+ .slave_alloc = scsih_slave_alloc,
+ .slave_configure = scsih_slave_configure,
+ .target_destroy = scsih_target_destroy,
+ .slave_destroy = scsih_slave_destroy,
+ .change_queue_depth = scsih_change_queue_depth,
+ .change_queue_type = scsih_change_queue_type,
+ .eh_abort_handler = scsih_abort,
+ .eh_device_reset_handler = scsih_dev_reset,
+ .eh_host_reset_handler = scsih_host_reset,
+ .bios_param = scsih_bios_param,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPT2SAS_SG_DEPTH,
+ .max_sectors = 8192,
+ .cmd_per_lun = 7,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = mpt2sas_host_attrs,
+ .sdev_attrs = mpt2sas_dev_attrs,
+};
+
+/**
+ * _scsih_expander_node_remove - removing expander device from list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * Removing object and freeing associated memory from the
+ * ioc->sas_expander_list.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt2sas_port;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_sibling;
+ unsigned long flags;
+
+ if (!sas_expander)
+ return;
+
+ /* remove sibling ports attached to this expander */
+ retry_device_search:
+ list_for_each_entry(mpt2sas_port,
+ &sas_expander->sas_port_list, port_list) {
+ if (mpt2sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device =
+ mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ mpt2sas_port->remote_identify.sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ continue;
+ _scsih_remove_device(ioc, sas_device->handle);
+ goto retry_device_search;
+ }
+ }
+
+ retry_expander_search:
+ list_for_each_entry(mpt2sas_port,
+ &sas_expander->sas_port_list, port_list) {
+
+ if (mpt2sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+ mpt2sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ expander_sibling =
+ mpt2sas_scsih_expander_find_by_sas_address(
+ ioc, mpt2sas_port->remote_identify.sas_address);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (!expander_sibling)
+ continue;
+ _scsih_expander_remove(ioc, expander_sibling->handle);
+ goto retry_expander_search;
+ }
+ }
+
+ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_expander->parent_handle);
+
+ printk(MPT2SAS_INFO_FMT "expander_remove: handle"
+ "(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ sas_expander->handle, (unsigned long long)
+ sas_expander->sas_address);
+
+ list_del(&sas_expander->list);
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+}
+
+/**
+ * scsih_remove - detach and remove add host
+ * @pdev: PCI device struct
+ *
+ * Return nothing.
+ */
+static void __devexit
+scsih_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct _sas_port *mpt2sas_port;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_sibling;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ ioc->remove_host = 1;
+ _scsih_fw_event_off(ioc);
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->firmware_event_thread;
+ ioc->firmware_event_thread = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ /* free ports attached to the sas_host */
+ retry_again:
+ list_for_each_entry(mpt2sas_port,
+ &ioc->sas_hba.sas_port_list, port_list) {
+ if (mpt2sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE) {
+ sas_device =
+ mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ mpt2sas_port->remote_identify.sas_address);
+ if (sas_device) {
+ _scsih_remove_device(ioc, sas_device->handle);
+ goto retry_again;
+ }
+ } else {
+ expander_sibling =
+ mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ mpt2sas_port->remote_identify.sas_address);
+ if (expander_sibling) {
+ _scsih_expander_remove(ioc,
+ expander_sibling->handle);
+ goto retry_again;
+ }
+ }
+ }
+
+ /* free phys attached to the sas_host */
+ if (ioc->sas_hba.num_phys) {
+ kfree(ioc->sas_hba.phy);
+ ioc->sas_hba.phy = NULL;
+ ioc->sas_hba.num_phys = 0;
+ }
+
+ sas_remove_host(shost);
+ mpt2sas_base_detach(ioc);
+ list_del(&ioc->list);
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+}
+
+/**
+ * _scsih_probe_boot_devices - reports 1st device
+ * @ioc: per adapter object
+ *
+ * If specified in bios page 2, this routine reports the 1st
+ * device scsi-ml or sas transport for persistent boot device
+ * purposes. Please refer to function _scsih_determine_boot_device()
+ */
+static void
+_scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ u8 is_raid;
+ void *device;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ u16 handle, parent_handle;
+ u64 sas_address;
+ unsigned long flags;
+ int rc;
+
+ device = NULL;
+ if (ioc->req_boot_device.device) {
+ device = ioc->req_boot_device.device;
+ is_raid = ioc->req_boot_device.is_raid;
+ } else if (ioc->req_alt_boot_device.device) {
+ device = ioc->req_alt_boot_device.device;
+ is_raid = ioc->req_alt_boot_device.is_raid;
+ } else if (ioc->current_boot_device.device) {
+ device = ioc->current_boot_device.device;
+ is_raid = ioc->current_boot_device.is_raid;
+ }
+
+ if (!device)
+ return;
+
+ if (is_raid) {
+ raid_device = device;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else {
+ sas_device = device;
+ handle = sas_device->handle;
+ parent_handle = sas_device->parent_handle;
+ sas_address = sas_device->sas_address;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_move_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->parent_handle)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ mpt2sas_transport_port_remove(ioc, sas_address,
+ parent_handle);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ }
+}
+
+/**
+ * _scsih_probe_raid - reporting raid volumes to scsi-ml
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct _raid_device *raid_device, *raid_next;
+ int rc;
+
+ list_for_each_entry_safe(raid_device, raid_next,
+ &ioc->raid_device_list, list) {
+ if (raid_device->starget)
+ continue;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+}
+
+/**
+ * _scsih_probe_sas - reporting raid volumes to sas transport
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *next;
+ unsigned long flags;
+ u16 handle, parent_handle;
+ u64 sas_address;
+
+ /* SAS Device List */
+ list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
+ list) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_move_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ handle = sas_device->handle;
+ parent_handle = sas_device->parent_handle;
+ sas_address = sas_device->sas_address;
+ if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ mpt2sas_transport_port_remove(ioc, sas_address,
+ parent_handle);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ }
+}
+
+/**
+ * _scsih_probe_devices - probing for devices
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ u16 volume_mapping_flags =
+ le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+
+ if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))
+ return; /* return when IOC doesn't support initiator mode */
+
+ _scsih_probe_boot_devices(ioc);
+
+ if (ioc->ir_firmware) {
+ if ((volume_mapping_flags &
+ MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) {
+ _scsih_probe_sas(ioc);
+ _scsih_probe_raid(ioc);
+ } else {
+ _scsih_probe_raid(ioc);
+ _scsih_probe_sas(ioc);
+ }
+ } else
+ _scsih_probe_sas(ioc);
+}
+
+/**
+ * scsih_probe - attach and add scsi host
+ * @pdev: PCI device struct
+ * @id: pci device id
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct MPT2SAS_ADAPTER *ioc;
+ struct Scsi_Host *shost;
+
+ shost = scsi_host_alloc(&scsih_driver_template,
+ sizeof(struct MPT2SAS_ADAPTER));
+ if (!shost)
+ return -ENODEV;
+
+ /* init local params */
+ ioc = shost_priv(shost);
+ memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER));
+ INIT_LIST_HEAD(&ioc->list);
+ list_add_tail(&ioc->list, &mpt2sas_ioc_list);
+ ioc->shost = shost;
+ ioc->id = mpt_ids++;
+ sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id);
+ ioc->pdev = pdev;
+ ioc->scsi_io_cb_idx = scsi_io_cb_idx;
+ ioc->tm_cb_idx = tm_cb_idx;
+ ioc->ctl_cb_idx = ctl_cb_idx;
+ ioc->base_cb_idx = base_cb_idx;
+ ioc->transport_cb_idx = transport_cb_idx;
+ ioc->config_cb_idx = config_cb_idx;
+ ioc->logging_level = logging_level;
+ /* misc semaphores and spin locks */
+ spin_lock_init(&ioc->ioc_reset_in_progress_lock);
+ spin_lock_init(&ioc->scsi_lookup_lock);
+ spin_lock_init(&ioc->sas_device_lock);
+ spin_lock_init(&ioc->sas_node_lock);
+ spin_lock_init(&ioc->fw_event_lock);
+ spin_lock_init(&ioc->raid_device_lock);
+
+ INIT_LIST_HEAD(&ioc->sas_device_list);
+ INIT_LIST_HEAD(&ioc->sas_device_init_list);
+ INIT_LIST_HEAD(&ioc->sas_expander_list);
+ INIT_LIST_HEAD(&ioc->fw_event_list);
+ INIT_LIST_HEAD(&ioc->raid_device_list);
+ INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list);
+
+ /* init shost parameters */
+ shost->max_cmd_len = 16;
+ shost->max_lun = max_lun;
+ shost->transportt = mpt2sas_transport_template;
+ shost->unique_id = ioc->id;
+
+ if ((scsi_add_host(shost, &pdev->dev))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ list_del(&ioc->list);
+ goto out_add_shost_fail;
+ }
+
+ /* event thread */
+ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
+ "fw_event%d", ioc->id);
+ ioc->firmware_event_thread = create_singlethread_workqueue(
+ ioc->firmware_event_name);
+ if (!ioc->firmware_event_thread) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_thread_fail;
+ }
+
+ ioc->wait_for_port_enable_to_complete = 1;
+ if ((mpt2sas_base_attach(ioc))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_attach_fail;
+ }
+
+ ioc->wait_for_port_enable_to_complete = 0;
+ _scsih_probe_devices(ioc);
+ return 0;
+
+ out_attach_fail:
+ destroy_workqueue(ioc->firmware_event_thread);
+ out_thread_fail:
+ list_del(&ioc->list);
+ scsi_remove_host(shost);
+ out_add_shost_fail:
+ return -ENODEV;
+}
+
+#ifdef CONFIG_PM
+/**
+ * scsih_suspend - power management suspend main entry point
+ * @pdev: PCI device struct
+ * @state: PM state change to (usually PCI_D3)
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+scsih_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ u32 device_state;
+
+ flush_scheduled_work();
+ scsi_block_requests(shost);
+ device_state = pci_choose_state(pdev, state);
+ printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, entering "
+ "operating state [D%d]\n", ioc->name, pdev,
+ pci_name(pdev), device_state);
+
+ mpt2sas_base_free_resources(ioc);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, device_state);
+ return 0;
+}
+
+/**
+ * scsih_resume - power management resume main entry point
+ * @pdev: PCI device struct
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+scsih_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ u32 device_state = pdev->current_state;
+ int r;
+
+ printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, previous "
+ "operating state [D%d]\n", ioc->name, pdev,
+ pci_name(pdev), device_state);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+ ioc->pdev = pdev;
+ r = mpt2sas_base_map_resources(ioc);
+ if (r)
+ return r;
+
+ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET);
+ scsi_unblock_requests(shost);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver scsih_driver = {
+ .name = MPT2SAS_DRIVER_NAME,
+ .id_table = scsih_pci_table,
+ .probe = scsih_probe,
+ .remove = __devexit_p(scsih_remove),
+#ifdef CONFIG_PM
+ .suspend = scsih_suspend,
+ .resume = scsih_resume,
+#endif
+};
+
+
+/**
+ * scsih_init - main entry point for this driver.
+ *
+ * Returns 0 success, anything else error.
+ */
+static int __init
+scsih_init(void)
+{
+ int error;
+
+ mpt_ids = 0;
+ printk(KERN_INFO "%s version %s loaded\n", MPT2SAS_DRIVER_NAME,
+ MPT2SAS_DRIVER_VERSION);
+
+ mpt2sas_transport_template =
+ sas_attach_transport(&mpt2sas_transport_functions);
+ if (!mpt2sas_transport_template)
+ return -ENODEV;
+
+ mpt2sas_base_initialize_callback_handler();
+
+ /* queuecommand callback hander */
+ scsi_io_cb_idx = mpt2sas_base_register_callback_handler(scsih_io_done);
+
+ /* task managment callback handler */
+ tm_cb_idx = mpt2sas_base_register_callback_handler(scsih_tm_done);
+
+ /* base internal commands callback handler */
+ base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done);
+
+ /* transport internal commands callback handler */
+ transport_cb_idx = mpt2sas_base_register_callback_handler(
+ mpt2sas_transport_done);
+
+ /* configuration page API internal commands callback handler */
+ config_cb_idx = mpt2sas_base_register_callback_handler(
+ mpt2sas_config_done);
+
+ /* ctl module callback handler */
+ ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done);
+
+ mpt2sas_ctl_init();
+
+ error = pci_register_driver(&scsih_driver);
+ if (error)
+ sas_release_transport(mpt2sas_transport_template);
+
+ return error;
+}
+
+/**
+ * scsih_exit - exit point for this driver (when it is a module).
+ *
+ * Returns 0 success, anything else error.
+ */
+static void __exit
+scsih_exit(void)
+{
+ printk(KERN_INFO "mpt2sas version %s unloading\n",
+ MPT2SAS_DRIVER_VERSION);
+
+ pci_unregister_driver(&scsih_driver);
+
+ sas_release_transport(mpt2sas_transport_template);
+ mpt2sas_base_release_callback_handler(scsi_io_cb_idx);
+ mpt2sas_base_release_callback_handler(tm_cb_idx);
+ mpt2sas_base_release_callback_handler(base_cb_idx);
+ mpt2sas_base_release_callback_handler(transport_cb_idx);
+ mpt2sas_base_release_callback_handler(config_cb_idx);
+ mpt2sas_base_release_callback_handler(ctl_cb_idx);
+
+ mpt2sas_ctl_exit();
+}
+
+module_init(scsih_init);
+module_exit(scsih_exit);
diff --git a/trunk/drivers/scsi/mpt2sas/mpt2sas_transport.c b/trunk/drivers/scsi/mpt2sas/mpt2sas_transport.c
new file mode 100644
index 000000000000..e03dc0b1e1a0
--- /dev/null
+++ b/trunk/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -0,0 +1,1211 @@
+/*
+ * SAS Transport Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c
+ * Copyright (C) 2007-2008 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include
+#include
+#include
+#include
+#include