blob: db92ccd8fed858ece061df3de3502ba66bc4cece [file] [log] [blame]
/*
* 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;
}