300 lines
9.2 KiB
C
300 lines
9.2 KiB
C
/*
|
|
* Copyright (C) 2005 - 2008 ServerEngines
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation. The full GNU General
|
|
* Public License is included in this distribution in the file called COPYING.
|
|
*
|
|
* Contact Information:
|
|
* linux-drivers@serverengines.com
|
|
*
|
|
* ServerEngines
|
|
* 209 N. Fair Oaks Ave
|
|
* Sunnyvale, CA 94085
|
|
*/
|
|
#include "hwlib.h"
|
|
#include "bestatus.h"
|
|
/*
|
|
This routine creates an event queue based on the client completion
|
|
queue configuration information.
|
|
|
|
FunctionObject - Handle to a function object
|
|
EqBaseVa - Base VA for a the EQ ring
|
|
SizeEncoding - The encoded size for the EQ entries. This value is
|
|
either CEV_EQ_SIZE_4 or CEV_EQ_SIZE_16
|
|
NumEntries - CEV_CQ_CNT_* values.
|
|
Watermark - Enables watermark based coalescing. This parameter
|
|
must be of the type CEV_WMARK_* if watermarks
|
|
are enabled. If watermarks to to be disabled
|
|
this value should be-1.
|
|
TimerDelay - If a timer delay is enabled this value should be the
|
|
time of the delay in 8 microsecond units. If
|
|
delays are not used this parameter should be
|
|
set to -1.
|
|
ppEqObject - Internal EQ Handle returned.
|
|
|
|
Returns BE_SUCCESS if successfull,, otherwise a useful error code
|
|
is returned.
|
|
|
|
IRQL < DISPATCH_LEVEL
|
|
*/
|
|
int
|
|
be_eq_create(struct be_function_object *pfob,
|
|
struct ring_desc *rd, u32 eqe_size, u32 num_entries,
|
|
u32 watermark, /* CEV_WMARK_* or -1 */
|
|
u32 timer_delay, /* in 8us units, or -1 */
|
|
struct be_eq_object *eq_object)
|
|
{
|
|
int status = BE_SUCCESS;
|
|
u32 num_entries_encoding, eqe_size_encoding, length;
|
|
struct FWCMD_COMMON_EQ_CREATE *fwcmd = NULL;
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
u32 n;
|
|
unsigned long irql;
|
|
|
|
ASSERT(rd);
|
|
ASSERT(eq_object);
|
|
|
|
switch (num_entries) {
|
|
case 256:
|
|
num_entries_encoding = CEV_EQ_CNT_256;
|
|
break;
|
|
case 512:
|
|
num_entries_encoding = CEV_EQ_CNT_512;
|
|
break;
|
|
case 1024:
|
|
num_entries_encoding = CEV_EQ_CNT_1024;
|
|
break;
|
|
case 2048:
|
|
num_entries_encoding = CEV_EQ_CNT_2048;
|
|
break;
|
|
case 4096:
|
|
num_entries_encoding = CEV_EQ_CNT_4096;
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
return BE_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (eqe_size) {
|
|
case 4:
|
|
eqe_size_encoding = CEV_EQ_SIZE_4;
|
|
break;
|
|
case 16:
|
|
eqe_size_encoding = CEV_EQ_SIZE_16;
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
return BE_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((eqe_size == 4 && num_entries < 1024) ||
|
|
(eqe_size == 16 && num_entries == 4096)) {
|
|
TRACE(DL_ERR, "Bad EQ size. eqe_size:%d num_entries:%d",
|
|
eqe_size, num_entries);
|
|
ASSERT(0);
|
|
return BE_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
memset(eq_object, 0, sizeof(*eq_object));
|
|
|
|
atomic_set(&eq_object->ref_count, 0);
|
|
eq_object->parent_function = pfob;
|
|
eq_object->eq_id = 0xFFFFFFFF;
|
|
|
|
INIT_LIST_HEAD(&eq_object->cq_list_head);
|
|
|
|
length = num_entries * eqe_size;
|
|
|
|
spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
|
wrb = be_function_peek_mcc_wrb(pfob);
|
|
if (!wrb) {
|
|
ASSERT(wrb);
|
|
TRACE(DL_ERR, "No free MCC WRBs in create EQ.");
|
|
status = BE_STATUS_NO_MCC_WRB;
|
|
goto Error;
|
|
}
|
|
/* Prepares an embedded fwcmd, including request/response sizes. */
|
|
fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_EQ_CREATE);
|
|
|
|
fwcmd->params.request.num_pages = PAGES_SPANNED(OFFSET_IN_PAGE(rd->va),
|
|
length);
|
|
n = pfob->pci_function_number;
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Func, &fwcmd->params.request.context, n);
|
|
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, valid, &fwcmd->params.request.context, 1);
|
|
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Size,
|
|
&fwcmd->params.request.context, eqe_size_encoding);
|
|
|
|
n = 0; /* Protection Domain is always 0 in Linux driver */
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, PD, &fwcmd->params.request.context, n);
|
|
|
|
/* Let the caller ARM the EQ with the doorbell. */
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Armed, &fwcmd->params.request.context, 0);
|
|
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Count, &fwcmd->params.request.context,
|
|
num_entries_encoding);
|
|
|
|
n = pfob->pci_function_number * 32;
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, EventVect,
|
|
&fwcmd->params.request.context, n);
|
|
if (watermark != -1) {
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, WME,
|
|
&fwcmd->params.request.context, 1);
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Watermark,
|
|
&fwcmd->params.request.context, watermark);
|
|
ASSERT(watermark <= CEV_WMARK_240);
|
|
} else
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, WME,
|
|
&fwcmd->params.request.context, 0);
|
|
if (timer_delay != -1) {
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR,
|
|
&fwcmd->params.request.context, 1);
|
|
|
|
ASSERT(timer_delay <= 250); /* max value according to EAS */
|
|
timer_delay = min(timer_delay, (u32)250);
|
|
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, Delay,
|
|
&fwcmd->params.request.context, timer_delay);
|
|
} else {
|
|
AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR,
|
|
&fwcmd->params.request.context, 0);
|
|
}
|
|
/* Create a page list for the FWCMD. */
|
|
be_rd_to_pa_list(rd, fwcmd->params.request.pages,
|
|
ARRAY_SIZE(fwcmd->params.request.pages));
|
|
|
|
status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL,
|
|
NULL, NULL, fwcmd, NULL);
|
|
if (status != BE_SUCCESS) {
|
|
TRACE(DL_ERR, "MCC to create EQ failed.");
|
|
goto Error;
|
|
}
|
|
/* Get the EQ id. The MPU allocates the IDs. */
|
|
eq_object->eq_id = fwcmd->params.response.eq_id;
|
|
|
|
Error:
|
|
spin_unlock_irqrestore(&pfob->post_lock, irql);
|
|
|
|
if (pfob->pend_queue_driving && pfob->mcc) {
|
|
pfob->pend_queue_driving = 0;
|
|
be_drive_mcc_wrb_queue(pfob->mcc);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
Deferences the given object. Once the object's reference count drops to
|
|
zero, the object is destroyed and all resources that are held by this
|
|
object are released. The on-chip context is also destroyed along with
|
|
the queue ID, and any mappings made into the UT.
|
|
|
|
eq_object - EQ handle returned from eq_object_create.
|
|
|
|
Returns BE_SUCCESS if successfull, otherwise a useful error code
|
|
is returned.
|
|
|
|
IRQL: IRQL < DISPATCH_LEVEL
|
|
*/
|
|
int be_eq_destroy(struct be_eq_object *eq_object)
|
|
{
|
|
int status = 0;
|
|
|
|
ASSERT(atomic_read(&eq_object->ref_count) == 0);
|
|
/* no CQs should reference this EQ now */
|
|
ASSERT(list_empty(&eq_object->cq_list_head));
|
|
|
|
/* Send fwcmd to destroy the EQ. */
|
|
status = be_function_ring_destroy(eq_object->parent_function,
|
|
eq_object->eq_id, FWCMD_RING_TYPE_EQ,
|
|
NULL, NULL, NULL, NULL);
|
|
ASSERT(status == 0);
|
|
|
|
return BE_SUCCESS;
|
|
}
|
|
/*
|
|
*---------------------------------------------------------------------------
|
|
* Function: be_eq_modify_delay
|
|
* Changes the EQ delay for a group of EQs.
|
|
* num_eq - The number of EQs in the eq_array to adjust.
|
|
* This also is the number of delay values in
|
|
* the eq_delay_array.
|
|
* eq_array - Array of struct be_eq_object pointers to adjust.
|
|
* eq_delay_array - Array of "num_eq" timer delays in units
|
|
* of microseconds. The be_eq_query_delay_range
|
|
* fwcmd returns the resolution and range of
|
|
* legal EQ delays.
|
|
* cb -
|
|
* cb_context -
|
|
* q_ctxt - Optional. Pointer to a previously allocated
|
|
* struct. If the MCC WRB ring is full, this
|
|
* structure is used to queue the operation. It
|
|
* will be posted to the MCC ring when space
|
|
* becomes available. All queued commands will
|
|
* be posted to the ring in the order they are
|
|
* received. It is always valid to pass a pointer to
|
|
* a generic be_generic_q_cntxt. However,
|
|
* the specific context structs
|
|
* are generally smaller than the generic struct.
|
|
* return pend_status - BE_SUCCESS (0) on success.
|
|
* BE_PENDING (postive value) if the FWCMD
|
|
* completion is pending. Negative error code on failure.
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
int
|
|
be_eq_modify_delay(struct be_function_object *pfob,
|
|
u32 num_eq, struct be_eq_object **eq_array,
|
|
u32 *eq_delay_array, mcc_wrb_cqe_callback cb,
|
|
void *cb_context, struct be_eq_modify_delay_q_ctxt *q_ctxt)
|
|
{
|
|
struct FWCMD_COMMON_MODIFY_EQ_DELAY *fwcmd = NULL;
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
int status = 0;
|
|
struct be_generic_q_ctxt *gen_ctxt = NULL;
|
|
u32 i;
|
|
unsigned long irql;
|
|
|
|
spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
|
wrb = be_function_peek_mcc_wrb(pfob);
|
|
if (!wrb) {
|
|
if (q_ctxt && cb) {
|
|
wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header;
|
|
gen_ctxt = (struct be_generic_q_ctxt *) q_ctxt;
|
|
gen_ctxt->context.bytes = sizeof(*q_ctxt);
|
|
} else {
|
|
status = BE_STATUS_NO_MCC_WRB;
|
|
goto Error;
|
|
}
|
|
}
|
|
/* Prepares an embedded fwcmd, including request/response sizes. */
|
|
fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_MODIFY_EQ_DELAY);
|
|
|
|
ASSERT(num_eq > 0);
|
|
ASSERT(num_eq <= ARRAY_SIZE(fwcmd->params.request.delay));
|
|
fwcmd->params.request.num_eq = num_eq;
|
|
for (i = 0; i < num_eq; i++) {
|
|
fwcmd->params.request.delay[i].eq_id = eq_array[i]->eq_id;
|
|
fwcmd->params.request.delay[i].delay_in_microseconds =
|
|
eq_delay_array[i];
|
|
}
|
|
|
|
/* Post the f/w command */
|
|
status = be_function_post_mcc_wrb(pfob, wrb, gen_ctxt,
|
|
cb, cb_context, NULL, NULL, fwcmd, NULL);
|
|
|
|
Error:
|
|
spin_unlock_irqrestore(&pfob->post_lock, irql);
|
|
|
|
if (pfob->pend_queue_driving && pfob->mcc) {
|
|
pfob->pend_queue_driving = 0;
|
|
be_drive_mcc_wrb_queue(pfob->mcc);
|
|
}
|
|
return status;
|
|
}
|
|
|