// 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: * src.c * * Abstract: Hardware Device Interface for PMC SRC based controllers
*/
/** * src_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 valu * @r3: third return value * @r4: forth return value * * This routine will send a synchronous command to the adapter and wait * for its completion.
*/
/* * 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.
*/ if (!dev->msi_enabled)
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
/* * Force the completion of the mask register write before issuing * the interrupt.
*/
src_readl(dev, MUnit.OIMR);
/* * Signal that there is a new synch command
*/
src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT);
if ((!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) &&
!dev->in_soft_reset) {
ok = 0;
start = jiffies;
if (command == IOP_RESET_ALWAYS) { /* Wait up to 10 sec */
delay = 10*HZ;
} else { /* Wait up to 5 minutes */
delay = 300*HZ;
} while (time_before(jiffies, start+delay)) {
udelay(5); /* Delay 5 microseconds to let Mon960 get info. */ /* * Mon960 will set doorbell0 bit when it has completed the command.
*/ if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) { /* * Clear the doorbell.
*/ if (dev->msi_enabled)
aac_src_access_devreg(dev,
AAC_CLEAR_SYNC_BIT); else
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
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]); if (command == GET_COMM_PREFERRED_SETTINGS)
dev->max_msix =
readl(&dev->IndexRegs->Mailbox[5]) & 0xFFFF; /* * Clear the synch command doorbell.
*/ if (!dev->msi_enabled)
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
}
/** * aac_src_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.
*/
case AdapNormCmdQue:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_1 << SRC_ODR_SHIFT); break; case HostNormRespNotFull:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_4 << SRC_ODR_SHIFT); break; case AdapNormRespQue:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_2 << SRC_ODR_SHIFT); break; case HostNormCmdNotFull:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_3 << SRC_ODR_SHIFT); break; case FastIo:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_6 << SRC_ODR_SHIFT); break; case AdapPrintfDone:
src_writel(dev, MUnit.ODR_C,
INBOUNDDOORBELL_5 << SRC_ODR_SHIFT); break; default:
BUG(); break;
}
}
/** * aac_src_start_adapter - activate adapter * @dev: Adapter * * Start up processing on an i960 based AAC adapter
*/
staticvoid aac_src_start_adapter(struct aac_dev *dev)
{ union aac_init *init; int i;
/* reset host_rrq_idx first */ for (i = 0; i < dev->max_msix; i++) {
dev->host_rrq_idx[i] = i * dev->vector_cap;
atomic_set(&dev->rrq_outstanding[i], 0);
}
atomic_set(&dev->msix_counter, 0);
dev->fibs_pushed_no = 0;
init = dev->init; if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
init->r8.host_elapsed_seconds =
cpu_to_le32(ktime_get_real_seconds());
src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
lower_32_bits(dev->init_pa),
upper_32_bits(dev->init_pa), sizeof(struct _r8) +
AAC_MAX_HRRQ * sizeof(struct _rrq),
0, 0, 0, NULL, NULL, NULL, NULL, NULL);
} else {
init->r7.host_elapsed_seconds =
cpu_to_le32(ktime_get_real_seconds()); // We can only use a 32 bit address here
src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
(u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
}
}
/** * aac_src_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.
*/ staticint aac_src_check_health(struct aac_dev *dev)
{
u32 status = src_readl(dev, MUnit.OMR);
/* * Check to see if the board panic'd.
*/ if (unlikely(status & KERNEL_PANIC)) goto err_blink;
/* * Check to see if the board failed any self tests.
*/ if (unlikely(status & SELF_TEST_FAILED)) goto err_out;
/* * Check to see if the board failed any self tests.
*/ if (unlikely(status & MONITOR_PANIC)) goto err_out;
/* * Wait for the adapter to be up and running.
*/ if (unlikely(!(status & KERNEL_UP_AND_RUNNING))) return -3; /* * Everything is OK
*/ return 0;
aac_clear_omr(dev);
val = readl(((char *)(dev->base) + IBW_SWR_OFFSET));
val |= 0x01;
writel(val, ((char *)(dev->base) + IBW_SWR_OFFSET));
msleep_interruptible(20000);
}
staticint aac_src_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
{ bool is_ctrl_up; int ret = 0;
if (bled < 0) goto invalid_out;
if (bled)
dev_err(&dev->pdev->dev, "adapter kernel panic'd %x.\n", bled);
/* * When there is a BlinkLED, IOP_RESET has not effect
*/ if (bled >= 2 && dev->sa_firmware && reset_type & HW_IOP_RESET)
reset_type &= ~HW_IOP_RESET;
dev_err(&dev->pdev->dev, "Controller reset type is %d\n", reset_type);
if (reset_type & HW_IOP_RESET) {
dev_info(&dev->pdev->dev, "Issuing IOP reset\n");
aac_send_iop_reset(dev);
/* * Creates a delay or wait till up and running comes thru
*/
is_ctrl_up = aac_is_ctrl_up_and_running(dev); if (!is_ctrl_up)
dev_err(&dev->pdev->dev, "IOP reset failed\n"); else {
dev_info(&dev->pdev->dev, "IOP reset succeeded\n"); goto set_startup;
}
}
if (!dev->sa_firmware) {
dev_err(&dev->pdev->dev, "ARC Reset attempt failed\n");
ret = -ENODEV; goto out;
}
dev->base_size = AAC_MIN_SRC_BAR0_SIZE; 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 = src_sync_cmd;
dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
if (dev->init_reset) {
dev->init_reset = false; if (!aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET))
++restart;
}
/* * Check to see if the board panic'd while booting.
*/
status = src_readl(dev, MUnit.OMR); if (status & KERNEL_PANIC) { if (aac_src_restart_adapter(dev,
aac_src_check_health(dev), IOP_HWSOFT_RESET)) goto error_iounmap;
++restart;
} /* * Check to see if the board failed any self tests.
*/
status = src_readl(dev, MUnit.OMR); 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 = src_readl(dev, MUnit.OMR)) &
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_src_restart_adapter(dev,
aac_src_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_src_interrupt_adapter;
dev->a_ops.adapter_disable_int = aac_src_disable_interrupt;
dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
dev->a_ops.adapter_notify = aac_src_notify_adapter;
dev->a_ops.adapter_sync_cmd = src_sync_cmd;
dev->a_ops.adapter_check_health = aac_src_check_health;
dev->a_ops.adapter_restart = aac_src_restart_adapter;
dev->a_ops.adapter_start = aac_src_start_adapter;
/* * First clear out all interrupts. Then enable the one's that we * can handle.
*/
aac_adapter_comm(dev, AAC_COMM_MESSAGE);
aac_adapter_disable_int(dev);
src_writel(dev, MUnit.ODR_C, 0xffffffff);
aac_adapter_enable_int(dev);
if (aac_init_adapter(dev) == NULL) goto error_iounmap; if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE1) goto error_iounmap;
if (!dev->sync_mode) { /* * Tell the adapter that all is configured, and it can * start accepting requests
*/
aac_src_start_adapter(dev);
} return 0;
error_iounmap:
return -1;
}
staticint aac_src_wait_sync(struct aac_dev *dev, int *status)
{ unsignedlong start = jiffies; unsignedlong usecs = 0; int delay = 5 * HZ; int rc = 1;
while (time_before(jiffies, start+delay)) { /* * Delay 5 microseconds to let Mon960 get info.
*/
udelay(5);
/* * Mon960 will set doorbell0 bit when it has completed the * command.
*/ if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) { /* * Clear: the doorbell.
*/ if (dev->msi_enabled)
aac_src_access_devreg(dev, AAC_CLEAR_SYNC_BIT); else
src_writel(dev, MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
rc = 0;
break;
}
/* * Yield the processor in case we are slow
*/
usecs = 1 * USEC_PER_MSEC;
usleep_range(usecs, usecs + 50);
} /* * Pull the synch status from Mailbox 0.
*/ if (status && !rc) {
status[0] = readl(&dev->IndexRegs->Mailbox[0]);
status[1] = readl(&dev->IndexRegs->Mailbox[1]);
status[2] = readl(&dev->IndexRegs->Mailbox[2]);
status[3] = readl(&dev->IndexRegs->Mailbox[3]);
status[4] = readl(&dev->IndexRegs->Mailbox[4]);
}
return rc;
}
/** * aac_src_soft_reset - perform soft reset to speed up * access * * Assumptions: That the controller is in a state where we can * bring it back to life with an init struct. We can only use * fast sync commands, as the timeout is 5 seconds. * * @dev: device to configure *
*/
staticint aac_src_soft_reset(struct aac_dev *dev)
{
u32 status_omr = src_readl(dev, MUnit.OMR);
u32 status[5]; int rc = 1; int state = 0; char *state_str[7] = { "GET_ADAPTER_PROPERTIES Failed", "GET_ADAPTER_PROPERTIES timeout", "SOFT_RESET not supported", "DROP_IO Failed", "DROP_IO timeout", "Check Health failed"
};
if (status_omr == INVALID_OMR) return 1; // pcie hosed
if (!(status_omr & KERNEL_UP_AND_RUNNING)) return 1; // not up and running
/* * We go into soft reset mode to allow us to handle response
*/
dev->in_soft_reset = 1;
dev->msi_enabled = status_omr & AAC_INT_MODE_MSIX;
dev->base_size = AAC_MIN_SRCV_BAR0_SIZE; 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 = src_sync_cmd;
dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
if (dev->init_reset) {
dev->init_reset = false; if (aac_src_soft_reset(dev)) {
aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET);
++restart;
}
}
/* * Check to see if flash update is running. * Wait for the adapter to be up and running. Wait up to 5 minutes
*/
status = src_readl(dev, MUnit.OMR); if (status & FLASH_UPD_PENDING) {
start = jiffies; do {
status = src_readl(dev, MUnit.OMR); if (time_after(jiffies, start+HZ*FWUPD_TIMEOUT)) {
printk(KERN_ERR "%s%d: adapter flash update failed.\n",
dev->name, instance); goto error_iounmap;
}
} while (!(status & FLASH_UPD_SUCCESS) &&
!(status & FLASH_UPD_FAILED)); /* Delay 10 seconds. * Because right now FW is doing a soft reset, * do not read scratch pad register at this time
*/
ssleep(10);
} /* * Check to see if the board panic'd while booting.
*/
status = src_readl(dev, MUnit.OMR); if (status & KERNEL_PANIC) { if (aac_src_restart_adapter(dev,
aac_src_check_health(dev), IOP_HWSOFT_RESET)) goto error_iounmap;
++restart;
} /* * Check to see if the board failed any self tests.
*/
status = src_readl(dev, MUnit.OMR); 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
*/ do {
status = src_readl(dev, MUnit.OMR); if (status == INVALID_OMR)
status = 0;
if (restart && aac_commit)
aac_commit = 1; /* * Fill in the common function dispatch table.
*/
dev->a_ops.adapter_interrupt = aac_src_interrupt_adapter;
dev->a_ops.adapter_disable_int = aac_src_disable_interrupt;
dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
dev->a_ops.adapter_notify = aac_src_notify_adapter;
dev->a_ops.adapter_sync_cmd = src_sync_cmd;
dev->a_ops.adapter_check_health = aac_src_check_health;
dev->a_ops.adapter_restart = aac_src_restart_adapter;
dev->a_ops.adapter_start = aac_src_start_adapter;
/* * First clear out all interrupts. Then enable the one's that we * can handle.
*/
aac_adapter_comm(dev, AAC_COMM_MESSAGE);
aac_adapter_disable_int(dev);
src_writel(dev, MUnit.ODR_C, 0xffffffff);
aac_adapter_enable_int(dev);
if (aac_init_adapter(dev) == NULL) goto error_iounmap; if ((dev->comm_interface != AAC_COMM_MESSAGE_TYPE2) &&
(dev->comm_interface != AAC_COMM_MESSAGE_TYPE3)) goto error_iounmap; if (dev->msi_enabled)
aac_src_access_devreg(dev, AAC_ENABLE_MSIX);
case AAC_DISABLE_INTERRUPT:
src_writel(dev,
MUnit.OIMR,
dev->OIMR = AAC_INT_DISABLE_ALL); break;
case AAC_ENABLE_MSIX: /* set bit 6 */
val = src_readl(dev, MUnit.IDR);
val |= 0x40;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR); /* unmask int. */
val = PMC_ALL_INTERRUPT_BITS;
src_writel(dev, MUnit.IOAR, val);
val = src_readl(dev, MUnit.OIMR);
src_writel(dev,
MUnit.OIMR,
val & (~(PMC_GLOBAL_INT_BIT2 | PMC_GLOBAL_INT_BIT0))); break;
case AAC_DISABLE_MSIX: /* reset bit 6 */
val = src_readl(dev, MUnit.IDR);
val &= ~0x40;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR); break;
case AAC_CLEAR_AIF_BIT: /* set bit 5 */
val = src_readl(dev, MUnit.IDR);
val |= 0x20;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR); break;
case AAC_CLEAR_SYNC_BIT: /* set bit 4 */
val = src_readl(dev, MUnit.IDR);
val |= 0x10;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR); break;
case AAC_ENABLE_INTX: /* set bit 7 */
val = src_readl(dev, MUnit.IDR);
val |= 0x80;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR); /* unmask int. */
val = PMC_ALL_INTERRUPT_BITS;
src_writel(dev, MUnit.IOAR, val);
src_readl(dev, MUnit.IOAR);
val = src_readl(dev, MUnit.OIMR);
src_writel(dev, MUnit.OIMR,
val & (~(PMC_GLOBAL_INT_BIT2))); break;
default: break;
}
}
staticint aac_src_get_sync_status(struct aac_dev *dev)
{ int msix_val = 0; int legacy_val = 0;
if (!dev->msi_enabled) { /* * if Legacy int status indicates cmd is not complete * sample MSIx register to see if it indiactes cmd complete, * if yes set the controller in MSIx mode and consider cmd * completed
*/
legacy_val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT; if (!(legacy_val & 1) && msix_val)
dev->msi_enabled = 1; return legacy_val;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.