Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
# Adaptec aacraid
obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \
dpcsup.o rx.o sa.o rkt.o nark.o src.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+660
View File
@@ -0,0 +1,660 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* comminit.c
*
* Abstract: This supports the initialization of the host adapter commuication interface.
* This is a platform dependent module for the pci cyclone board.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/mm.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include "aacraid.h"
struct aac_common aac_config = {
.irq_mod = 1
};
static inline int aac_is_msix_mode(struct aac_dev *dev)
{
u32 status = 0;
if (aac_is_src(dev))
status = src_readl(dev, MUnit.OMR);
return (status & AAC_INT_MODE_MSIX);
}
static inline void aac_change_to_intx(struct aac_dev *dev)
{
aac_src_access_devreg(dev, AAC_DISABLE_MSIX);
aac_src_access_devreg(dev, AAC_ENABLE_INTX);
}
static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
{
unsigned char *base;
unsigned long size, align;
const unsigned long fibsize = dev->max_fib_size;
const unsigned long printfbufsiz = 256;
unsigned long host_rrq_size, aac_init_size;
union aac_init *init;
dma_addr_t phys;
unsigned long aac_max_hostphysmempages;
if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) ||
(dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) ||
(dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 &&
!dev->sa_firmware)) {
host_rrq_size =
(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)
* sizeof(u32);
aac_init_size = sizeof(union aac_init);
} else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 &&
dev->sa_firmware) {
host_rrq_size = (dev->scsi_host_ptr->can_queue
+ AAC_NUM_MGT_FIB) * sizeof(u32) * AAC_MAX_MSIX;
aac_init_size = sizeof(union aac_init) +
(AAC_MAX_HRRQ - 1) * sizeof(struct _rrq);
} else {
host_rrq_size = 0;
aac_init_size = sizeof(union aac_init);
}
size = fibsize + aac_init_size + commsize + commalign +
printfbufsiz + host_rrq_size;
base = dma_alloc_coherent(&dev->pdev->dev, size, &phys, GFP_KERNEL);
if (base == NULL) {
printk(KERN_ERR "aacraid: unable to create mapping.\n");
return 0;
}
dev->comm_addr = (void *)base;
dev->comm_phys = phys;
dev->comm_size = size;
if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) ||
(dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) ||
(dev->comm_interface == AAC_COMM_MESSAGE_TYPE3)) {
dev->host_rrq = (u32 *)(base + fibsize);
dev->host_rrq_pa = phys + fibsize;
memset(dev->host_rrq, 0, host_rrq_size);
}
dev->init = (union aac_init *)(base + fibsize + host_rrq_size);
dev->init_pa = phys + fibsize + host_rrq_size;
init = dev->init;
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
int i;
u64 addr;
init->r8.init_struct_revision =
cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_8);
init->r8.init_flags = cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
INITFLAGS_DRIVER_USES_UTC_TIME |
INITFLAGS_DRIVER_SUPPORTS_PM);
init->r8.init_flags |=
cpu_to_le32(INITFLAGS_DRIVER_SUPPORTS_HBA_MODE);
init->r8.rr_queue_count = cpu_to_le32(dev->max_msix);
init->r8.max_io_size =
cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
init->r8.max_num_aif = init->r8.reserved1 =
init->r8.reserved2 = 0;
for (i = 0; i < dev->max_msix; i++) {
addr = (u64)dev->host_rrq_pa + dev->vector_cap * i *
sizeof(u32);
init->r8.rrq[i].host_addr_high = cpu_to_le32(
upper_32_bits(addr));
init->r8.rrq[i].host_addr_low = cpu_to_le32(
lower_32_bits(addr));
init->r8.rrq[i].msix_id = i;
init->r8.rrq[i].element_count = cpu_to_le16(
(u16)dev->vector_cap);
init->r8.rrq[i].comp_thresh =
init->r8.rrq[i].unused = 0;
}
pr_warn("aacraid: Comm Interface type3 enabled\n");
} else {
init->r7.init_struct_revision =
cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
if (dev->max_fib_size != sizeof(struct hw_fib))
init->r7.init_struct_revision =
cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4);
init->r7.no_of_msix_vectors = cpu_to_le32(SA_MINIPORT_REVISION);
init->r7.fsrev = cpu_to_le32(dev->fsrev);
/*
* Adapter Fibs are the first thing allocated so that they
* start page aligned
*/
dev->aif_base_va = (struct hw_fib *)base;
init->r7.adapter_fibs_virtual_address = 0;
init->r7.adapter_fibs_physical_address = cpu_to_le32((u32)phys);
init->r7.adapter_fibs_size = cpu_to_le32(fibsize);
init->r7.adapter_fib_align = cpu_to_le32(sizeof(struct hw_fib));
/*
* number of 4k pages of host physical memory. The aacraid fw
* needs this number to be less than 4gb worth of pages. New
* firmware doesn't have any issues with the mapping system, but
* older Firmware did, and had *troubles* dealing with the math
* overloading past 32 bits, thus we must limit this field.
*/
aac_max_hostphysmempages =
dma_get_required_mask(&dev->pdev->dev) >> 12;
if (aac_max_hostphysmempages < AAC_MAX_HOSTPHYSMEMPAGES)
init->r7.host_phys_mem_pages =
cpu_to_le32(aac_max_hostphysmempages);
else
init->r7.host_phys_mem_pages =
cpu_to_le32(AAC_MAX_HOSTPHYSMEMPAGES);
init->r7.init_flags =
cpu_to_le32(INITFLAGS_DRIVER_USES_UTC_TIME |
INITFLAGS_DRIVER_SUPPORTS_PM);
init->r7.max_io_commands =
cpu_to_le32(dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB);
init->r7.max_io_size =
cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
init->r7.max_fib_size = cpu_to_le32(dev->max_fib_size);
init->r7.max_num_aif = cpu_to_le32(dev->max_num_aif);
if (dev->comm_interface == AAC_COMM_MESSAGE) {
init->r7.init_flags |=
cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED);
pr_warn("aacraid: Comm Interface enabled\n");
} else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) {
init->r7.init_struct_revision =
cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_6);
init->r7.init_flags |=
cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
INITFLAGS_NEW_COMM_TYPE1_SUPPORTED |
INITFLAGS_FAST_JBOD_SUPPORTED);
init->r7.host_rrq_addr_high =
cpu_to_le32(upper_32_bits(dev->host_rrq_pa));
init->r7.host_rrq_addr_low =
cpu_to_le32(lower_32_bits(dev->host_rrq_pa));
pr_warn("aacraid: Comm Interface type1 enabled\n");
} else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) {
init->r7.init_struct_revision =
cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_7);
init->r7.init_flags |=
cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
INITFLAGS_NEW_COMM_TYPE2_SUPPORTED |
INITFLAGS_FAST_JBOD_SUPPORTED);
init->r7.host_rrq_addr_high =
cpu_to_le32(upper_32_bits(dev->host_rrq_pa));
init->r7.host_rrq_addr_low =
cpu_to_le32(lower_32_bits(dev->host_rrq_pa));
init->r7.no_of_msix_vectors =
cpu_to_le32(dev->max_msix);
/* must be the COMM_PREFERRED_SETTINGS values */
pr_warn("aacraid: Comm Interface type2 enabled\n");
}
}
/*
* Increment the base address by the amount already used
*/
base = base + fibsize + host_rrq_size + aac_init_size;
phys = (dma_addr_t)((ulong)phys + fibsize + host_rrq_size +
aac_init_size);
/*
* Align the beginning of Headers to commalign
*/
align = (commalign - ((uintptr_t)(base) & (commalign - 1)));
base = base + align;
phys = phys + align;
/*
* Fill in addresses of the Comm Area Headers and Queues
*/
*commaddr = base;
if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3)
init->r7.comm_header_address = cpu_to_le32((u32)phys);
/*
* Increment the base address by the size of the CommArea
*/
base = base + commsize;
phys = phys + commsize;
/*
* Place the Printf buffer area after the Fast I/O comm area.
*/
dev->printfbuf = (void *)base;
if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3) {
init->r7.printfbuf = cpu_to_le32(phys);
init->r7.printfbufsiz = cpu_to_le32(printfbufsiz);
}
memset(base, 0, printfbufsiz);
return 1;
}
static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem, int qsize)
{
atomic_set(&q->numpending, 0);
q->dev = dev;
init_waitqueue_head(&q->cmdready);
INIT_LIST_HEAD(&q->cmdq);
init_waitqueue_head(&q->qfull);
spin_lock_init(&q->lockdata);
q->lock = &q->lockdata;
q->headers.producer = (__le32 *)mem;
q->headers.consumer = (__le32 *)(mem+1);
*(q->headers.producer) = cpu_to_le32(qsize);
*(q->headers.consumer) = cpu_to_le32(qsize);
q->entries = qsize;
}
static bool wait_for_io_iter(struct scsi_cmnd *cmd, void *data)
{
int *active = data;
if (aac_priv(cmd)->owner == AAC_OWNER_FIRMWARE)
*active = *active + 1;
return true;
}
static void aac_wait_for_io_completion(struct aac_dev *aac)
{
int i = 0, active;
for (i = 60; i; --i) {
active = 0;
scsi_host_busy_iter(aac->scsi_host_ptr,
wait_for_io_iter, &active);
/*
* We can exit If all the commands are complete
*/
if (active == 0)
break;
dev_info(&aac->pdev->dev,
"Wait for %d commands to complete\n", active);
ssleep(1);
}
if (active)
dev_err(&aac->pdev->dev,
"%d outstanding commands during shutdown\n", active);
}
/**
* aac_send_shutdown - shutdown an adapter
* @dev: Adapter to shutdown
*
* This routine will send a VM_CloseAll (shutdown) request to the adapter.
*/
int aac_send_shutdown(struct aac_dev * dev)
{
struct fib * fibctx;
struct aac_close *cmd;
int status = 0;
if (aac_adapter_check_health(dev))
return status;
if (!dev->adapter_shutdown) {
mutex_lock(&dev->ioctl_mutex);
dev->adapter_shutdown = 1;
mutex_unlock(&dev->ioctl_mutex);
}
aac_wait_for_io_completion(dev);
fibctx = aac_fib_alloc(dev);
if (!fibctx)
return -ENOMEM;
aac_fib_init(fibctx);
cmd = (struct aac_close *) fib_data(fibctx);
cmd->command = cpu_to_le32(VM_CloseAll);
cmd->cid = cpu_to_le32(0xfffffffe);
status = aac_fib_send(ContainerCommand,
fibctx,
sizeof(struct aac_close),
FsaNormal,
-2 /* Timeout silently */, 1,
NULL, NULL);
if (status >= 0)
aac_fib_complete(fibctx);
/* FIB should be freed only after getting the response from the F/W */
if (status != -ERESTARTSYS)
aac_fib_free(fibctx);
if (aac_is_src(dev) &&
dev->msi_enabled)
aac_set_intx_mode(dev);
return status;
}
/**
* aac_comm_init - Initialise FSA data structures
* @dev: Adapter to initialise
*
* Initializes the data structures that are required for the FSA commuication
* interface to operate.
* Returns
* 1 - if we were able to init the commuication interface.
* 0 - If there were errors initing. This is a fatal error.
*/
static int aac_comm_init(struct aac_dev * dev)
{
unsigned long hdrsize = (sizeof(u32) * NUMBER_OF_COMM_QUEUES) * 2;
unsigned long queuesize = sizeof(struct aac_entry) * TOTAL_QUEUE_ENTRIES;
u32 *headers;
struct aac_entry * queues;
unsigned long size;
struct aac_queue_block * comm = dev->queues;
/*
* Now allocate and initialize the zone structures used as our
* pool of FIB context records. The size of the zone is based
* on the system memory size. We also initialize the mutex used
* to protect the zone.
*/
spin_lock_init(&dev->fib_lock);
/*
* Allocate the physically contiguous space for the commuication
* queue headers.
*/
size = hdrsize + queuesize;
if (!aac_alloc_comm(dev, (void * *)&headers, size, QUEUE_ALIGNMENT))
return -ENOMEM;
queues = (struct aac_entry *)(((ulong)headers) + hdrsize);
/* Adapter to Host normal priority Command queue */
comm->queue[HostNormCmdQueue].base = queues;
aac_queue_init(dev, &comm->queue[HostNormCmdQueue], headers, HOST_NORM_CMD_ENTRIES);
queues += HOST_NORM_CMD_ENTRIES;
headers += 2;
/* Adapter to Host high priority command queue */
comm->queue[HostHighCmdQueue].base = queues;
aac_queue_init(dev, &comm->queue[HostHighCmdQueue], headers, HOST_HIGH_CMD_ENTRIES);
queues += HOST_HIGH_CMD_ENTRIES;
headers +=2;
/* Host to adapter normal priority command queue */
comm->queue[AdapNormCmdQueue].base = queues;
aac_queue_init(dev, &comm->queue[AdapNormCmdQueue], headers, ADAP_NORM_CMD_ENTRIES);
queues += ADAP_NORM_CMD_ENTRIES;
headers += 2;
/* host to adapter high priority command queue */
comm->queue[AdapHighCmdQueue].base = queues;
aac_queue_init(dev, &comm->queue[AdapHighCmdQueue], headers, ADAP_HIGH_CMD_ENTRIES);
queues += ADAP_HIGH_CMD_ENTRIES;
headers += 2;
/* adapter to host normal priority response queue */
comm->queue[HostNormRespQueue].base = queues;
aac_queue_init(dev, &comm->queue[HostNormRespQueue], headers, HOST_NORM_RESP_ENTRIES);
queues += HOST_NORM_RESP_ENTRIES;
headers += 2;
/* adapter to host high priority response queue */
comm->queue[HostHighRespQueue].base = queues;
aac_queue_init(dev, &comm->queue[HostHighRespQueue], headers, HOST_HIGH_RESP_ENTRIES);
queues += HOST_HIGH_RESP_ENTRIES;
headers += 2;
/* host to adapter normal priority response queue */
comm->queue[AdapNormRespQueue].base = queues;
aac_queue_init(dev, &comm->queue[AdapNormRespQueue], headers, ADAP_NORM_RESP_ENTRIES);
queues += ADAP_NORM_RESP_ENTRIES;
headers += 2;
/* host to adapter high priority response queue */
comm->queue[AdapHighRespQueue].base = queues;
aac_queue_init(dev, &comm->queue[AdapHighRespQueue], headers, ADAP_HIGH_RESP_ENTRIES);
comm->queue[AdapNormCmdQueue].lock = comm->queue[HostNormRespQueue].lock;
comm->queue[AdapHighCmdQueue].lock = comm->queue[HostHighRespQueue].lock;
comm->queue[AdapNormRespQueue].lock = comm->queue[HostNormCmdQueue].lock;
comm->queue[AdapHighRespQueue].lock = comm->queue[HostHighCmdQueue].lock;
return 0;
}
void aac_define_int_mode(struct aac_dev *dev)
{
int i, msi_count, min_msix;
msi_count = i = 0;
/* max. vectors from GET_COMM_PREFERRED_SETTINGS */
if (dev->max_msix == 0 ||
dev->pdev->device == PMC_DEVICE_S6 ||
dev->sync_mode) {
dev->max_msix = 1;
dev->vector_cap =
dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB;
return;
}
/* Don't bother allocating more MSI-X vectors than cpus */
msi_count = min(dev->max_msix,
(unsigned int)num_online_cpus());
dev->max_msix = msi_count;
if (msi_count > AAC_MAX_MSIX)
msi_count = AAC_MAX_MSIX;
if (msi_count > 1 &&
pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX)) {
min_msix = 2;
i = pci_alloc_irq_vectors(dev->pdev,
min_msix, msi_count, PCI_IRQ_MSIX);
if (i > 0) {
dev->msi_enabled = 1;
msi_count = i;
} else {
dev->msi_enabled = 0;
dev_err(&dev->pdev->dev,
"MSIX not supported!! Will try INTX 0x%x.\n", i);
}
}
if (!dev->msi_enabled)
dev->max_msix = msi_count = 1;
else {
if (dev->max_msix > msi_count)
dev->max_msix = msi_count;
}
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 && dev->sa_firmware)
dev->vector_cap = dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB;
else
dev->vector_cap = (dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB) / msi_count;
}
struct aac_dev *aac_init_adapter(struct aac_dev *dev)
{
u32 status[5];
struct Scsi_Host * host = dev->scsi_host_ptr;
extern int aac_sync_mode;
/*
* Check the preferred comm settings, defaults from template.
*/
dev->management_fib_count = 0;
spin_lock_init(&dev->manage_lock);
spin_lock_init(&dev->sync_lock);
spin_lock_init(&dev->iq_lock);
dev->max_fib_size = sizeof(struct hw_fib);
dev->sg_tablesize = host->sg_tablesize = (dev->max_fib_size
- sizeof(struct aac_fibhdr) - sizeof(struct aac_write))
/ sizeof(struct sgentry);
dev->comm_interface = AAC_COMM_PRODUCER;
dev->raw_io_interface = dev->raw_io_64 = 0;
/*
* Enable INTX mode, if not done already Enabled
*/
if (aac_is_msix_mode(dev)) {
aac_change_to_intx(dev);
dev_info(&dev->pdev->dev, "Changed firmware to INTX mode");
}
if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES,
0, 0, 0, 0, 0, 0,
status+0, status+1, status+2, status+3, status+4)) &&
(status[0] == 0x00000001)) {
dev->doorbell_mask = status[3];
if (status[1] & AAC_OPT_NEW_COMM_64)
dev->raw_io_64 = 1;
dev->sync_mode = aac_sync_mode;
if (dev->a_ops.adapter_comm &&
(status[1] & AAC_OPT_NEW_COMM)) {
dev->comm_interface = AAC_COMM_MESSAGE;
dev->raw_io_interface = 1;
if ((status[1] & AAC_OPT_NEW_COMM_TYPE1)) {
/* driver supports TYPE1 (Tupelo) */
dev->comm_interface = AAC_COMM_MESSAGE_TYPE1;
} else if (status[1] & AAC_OPT_NEW_COMM_TYPE2) {
/* driver supports TYPE2 (Denali, Yosemite) */
dev->comm_interface = AAC_COMM_MESSAGE_TYPE2;
} else if (status[1] & AAC_OPT_NEW_COMM_TYPE3) {
/* driver supports TYPE3 (Yosemite, Thor) */
dev->comm_interface = AAC_COMM_MESSAGE_TYPE3;
} else if (status[1] & AAC_OPT_NEW_COMM_TYPE4) {
/* not supported TYPE - switch to sync. mode */
dev->comm_interface = AAC_COMM_MESSAGE_TYPE2;
dev->sync_mode = 1;
}
}
if ((status[1] & le32_to_cpu(AAC_OPT_EXTENDED)) &&
(status[4] & le32_to_cpu(AAC_EXTOPT_SA_FIRMWARE)))
dev->sa_firmware = 1;
else
dev->sa_firmware = 0;
if (status[4] & le32_to_cpu(AAC_EXTOPT_SOFT_RESET))
dev->soft_reset_support = 1;
else
dev->soft_reset_support = 0;
if ((dev->comm_interface == AAC_COMM_MESSAGE) &&
(status[2] > dev->base_size)) {
aac_adapter_ioremap(dev, 0);
dev->base_size = status[2];
if (aac_adapter_ioremap(dev, status[2])) {
/* remap failed, go back ... */
dev->comm_interface = AAC_COMM_PRODUCER;
if (aac_adapter_ioremap(dev, AAC_MIN_FOOTPRINT_SIZE)) {
printk(KERN_WARNING
"aacraid: unable to map adapter.\n");
return NULL;
}
}
}
}
dev->max_msix = 0;
dev->msi_enabled = 0;
dev->adapter_shutdown = 0;
if ((!aac_adapter_sync_cmd(dev, GET_COMM_PREFERRED_SETTINGS,
0, 0, 0, 0, 0, 0,
status+0, status+1, status+2, status+3, status+4))
&& (status[0] == 0x00000001)) {
/*
* status[1] >> 16 maximum command size in KB
* status[1] & 0xFFFF maximum FIB size
* status[2] >> 16 maximum SG elements to driver
* status[2] & 0xFFFF maximum SG elements from driver
* status[3] & 0xFFFF maximum number FIBs outstanding
*/
host->max_sectors = (status[1] >> 16) << 1;
/* Multiple of 32 for PMC */
dev->max_fib_size = status[1] & 0xFFE0;
host->sg_tablesize = status[2] >> 16;
dev->sg_tablesize = status[2] & 0xFFFF;
if (aac_is_src(dev)) {
if (host->can_queue > (status[3] >> 16) -
AAC_NUM_MGT_FIB)
host->can_queue = (status[3] >> 16) -
AAC_NUM_MGT_FIB;
} else if (host->can_queue > (status[3] & 0xFFFF) -
AAC_NUM_MGT_FIB)
host->can_queue = (status[3] & 0xFFFF) -
AAC_NUM_MGT_FIB;
dev->max_num_aif = status[4] & 0xFFFF;
}
if (numacb > 0) {
if (numacb < host->can_queue)
host->can_queue = numacb;
else
pr_warn("numacb=%d ignored\n", numacb);
}
if (aac_is_src(dev))
aac_define_int_mode(dev);
/*
* Ok now init the communication subsystem
*/
dev->queues = kzalloc_obj(struct aac_queue_block);
if (dev->queues == NULL) {
printk(KERN_ERR "Error could not allocate comm region.\n");
return NULL;
}
if (aac_comm_init(dev)<0){
kfree(dev->queues);
dev->queues = NULL;
return NULL;
}
/*
* Initialize the list of fibs
*/
if (aac_fib_setup(dev) < 0) {
kfree(dev->queues);
dev->queues = NULL;
return NULL;
}
INIT_LIST_HEAD(&dev->fib_list);
INIT_LIST_HEAD(&dev->sync_fib_list);
return dev;
}
File diff suppressed because it is too large Load Diff
+456
View File
@@ -0,0 +1,456 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* dpcsup.c
*
* Abstract: All DPC processing routines for the cyclone board occur here.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/blkdev.h>
#include "aacraid.h"
/**
* aac_response_normal - Handle command replies
* @q: Queue to read from
*
* This DPC routine will be run when the adapter interrupts us to let us
* know there is a response on our normal priority queue. We will pull off
* all QE there are and wake up all the waiters before exiting. We will
* take a spinlock out on the queue before operating on it.
*/
unsigned int aac_response_normal(struct aac_queue * q)
{
struct aac_dev * dev = q->dev;
struct aac_entry *entry;
struct hw_fib * hwfib;
struct fib * fib;
int consumed = 0;
unsigned long flags, mflags;
spin_lock_irqsave(q->lock, flags);
/*
* Keep pulling response QEs off the response queue and waking
* up the waiters until there are no more QEs. We then return
* back to the system. If no response was requested we just
* deallocate the Fib here and continue.
*/
while(aac_consumer_get(dev, q, &entry))
{
int fast;
u32 index = le32_to_cpu(entry->addr);
fast = index & 0x01;
fib = &dev->fibs[index >> 2];
hwfib = fib->hw_fib_va;
aac_consumer_free(dev, q, HostNormRespQueue);
/*
* Remove this fib from the Outstanding I/O queue.
* But only if it has not already been timed out.
*
* If the fib has been timed out already, then just
* continue. The caller has already been notified that
* the fib timed out.
*/
atomic_dec(&dev->queues->queue[AdapNormCmdQueue].numpending);
if (unlikely(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
spin_unlock_irqrestore(q->lock, flags);
aac_fib_complete(fib);
aac_fib_free(fib);
spin_lock_irqsave(q->lock, flags);
continue;
}
spin_unlock_irqrestore(q->lock, flags);
if (fast) {
/*
* Doctor the fib
*/
*(__le32 *)hwfib->data = cpu_to_le32(ST_OK);
hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
}
FIB_COUNTER_INCREMENT(aac_config.FibRecved);
if (hwfib->header.Command == cpu_to_le16(NuFileSystem))
{
__le32 *pstatus = (__le32 *)hwfib->data;
if (*pstatus & cpu_to_le32(0xffff0000))
*pstatus = cpu_to_le32(ST_OK);
}
if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async))
{
if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected)) {
FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
} else {
FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
}
/*
* NOTE: we cannot touch the fib after this
* call, because it may have been deallocated.
*/
fib->callback(fib->callback_data, fib);
} else {
unsigned long flagv;
spin_lock_irqsave(&fib->event_lock, flagv);
if (!fib->done) {
fib->done = 1;
complete(&fib->event_wait);
}
spin_unlock_irqrestore(&fib->event_lock, flagv);
spin_lock_irqsave(&dev->manage_lock, mflags);
dev->management_fib_count--;
spin_unlock_irqrestore(&dev->manage_lock, mflags);
FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
if (fib->done == 2) {
spin_lock_irqsave(&fib->event_lock, flagv);
fib->done = 0;
spin_unlock_irqrestore(&fib->event_lock, flagv);
aac_fib_complete(fib);
aac_fib_free(fib);
}
}
consumed++;
spin_lock_irqsave(q->lock, flags);
}
if (consumed > aac_config.peak_fibs)
aac_config.peak_fibs = consumed;
if (consumed == 0)
aac_config.zero_fibs++;
spin_unlock_irqrestore(q->lock, flags);
return 0;
}
/**
* aac_command_normal - handle commands
* @q: queue to process
*
* This DPC routine will be queued when the adapter interrupts us to
* let us know there is a command on our normal priority queue. We will
* pull off all QE there are and wake up all the waiters before exiting.
* We will take a spinlock out on the queue before operating on it.
*/
unsigned int aac_command_normal(struct aac_queue *q)
{
struct aac_dev * dev = q->dev;
struct aac_entry *entry;
unsigned long flags;
spin_lock_irqsave(q->lock, flags);
/*
* Keep pulling response QEs off the response queue and waking
* up the waiters until there are no more QEs. We then return
* back to the system.
*/
while(aac_consumer_get(dev, q, &entry))
{
struct fib fibctx;
struct hw_fib * hw_fib;
u32 index;
struct fib *fib = &fibctx;
index = le32_to_cpu(entry->addr) / sizeof(struct hw_fib);
hw_fib = &dev->aif_base_va[index];
/*
* Allocate a FIB at all costs. For non queued stuff
* we can just use the stack so we are happy. We need
* a fib object in order to manage the linked lists
*/
if (dev->aif_thread)
if((fib = kmalloc_obj(struct fib, GFP_ATOMIC)) == NULL)
fib = &fibctx;
memset(fib, 0, sizeof(struct fib));
INIT_LIST_HEAD(&fib->fiblink);
fib->type = FSAFS_NTC_FIB_CONTEXT;
fib->size = sizeof(struct fib);
fib->hw_fib_va = hw_fib;
fib->data = hw_fib->data;
fib->dev = dev;
if (dev->aif_thread && fib != &fibctx) {
list_add_tail(&fib->fiblink, &q->cmdq);
aac_consumer_free(dev, q, HostNormCmdQueue);
wake_up_interruptible(&q->cmdready);
} else {
aac_consumer_free(dev, q, HostNormCmdQueue);
spin_unlock_irqrestore(q->lock, flags);
/*
* Set the status of this FIB
*/
*(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
aac_fib_adapter_complete(fib, sizeof(u32));
spin_lock_irqsave(q->lock, flags);
}
}
spin_unlock_irqrestore(q->lock, flags);
return 0;
}
/*
*
* aac_aif_callback
* @context: the context set in the fib - here it is scsi cmd
* @fibptr: pointer to the fib
*
* Handles the AIFs - new method (SRC)
*
*/
static void aac_aif_callback(void *context, struct fib * fibptr)
{
struct fib *fibctx;
struct aac_dev *dev;
struct aac_aifcmd *cmd;
fibctx = (struct fib *)context;
BUG_ON(fibptr == NULL);
dev = fibptr->dev;
if ((fibptr->hw_fib_va->header.XferState &
cpu_to_le32(NoMoreAifDataAvailable)) ||
dev->sa_firmware) {
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
return;
}
aac_intr_normal(dev, 0, 1, 0, fibptr->hw_fib_va);
aac_fib_init(fibctx);
cmd = (struct aac_aifcmd *) fib_data(fibctx);
cmd->command = cpu_to_le32(AifReqEvent);
aac_fib_send(AifRequest,
fibctx,
sizeof(struct hw_fib)-sizeof(struct aac_fibhdr),
FsaNormal,
0, 1,
(fib_callback)aac_aif_callback, fibctx);
}
/*
* aac_intr_normal - Handle command replies
* @dev: Device
* @index: completion reference
*
* This DPC routine will be run when the adapter interrupts us to let us
* know there is a response on our normal priority queue. We will pull off
* all QE there are and wake up all the waiters before exiting.
*/
unsigned int aac_intr_normal(struct aac_dev *dev, u32 index, int isAif,
int isFastResponse, struct hw_fib *aif_fib)
{
unsigned long mflags;
dprintk((KERN_INFO "aac_intr_normal(%p,%x)\n", dev, index));
if (isAif == 1) { /* AIF - common */
struct hw_fib * hw_fib;
struct fib * fib;
struct aac_queue *q = &dev->queues->queue[HostNormCmdQueue];
unsigned long flags;
/*
* Allocate a FIB. For non queued stuff we can just use
* the stack so we are happy. We need a fib object in order to
* manage the linked lists.
*/
if ((!dev->aif_thread)
|| (!(fib = kzalloc_obj(struct fib, GFP_ATOMIC))))
return 1;
if (!(hw_fib = kzalloc_obj(struct hw_fib, GFP_ATOMIC))) {
kfree (fib);
return 1;
}
if (dev->sa_firmware) {
fib->hbacmd_size = index; /* store event type */
} else if (aif_fib != NULL) {
memcpy(hw_fib, aif_fib, sizeof(struct hw_fib));
} else {
memcpy(hw_fib, (struct hw_fib *)
(((uintptr_t)(dev->regs.sa)) + index),
sizeof(struct hw_fib));
}
INIT_LIST_HEAD(&fib->fiblink);
fib->type = FSAFS_NTC_FIB_CONTEXT;
fib->size = sizeof(struct fib);
fib->hw_fib_va = hw_fib;
fib->data = hw_fib->data;
fib->dev = dev;
spin_lock_irqsave(q->lock, flags);
list_add_tail(&fib->fiblink, &q->cmdq);
wake_up_interruptible(&q->cmdready);
spin_unlock_irqrestore(q->lock, flags);
return 1;
} else if (isAif == 2) { /* AIF - new (SRC) */
struct fib *fibctx;
struct aac_aifcmd *cmd;
fibctx = aac_fib_alloc(dev);
if (!fibctx)
return 1;
aac_fib_init(fibctx);
cmd = (struct aac_aifcmd *) fib_data(fibctx);
cmd->command = cpu_to_le32(AifReqEvent);
return aac_fib_send(AifRequest,
fibctx,
sizeof(struct hw_fib)-sizeof(struct aac_fibhdr),
FsaNormal,
0, 1,
(fib_callback)aac_aif_callback, fibctx);
} else {
struct fib *fib = &dev->fibs[index];
int start_callback = 0;
/*
* Remove this fib from the Outstanding I/O queue.
* But only if it has not already been timed out.
*
* If the fib has been timed out already, then just
* continue. The caller has already been notified that
* the fib timed out.
*/
atomic_dec(&dev->queues->queue[AdapNormCmdQueue].numpending);
if (unlikely(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
aac_fib_complete(fib);
aac_fib_free(fib);
return 0;
}
FIB_COUNTER_INCREMENT(aac_config.FibRecved);
if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) {
if (isFastResponse)
fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
if (fib->callback) {
start_callback = 1;
} else {
unsigned long flagv;
int completed = 0;
dprintk((KERN_INFO "event_wait up\n"));
spin_lock_irqsave(&fib->event_lock, flagv);
if (fib->done == 2) {
fib->done = 1;
completed = 1;
} else {
fib->done = 1;
complete(&fib->event_wait);
}
spin_unlock_irqrestore(&fib->event_lock, flagv);
spin_lock_irqsave(&dev->manage_lock, mflags);
dev->management_fib_count--;
spin_unlock_irqrestore(&dev->manage_lock,
mflags);
FIB_COUNTER_INCREMENT(aac_config.NativeRecved);
if (completed)
aac_fib_complete(fib);
}
} else {
struct hw_fib *hwfib = fib->hw_fib_va;
if (isFastResponse) {
/* Doctor the fib */
*(__le32 *)hwfib->data = cpu_to_le32(ST_OK);
hwfib->header.XferState |=
cpu_to_le32(AdapterProcessed);
fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
}
if (hwfib->header.Command ==
cpu_to_le16(NuFileSystem)) {
__le32 *pstatus = (__le32 *)hwfib->data;
if (*pstatus & cpu_to_le32(0xffff0000))
*pstatus = cpu_to_le32(ST_OK);
}
if (hwfib->header.XferState &
cpu_to_le32(NoResponseExpected | Async)) {
if (hwfib->header.XferState & cpu_to_le32(
NoResponseExpected)) {
FIB_COUNTER_INCREMENT(
aac_config.NoResponseRecved);
} else {
FIB_COUNTER_INCREMENT(
aac_config.AsyncRecved);
}
start_callback = 1;
} else {
unsigned long flagv;
int completed = 0;
dprintk((KERN_INFO "event_wait up\n"));
spin_lock_irqsave(&fib->event_lock, flagv);
if (fib->done == 2) {
fib->done = 1;
completed = 1;
} else {
fib->done = 1;
complete(&fib->event_wait);
}
spin_unlock_irqrestore(&fib->event_lock, flagv);
spin_lock_irqsave(&dev->manage_lock, mflags);
dev->management_fib_count--;
spin_unlock_irqrestore(&dev->manage_lock,
mflags);
FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
if (completed)
aac_fib_complete(fib);
}
}
if (start_callback) {
/*
* NOTE: we cannot touch the fib after this
* call, because it may have been deallocated.
*/
if (likely(fib->callback && fib->callback_data)) {
fib->callback(fib->callback_data, fib);
} else {
aac_fib_complete(fib);
aac_fib_free(fib);
}
}
return 0;
}
}
File diff suppressed because it is too large Load Diff
+72
View File
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* nark.c
*
* Abstract: Hardware Device Interface for NEMER/ARK
*/
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
/**
* aac_nark_ioremap
* @dev: device to ioremap
* @size: mapping resize request
*
*/
static int aac_nark_ioremap(struct aac_dev * dev, u32 size)
{
if (!size) {
iounmap(dev->regs.rx);
dev->regs.rx = NULL;
iounmap(dev->base);
dev->base = NULL;
return 0;
}
dev->base_start = pci_resource_start(dev->pdev, 2);
dev->regs.rx = ioremap((u64)pci_resource_start(dev->pdev, 0) |
((u64)pci_resource_start(dev->pdev, 1) << 32),
sizeof(struct rx_registers) - sizeof(struct rx_inbound));
dev->base = NULL;
if (dev->regs.rx == NULL)
return -1;
dev->base = ioremap(dev->base_start, size);
if (dev->base == NULL) {
iounmap(dev->regs.rx);
dev->regs.rx = NULL;
return -1;
}
dev->IndexRegs = &((struct rx_registers __iomem *)dev->base)->IndexRegs;
return 0;
}
/**
* aac_nark_init - initialize an NEMER/ARK Split Bar card
* @dev: device to configure
*
*/
int aac_nark_init(struct aac_dev * dev)
{
/*
* Fill in the function dispatch table.
*/
dev->a_ops.adapter_ioremap = aac_nark_ioremap;
dev->a_ops.adapter_comm = aac_rx_select_comm;
return _aac_rx_init(dev);
}
+95
View File
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* rkt.c
*
* Abstract: Hardware miniport for Drawbridge specific hardware functions.
*/
#include <linux/blkdev.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
#define AAC_NUM_IO_FIB_RKT (246 - AAC_NUM_MGT_FIB)
/**
* aac_rkt_select_comm - Select communications method
* @dev: Adapter
* @comm: communications method
*/
static int aac_rkt_select_comm(struct aac_dev *dev, int comm)
{
int retval;
retval = aac_rx_select_comm(dev, comm);
if (comm == AAC_COMM_MESSAGE) {
/*
* FIB Setup has already been done, but we can minimize the
* damage by at least ensuring the OS never issues more
* commands than we can handle. The Rocket adapters currently
* can only handle 246 commands and 8 AIFs at the same time,
* and in fact do notify us accordingly if we negotiate the
* FIB size. The problem that causes us to add this check is
* to ensure that we do not overdo it with the adapter when a
* hard coded FIB override is being utilized. This special
* case warrants this half baked, but convenient, check here.
*/
if (dev->scsi_host_ptr->can_queue > AAC_NUM_IO_FIB_RKT) {
dev->init->r7.max_io_commands =
cpu_to_le32(AAC_NUM_IO_FIB_RKT + AAC_NUM_MGT_FIB);
dev->scsi_host_ptr->can_queue = AAC_NUM_IO_FIB_RKT;
}
}
return retval;
}
/**
* aac_rkt_ioremap
* @dev: device to ioremap
* @size: mapping resize request
*
*/
static int aac_rkt_ioremap(struct aac_dev * dev, u32 size)
{
if (!size) {
iounmap(dev->regs.rkt);
return 0;
}
dev->base = dev->regs.rkt = ioremap(dev->base_start, size);
if (dev->base == NULL)
return -1;
dev->IndexRegs = &dev->regs.rkt->IndexRegs;
return 0;
}
/**
* aac_rkt_init - initialize an i960 based AAC card
* @dev: device to configure
*
* Allocate and set up resources for the i960 based AAC variants. The
* device_interface in the commregion will be allocated and linked
* to the comm region.
*/
int aac_rkt_init(struct aac_dev *dev)
{
/*
* Fill in the function dispatch table.
*/
dev->a_ops.adapter_ioremap = aac_rkt_ioremap;
dev->a_ops.adapter_comm = aac_rkt_select_comm;
return _aac_rx_init(dev);
}
+684
View File
@@ -0,0 +1,684 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* rx.c
*
* Abstract: Hardware miniport for Drawbridge specific hardware functions.
*/
#include <linux/kernel.h>
#include <linux/hex.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
static irqreturn_t aac_rx_intr_producer(int irq, void *dev_id)
{
struct aac_dev *dev = dev_id;
unsigned long bellbits;
u8 intstat = rx_readb(dev, MUnit.OISR);
/*
* Read mask and invert because drawbridge is reversed.
* This allows us to only service interrupts that have
* been enabled.
* Check to see if this is our interrupt. If it isn't just return
*/
if (likely(intstat & ~(dev->OIMR))) {
bellbits = rx_readl(dev, OutboundDoorbellReg);
if (unlikely(bellbits & DoorBellPrintfReady)) {
aac_printf(dev, readl (&dev->IndexRegs->Mailbox[5]));
rx_writel(dev, MUnit.ODR,DoorBellPrintfReady);
rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone);
}
else if (unlikely(bellbits & DoorBellAdapterNormCmdReady)) {
rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady);
aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
}
else if (likely(bellbits & DoorBellAdapterNormRespReady)) {
rx_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady);
aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
}
else if (unlikely(bellbits & DoorBellAdapterNormCmdNotFull)) {
rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
}
else if (unlikely(bellbits & DoorBellAdapterNormRespNotFull)) {
rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull);
}
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static irqreturn_t aac_rx_intr_message(int irq, void *dev_id)
{
int isAif, isFastResponse, isSpecial;
struct aac_dev *dev = dev_id;
u32 Index = rx_readl(dev, MUnit.OutboundQueue);
if (unlikely(Index == 0xFFFFFFFFL))
Index = rx_readl(dev, MUnit.OutboundQueue);
if (likely(Index != 0xFFFFFFFFL)) {
do {
isAif = isFastResponse = isSpecial = 0;
if (Index & 0x00000002L) {
isAif = 1;
if (Index == 0xFFFFFFFEL)
isSpecial = 1;
Index &= ~0x00000002L;
} else {
if (Index & 0x00000001L)
isFastResponse = 1;
Index >>= 2;
}
if (!isSpecial) {
if (unlikely(aac_intr_normal(dev,
Index, isAif,
isFastResponse, NULL))) {
rx_writel(dev,
MUnit.OutboundQueue,
Index);
rx_writel(dev,
MUnit.ODR,
DoorBellAdapterNormRespReady);
}
}
Index = rx_readl(dev, MUnit.OutboundQueue);
} while (Index != 0xFFFFFFFFL);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/**
* aac_rx_disable_interrupt - Disable interrupts
* @dev: Adapter
*/
static void aac_rx_disable_interrupt(struct aac_dev *dev)
{
rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff);
}
/**
* aac_rx_enable_interrupt_producer - Enable interrupts
* @dev: Adapter
*/
static void aac_rx_enable_interrupt_producer(struct aac_dev *dev)
{
rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb);
}
/**
* aac_rx_enable_interrupt_message - Enable interrupts
* @dev: Adapter
*/
static void aac_rx_enable_interrupt_message(struct aac_dev *dev)
{
rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xf7);
}
/**
* rx_sync_cmd - send a command and wait
* @dev: Adapter
* @command: Command to execute
* @p1: first parameter
* @p2: second parameter
* @p3: third parameter
* @p4: forth parameter
* @p5: fifth parameter
* @p6: sixth parameter
* @status: adapter status
* @r1: first return value
* @r2: second return value
* @r3: third return value
* @r4: forth return value
*
* This routine will send a synchronous command to the adapter and wait
* for its completion.
*/
static int rx_sync_cmd(struct aac_dev *dev, u32 command,
u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,
u32 *status, u32 * r1, u32 * r2, u32 * r3, u32 * r4)
{
unsigned long start;
int ok;
/*
* Write the command into Mailbox 0
*/
writel(command, &dev->IndexRegs->Mailbox[0]);
/*
* Write the parameters into Mailboxes 1 - 6
*/
writel(p1, &dev->IndexRegs->Mailbox[1]);
writel(p2, &dev->IndexRegs->Mailbox[2]);
writel(p3, &dev->IndexRegs->Mailbox[3]);
writel(p4, &dev->IndexRegs->Mailbox[4]);
/*
* Clear the synch command doorbell to start on a clean slate.
*/
rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
/*
* Disable doorbell interrupts
*/
rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff);
/*
* Force the completion of the mask register write before issuing
* the interrupt.
*/
rx_readb (dev, MUnit.OIMR);
/*
* Signal that there is a new synch command
*/
rx_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0);
ok = 0;
start = jiffies;
/*
* Wait up to 30 seconds
*/
while (time_before(jiffies, start+30*HZ))
{
udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
/*
* Mon960 will set doorbell0 bit when it has completed the command.
*/
if (rx_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) {
/*
* Clear the doorbell.
*/
rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
ok = 1;
break;
}
/*
* Yield the processor in case we are slow
*/
msleep(1);
}
if (unlikely(ok != 1)) {
/*
* Restore interrupt mask even though we timed out
*/
aac_adapter_enable_int(dev);
return -ETIMEDOUT;
}
/*
* Pull the synch status from Mailbox 0.
*/
if (status)
*status = readl(&dev->IndexRegs->Mailbox[0]);
if (r1)
*r1 = readl(&dev->IndexRegs->Mailbox[1]);
if (r2)
*r2 = readl(&dev->IndexRegs->Mailbox[2]);
if (r3)
*r3 = readl(&dev->IndexRegs->Mailbox[3]);
if (r4)
*r4 = readl(&dev->IndexRegs->Mailbox[4]);
/*
* Clear the synch command doorbell.
*/
rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
/*
* Restore interrupt mask
*/
aac_adapter_enable_int(dev);
return 0;
}
/**
* aac_rx_interrupt_adapter - interrupt adapter
* @dev: Adapter
*
* Send an interrupt to the i960 and breakpoint it.
*/
static void aac_rx_interrupt_adapter(struct aac_dev *dev)
{
rx_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
}
/**
* aac_rx_notify_adapter - send an event to the adapter
* @dev: Adapter
* @event: Event to send
*
* Notify the i960 that something it probably cares about has
* happened.
*/
static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event)
{
switch (event) {
case AdapNormCmdQue:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1);
break;
case HostNormRespNotFull:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4);
break;
case AdapNormRespQue:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2);
break;
case HostNormCmdNotFull:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3);
break;
case HostShutdown:
break;
case FastIo:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6);
break;
case AdapPrintfDone:
rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5);
break;
default:
BUG();
break;
}
}
/**
* aac_rx_start_adapter - activate adapter
* @dev: Adapter
*
* Start up processing on an i960 based AAC adapter
*/
static void aac_rx_start_adapter(struct aac_dev *dev)
{
union aac_init *init;
init = dev->init;
init->r7.host_elapsed_seconds = cpu_to_le32(ktime_get_real_seconds());
// We can only use a 32 bit address here
rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa,
0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
}
/**
* aac_rx_check_health
* @dev: device to check if healthy
*
* Will attempt to determine if the specified adapter is alive and
* capable of handling requests, returning 0 if alive.
*/
static int aac_rx_check_health(struct aac_dev *dev)
{
u32 status = rx_readl(dev, MUnit.OMRx[0]);
/*
* Check to see if the board failed any self tests.
*/
if (unlikely(status & SELF_TEST_FAILED))
return -1;
/*
* Check to see if the board panic'd.
*/
if (unlikely(status & KERNEL_PANIC)) {
char * buffer;
struct POSTSTATUS {
__le32 Post_Command;
__le32 Post_Address;
} * post;
dma_addr_t paddr, baddr;
int ret;
if (likely((status & 0xFF000000L) == 0xBC000000L))
return (status >> 16) & 0xFF;
buffer = dma_alloc_coherent(&dev->pdev->dev, 512, &baddr,
GFP_KERNEL);
ret = -2;
if (unlikely(buffer == NULL))
return ret;
post = dma_alloc_coherent(&dev->pdev->dev,
sizeof(struct POSTSTATUS), &paddr,
GFP_KERNEL);
if (unlikely(post == NULL)) {
dma_free_coherent(&dev->pdev->dev, 512, buffer, baddr);
return ret;
}
memset(buffer, 0, 512);
post->Post_Command = cpu_to_le32(COMMAND_POST_RESULTS);
post->Post_Address = cpu_to_le32(baddr);
rx_writel(dev, MUnit.IMRx[0], paddr);
rx_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
dma_free_coherent(&dev->pdev->dev, sizeof(struct POSTSTATUS),
post, paddr);
if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) {
ret = (hex_to_bin(buffer[2]) << 4) +
hex_to_bin(buffer[3]);
}
dma_free_coherent(&dev->pdev->dev, 512, buffer, baddr);
return ret;
}
/*
* Wait for the adapter to be up and running.
*/
if (unlikely(!(status & KERNEL_UP_AND_RUNNING)))
return -3;
/*
* Everything is OK
*/
return 0;
}
/**
* aac_rx_deliver_producer
* @fib: fib to issue
*
* Will send a fib, returning 0 if successful.
*/
int aac_rx_deliver_producer(struct fib * fib)
{
struct aac_dev *dev = fib->dev;
struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];
u32 Index;
unsigned long nointr = 0;
aac_queue_get( dev, &Index, AdapNormCmdQueue, fib->hw_fib_va, 1, fib, &nointr);
atomic_inc(&q->numpending);
*(q->headers.producer) = cpu_to_le32(Index + 1);
if (!(nointr & aac_config.irq_mod))
aac_adapter_notify(dev, AdapNormCmdQueue);
return 0;
}
/**
* aac_rx_deliver_message
* @fib: fib to issue
*
* Will send a fib, returning 0 if successful.
*/
static int aac_rx_deliver_message(struct fib * fib)
{
struct aac_dev *dev = fib->dev;
struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];
u32 Index;
u64 addr;
volatile void __iomem *device;
unsigned long count = 10000000L; /* 50 seconds */
atomic_inc(&q->numpending);
for(;;) {
Index = rx_readl(dev, MUnit.InboundQueue);
if (unlikely(Index == 0xFFFFFFFFL))
Index = rx_readl(dev, MUnit.InboundQueue);
if (likely(Index != 0xFFFFFFFFL))
break;
if (--count == 0) {
atomic_dec(&q->numpending);
return -ETIMEDOUT;
}
udelay(5);
}
device = dev->base + Index;
addr = fib->hw_fib_pa;
writel((u32)(addr & 0xffffffff), device);
device += sizeof(u32);
writel((u32)(addr >> 32), device);
device += sizeof(u32);
writel(le16_to_cpu(fib->hw_fib_va->header.Size), device);
rx_writel(dev, MUnit.InboundQueue, Index);
return 0;
}
/**
* aac_rx_ioremap
* @dev: adapter
* @size: mapping resize request
*
*/
static int aac_rx_ioremap(struct aac_dev * dev, u32 size)
{
if (!size) {
iounmap(dev->regs.rx);
return 0;
}
dev->base = dev->regs.rx = ioremap(dev->base_start, size);
if (dev->base == NULL)
return -1;
dev->IndexRegs = &dev->regs.rx->IndexRegs;
return 0;
}
static int aac_rx_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
{
u32 var = 0;
if (!(dev->supplement_adapter_info.supported_options2 &
AAC_OPTION_MU_RESET) || (bled >= 0) || (bled == -2)) {
if (bled)
printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n",
dev->name, dev->id, bled);
else {
bled = aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS,
0, 0, 0, 0, 0, 0, &var, NULL, NULL, NULL, NULL);
if (!bled && (var != 0x00000001) && (var != 0x3803000F))
bled = -EINVAL;
}
if (bled && (bled != -ETIMEDOUT))
bled = aac_adapter_sync_cmd(dev, IOP_RESET,
0, 0, 0, 0, 0, 0, &var, NULL, NULL, NULL, NULL);
if (bled && (bled != -ETIMEDOUT))
return -EINVAL;
}
if (bled && (var == 0x3803000F)) { /* USE_OTHER_METHOD */
rx_writel(dev, MUnit.reserved2, 3);
msleep(5000); /* Delay 5 seconds */
var = 0x00000001;
}
if (bled && (var != 0x00000001))
return -EINVAL;
ssleep(5);
if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
return -ENODEV;
if (startup_timeout < 300)
startup_timeout = 300;
return 0;
}
/**
* aac_rx_select_comm - Select communications method
* @dev: Adapter
* @comm: communications method
*/
int aac_rx_select_comm(struct aac_dev *dev, int comm)
{
switch (comm) {
case AAC_COMM_PRODUCER:
dev->a_ops.adapter_enable_int = aac_rx_enable_interrupt_producer;
dev->a_ops.adapter_intr = aac_rx_intr_producer;
dev->a_ops.adapter_deliver = aac_rx_deliver_producer;
break;
case AAC_COMM_MESSAGE:
dev->a_ops.adapter_enable_int = aac_rx_enable_interrupt_message;
dev->a_ops.adapter_intr = aac_rx_intr_message;
dev->a_ops.adapter_deliver = aac_rx_deliver_message;
break;
default:
return 1;
}
return 0;
}
/**
* _aac_rx_init - initialize an i960 based AAC card
* @dev: device to configure
*
* Allocate and set up resources for the i960 based AAC variants. The
* device_interface in the commregion will be allocated and linked
* to the comm region.
*/
int _aac_rx_init(struct aac_dev *dev)
{
unsigned long start;
unsigned long status;
int restart = 0;
int instance = dev->id;
const char * name = dev->name;
if (aac_adapter_ioremap(dev, dev->base_size)) {
printk(KERN_WARNING "%s: unable to map adapter.\n", name);
goto error_iounmap;
}
/* Failure to reset here is an option ... */
dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
dev->a_ops.adapter_enable_int = aac_rx_disable_interrupt;
dev->OIMR = status = rx_readb (dev, MUnit.OIMR);
if (((status & 0x0c) != 0x0c) || dev->init_reset) {
dev->init_reset = false;
if (!aac_rx_restart_adapter(dev, 0, IOP_HWSOFT_RESET)) {
/* Make sure the Hardware FIFO is empty */
while ((++restart < 512) &&
(rx_readl(dev, MUnit.OutboundQueue) != 0xFFFFFFFFL));
}
}
/*
* Check to see if the board panic'd while booting.
*/
status = rx_readl(dev, MUnit.OMRx[0]);
if (status & KERNEL_PANIC) {
if (aac_rx_restart_adapter(dev,
aac_rx_check_health(dev), IOP_HWSOFT_RESET))
goto error_iounmap;
++restart;
}
/*
* Check to see if the board failed any self tests.
*/
status = rx_readl(dev, MUnit.OMRx[0]);
if (status & SELF_TEST_FAILED) {
printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
goto error_iounmap;
}
/*
* Check to see if the monitor panic'd while booting.
*/
if (status & MONITOR_PANIC) {
printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
goto error_iounmap;
}
start = jiffies;
/*
* Wait for the adapter to be up and running. Wait up to 3 minutes
*/
while (!((status = rx_readl(dev, MUnit.OMRx[0])) & KERNEL_UP_AND_RUNNING))
{
if ((restart &&
(status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) ||
time_after(jiffies, start+HZ*startup_timeout)) {
printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n",
dev->name, instance, status);
goto error_iounmap;
}
if (!restart &&
((status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC)) ||
time_after(jiffies, start + HZ *
((startup_timeout > 60)
? (startup_timeout - 60)
: (startup_timeout / 2))))) {
if (likely(!aac_rx_restart_adapter(dev,
aac_rx_check_health(dev), IOP_HWSOFT_RESET)))
start = jiffies;
++restart;
}
msleep(1);
}
if (restart && aac_commit)
aac_commit = 1;
/*
* Fill in the common function dispatch table.
*/
dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter;
dev->a_ops.adapter_disable_int = aac_rx_disable_interrupt;
dev->a_ops.adapter_notify = aac_rx_notify_adapter;
dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
dev->a_ops.adapter_check_health = aac_rx_check_health;
dev->a_ops.adapter_restart = aac_rx_restart_adapter;
dev->a_ops.adapter_start = aac_rx_start_adapter;
/*
* First clear out all interrupts. Then enable the one's that we
* can handle.
*/
aac_adapter_comm(dev, AAC_COMM_PRODUCER);
aac_adapter_disable_int(dev);
rx_writel(dev, MUnit.ODR, 0xffffffff);
aac_adapter_enable_int(dev);
if (aac_init_adapter(dev) == NULL)
goto error_iounmap;
aac_adapter_comm(dev, dev->comm_interface);
dev->sync_mode = 0; /* sync. mode not supported */
dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED, "aacraid", dev) < 0) {
if (dev->msi)
pci_disable_msi(dev->pdev);
printk(KERN_ERR "%s%d: Interrupt unavailable.\n",
name, instance);
goto error_iounmap;
}
dev->dbg_base = dev->base_start;
dev->dbg_base_mapped = dev->base;
dev->dbg_size = dev->base_size;
aac_adapter_enable_int(dev);
/*
* Tell the adapter that all is configured, and it can
* start accepting requests
*/
aac_rx_start_adapter(dev);
return 0;
error_iounmap:
return -1;
}
int aac_rx_init(struct aac_dev *dev)
{
/*
* Fill in the function dispatch table.
*/
dev->a_ops.adapter_ioremap = aac_rx_ioremap;
dev->a_ops.adapter_comm = aac_rx_select_comm;
return _aac_rx_init(dev);
}
+413
View File
@@ -0,0 +1,413 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
* 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
* 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* sa.c
*
* Abstract: Drawbridge specific support functions
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
static irqreturn_t aac_sa_intr(int irq, void *dev_id)
{
struct aac_dev *dev = dev_id;
unsigned short intstat, mask;
intstat = sa_readw(dev, DoorbellReg_p);
/*
* Read mask and invert because drawbridge is reversed.
* This allows us to only service interrupts that have been enabled.
*/
mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
/* Check to see if this is our interrupt. If it isn't just return */
if (intstat & mask) {
if (intstat & PrintfReady) {
aac_printf(dev, sa_readl(dev, Mailbox5));
sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
sa_writew(dev, DoorbellReg_s, PrintfDone);
} else if (intstat & DOORBELL_1) { // dev -> Host Normal Command Ready
sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
} else if (intstat & DOORBELL_2) { // dev -> Host Normal Response Ready
sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
} else if (intstat & DOORBELL_3) { // dev -> Host Normal Command Not Full
sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
} else if (intstat & DOORBELL_4) { // dev -> Host Normal Response Not Full
sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
}
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/**
* aac_sa_disable_interrupt - disable interrupt
* @dev: Which adapter to enable.
*/
static void aac_sa_disable_interrupt (struct aac_dev *dev)
{
sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff);
}
/**
* aac_sa_enable_interrupt - enable interrupt
* @dev: Which adapter to enable.
*/
static void aac_sa_enable_interrupt (struct aac_dev *dev)
{
sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 |
DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
}
/**
* aac_sa_notify_adapter - handle adapter notification
* @dev: Adapter that notification is for
* @event: Event to notidy
*
* Notify the adapter of an event
*/
static void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
{
switch (event) {
case AdapNormCmdQue:
sa_writew(dev, DoorbellReg_s,DOORBELL_1);
break;
case HostNormRespNotFull:
sa_writew(dev, DoorbellReg_s,DOORBELL_4);
break;
case AdapNormRespQue:
sa_writew(dev, DoorbellReg_s,DOORBELL_2);
break;
case HostNormCmdNotFull:
sa_writew(dev, DoorbellReg_s,DOORBELL_3);
break;
case HostShutdown:
/*
sa_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
*/
break;
case FastIo:
sa_writew(dev, DoorbellReg_s,DOORBELL_6);
break;
case AdapPrintfDone:
sa_writew(dev, DoorbellReg_s,DOORBELL_5);
break;
default:
BUG();
break;
}
}
/**
* sa_sync_cmd - send a command and wait
* @dev: Adapter
* @command: Command to execute
* @p1: first parameter
* @p2: second parameter
* @p3: third parameter
* @p4: forth parameter
* @p5: fifth parameter
* @p6: sixth parameter
* @ret: adapter status
* @r1: first return value
* @r2: second return value
* @r3: third return value
* @r4: forth return value
*
* This routine will send a synchronous command to the adapter and wait
* for its completion.
*/
static int sa_sync_cmd(struct aac_dev *dev, u32 command,
u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,
u32 *ret, u32 *r1, u32 *r2, u32 *r3, u32 *r4)
{
unsigned long start;
int ok;
/*
* Write the Command into Mailbox 0
*/
sa_writel(dev, Mailbox0, command);
/*
* Write the parameters into Mailboxes 1 - 4
*/
sa_writel(dev, Mailbox1, p1);
sa_writel(dev, Mailbox2, p2);
sa_writel(dev, Mailbox3, p3);
sa_writel(dev, Mailbox4, p4);
/*
* Clear the synch command doorbell to start on a clean slate.
*/
sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
/*
* Signal that there is a new synch command
*/
sa_writew(dev, DoorbellReg_s, DOORBELL_0);
ok = 0;
start = jiffies;
while(time_before(jiffies, start+30*HZ))
{
/*
* Delay 5uS so that the monitor gets access
*/
udelay(5);
/*
* Mon110 will set doorbell0 bit when it has
* completed the command.
*/
if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0) {
ok = 1;
break;
}
msleep(1);
}
if (ok != 1)
return -ETIMEDOUT;
/*
* Clear the synch command doorbell.
*/
sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
/*
* Pull the synch status from Mailbox 0.
*/
if (ret)
*ret = sa_readl(dev, Mailbox0);
if (r1)
*r1 = sa_readl(dev, Mailbox1);
if (r2)
*r2 = sa_readl(dev, Mailbox2);
if (r3)
*r3 = sa_readl(dev, Mailbox3);
if (r4)
*r4 = sa_readl(dev, Mailbox4);
return 0;
}
/**
* aac_sa_interrupt_adapter - interrupt an adapter
* @dev: Which adapter to enable.
*
* Breakpoint an adapter.
*/
static void aac_sa_interrupt_adapter (struct aac_dev *dev)
{
sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
}
/**
* aac_sa_start_adapter - activate adapter
* @dev: Adapter
*
* Start up processing on an ARM based AAC adapter
*/
static void aac_sa_start_adapter(struct aac_dev *dev)
{
union aac_init *init;
/*
* Fill in the remaining pieces of the init.
*/
init = dev->init;
init->r7.host_elapsed_seconds = cpu_to_le32(ktime_get_real_seconds());
/* We can only use a 32 bit address here */
sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
(u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
}
static int aac_sa_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
{
return -EINVAL;
}
/**
* aac_sa_check_health
* @dev: device to check if healthy
*
* Will attempt to determine if the specified adapter is alive and
* capable of handling requests, returning 0 if alive.
*/
static int aac_sa_check_health(struct aac_dev *dev)
{
long status = sa_readl(dev, Mailbox7);
/*
* Check to see if the board failed any self tests.
*/
if (status & SELF_TEST_FAILED)
return -1;
/*
* Check to see if the board panic'd while booting.
*/
if (status & KERNEL_PANIC)
return -2;
/*
* Wait for the adapter to be up and running. Wait up to 3 minutes
*/
if (!(status & KERNEL_UP_AND_RUNNING))
return -3;
/*
* Everything is OK
*/
return 0;
}
/**
* aac_sa_ioremap
* @dev: device to ioremap
* @size: mapping resize request
*
*/
static int aac_sa_ioremap(struct aac_dev * dev, u32 size)
{
if (!size) {
iounmap(dev->regs.sa);
return 0;
}
dev->base = dev->regs.sa = ioremap(dev->base_start, size);
return (dev->base == NULL) ? -1 : 0;
}
/**
* aac_sa_init - initialize an ARM based AAC card
* @dev: device to configure
*
* Allocate and set up resources for the ARM based AAC variants. The
* device_interface in the commregion will be allocated and linked
* to the comm region.
*/
int aac_sa_init(struct aac_dev *dev)
{
unsigned long start;
unsigned long status;
int instance;
const char *name;
instance = dev->id;
name = dev->name;
/*
* Fill in the function dispatch table.
*/
dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
dev->a_ops.adapter_notify = aac_sa_notify_adapter;
dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
dev->a_ops.adapter_check_health = aac_sa_check_health;
dev->a_ops.adapter_restart = aac_sa_restart_adapter;
dev->a_ops.adapter_start = aac_sa_start_adapter;
dev->a_ops.adapter_intr = aac_sa_intr;
dev->a_ops.adapter_deliver = aac_rx_deliver_producer;
dev->a_ops.adapter_ioremap = aac_sa_ioremap;
if (aac_sa_ioremap(dev, dev->base_size)) {
printk(KERN_WARNING "%s: unable to map adapter.\n", name);
goto error_iounmap;
}
/*
* Check to see if the board failed any self tests.
*/
if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
goto error_iounmap;
}
/*
* Check to see if the board panic'd while booting.
*/
if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
goto error_iounmap;
}
start = jiffies;
/*
* Wait for the adapter to be up and running. Wait up to 3 minutes.
*/
while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
if (time_after(jiffies, start+startup_timeout*HZ)) {
status = sa_readl(dev, Mailbox7);
printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %lx.\n",
name, instance, status);
goto error_iounmap;
}
msleep(1);
}
/*
* First clear out all interrupts. Then enable the one's that
* we can handle.
*/
aac_adapter_disable_int(dev);
aac_adapter_enable_int(dev);
if(aac_init_adapter(dev) == NULL)
goto error_irq;
dev->sync_mode = 0; /* sync. mode not supported */
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED, "aacraid", (void *)dev) < 0) {
printk(KERN_WARNING "%s%d: Interrupt unavailable.\n",
name, instance);
goto error_iounmap;
}
dev->dbg_base = dev->base_start;
dev->dbg_base_mapped = dev->base;
dev->dbg_size = dev->base_size;
aac_adapter_enable_int(dev);
/*
* Tell the adapter that all is configure, and it can start
* accepting requests
*/
aac_sa_start_adapter(dev);
return 0;
error_irq:
aac_sa_disable_interrupt(dev);
free_irq(dev->pdev->irq, (void *)dev);
error_iounmap:
return -1;
}
File diff suppressed because it is too large Load Diff