You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
[SCSI] aic94xx: new driver
This is the end point of the separate aic94xx driver based on the original driver and transport class from Luben Tuikov <ltuikov@yahoo.com> The log of the separate development is: Alexis Bruemmer: o aic94xx: fix hotplug/unplug for expanderless systems o aic94xx: disable split completion timer/setting by default o aic94xx: wide port off expander support o aic94xx: remove various inline functions o aic94xx: use bitops o aic94xx: remove queue comment o aic94xx: remove sas_common.c o aic94xx: sas remove depot's o aic94xx: use available list_for_each_entry_safe_reverse() o aic94xx: sas header file merge James Bottomley: o aic94xx: fix TF_TMF_NO_CTX processing o aic94xx: convert to request_firmware interface o aic94xx: fix hotplug/unplug o aic94xx: add link error counts to the expander phys o aic94xx: add transport class phy reset capability o aic94xx: remove local_attached flag o Remove README o Fixup Makefile variable for libsas rename o Rename sas->libsas o aic94xx: correct return code for sas_discover_event o aic94xx: use parent backlink port o aic94xx: remove channel abstraction o aic94xx: fix routing algorithms o aic94xx: add backlink port o aic94xx: fix cascaded expander properties o aic94xx: fix sleep under lock o aic94xx: fix panic on module removal in complex topology o aic94xx: make use of the new sas_port o rename sas_port to asd_sas_port o Fix for eh_strategy_handler move o aic94xx: move entirely over to correct transport class formulation o remove last vestages of sas_rphy_alloc() o update for eh_timed_out move o Preliminary expander support for aic94xx o sas: remove event thread o minor warning cleanups o remove last vestiges of id mapping arrays o Further updates o Convert aic94xx over entirely to the transport class end device and o update aic94xx/sas to use the new sas transport class end device o [PATCH] aic94xx: attaching to the sas transport class o Add missing completion removal from prior patch o [PATCH] aic94xx: attaching to the sas transport class o Build fixes from akpm Jeff Garzik: o [scsi aic94xx] Remove ->owner from PCI info table Luben Tuikov: o initial aic94xx driver Mike Anderson: o aic94xx: fix panic on module insertion o aic94xx: stub out SATA_DEV case o aic94xx: compile warning cleanups o aic94xx: sas_alloc_task o aic94xx: ref count update o aic94xx nexus loss time value o [PATCH] aic94xx: driver assertion in non-x86 BIOS env Randy Dunlap: o libsas: externs not needed Robert Tarte: o aic94xx: sequence patch - fixes SATA support Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
committed by
James Bottomley
parent
f4ad7b5807
commit
2908d778ab
484
Documentation/scsi/libsas.txt
Normal file
484
Documentation/scsi/libsas.txt
Normal file
@@ -0,0 +1,484 @@
|
||||
SAS Layer
|
||||
---------
|
||||
|
||||
The SAS Layer is a management infrastructure which manages
|
||||
SAS LLDDs. It sits between SCSI Core and SAS LLDDs. The
|
||||
layout is as follows: while SCSI Core is concerned with
|
||||
SAM/SPC issues, and a SAS LLDD+sequencer is concerned with
|
||||
phy/OOB/link management, the SAS layer is concerned with:
|
||||
|
||||
* SAS Phy/Port/HA event management (LLDD generates,
|
||||
SAS Layer processes),
|
||||
* SAS Port management (creation/destruction),
|
||||
* SAS Domain discovery and revalidation,
|
||||
* SAS Domain device management,
|
||||
* SCSI Host registration/unregistration,
|
||||
* Device registration with SCSI Core (SAS) or libata
|
||||
(SATA), and
|
||||
* Expander management and exporting expander control
|
||||
to user space.
|
||||
|
||||
A SAS LLDD is a PCI device driver. It is concerned with
|
||||
phy/OOB management, and vendor specific tasks and generates
|
||||
events to the SAS layer.
|
||||
|
||||
The SAS Layer does most SAS tasks as outlined in the SAS 1.1
|
||||
spec.
|
||||
|
||||
The sas_ha_struct describes the SAS LLDD to the SAS layer.
|
||||
Most of it is used by the SAS Layer but a few fields need to
|
||||
be initialized by the LLDDs.
|
||||
|
||||
After initializing your hardware, from the probe() function
|
||||
you call sas_register_ha(). It will register your LLDD with
|
||||
the SCSI subsystem, creating a SCSI host and it will
|
||||
register your SAS driver with the sysfs SAS tree it creates.
|
||||
It will then return. Then you enable your phys to actually
|
||||
start OOB (at which point your driver will start calling the
|
||||
notify_* event callbacks).
|
||||
|
||||
Structure descriptions:
|
||||
|
||||
struct sas_phy --------------------
|
||||
Normally this is statically embedded to your driver's
|
||||
phy structure:
|
||||
struct my_phy {
|
||||
blah;
|
||||
struct sas_phy sas_phy;
|
||||
bleh;
|
||||
};
|
||||
And then all the phys are an array of my_phy in your HA
|
||||
struct (shown below).
|
||||
|
||||
Then as you go along and initialize your phys you also
|
||||
initialize the sas_phy struct, along with your own
|
||||
phy structure.
|
||||
|
||||
In general, the phys are managed by the LLDD and the ports
|
||||
are managed by the SAS layer. So the phys are initialized
|
||||
and updated by the LLDD and the ports are initialized and
|
||||
updated by the SAS layer.
|
||||
|
||||
There is a scheme where the LLDD can RW certain fields,
|
||||
and the SAS layer can only read such ones, and vice versa.
|
||||
The idea is to avoid unnecessary locking.
|
||||
|
||||
enabled -- must be set (0/1)
|
||||
id -- must be set [0,MAX_PHYS)
|
||||
class, proto, type, role, oob_mode, linkrate -- must be set
|
||||
oob_mode -- you set this when OOB has finished and then notify
|
||||
the SAS Layer.
|
||||
|
||||
sas_addr -- this normally points to an array holding the sas
|
||||
address of the phy, possibly somewhere in your my_phy
|
||||
struct.
|
||||
|
||||
attached_sas_addr -- set this when you (LLDD) receive an
|
||||
IDENTIFY frame or a FIS frame, _before_ notifying the SAS
|
||||
layer. The idea is that sometimes the LLDD may want to fake
|
||||
or provide a different SAS address on that phy/port and this
|
||||
allows it to do this. At best you should copy the sas
|
||||
address from the IDENTIFY frame or maybe generate a SAS
|
||||
address for SATA directly attached devices. The Discover
|
||||
process may later change this.
|
||||
|
||||
frame_rcvd -- this is where you copy the IDENTIFY/FIS frame
|
||||
when you get it; you lock, copy, set frame_rcvd_size and
|
||||
unlock the lock, and then call the event. It is a pointer
|
||||
since there's no way to know your hw frame size _exactly_,
|
||||
so you define the actual array in your phy struct and let
|
||||
this pointer point to it. You copy the frame from your
|
||||
DMAable memory to that area holding the lock.
|
||||
|
||||
sas_prim -- this is where primitives go when they're
|
||||
received. See sas.h. Grab the lock, set the primitive,
|
||||
release the lock, notify.
|
||||
|
||||
port -- this points to the sas_port if the phy belongs
|
||||
to a port -- the LLDD only reads this. It points to the
|
||||
sas_port this phy is part of. Set by the SAS Layer.
|
||||
|
||||
ha -- may be set; the SAS layer sets it anyway.
|
||||
|
||||
lldd_phy -- you should set this to point to your phy so you
|
||||
can find your way around faster when the SAS layer calls one
|
||||
of your callbacks and passes you a phy. If the sas_phy is
|
||||
embedded you can also use container_of -- whatever you
|
||||
prefer.
|
||||
|
||||
|
||||
struct sas_port --------------------
|
||||
The LLDD doesn't set any fields of this struct -- it only
|
||||
reads them. They should be self explanatory.
|
||||
|
||||
phy_mask is 32 bit, this should be enough for now, as I
|
||||
haven't heard of a HA having more than 8 phys.
|
||||
|
||||
lldd_port -- I haven't found use for that -- maybe other
|
||||
LLDD who wish to have internal port representation can make
|
||||
use of this.
|
||||
|
||||
|
||||
struct sas_ha_struct --------------------
|
||||
It normally is statically declared in your own LLDD
|
||||
structure describing your adapter:
|
||||
struct my_sas_ha {
|
||||
blah;
|
||||
struct sas_ha_struct sas_ha;
|
||||
struct my_phy phys[MAX_PHYS];
|
||||
struct sas_port sas_ports[MAX_PHYS]; /* (1) */
|
||||
bleh;
|
||||
};
|
||||
|
||||
(1) If your LLDD doesn't have its own port representation.
|
||||
|
||||
What needs to be initialized (sample function given below).
|
||||
|
||||
pcidev
|
||||
sas_addr -- since the SAS layer doesn't want to mess with
|
||||
memory allocation, etc, this points to statically
|
||||
allocated array somewhere (say in your host adapter
|
||||
structure) and holds the SAS address of the host
|
||||
adapter as given by you or the manufacturer, etc.
|
||||
sas_port
|
||||
sas_phy -- an array of pointers to structures. (see
|
||||
note above on sas_addr).
|
||||
These must be set. See more notes below.
|
||||
num_phys -- the number of phys present in the sas_phy array,
|
||||
and the number of ports present in the sas_port
|
||||
array. There can be a maximum num_phys ports (one per
|
||||
port) so we drop the num_ports, and only use
|
||||
num_phys.
|
||||
|
||||
The event interface:
|
||||
|
||||
/* LLDD calls these to notify the class of an event. */
|
||||
void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event);
|
||||
void (*notify_port_event)(struct sas_phy *, enum port_event);
|
||||
void (*notify_phy_event)(struct sas_phy *, enum phy_event);
|
||||
|
||||
When sas_register_ha() returns, those are set and can be
|
||||
called by the LLDD to notify the SAS layer of such events
|
||||
the SAS layer.
|
||||
|
||||
The port notification:
|
||||
|
||||
/* The class calls these to notify the LLDD of an event. */
|
||||
void (*lldd_port_formed)(struct sas_phy *);
|
||||
void (*lldd_port_deformed)(struct sas_phy *);
|
||||
|
||||
If the LLDD wants notification when a port has been formed
|
||||
or deformed it sets those to a function satisfying the type.
|
||||
|
||||
A SAS LLDD should also implement at least one of the Task
|
||||
Management Functions (TMFs) described in SAM:
|
||||
|
||||
/* Task Management Functions. Must be called from process context. */
|
||||
int (*lldd_abort_task)(struct sas_task *);
|
||||
int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_clear_aca)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_I_T_nexus_reset)(struct domain_device *);
|
||||
int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_query_task)(struct sas_task *);
|
||||
|
||||
For more information please read SAM from T10.org.
|
||||
|
||||
Port and Adapter management:
|
||||
|
||||
/* Port and Adapter management */
|
||||
int (*lldd_clear_nexus_port)(struct sas_port *);
|
||||
int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
|
||||
|
||||
A SAS LLDD should implement at least one of those.
|
||||
|
||||
Phy management:
|
||||
|
||||
/* Phy management */
|
||||
int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
|
||||
|
||||
lldd_ha -- set this to point to your HA struct. You can also
|
||||
use container_of if you embedded it as shown above.
|
||||
|
||||
A sample initialization and registration function
|
||||
can look like this (called last thing from probe())
|
||||
*but* before you enable the phys to do OOB:
|
||||
|
||||
static int register_sas_ha(struct my_sas_ha *my_ha)
|
||||
{
|
||||
int i;
|
||||
static struct sas_phy *sas_phys[MAX_PHYS];
|
||||
static struct sas_port *sas_ports[MAX_PHYS];
|
||||
|
||||
my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
|
||||
|
||||
for (i = 0; i < MAX_PHYS; i++) {
|
||||
sas_phys[i] = &my_ha->phys[i].sas_phy;
|
||||
sas_ports[i] = &my_ha->sas_ports[i];
|
||||
}
|
||||
|
||||
my_ha->sas_ha.sas_phy = sas_phys;
|
||||
my_ha->sas_ha.sas_port = sas_ports;
|
||||
my_ha->sas_ha.num_phys = MAX_PHYS;
|
||||
|
||||
my_ha->sas_ha.lldd_port_formed = my_port_formed;
|
||||
|
||||
my_ha->sas_ha.lldd_dev_found = my_dev_found;
|
||||
my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
|
||||
|
||||
my_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num; (1)
|
||||
|
||||
my_ha->sas_ha.lldd_queue_size = ha_can_queue;
|
||||
my_ha->sas_ha.lldd_execute_task = my_execute_task;
|
||||
|
||||
my_ha->sas_ha.lldd_abort_task = my_abort_task;
|
||||
my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
|
||||
my_ha->sas_ha.lldd_clear_aca = my_clear_aca;
|
||||
my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
|
||||
my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
|
||||
my_ha->sas_ha.lldd_lu_reset = my_lu_reset;
|
||||
my_ha->sas_ha.lldd_query_task = my_query_task;
|
||||
|
||||
my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
|
||||
my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
|
||||
|
||||
my_ha->sas_ha.lldd_control_phy = my_control_phy;
|
||||
|
||||
return sas_register_ha(&my_ha->sas_ha);
|
||||
}
|
||||
|
||||
(1) This is normally a LLDD parameter, something of the
|
||||
lines of a task collector. What it tells the SAS Layer is
|
||||
whether the SAS layer should run in Direct Mode (default:
|
||||
value 0 or 1) or Task Collector Mode (value greater than 1).
|
||||
|
||||
In Direct Mode, the SAS Layer calls Execute Task as soon as
|
||||
it has a command to send to the SDS, _and_ this is a single
|
||||
command, i.e. not linked.
|
||||
|
||||
Some hardware (e.g. aic94xx) has the capability to DMA more
|
||||
than one task at a time (interrupt) from host memory. Task
|
||||
Collector Mode is an optional feature for HAs which support
|
||||
this in their hardware. (Again, it is completely optional
|
||||
even if your hardware supports it.)
|
||||
|
||||
In Task Collector Mode, the SAS Layer would do _natural_
|
||||
coalescing of tasks and at the appropriate moment it would
|
||||
call your driver to DMA more than one task in a single HA
|
||||
interrupt. DMBS may want to use this by insmod/modprobe
|
||||
setting the lldd_max_execute_num to something greater than
|
||||
1.
|
||||
|
||||
(2) SAS 1.1 does not define I_T Nexus Reset TMF.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
Events are _the only way_ a SAS LLDD notifies the SAS layer
|
||||
of anything. There is no other method or way a LLDD to tell
|
||||
the SAS layer of anything happening internally or in the SAS
|
||||
domain.
|
||||
|
||||
Phy events:
|
||||
PHYE_LOSS_OF_SIGNAL, (C)
|
||||
PHYE_OOB_DONE,
|
||||
PHYE_OOB_ERROR, (C)
|
||||
PHYE_SPINUP_HOLD.
|
||||
|
||||
Port events, passed on a _phy_:
|
||||
PORTE_BYTES_DMAED, (M)
|
||||
PORTE_BROADCAST_RCVD, (E)
|
||||
PORTE_LINK_RESET_ERR, (C)
|
||||
PORTE_TIMER_EVENT, (C)
|
||||
PORTE_HARD_RESET.
|
||||
|
||||
Host Adapter event:
|
||||
HAE_RESET
|
||||
|
||||
A SAS LLDD should be able to generate
|
||||
- at least one event from group C (choice),
|
||||
- events marked M (mandatory) are mandatory (only one),
|
||||
- events marked E (expander) if it wants the SAS layer
|
||||
to handle domain revalidation (only one such).
|
||||
- Unmarked events are optional.
|
||||
|
||||
Meaning:
|
||||
|
||||
HAE_RESET -- when your HA got internal error and was reset.
|
||||
|
||||
PORTE_BYTES_DMAED -- on receiving an IDENTIFY/FIS frame
|
||||
PORTE_BROADCAST_RCVD -- on receiving a primitive
|
||||
PORTE_LINK_RESET_ERR -- timer expired, loss of signal, loss
|
||||
of DWS, etc. (*)
|
||||
PORTE_TIMER_EVENT -- DWS reset timeout timer expired (*)
|
||||
PORTE_HARD_RESET -- Hard Reset primitive received.
|
||||
|
||||
PHYE_LOSS_OF_SIGNAL -- the device is gone (*)
|
||||
PHYE_OOB_DONE -- OOB went fine and oob_mode is valid
|
||||
PHYE_OOB_ERROR -- Error while doing OOB, the device probably
|
||||
got disconnected. (*)
|
||||
PHYE_SPINUP_HOLD -- SATA is present, COMWAKE not sent.
|
||||
|
||||
(*) should set/clear the appropriate fields in the phy,
|
||||
or alternatively call the inlined sas_phy_disconnected()
|
||||
which is just a helper, from their tasklet.
|
||||
|
||||
The Execute Command SCSI RPC:
|
||||
|
||||
int (*lldd_execute_task)(struct sas_task *, int num,
|
||||
unsigned long gfp_flags);
|
||||
|
||||
Used to queue a task to the SAS LLDD. @task is the tasks to
|
||||
be executed. @num should be the number of tasks being
|
||||
queued at this function call (they are linked listed via
|
||||
task::list), @gfp_mask should be the gfp_mask defining the
|
||||
context of the caller.
|
||||
|
||||
This function should implement the Execute Command SCSI RPC,
|
||||
or if you're sending a SCSI Task as linked commands, you
|
||||
should also use this function.
|
||||
|
||||
That is, when lldd_execute_task() is called, the command(s)
|
||||
go out on the transport *immediately*. There is *no*
|
||||
queuing of any sort and at any level in a SAS LLDD.
|
||||
|
||||
The use of task::list is two-fold, one for linked commands,
|
||||
the other discussed below.
|
||||
|
||||
It is possible to queue up more than one task at a time, by
|
||||
initializing the list element of struct sas_task, and
|
||||
passing the number of tasks enlisted in this manner in num.
|
||||
|
||||
Returns: -SAS_QUEUE_FULL, -ENOMEM, nothing was queued;
|
||||
0, the task(s) were queued.
|
||||
|
||||
If you want to pass num > 1, then either
|
||||
A) you're the only caller of this function and keep track
|
||||
of what you've queued to the LLDD, or
|
||||
B) you know what you're doing and have a strategy of
|
||||
retrying.
|
||||
|
||||
As opposed to queuing one task at a time (function call),
|
||||
batch queuing of tasks, by having num > 1, greatly
|
||||
simplifies LLDD code, sequencer code, and _hardware design_,
|
||||
and has some performance advantages in certain situations
|
||||
(DBMS).
|
||||
|
||||
The LLDD advertises if it can take more than one command at
|
||||
a time at lldd_execute_task(), by setting the
|
||||
lldd_max_execute_num parameter (controlled by "collector"
|
||||
module parameter in aic94xx SAS LLDD).
|
||||
|
||||
You should leave this to the default 1, unless you know what
|
||||
you're doing.
|
||||
|
||||
This is a function of the LLDD, to which the SAS layer can
|
||||
cater to.
|
||||
|
||||
int lldd_queue_size
|
||||
The host adapter's queue size. This is the maximum
|
||||
number of commands the lldd can have pending to domain
|
||||
devices on behalf of all upper layers submitting through
|
||||
lldd_execute_task().
|
||||
|
||||
You really want to set this to something (much) larger than
|
||||
1.
|
||||
|
||||
This _really_ has absolutely nothing to do with queuing.
|
||||
There is no queuing in SAS LLDDs.
|
||||
|
||||
struct sas_task {
|
||||
dev -- the device this task is destined to
|
||||
list -- must be initialized (INIT_LIST_HEAD)
|
||||
task_proto -- _one_ of enum sas_proto
|
||||
scatter -- pointer to scatter gather list array
|
||||
num_scatter -- number of elements in scatter
|
||||
total_xfer_len -- total number of bytes expected to be transfered
|
||||
data_dir -- PCI_DMA_...
|
||||
task_done -- callback when the task has finished execution
|
||||
};
|
||||
|
||||
When an external entity, entity other than the LLDD or the
|
||||
SAS Layer, wants to work with a struct domain_device, it
|
||||
_must_ call kobject_get() when getting a handle on the
|
||||
device and kobject_put() when it is done with the device.
|
||||
|
||||
This does two things:
|
||||
A) implements proper kfree() for the device;
|
||||
B) increments/decrements the kref for all players:
|
||||
domain_device
|
||||
all domain_device's ... (if past an expander)
|
||||
port
|
||||
host adapter
|
||||
pci device
|
||||
and up the ladder, etc.
|
||||
|
||||
DISCOVERY
|
||||
---------
|
||||
|
||||
The sysfs tree has the following purposes:
|
||||
a) It shows you the physical layout of the SAS domain at
|
||||
the current time, i.e. how the domain looks in the
|
||||
physical world right now.
|
||||
b) Shows some device parameters _at_discovery_time_.
|
||||
|
||||
This is a link to the tree(1) program, very useful in
|
||||
viewing the SAS domain:
|
||||
ftp://mama.indstate.edu/linux/tree/
|
||||
I expect user space applications to actually create a
|
||||
graphical interface of this.
|
||||
|
||||
That is, the sysfs domain tree doesn't show or keep state if
|
||||
you e.g., change the meaning of the READY LED MEANING
|
||||
setting, but it does show you the current connection status
|
||||
of the domain device.
|
||||
|
||||
Keeping internal device state changes is responsibility of
|
||||
upper layers (Command set drivers) and user space.
|
||||
|
||||
When a device or devices are unplugged from the domain, this
|
||||
is reflected in the sysfs tree immediately, and the device(s)
|
||||
removed from the system.
|
||||
|
||||
The structure domain_device describes any device in the SAS
|
||||
domain. It is completely managed by the SAS layer. A task
|
||||
points to a domain device, this is how the SAS LLDD knows
|
||||
where to send the task(s) to. A SAS LLDD only reads the
|
||||
contents of the domain_device structure, but it never creates
|
||||
or destroys one.
|
||||
|
||||
Expander management from User Space
|
||||
-----------------------------------
|
||||
|
||||
In each expander directory in sysfs, there is a file called
|
||||
"smp_portal". It is a binary sysfs attribute file, which
|
||||
implements an SMP portal (Note: this is *NOT* an SMP port),
|
||||
to which user space applications can send SMP requests and
|
||||
receive SMP responses.
|
||||
|
||||
Functionality is deceptively simple:
|
||||
|
||||
1. Build the SMP frame you want to send. The format and layout
|
||||
is described in the SAS spec. Leave the CRC field equal 0.
|
||||
open(2)
|
||||
2. Open the expander's SMP portal sysfs file in RW mode.
|
||||
write(2)
|
||||
3. Write the frame you built in 1.
|
||||
read(2)
|
||||
4. Read the amount of data you expect to receive for the frame you built.
|
||||
If you receive different amount of data you expected to receive,
|
||||
then there was some kind of error.
|
||||
close(2)
|
||||
All this process is shown in detail in the function do_smp_func()
|
||||
and its callers, in the file "expander_conf.c".
|
||||
|
||||
The kernel functionality is implemented in the file
|
||||
"sas_expander.c".
|
||||
|
||||
The program "expander_conf.c" implements this. It takes one
|
||||
argument, the sysfs file name of the SMP portal to the
|
||||
expander, and gives expander information, including routing
|
||||
tables.
|
||||
|
||||
The SMP portal gives you complete control of the expander,
|
||||
so please be careful.
|
||||
@@ -209,7 +209,7 @@ config SCSI_LOGGING
|
||||
there should be no noticeable performance impact as long as you have
|
||||
logging turned off.
|
||||
|
||||
menu "SCSI Transport Attributes"
|
||||
menu "SCSI Transports"
|
||||
depends on SCSI
|
||||
|
||||
config SCSI_SPI_ATTRS
|
||||
@@ -242,6 +242,8 @@ config SCSI_SAS_ATTRS
|
||||
If you wish to export transport-specific information about
|
||||
each attached SAS device to sysfs, say Y.
|
||||
|
||||
source "drivers/scsi/libsas/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "SCSI low-level drivers"
|
||||
@@ -431,6 +433,7 @@ config SCSI_AIC7XXX_OLD
|
||||
module will be called aic7xxx_old.
|
||||
|
||||
source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
|
||||
source "drivers/scsi/aic94xx/Kconfig"
|
||||
|
||||
# All the I2O code and drivers do not seem to be 64bit safe.
|
||||
config SCSI_DPT_I2O
|
||||
|
||||
@@ -32,6 +32,7 @@ obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o
|
||||
obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o
|
||||
obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
|
||||
obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o
|
||||
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
|
||||
|
||||
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
|
||||
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
|
||||
@@ -68,6 +69,7 @@ obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx/
|
||||
obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/
|
||||
obj-$(CONFIG_SCSI_AACRAID) += aacraid/
|
||||
obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
|
||||
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
|
||||
obj-$(CONFIG_SCSI_IPS) += ips.o
|
||||
obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
|
||||
obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
|
||||
|
||||
41
drivers/scsi/aic94xx/Kconfig
Normal file
41
drivers/scsi/aic94xx/Kconfig
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Kernel configuration file for aic94xx SAS/SATA driver.
|
||||
#
|
||||
# Copyright (c) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (c) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This file is part of the aic94xx driver.
|
||||
#
|
||||
# The aic94xx driver is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# The aic94xx driver is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Aic94xx Driver; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
#
|
||||
|
||||
config SCSI_AIC94XX
|
||||
tristate "Adaptec AIC94xx SAS/SATA support"
|
||||
depends on PCI
|
||||
select SCSI_SAS_LIBSAS
|
||||
help
|
||||
This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X
|
||||
AIC94xx chip based host adapters.
|
||||
|
||||
config AIC94XX_DEBUG
|
||||
bool "Compile in debug mode"
|
||||
default y
|
||||
depends on SCSI_AIC94XX
|
||||
help
|
||||
Compiles the aic94xx driver in debug mode. In debug mode,
|
||||
the driver prints some messages to the console.
|
||||
39
drivers/scsi/aic94xx/Makefile
Normal file
39
drivers/scsi/aic94xx/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Makefile for Adaptec aic94xx SAS/SATA driver.
|
||||
#
|
||||
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This file is part of the the aic94xx driver.
|
||||
#
|
||||
# The aic94xx driver is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# The aic94xx driver is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with the aic94xx driver; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
ifeq ($(CONFIG_AIC94XX_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DASD_DEBUG -DASD_ENTER_EXIT
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
|
||||
aic94xx-y += aic94xx_init.o \
|
||||
aic94xx_hwi.o \
|
||||
aic94xx_reg.o \
|
||||
aic94xx_sds.o \
|
||||
aic94xx_seq.o \
|
||||
aic94xx_dump.o \
|
||||
aic94xx_scb.o \
|
||||
aic94xx_dev.o \
|
||||
aic94xx_tmf.o \
|
||||
aic94xx_task.o
|
||||
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* $Id: //depot/aic94xx/aic94xx.h#31 $
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_H_
|
||||
#define _AIC94XX_H_
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
#define ASD_DRIVER_NAME "aic94xx"
|
||||
#define ASD_DRIVER_DESCRIPTION "Adaptec aic94xx SAS/SATA driver"
|
||||
|
||||
#define asd_printk(fmt, ...) printk(KERN_NOTICE ASD_DRIVER_NAME ": " fmt, ## __VA_ARGS__)
|
||||
|
||||
#ifdef ASD_ENTER_EXIT
|
||||
#define ENTER printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
|
||||
__FUNCTION__)
|
||||
#define EXIT printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
|
||||
__FUNCTION__)
|
||||
#else
|
||||
#define ENTER
|
||||
#define EXIT
|
||||
#endif
|
||||
|
||||
#ifdef ASD_DEBUG
|
||||
#define ASD_DPRINTK asd_printk
|
||||
#else
|
||||
#define ASD_DPRINTK(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* 2*ITNL timeout + 1 second */
|
||||
#define AIC94XX_SCB_TIMEOUT (5*HZ)
|
||||
|
||||
extern kmem_cache_t *asd_dma_token_cache;
|
||||
extern kmem_cache_t *asd_ascb_cache;
|
||||
extern char sas_addr_str[2*SAS_ADDR_SIZE + 1];
|
||||
|
||||
static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2)
|
||||
snprintf(p, 3, "%02X", sas_addr[i]);
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
static inline void asd_destringify_sas_addr(u8 *sas_addr, const char *p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++) {
|
||||
u8 h, l;
|
||||
if (!*p)
|
||||
break;
|
||||
h = isdigit(*p) ? *p-'0' : *p-'A'+10;
|
||||
p++;
|
||||
l = isdigit(*p) ? *p-'0' : *p-'A'+10;
|
||||
p++;
|
||||
sas_addr[i] = (h<<4) | l;
|
||||
}
|
||||
}
|
||||
|
||||
struct asd_ha_struct;
|
||||
struct asd_ascb;
|
||||
|
||||
int asd_read_ocm(struct asd_ha_struct *asd_ha);
|
||||
int asd_read_flash(struct asd_ha_struct *asd_ha);
|
||||
|
||||
int asd_dev_found(struct domain_device *dev);
|
||||
void asd_dev_gone(struct domain_device *dev);
|
||||
|
||||
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
|
||||
|
||||
int asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags);
|
||||
|
||||
/* ---------- TMFs ---------- */
|
||||
int asd_abort_task(struct sas_task *);
|
||||
int asd_abort_task_set(struct domain_device *, u8 *lun);
|
||||
int asd_clear_aca(struct domain_device *, u8 *lun);
|
||||
int asd_clear_task_set(struct domain_device *, u8 *lun);
|
||||
int asd_lu_reset(struct domain_device *, u8 *lun);
|
||||
int asd_query_task(struct sas_task *);
|
||||
|
||||
/* ---------- Adapter and Port management ---------- */
|
||||
int asd_clear_nexus_port(struct asd_sas_port *port);
|
||||
int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
|
||||
|
||||
/* ---------- Phy Management ---------- */
|
||||
int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func);
|
||||
|
||||
#endif
|
||||
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA DDB management
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* $Id: //depot/aic94xx/aic94xx_dev.c#21 $
|
||||
*/
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_sas.h"
|
||||
|
||||
#define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \
|
||||
(_ha)->hw_prof.max_ddbs)
|
||||
#define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
|
||||
#define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
|
||||
|
||||
static inline int asd_get_ddb(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ddb, i;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
ddb = FIND_FREE_DDB(asd_ha);
|
||||
if (ddb >= asd_ha->hw_prof.max_ddbs) {
|
||||
ddb = -ENOMEM;
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
goto out;
|
||||
}
|
||||
SET_DDB(ddb, asd_ha);
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
|
||||
for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
|
||||
asd_ddbsite_write_dword(asd_ha, ddb, i, 0);
|
||||
out:
|
||||
return ddb;
|
||||
}
|
||||
|
||||
#define INIT_CONN_TAG offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag)
|
||||
#define DEST_SAS_ADDR offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr)
|
||||
#define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head)
|
||||
#define DDB_TYPE offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type)
|
||||
#define CONN_MASK offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask)
|
||||
#define DDB_TARG_FLAGS offsetof(struct asd_ddb_ssp_smp_target_port, flags)
|
||||
#define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2)
|
||||
#define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail)
|
||||
#define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail)
|
||||
#define SISTER_DDB offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb)
|
||||
#define MAX_CCONN offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn)
|
||||
#define NUM_CTX offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts)
|
||||
#define ATA_CMD_SCBPTR offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr)
|
||||
#define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask)
|
||||
#define NUM_SATA_TAGS offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags)
|
||||
#define SATA_STATUS offsetof(struct asd_ddb_stp_sata_target_port, sata_status)
|
||||
#define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr)
|
||||
#define ITNL_TIMEOUT offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout)
|
||||
|
||||
static inline void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!ddb || ddb >= 0xFFFF)
|
||||
return;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED);
|
||||
spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
CLEAR_DDB(ddb, asd_ha);
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
}
|
||||
|
||||
static inline void asd_set_ddb_type(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
|
||||
if (dev->dev_type == SATA_PM_PORT)
|
||||
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT);
|
||||
else if (dev->tproto)
|
||||
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET);
|
||||
else
|
||||
asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR);
|
||||
}
|
||||
|
||||
static int asd_init_sata_tag_ddb(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb, i;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
|
||||
SISTER_DDB, ddb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int asd_init_sata(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
u32 qdepth = 0;
|
||||
int res = 0;
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
|
||||
if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
|
||||
dev->sata_dev.identify_device &&
|
||||
dev->sata_dev.identify_device[10] != 0) {
|
||||
u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
|
||||
u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
|
||||
|
||||
if (w76 & 0x100) /* NCQ? */
|
||||
qdepth = (w75 & 0x1F) + 1;
|
||||
asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
|
||||
(1<<qdepth)-1);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
|
||||
}
|
||||
if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
|
||||
dev->dev_type == SATA_PM_PORT) {
|
||||
struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
|
||||
dev->frame_rcvd;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
|
||||
}
|
||||
asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
|
||||
if (qdepth > 0)
|
||||
res = asd_init_sata_tag_ddb(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int asd_init_target_ddb(struct domain_device *dev)
|
||||
{
|
||||
int ddb, i;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
u8 flags = 0;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
dev->lldd_dev = (void *) (unsigned long) ddb;
|
||||
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, 1, 0);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF);
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++)
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i,
|
||||
dev->sas_addr[i]);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF);
|
||||
asd_set_ddb_type(dev);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask);
|
||||
if (dev->port->oob_mode != SATA_OOB_MODE) {
|
||||
flags |= OPEN_REQUIRED;
|
||||
if ((dev->dev_type == SATA_DEV) ||
|
||||
(dev->tproto & SAS_PROTO_STP)) {
|
||||
struct smp_resp *rps_resp = &dev->sata_dev.rps_resp;
|
||||
if (rps_resp->frame_type == SMP_RESPONSE &&
|
||||
rps_resp->function == SMP_REPORT_PHY_SATA &&
|
||||
rps_resp->result == SMP_RESP_FUNC_ACC) {
|
||||
if (rps_resp->rps.affil_valid)
|
||||
flags |= STP_AFFIL_POL;
|
||||
if (rps_resp->rps.affil_supp)
|
||||
flags |= SUPPORTS_AFFIL;
|
||||
}
|
||||
} else {
|
||||
flags |= CONCURRENT_CONN_SUPP;
|
||||
if (!dev->parent &&
|
||||
(dev->dev_type == EDGE_DEV ||
|
||||
dev->dev_type == FANOUT_DEV))
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
|
||||
4);
|
||||
else
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
|
||||
dev->pathways);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
|
||||
}
|
||||
}
|
||||
if (dev->dev_type == SATA_PM)
|
||||
flags |= SATA_MULTIPORT;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
|
||||
|
||||
flags = 0;
|
||||
if (dev->tproto & SAS_PROTO_STP)
|
||||
flags |= STP_CL_POL_NO_TX;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
|
||||
|
||||
if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTO_STP)) {
|
||||
i = asd_init_sata(dev);
|
||||
if (i < 0) {
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->dev_type == SAS_END_DEV) {
|
||||
struct sas_end_device *rdev = rphy_to_end_device(dev->rphy);
|
||||
if (rdev->I_T_nexus_loss_timeout > 0)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
|
||||
min(rdev->I_T_nexus_loss_timeout,
|
||||
(u16)ITNL_TIMEOUT_CONST));
|
||||
else
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
|
||||
(u16)ITNL_TIMEOUT_CONST);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asd_init_sata_pm_table_ddb(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb, i;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
for (i = 0; i < 32; i += 2)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
|
||||
SISTER_DDB, ddb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags)
|
||||
#define PARENT_DDB offsetof(struct asd_ddb_sata_pm_port, parent_ddb)
|
||||
|
||||
/**
|
||||
* asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port
|
||||
* dev: pointer to domain device
|
||||
*
|
||||
* For SATA Port Multiplier Ports we need to allocate one SATA Port
|
||||
* Multiplier Port DDB and depending on whether the target on it
|
||||
* supports SATA II NCQ, one SATA Tag DDB.
|
||||
*/
|
||||
static int asd_init_sata_pm_port_ddb(struct domain_device *dev)
|
||||
{
|
||||
int ddb, i, parent_ddb, pmtable_ddb;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
u8 flags;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
asd_set_ddb_type(dev);
|
||||
flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
|
||||
asd_init_sata(dev);
|
||||
|
||||
parent_ddb = (int) (unsigned long) dev->parent->lldd_dev;
|
||||
asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb);
|
||||
pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB);
|
||||
asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb);
|
||||
|
||||
if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) {
|
||||
i = asd_init_sata_tag_ddb(dev);
|
||||
if (i < 0) {
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asd_init_initiator_ddb(struct domain_device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_init_sata_pm_ddb -- SATA Port Multiplier
|
||||
* dev: pointer to domain device
|
||||
*
|
||||
* For STP and direct-attached SATA Port Multipliers we need
|
||||
* one target port DDB entry and one SATA PM table DDB entry.
|
||||
*/
|
||||
static int asd_init_sata_pm_ddb(struct domain_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res = asd_init_target_ddb(dev);
|
||||
if (res)
|
||||
goto out;
|
||||
res = asd_init_sata_pm_table_ddb(dev);
|
||||
if (res)
|
||||
asd_free_ddb(dev->port->ha->lldd_ha,
|
||||
(int) (unsigned long) dev->lldd_dev);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_dev_found(struct domain_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
switch (dev->dev_type) {
|
||||
case SATA_PM:
|
||||
res = asd_init_sata_pm_ddb(dev);
|
||||
break;
|
||||
case SATA_PM_PORT:
|
||||
res = asd_init_sata_pm_port_ddb(dev);
|
||||
break;
|
||||
default:
|
||||
if (dev->tproto)
|
||||
res = asd_init_target_ddb(dev);
|
||||
else
|
||||
res = asd_init_initiator_ddb(dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void asd_dev_gone(struct domain_device *dev)
|
||||
{
|
||||
int ddb, sister_ddb;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
|
||||
ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB);
|
||||
|
||||
if (sister_ddb != 0xFFFF)
|
||||
asd_free_ddb(asd_ha, sister_ddb);
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
dev->lldd_dev = NULL;
|
||||
}
|
||||
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
File diff suppressed because it is too large
Load Diff
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver dump header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_DUMP_H_
|
||||
#define _AIC94XX_DUMP_H_
|
||||
|
||||
#ifdef ASD_DEBUG
|
||||
|
||||
void asd_dump_ddb_0(struct asd_ha_struct *asd_ha);
|
||||
void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no);
|
||||
void asd_dump_scb_sites(struct asd_ha_struct *asd_ha);
|
||||
void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
void asd_dump_frame_rcvd(struct asd_phy *phy,
|
||||
struct done_list_struct *dl);
|
||||
void asd_dump_scb_list(struct asd_ascb *ascb, int num);
|
||||
#else /* ASD_DEBUG */
|
||||
|
||||
static inline void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) { }
|
||||
static inline void asd_dump_target_ddb(struct asd_ha_struct *asd_ha,
|
||||
u16 site_no) { }
|
||||
static inline void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) { }
|
||||
static inline void asd_dump_seq_state(struct asd_ha_struct *asd_ha,
|
||||
u8 lseq_mask) { }
|
||||
static inline void asd_dump_frame_rcvd(struct asd_phy *phy,
|
||||
struct done_list_struct *dl) { }
|
||||
static inline void asd_dump_scb_list(struct asd_ascb *ascb, int num) { }
|
||||
#endif /* ASD_DEBUG */
|
||||
|
||||
#endif /* _AIC94XX_DUMP_H_ */
|
||||
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
File diff suppressed because it is too large
Load Diff
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver hardware interface header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_HWI_H_
|
||||
#define _AIC94XX_HWI_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
|
||||
/* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */
|
||||
#define ASD_MAX_PHYS 8
|
||||
#define ASD_PCBA_SN_SIZE 12
|
||||
|
||||
/* Those are to be further named properly, the "RAZORx" part, and
|
||||
* subsequently included in include/linux/pci_ids.h.
|
||||
*/
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR10 0x410
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR12 0x412
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR1E 0x41E
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR30 0x430
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR32 0x432
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3E 0x43E
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3F 0x43F
|
||||
|
||||
struct asd_ha_addrspace {
|
||||
void __iomem *addr;
|
||||
unsigned long start; /* pci resource start */
|
||||
unsigned long len; /* pci resource len */
|
||||
unsigned long flags; /* pci resource flags */
|
||||
|
||||
/* addresses internal to the host adapter */
|
||||
u32 swa_base; /* mmspace 1 (MBAR1) uses this only */
|
||||
u32 swb_base;
|
||||
u32 swc_base;
|
||||
};
|
||||
|
||||
struct bios_struct {
|
||||
int present;
|
||||
u8 maj;
|
||||
u8 min;
|
||||
u32 bld;
|
||||
};
|
||||
|
||||
struct unit_element_struct {
|
||||
u16 num;
|
||||
u16 size;
|
||||
void *area;
|
||||
};
|
||||
|
||||
struct flash_struct {
|
||||
u32 bar;
|
||||
int present;
|
||||
int wide;
|
||||
u8 manuf;
|
||||
u8 dev_id;
|
||||
u8 sec_prot;
|
||||
|
||||
u32 dir_offs;
|
||||
};
|
||||
|
||||
struct asd_phy_desc {
|
||||
/* From CTRL-A settings, then set to what is appropriate */
|
||||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
u8 max_sas_lrate;
|
||||
u8 min_sas_lrate;
|
||||
u8 max_sata_lrate;
|
||||
u8 min_sata_lrate;
|
||||
u8 flags;
|
||||
#define ASD_CRC_DIS 1
|
||||
#define ASD_SATA_SPINUP_HOLD 2
|
||||
|
||||
u8 phy_control_0; /* mode 5 reg 0x160 */
|
||||
u8 phy_control_1; /* mode 5 reg 0x161 */
|
||||
u8 phy_control_2; /* mode 5 reg 0x162 */
|
||||
u8 phy_control_3; /* mode 5 reg 0x163 */
|
||||
};
|
||||
|
||||
struct asd_dma_tok {
|
||||
void *vaddr;
|
||||
dma_addr_t dma_handle;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct hw_profile {
|
||||
struct bios_struct bios;
|
||||
struct unit_element_struct ue;
|
||||
struct flash_struct flash;
|
||||
|
||||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
char pcba_sn[ASD_PCBA_SN_SIZE+1];
|
||||
|
||||
u8 enabled_phys; /* mask of enabled phys */
|
||||
struct asd_phy_desc phy_desc[ASD_MAX_PHYS];
|
||||
u32 max_scbs; /* absolute sequencer scb queue size */
|
||||
struct asd_dma_tok *scb_ext;
|
||||
u32 max_ddbs;
|
||||
struct asd_dma_tok *ddb_ext;
|
||||
|
||||
spinlock_t ddb_lock;
|
||||
void *ddb_bitmap;
|
||||
|
||||
int num_phys; /* ENABLEABLE */
|
||||
int max_phys; /* REPORTED + ENABLEABLE */
|
||||
|
||||
unsigned addr_range; /* max # of addrs; max # of possible ports */
|
||||
unsigned port_name_base;
|
||||
unsigned dev_name_base;
|
||||
unsigned sata_name_base;
|
||||
};
|
||||
|
||||
struct asd_ascb {
|
||||
struct list_head list;
|
||||
struct asd_ha_struct *ha;
|
||||
|
||||
struct scb *scb; /* equals dma_scb->vaddr */
|
||||
struct asd_dma_tok dma_scb;
|
||||
struct asd_dma_tok *sg_arr;
|
||||
|
||||
void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *);
|
||||
u8 uldd_timer:1;
|
||||
|
||||
/* internally generated command */
|
||||
struct timer_list timer;
|
||||
struct completion completion;
|
||||
u8 tag_valid:1;
|
||||
__be16 tag; /* error recovery only */
|
||||
|
||||
/* If this is an Empty SCB, index of first edb in seq->edb_arr. */
|
||||
int edb_index;
|
||||
|
||||
/* Used by the timer timeout function. */
|
||||
int tc_index;
|
||||
|
||||
void *uldd_task;
|
||||
};
|
||||
|
||||
#define ASD_DL_SIZE_BITS 0x8
|
||||
#define ASD_DL_SIZE (1<<(2+ASD_DL_SIZE_BITS))
|
||||
#define ASD_DEF_DL_TOGGLE 0x01
|
||||
|
||||
struct asd_seq_data {
|
||||
spinlock_t pend_q_lock;
|
||||
u16 scbpro;
|
||||
int pending;
|
||||
struct list_head pend_q;
|
||||
int can_queue; /* per adapter */
|
||||
struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */
|
||||
|
||||
spinlock_t tc_index_lock;
|
||||
void **tc_index_array;
|
||||
void *tc_index_bitmap;
|
||||
int tc_index_bitmap_bits;
|
||||
|
||||
struct tasklet_struct dl_tasklet;
|
||||
struct done_list_struct *dl; /* array of done list entries, equals */
|
||||
struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */
|
||||
int dl_toggle;
|
||||
int dl_next;
|
||||
|
||||
int num_edbs;
|
||||
struct asd_dma_tok **edb_arr;
|
||||
int num_escbs;
|
||||
struct asd_ascb **escb_arr; /* array of pointers to escbs */
|
||||
};
|
||||
|
||||
/* This is the Host Adapter structure. It describes the hardware
|
||||
* SAS adapter.
|
||||
*/
|
||||
struct asd_ha_struct {
|
||||
struct pci_dev *pcidev;
|
||||
const char *name;
|
||||
|
||||
struct sas_ha_struct sas_ha;
|
||||
|
||||
u8 revision_id;
|
||||
|
||||
int iospace;
|
||||
spinlock_t iolock;
|
||||
struct asd_ha_addrspace io_handle[2];
|
||||
|
||||
struct hw_profile hw_prof;
|
||||
|
||||
struct asd_phy phys[ASD_MAX_PHYS];
|
||||
struct asd_sas_port ports[ASD_MAX_PHYS];
|
||||
|
||||
struct dma_pool *scb_pool;
|
||||
|
||||
struct asd_seq_data seq; /* sequencer related */
|
||||
};
|
||||
|
||||
/* ---------- Common macros ---------- */
|
||||
|
||||
#define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle))
|
||||
#define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8) \
|
||||
? ((u32)((__dma_handle) >> 32)) \
|
||||
: ((u32)0))
|
||||
|
||||
#define dev_to_asd_ha(__dev) pci_get_drvdata(to_pci_dev(__dev))
|
||||
#define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF \
|
||||
&& ((__site_no) & 0xF0FF) > 0x001F)
|
||||
/* For each bit set in __lseq_mask, set __lseq to equal the bit
|
||||
* position of the set bit and execute the statement following.
|
||||
* __mc is the temporary mask, used as a mask "counter".
|
||||
*/
|
||||
#define for_each_sequencer(__lseq_mask, __mc, __lseq) \
|
||||
for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
|
||||
if (((__mc) & 1))
|
||||
#define for_each_phy(__lseq_mask, __mc, __lseq) \
|
||||
for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
|
||||
if (((__mc) & 1))
|
||||
|
||||
#define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I)))
|
||||
|
||||
/* ---------- DMA allocs ---------- */
|
||||
|
||||
static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags)
|
||||
{
|
||||
return kmem_cache_alloc(asd_dma_token_cache, flags);
|
||||
}
|
||||
|
||||
static inline void asd_dmatok_free(struct asd_dma_tok *token)
|
||||
{
|
||||
kmem_cache_free(asd_dma_token_cache, token);
|
||||
}
|
||||
|
||||
static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct *
|
||||
asd_ha, size_t size,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct asd_dma_tok *token = asd_dmatok_alloc(flags);
|
||||
if (token) {
|
||||
token->size = size;
|
||||
token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev,
|
||||
token->size,
|
||||
&token->dma_handle,
|
||||
flags);
|
||||
if (!token->vaddr) {
|
||||
asd_dmatok_free(token);
|
||||
token = NULL;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
static inline void asd_free_coherent(struct asd_ha_struct *asd_ha,
|
||||
struct asd_dma_tok *token)
|
||||
{
|
||||
if (token) {
|
||||
dma_free_coherent(&asd_ha->pcidev->dev, token->size,
|
||||
token->vaddr, token->dma_handle);
|
||||
asd_dmatok_free(token);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void asd_init_ascb(struct asd_ha_struct *asd_ha,
|
||||
struct asd_ascb *ascb)
|
||||
{
|
||||
INIT_LIST_HEAD(&ascb->list);
|
||||
ascb->scb = ascb->dma_scb.vaddr;
|
||||
ascb->ha = asd_ha;
|
||||
ascb->timer.function = NULL;
|
||||
init_timer(&ascb->timer);
|
||||
ascb->tc_index = -1;
|
||||
init_completion(&ascb->completion);
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline void asd_tc_index_release(struct asd_seq_data *seq, int index)
|
||||
{
|
||||
seq->tc_index_array[index] = NULL;
|
||||
clear_bit(index, seq->tc_index_bitmap);
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr)
|
||||
{
|
||||
int index;
|
||||
|
||||
index = find_first_zero_bit(seq->tc_index_bitmap,
|
||||
seq->tc_index_bitmap_bits);
|
||||
if (index == seq->tc_index_bitmap_bits)
|
||||
return -1;
|
||||
|
||||
seq->tc_index_array[index] = ptr;
|
||||
set_bit(index, seq->tc_index_bitmap);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
|
||||
{
|
||||
return seq->tc_index_array[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ascb_free -- free a single aSCB after is has completed
|
||||
* @ascb: pointer to the aSCB of interest
|
||||
*
|
||||
* This frees an aSCB after it has been executed/completed by
|
||||
* the sequencer.
|
||||
*/
|
||||
static inline void asd_ascb_free(struct asd_ascb *ascb)
|
||||
{
|
||||
if (ascb) {
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
unsigned long flags;
|
||||
|
||||
BUG_ON(!list_empty(&ascb->list));
|
||||
spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags);
|
||||
asd_tc_index_release(&ascb->ha->seq, ascb->tc_index);
|
||||
spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags);
|
||||
dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
|
||||
ascb->dma_scb.dma_handle);
|
||||
kmem_cache_free(asd_ascb_cache, ascb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ascb_list_free -- free a list of ascbs
|
||||
* @ascb_list: a list of ascbs
|
||||
*
|
||||
* This function will free a list of ascbs allocated by asd_ascb_alloc_list.
|
||||
* It is used when say the scb queueing function returned QUEUE_FULL,
|
||||
* and we do not need the ascbs any more.
|
||||
*/
|
||||
static inline void asd_ascb_free_list(struct asd_ascb *ascb_list)
|
||||
{
|
||||
LIST_HEAD(list);
|
||||
struct list_head *n, *pos;
|
||||
|
||||
__list_add(&list, ascb_list->list.prev, &ascb_list->list);
|
||||
list_for_each_safe(pos, n, &list) {
|
||||
list_del_init(pos);
|
||||
asd_ascb_free(list_entry(pos, struct asd_ascb, list));
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Function declarations ---------- */
|
||||
|
||||
int asd_init_hw(struct asd_ha_struct *asd_ha);
|
||||
irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs);
|
||||
|
||||
|
||||
struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
|
||||
*asd_ha, int *num,
|
||||
unsigned int gfp_mask);
|
||||
|
||||
int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
|
||||
int num);
|
||||
int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
|
||||
int num);
|
||||
|
||||
int asd_init_post_escbs(struct asd_ha_struct *asd_ha);
|
||||
void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc);
|
||||
void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
|
||||
void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
|
||||
int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask);
|
||||
void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
|
||||
u8 subfunc);
|
||||
|
||||
void asd_ascb_timedout(unsigned long data);
|
||||
int asd_chip_hardrst(struct asd_ha_struct *asd_ha);
|
||||
|
||||
#endif
|
||||
860
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
860
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
File diff suppressed because it is too large
Load Diff
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver register access.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx.h"
|
||||
|
||||
/* Writing to device address space.
|
||||
* Offset comes before value to remind that the operation of
|
||||
* this function is *offs = val.
|
||||
*/
|
||||
static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u8 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outb(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writeb(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u16 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outw(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writew(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u32 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outl(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writel(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/* Reading from device address space.
|
||||
*/
|
||||
static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u8 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inb((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readb(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u16 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inw((unsigned long)asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readw(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u32 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inl((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readl(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swa(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swc(void)
|
||||
{
|
||||
return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swb(void)
|
||||
{
|
||||
return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
|
||||
}
|
||||
|
||||
/* We know that the register wanted is in the range
|
||||
* of the sliding window.
|
||||
*/
|
||||
#define ASD_READ_SW(ww, type, ord) \
|
||||
static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
return asd_read_##ord (asd_ha, (unsigned long) map_offs); \
|
||||
}
|
||||
|
||||
#define ASD_WRITE_SW(ww, type, ord) \
|
||||
static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
asd_write_##ord (asd_ha, (unsigned long) map_offs, val); \
|
||||
}
|
||||
|
||||
ASD_READ_SW(swa, u8, byte);
|
||||
ASD_READ_SW(swa, u16, word);
|
||||
ASD_READ_SW(swa, u32, dword);
|
||||
|
||||
ASD_READ_SW(swb, u8, byte);
|
||||
ASD_READ_SW(swb, u16, word);
|
||||
ASD_READ_SW(swb, u32, dword);
|
||||
|
||||
ASD_READ_SW(swc, u8, byte);
|
||||
ASD_READ_SW(swc, u16, word);
|
||||
ASD_READ_SW(swc, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swa, u8, byte);
|
||||
ASD_WRITE_SW(swa, u16, word);
|
||||
ASD_WRITE_SW(swa, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swb, u8, byte);
|
||||
ASD_WRITE_SW(swb, u16, word);
|
||||
ASD_WRITE_SW(swb, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swc, u8, byte);
|
||||
ASD_WRITE_SW(swc, u16, word);
|
||||
ASD_WRITE_SW(swc, u32, dword);
|
||||
|
||||
/*
|
||||
* A word about sliding windows:
|
||||
* MBAR0 is divided into sliding windows A, C and B, in that order.
|
||||
* SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
|
||||
* SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
|
||||
* From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
|
||||
* SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
|
||||
* See asd_init_sw() in aic94xx_hwi.c
|
||||
*
|
||||
* We map the most common registers we'd access of the internal 4GB
|
||||
* host adapter memory space. If a register/internal memory location
|
||||
* is wanted which is not mapped, we slide SWB, by paging it,
|
||||
* see asd_move_swb() in aic94xx_reg.c.
|
||||
*/
|
||||
|
||||
/**
|
||||
* asd_move_swb -- move sliding window B
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @reg: register desired to be within range of the new window
|
||||
*/
|
||||
static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
u32 base = reg & ~(MBAR0_SWB_SIZE-1);
|
||||
pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
|
||||
asd_ha->io_handle[0].swb_base = base;
|
||||
}
|
||||
|
||||
static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
asd_write_swa_byte (asd_ha, reg,val);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
asd_write_swc_byte (asd_ha, reg, val);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
#define ASD_WRITE_REG(type, ord) \
|
||||
void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
asd_write_swa_##ord (asd_ha, reg,val); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
asd_write_swc_##ord (asd_ha, reg, val); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
}
|
||||
|
||||
ASD_WRITE_REG(u8, byte);
|
||||
ASD_WRITE_REG(u16,word);
|
||||
ASD_WRITE_REG(u32,dword);
|
||||
|
||||
static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
u8 val;
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
val = asd_read_swa_byte (asd_ha, reg);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
val = asd_read_swc_byte (asd_ha, reg);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
#define ASD_READ_REG(type, ord) \
|
||||
type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
type val; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
val = asd_read_swa_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
val = asd_read_swc_##ord (asd_ha, reg); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_REG(u8, byte);
|
||||
ASD_READ_REG(u16,word);
|
||||
ASD_READ_REG(u32,dword);
|
||||
|
||||
/**
|
||||
* asd_read_reg_string -- read a string of bytes from io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @dst: pointer to a destination buffer where data will be written to
|
||||
* @offs: start offset (register) to read from
|
||||
* @count: number of bytes to read
|
||||
*/
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = dst;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
*p = __asd_read_reg_byte(asd_ha, offs);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_write_reg_string -- write a string of bytes to io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @src: pointer to source buffer where data will be read from
|
||||
* @offs: start offset (register) to write to
|
||||
* @count: number of bytes to write
|
||||
*/
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = src;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
__asd_write_reg_byte(asd_ha, offs, *p);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
||||
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver hardware registers definitions.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_REG_H_
|
||||
#define _AIC94XX_REG_H_
|
||||
|
||||
#include <asm/io.h>
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
/* Values */
|
||||
#define AIC9410_DEV_REV_B0 0x8
|
||||
|
||||
/* MBAR0, SWA, SWB, SWC, internal memory space addresses */
|
||||
#define REG_BASE_ADDR 0xB8000000
|
||||
#define REG_BASE_ADDR_CSEQCIO 0xB8002000
|
||||
#define REG_BASE_ADDR_EXSI 0xB8042800
|
||||
|
||||
#define MBAR0_SWA_SIZE 0x58
|
||||
extern u32 MBAR0_SWB_SIZE;
|
||||
#define MBAR0_SWC_SIZE 0x8
|
||||
|
||||
/* MBAR1, points to On Chip Memory */
|
||||
#define OCM_BASE_ADDR 0xA0000000
|
||||
#define OCM_MAX_SIZE 0x20000
|
||||
|
||||
/* Smallest address possible to reference */
|
||||
#define ALL_BASE_ADDR OCM_BASE_ADDR
|
||||
|
||||
/* PCI configuration space registers */
|
||||
#define PCI_IOBAR_OFFSET 4
|
||||
|
||||
#define PCI_CONF_MBAR1 0x6C
|
||||
#define PCI_CONF_MBAR0_SWA 0x70
|
||||
#define PCI_CONF_MBAR0_SWB 0x74
|
||||
#define PCI_CONF_MBAR0_SWC 0x78
|
||||
#define PCI_CONF_MBAR_KEY 0x7C
|
||||
#define PCI_CONF_FLSH_BAR 0xB8
|
||||
|
||||
#include "aic94xx_reg_def.h"
|
||||
|
||||
u8 asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u16 asd_read_reg_word(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u32 asd_read_reg_dword(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
|
||||
void asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val);
|
||||
void asd_write_reg_word(struct asd_ha_struct *asd_ha, u32 reg, u16 val);
|
||||
void asd_write_reg_dword(struct asd_ha_struct *asd_ha, u32 reg, u32 val);
|
||||
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count);
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count);
|
||||
|
||||
#define ASD_READ_OCM(type, ord, S) \
|
||||
static inline type asd_read_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
type val = read##S (io_handle->addr + (unsigned long) offs); \
|
||||
rmb(); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_OCM(u8, byte, b);
|
||||
ASD_READ_OCM(u16,word, w);
|
||||
ASD_READ_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_WRITE_OCM(type, ord, S) \
|
||||
static inline void asd_write_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
write##S (val, io_handle->addr + (unsigned long) offs); \
|
||||
return; \
|
||||
}
|
||||
|
||||
ASD_WRITE_OCM(u8, byte, b);
|
||||
ASD_WRITE_OCM(u16,word, w);
|
||||
ASD_WRITE_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_DDBSITE_READ(type, ord) \
|
||||
static inline type asd_ddbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_READ(u32, dword);
|
||||
ASD_DDBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_ddbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_DDBSITE_WRITE(type, ord) \
|
||||
static inline void asd_ddbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_WRITE(u32, dword);
|
||||
ASD_DDBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_ddbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_ddbsite_write_word(asd_ha, ddb_site_no, base, rval);
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_READ(type, ord) \
|
||||
static inline type asd_scbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_READ(u32, dword);
|
||||
ASD_SCBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_scbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_WRITE(type, ord) \
|
||||
static inline void asd_scbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_WRITE(u32, dword);
|
||||
ASD_SCBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_scbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_scbsite_read_word(asd_ha, scb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_scbsite_write_word(asd_ha, scb_site_no, base, rval);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ddbsite_update_word -- atomically update a word in a ddb site
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @ddb_site_no: the DDB site number
|
||||
* @offs: the offset into the DDB
|
||||
* @oldval: old value found in that offset
|
||||
* @newval: the new value to replace it
|
||||
*
|
||||
* This function is used when the sequencers are running and we need to
|
||||
* update a DDB site atomically without expensive pausing and upausing
|
||||
* of the sequencers and accessing the DDB site through the CIO bus.
|
||||
*
|
||||
* Return 0 on success; -EFAULT on parity error; -EAGAIN if the old value
|
||||
* is different than the current value at that offset.
|
||||
*/
|
||||
static inline int asd_ddbsite_update_word(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u16 oldval, u16 newval)
|
||||
{
|
||||
u8 done;
|
||||
u16 oval = asd_ddbsite_read_word(asd_ha, ddb_site_no, offs);
|
||||
if (oval != oldval)
|
||||
return -EAGAIN;
|
||||
asd_write_reg_word(asd_ha, AOLDDATA, oldval);
|
||||
asd_write_reg_word(asd_ha, ANEWDATA, newval);
|
||||
do {
|
||||
done = asd_read_reg_byte(asd_ha, ATOMICSTATCTL);
|
||||
} while (!(done & ATOMICDONE));
|
||||
if (done & ATOMICERR)
|
||||
return -EFAULT; /* parity error */
|
||||
else if (done & ATOMICWIN)
|
||||
return 0; /* success */
|
||||
else
|
||||
return -EAGAIN; /* oldval different than current value */
|
||||
}
|
||||
|
||||
static inline int asd_ddbsite_update_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u8 _oldval, u8 _newval)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 oval;
|
||||
u16 nval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1) {
|
||||
if ((nval >> 8) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (_newval << 8) | (nval & 0xFF);
|
||||
oval = (_oldval << 8) | (nval & 0xFF);
|
||||
} else {
|
||||
if ((nval & 0xFF) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (nval & 0xFF00) | _newval;
|
||||
oval = (nval & 0xFF00) | _oldval;
|
||||
}
|
||||
return asd_ddbsite_update_word(asd_ha, ddb_site_no, base, oval, nval);
|
||||
}
|
||||
|
||||
static inline void asd_write_reg_addr(struct asd_ha_struct *asd_ha, u32 reg,
|
||||
dma_addr_t dma_handle)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, reg, ASD_BUSADDR_LO(dma_handle));
|
||||
asd_write_reg_dword(asd_ha, reg+4, ASD_BUSADDR_HI(dma_handle));
|
||||
}
|
||||
|
||||
static inline u32 asd_get_cmdctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* DCHREVISION returns 0, possibly broken */
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 65536 : 32768;
|
||||
}
|
||||
|
||||
static inline u32 asd_get_devctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 8192 : 4096;
|
||||
}
|
||||
|
||||
static inline void asd_disable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN);
|
||||
}
|
||||
|
||||
static inline void asd_enable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* Enable COM SAS interrupt on errors, COMSTAT */
|
||||
asd_write_reg_dword(asd_ha, COMSTATEN,
|
||||
EN_CSBUFPERR | EN_CSERR | EN_OVLYERR);
|
||||
/* Enable DCH SAS CFIFTOERR */
|
||||
asd_write_reg_dword(asd_ha, DCHSTATUS, EN_CFIFTOERR);
|
||||
/* Enable Host Device interrupts */
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, SET_CHIMINTEN);
|
||||
}
|
||||
|
||||
#endif
|
||||
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
File diff suppressed because it is too large
Load Diff
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
File diff suppressed because it is too large
Load Diff
732
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
732
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
File diff suppressed because it is too large
Load Diff
1136
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
1136
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
File diff suppressed because it is too large
Load Diff
1401
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
1401
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
File diff suppressed because it is too large
Load Diff
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver sequencer interface header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_SEQ_H_
|
||||
#define _AIC94XX_SEQ_H_
|
||||
|
||||
#define CSEQ_NUM_VECS 3
|
||||
#define LSEQ_NUM_VECS 11
|
||||
|
||||
#define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw"
|
||||
|
||||
/* Note: All quantites in the sequencer file are little endian */
|
||||
struct sequencer_file_header {
|
||||
/* Checksum of the entire contents of the sequencer excluding
|
||||
* these four bytes */
|
||||
u32 csum;
|
||||
/* numeric major version */
|
||||
u32 major;
|
||||
/* numeric minor version */
|
||||
u32 minor;
|
||||
/* version string printed by driver */
|
||||
char version[16];
|
||||
u32 cseq_table_offset;
|
||||
u32 cseq_table_size;
|
||||
u32 lseq_table_offset;
|
||||
u32 lseq_table_size;
|
||||
u32 cseq_code_offset;
|
||||
u32 cseq_code_size;
|
||||
u32 lseq_code_offset;
|
||||
u32 lseq_code_size;
|
||||
u16 mode2_task;
|
||||
u16 cseq_idle_loop;
|
||||
u16 lseq_idle_loop;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int asd_pause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_unpause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_init_seqs(struct asd_ha_struct *asd_ha);
|
||||
int asd_start_seqs(struct asd_ha_struct *asd_ha);
|
||||
|
||||
void asd_update_port_links(struct asd_sas_phy *phy);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user