566 lines
15 KiB
C
566 lines
15 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"
|
|
|
|
|
|
int
|
|
be_function_internal_query_firmware_config(struct be_function_object *pfob,
|
|
struct BE_FIRMWARE_CONFIG *config)
|
|
{
|
|
struct FWCMD_COMMON_FIRMWARE_CONFIG *fwcmd = NULL;
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
int status = 0;
|
|
unsigned long irql;
|
|
struct be_mcc_wrb_response_copy rc;
|
|
|
|
spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
|
wrb = be_function_peek_mcc_wrb(pfob);
|
|
if (!wrb) {
|
|
TRACE(DL_ERR, "MCC wrb peek failed.");
|
|
status = BE_STATUS_NO_MCC_WRB;
|
|
goto error;
|
|
}
|
|
/* Prepares an embedded fwcmd, including request/response sizes. */
|
|
fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_FIRMWARE_CONFIG);
|
|
|
|
rc.length = FIELD_SIZEOF(struct FWCMD_COMMON_FIRMWARE_CONFIG,
|
|
params.response);
|
|
rc.fwcmd_offset = offsetof(struct FWCMD_COMMON_FIRMWARE_CONFIG,
|
|
params.response);
|
|
rc.va = config;
|
|
|
|
/* Post the f/w command */
|
|
status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL,
|
|
NULL, NULL, NULL, fwcmd, &rc);
|
|
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;
|
|
}
|
|
|
|
/*
|
|
This allocates and initializes a function object based on the information
|
|
provided by upper layer drivers.
|
|
|
|
Returns BE_SUCCESS on success and an appropriate int on failure.
|
|
|
|
A function object represents a single BladeEngine (logical) PCI function.
|
|
That is a function object either represents
|
|
the networking side of BladeEngine or the iSCSI side of BladeEngine.
|
|
|
|
This routine will also detect and create an appropriate PD object for the
|
|
PCI function as needed.
|
|
*/
|
|
int
|
|
be_function_object_create(u8 __iomem *csr_va, u8 __iomem *db_va,
|
|
u8 __iomem *pci_va, u32 function_type,
|
|
struct ring_desc *mailbox, struct be_function_object *pfob)
|
|
{
|
|
int status;
|
|
|
|
ASSERT(pfob); /* not a magic assert */
|
|
ASSERT(function_type <= 2);
|
|
|
|
TRACE(DL_INFO, "Create function object. type:%s object:0x%p",
|
|
(function_type == BE_FUNCTION_TYPE_ISCSI ? "iSCSI" :
|
|
(function_type == BE_FUNCTION_TYPE_NETWORK ? "Network" :
|
|
"Arm")), pfob);
|
|
|
|
memset(pfob, 0, sizeof(*pfob));
|
|
|
|
pfob->type = function_type;
|
|
pfob->csr_va = csr_va;
|
|
pfob->db_va = db_va;
|
|
pfob->pci_va = pci_va;
|
|
|
|
spin_lock_init(&pfob->cq_lock);
|
|
spin_lock_init(&pfob->post_lock);
|
|
spin_lock_init(&pfob->mcc_context_lock);
|
|
|
|
|
|
pfob->pci_function_number = 1;
|
|
|
|
|
|
pfob->emulate = false;
|
|
TRACE(DL_NOTE, "Non-emulation mode");
|
|
status = be_drive_POST(pfob);
|
|
if (status != BE_SUCCESS) {
|
|
TRACE(DL_ERR, "BladeEngine POST failed.");
|
|
goto error;
|
|
}
|
|
|
|
/* Initialize the mailbox */
|
|
status = be_mpu_init_mailbox(pfob, mailbox);
|
|
if (status != BE_SUCCESS) {
|
|
TRACE(DL_ERR, "Failed to initialize mailbox.");
|
|
goto error;
|
|
}
|
|
/*
|
|
* Cache the firmware config for ASSERTs in hwclib and later
|
|
* driver queries.
|
|
*/
|
|
status = be_function_internal_query_firmware_config(pfob,
|
|
&pfob->fw_config);
|
|
if (status != BE_SUCCESS) {
|
|
TRACE(DL_ERR, "Failed to query firmware config.");
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (status != BE_SUCCESS) {
|
|
/* No cleanup necessary */
|
|
TRACE(DL_ERR, "Failed to create function.");
|
|
memset(pfob, 0, sizeof(*pfob));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
This routine drops the reference count on a given function object. Once
|
|
the reference count falls to zero, the function object is destroyed and all
|
|
resources held are freed.
|
|
|
|
FunctionObject - The function object to drop the reference to.
|
|
*/
|
|
int be_function_object_destroy(struct be_function_object *pfob)
|
|
{
|
|
TRACE(DL_INFO, "Destroy pfob. Object:0x%p",
|
|
pfob);
|
|
|
|
|
|
ASSERT(pfob->mcc == NULL);
|
|
|
|
return BE_SUCCESS;
|
|
}
|
|
|
|
int be_function_cleanup(struct be_function_object *pfob)
|
|
{
|
|
int status = 0;
|
|
u32 isr;
|
|
u32 host_intr;
|
|
struct PCICFG_HOST_TIMER_INT_CTRL_CSR_AMAP ctrl;
|
|
|
|
|
|
if (pfob->type == BE_FUNCTION_TYPE_NETWORK) {
|
|
status = be_rxf_multicast_config(pfob, false, 0,
|
|
NULL, NULL, NULL, NULL);
|
|
ASSERT(status == BE_SUCCESS);
|
|
}
|
|
/* VLAN */
|
|
status = be_rxf_vlan_config(pfob, false, 0, NULL, NULL, NULL, NULL);
|
|
ASSERT(status == BE_SUCCESS);
|
|
/*
|
|
* MCC Queue -- Switches to mailbox mode. May want to destroy
|
|
* all but the MCC CQ before this call if polling CQ is much better
|
|
* performance than polling mailbox register.
|
|
*/
|
|
if (pfob->mcc)
|
|
status = be_mcc_ring_destroy(pfob->mcc);
|
|
/*
|
|
* If interrupts are disabled, clear any CEV interrupt assertions that
|
|
* fired after we stopped processing EQs.
|
|
*/
|
|
ctrl.dw[0] = PCICFG1_READ(pfob, host_timer_int_ctrl);
|
|
host_intr = AMAP_GET_BITS_PTR(PCICFG_HOST_TIMER_INT_CTRL_CSR,
|
|
hostintr, ctrl.dw);
|
|
if (!host_intr)
|
|
if (pfob->type == BE_FUNCTION_TYPE_NETWORK)
|
|
isr = CSR_READ(pfob, cev.isr1);
|
|
else
|
|
isr = CSR_READ(pfob, cev.isr0);
|
|
else
|
|
/* This should never happen... */
|
|
TRACE(DL_ERR, "function_cleanup called with interrupt enabled");
|
|
/* Function object destroy */
|
|
status = be_function_object_destroy(pfob);
|
|
ASSERT(status == BE_SUCCESS);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
void *
|
|
be_function_prepare_embedded_fwcmd(struct be_function_object *pfob,
|
|
struct MCC_WRB_AMAP *wrb, u32 payld_len, u32 request_length,
|
|
u32 response_length, u32 opcode, u32 subsystem)
|
|
{
|
|
struct FWCMD_REQUEST_HEADER *header = NULL;
|
|
u32 n;
|
|
|
|
ASSERT(wrb);
|
|
|
|
n = offsetof(struct BE_MCC_WRB_AMAP, payload)/8;
|
|
AMAP_SET_BITS_PTR(MCC_WRB, embedded, wrb, 1);
|
|
AMAP_SET_BITS_PTR(MCC_WRB, payload_length, wrb, min(payld_len, n));
|
|
header = (struct FWCMD_REQUEST_HEADER *)((u8 *)wrb + n);
|
|
|
|
header->timeout = 0;
|
|
header->domain = 0;
|
|
header->request_length = max(request_length, response_length);
|
|
header->opcode = opcode;
|
|
header->subsystem = subsystem;
|
|
|
|
return header;
|
|
}
|
|
|
|
void *
|
|
be_function_prepare_nonembedded_fwcmd(struct be_function_object *pfob,
|
|
struct MCC_WRB_AMAP *wrb,
|
|
void *fwcmd_va, u64 fwcmd_pa,
|
|
u32 payld_len,
|
|
u32 request_length,
|
|
u32 response_length,
|
|
u32 opcode, u32 subsystem)
|
|
{
|
|
struct FWCMD_REQUEST_HEADER *header = NULL;
|
|
u32 n;
|
|
struct MCC_WRB_PAYLOAD_AMAP *plp;
|
|
|
|
ASSERT(wrb);
|
|
ASSERT(fwcmd_va);
|
|
|
|
header = (struct FWCMD_REQUEST_HEADER *) fwcmd_va;
|
|
|
|
AMAP_SET_BITS_PTR(MCC_WRB, embedded, wrb, 0);
|
|
AMAP_SET_BITS_PTR(MCC_WRB, payload_length, wrb, payld_len);
|
|
|
|
/*
|
|
* Assume one fragment. The caller may override the SGL by
|
|
* rewriting the 0th length and adding more entries. They
|
|
* will also need to update the sge_count.
|
|
*/
|
|
AMAP_SET_BITS_PTR(MCC_WRB, sge_count, wrb, 1);
|
|
|
|
n = offsetof(struct BE_MCC_WRB_AMAP, payload)/8;
|
|
plp = (struct MCC_WRB_PAYLOAD_AMAP *)((u8 *)wrb + n);
|
|
AMAP_SET_BITS_PTR(MCC_WRB_PAYLOAD, sgl[0].length, plp, payld_len);
|
|
AMAP_SET_BITS_PTR(MCC_WRB_PAYLOAD, sgl[0].pa_lo, plp, (u32)fwcmd_pa);
|
|
AMAP_SET_BITS_PTR(MCC_WRB_PAYLOAD, sgl[0].pa_hi, plp,
|
|
upper_32_bits(fwcmd_pa));
|
|
|
|
header->timeout = 0;
|
|
header->domain = 0;
|
|
header->request_length = max(request_length, response_length);
|
|
header->opcode = opcode;
|
|
header->subsystem = subsystem;
|
|
|
|
return header;
|
|
}
|
|
|
|
struct MCC_WRB_AMAP *
|
|
be_function_peek_mcc_wrb(struct be_function_object *pfob)
|
|
{
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
u32 offset;
|
|
|
|
if (pfob->mcc)
|
|
wrb = _be_mpu_peek_ring_wrb(pfob->mcc, false);
|
|
else {
|
|
offset = offsetof(struct BE_MCC_MAILBOX_AMAP, wrb)/8;
|
|
wrb = (struct MCC_WRB_AMAP *) ((u8 *) pfob->mailbox.va +
|
|
offset);
|
|
}
|
|
|
|
if (wrb)
|
|
memset(wrb, 0, sizeof(struct MCC_WRB_AMAP));
|
|
|
|
return wrb;
|
|
}
|
|
|
|
#if defined(BE_DEBUG)
|
|
void be_function_debug_print_wrb(struct be_function_object *pfob,
|
|
struct MCC_WRB_AMAP *wrb, void *optional_fwcmd_va,
|
|
struct be_mcc_wrb_context *wrb_context)
|
|
{
|
|
|
|
struct FWCMD_REQUEST_HEADER *header = NULL;
|
|
u8 embedded;
|
|
u32 n;
|
|
|
|
embedded = AMAP_GET_BITS_PTR(MCC_WRB, embedded, wrb);
|
|
|
|
if (embedded) {
|
|
n = offsetof(struct BE_MCC_WRB_AMAP, payload)/8;
|
|
header = (struct FWCMD_REQUEST_HEADER *)((u8 *)wrb + n);
|
|
} else {
|
|
header = (struct FWCMD_REQUEST_HEADER *) optional_fwcmd_va;
|
|
}
|
|
|
|
/* Save the completed count before posting for a debug assert. */
|
|
|
|
if (header) {
|
|
wrb_context->opcode = header->opcode;
|
|
wrb_context->subsystem = header->subsystem;
|
|
|
|
} else {
|
|
wrb_context->opcode = 0;
|
|
wrb_context->subsystem = 0;
|
|
}
|
|
}
|
|
#else
|
|
#define be_function_debug_print_wrb(a_, b_, c_, d_)
|
|
#endif
|
|
|
|
int
|
|
be_function_post_mcc_wrb(struct be_function_object *pfob,
|
|
struct MCC_WRB_AMAP *wrb,
|
|
struct be_generic_q_ctxt *q_ctxt,
|
|
mcc_wrb_cqe_callback cb, void *cb_context,
|
|
mcc_wrb_cqe_callback internal_cb,
|
|
void *internal_cb_context, void *optional_fwcmd_va,
|
|
struct be_mcc_wrb_response_copy *rc)
|
|
{
|
|
int status;
|
|
struct be_mcc_wrb_context *wrb_context = NULL;
|
|
u64 *p;
|
|
|
|
if (q_ctxt) {
|
|
/* Initialize context. */
|
|
q_ctxt->context.internal_cb = internal_cb;
|
|
q_ctxt->context.internal_cb_context = internal_cb_context;
|
|
q_ctxt->context.cb = cb;
|
|
q_ctxt->context.cb_context = cb_context;
|
|
if (rc) {
|
|
q_ctxt->context.copy.length = rc->length;
|
|
q_ctxt->context.copy.fwcmd_offset = rc->fwcmd_offset;
|
|
q_ctxt->context.copy.va = rc->va;
|
|
} else
|
|
q_ctxt->context.copy.length = 0;
|
|
|
|
q_ctxt->context.optional_fwcmd_va = optional_fwcmd_va;
|
|
|
|
/* Queue this request */
|
|
status = be_function_queue_mcc_wrb(pfob, q_ctxt);
|
|
|
|
goto Error;
|
|
}
|
|
/*
|
|
* Allocate a WRB context struct to hold the callback pointers,
|
|
* status, etc. This is required if commands complete out of order.
|
|
*/
|
|
wrb_context = _be_mcc_allocate_wrb_context(pfob);
|
|
if (!wrb_context) {
|
|
TRACE(DL_WARN, "Failed to allocate MCC WRB context.");
|
|
status = BE_STATUS_SYSTEM_RESOURCES;
|
|
goto Error;
|
|
}
|
|
/* Initialize context. */
|
|
memset(wrb_context, 0, sizeof(*wrb_context));
|
|
wrb_context->internal_cb = internal_cb;
|
|
wrb_context->internal_cb_context = internal_cb_context;
|
|
wrb_context->cb = cb;
|
|
wrb_context->cb_context = cb_context;
|
|
if (rc) {
|
|
wrb_context->copy.length = rc->length;
|
|
wrb_context->copy.fwcmd_offset = rc->fwcmd_offset;
|
|
wrb_context->copy.va = rc->va;
|
|
} else
|
|
wrb_context->copy.length = 0;
|
|
wrb_context->wrb = wrb;
|
|
|
|
/*
|
|
* Copy the context pointer into the WRB opaque tag field.
|
|
* Verify assumption of 64-bit tag with a compile time assert.
|
|
*/
|
|
p = (u64 *) ((u8 *)wrb + offsetof(struct BE_MCC_WRB_AMAP, tag)/8);
|
|
*p = (u64)(size_t)wrb_context;
|
|
|
|
/* Print info about this FWCMD for debug builds. */
|
|
be_function_debug_print_wrb(pfob, wrb, optional_fwcmd_va, wrb_context);
|
|
|
|
/*
|
|
* issue the WRB to the MPU as appropriate
|
|
*/
|
|
if (pfob->mcc) {
|
|
/*
|
|
* we're in WRB mode, pass to the mcc layer
|
|
*/
|
|
status = _be_mpu_post_wrb_ring(pfob->mcc, wrb, wrb_context);
|
|
} else {
|
|
/*
|
|
* we're in mailbox mode
|
|
*/
|
|
status = _be_mpu_post_wrb_mailbox(pfob, wrb, wrb_context);
|
|
|
|
/* mailbox mode always completes synchronously */
|
|
ASSERT(status != BE_STATUS_PENDING);
|
|
}
|
|
|
|
Error:
|
|
|
|
return status;
|
|
}
|
|
|
|
int
|
|
be_function_ring_destroy(struct be_function_object *pfob,
|
|
u32 id, u32 ring_type, mcc_wrb_cqe_callback cb,
|
|
void *cb_context, mcc_wrb_cqe_callback internal_cb,
|
|
void *internal_cb_context)
|
|
{
|
|
|
|
struct FWCMD_COMMON_RING_DESTROY *fwcmd = NULL;
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
int status = 0;
|
|
unsigned long irql;
|
|
|
|
spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
|
TRACE(DL_INFO, "Destroy ring id:%d type:%d", id, ring_type);
|
|
|
|
wrb = be_function_peek_mcc_wrb(pfob);
|
|
if (!wrb) {
|
|
ASSERT(wrb);
|
|
TRACE(DL_ERR, "No free MCC WRBs in destroy ring.");
|
|
status = BE_STATUS_NO_MCC_WRB;
|
|
goto Error;
|
|
}
|
|
/* Prepares an embedded fwcmd, including request/response sizes. */
|
|
fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_RING_DESTROY);
|
|
|
|
fwcmd->params.request.id = id;
|
|
fwcmd->params.request.ring_type = ring_type;
|
|
|
|
/* Post the f/w command */
|
|
status = be_function_post_mcc_wrb(pfob, wrb, NULL, cb, cb_context,
|
|
internal_cb, internal_cb_context, fwcmd, NULL);
|
|
if (status != BE_SUCCESS && status != BE_PENDING) {
|
|
TRACE(DL_ERR, "Ring destroy fwcmd failed. id:%d ring_type:%d",
|
|
id, ring_type);
|
|
goto Error;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
be_rd_to_pa_list(struct ring_desc *rd, struct PHYS_ADDR *pa_list, u32 max_num)
|
|
{
|
|
u32 num_pages = PAGES_SPANNED(rd->va, rd->length);
|
|
u32 i = 0;
|
|
u64 pa = rd->pa;
|
|
__le64 lepa;
|
|
|
|
ASSERT(pa_list);
|
|
ASSERT(pa);
|
|
|
|
for (i = 0; i < min(num_pages, max_num); i++) {
|
|
lepa = cpu_to_le64(pa);
|
|
pa_list[i].lo = (u32)lepa;
|
|
pa_list[i].hi = upper_32_bits(lepa);
|
|
pa += PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Function: be_function_get_fw_version
|
|
* Retrieves the firmware version on the adpater. If the callback is
|
|
* NULL this call executes synchronously. If the callback is not NULL,
|
|
* the returned status will be BE_PENDING if the command was issued
|
|
* successfully.
|
|
* pfob -
|
|
* fwv - Pointer to response buffer if callback is NULL.
|
|
* cb - Callback function invoked when the FWCMD completes.
|
|
* cb_context - Passed to the callback function.
|
|
* 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_function_get_fw_version(struct be_function_object *pfob,
|
|
struct FWCMD_COMMON_GET_FW_VERSION_RESPONSE_PAYLOAD *fwv,
|
|
mcc_wrb_cqe_callback cb, void *cb_context)
|
|
{
|
|
int status = BE_SUCCESS;
|
|
struct MCC_WRB_AMAP *wrb = NULL;
|
|
struct FWCMD_COMMON_GET_FW_VERSION *fwcmd = NULL;
|
|
unsigned long irql;
|
|
struct be_mcc_wrb_response_copy rc;
|
|
|
|
spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
|
wrb = be_function_peek_mcc_wrb(pfob);
|
|
if (!wrb) {
|
|
TRACE(DL_ERR, "MCC wrb peek failed.");
|
|
status = BE_STATUS_NO_MCC_WRB;
|
|
goto Error;
|
|
}
|
|
|
|
if (!cb && !fwv) {
|
|
TRACE(DL_ERR, "callback and response buffer NULL!");
|
|
status = BE_NOT_OK;
|
|
goto Error;
|
|
}
|
|
/* Prepares an embedded fwcmd, including request/response sizes. */
|
|
fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_GET_FW_VERSION);
|
|
|
|
rc.length = FIELD_SIZEOF(struct FWCMD_COMMON_GET_FW_VERSION,
|
|
params.response);
|
|
rc.fwcmd_offset = offsetof(struct FWCMD_COMMON_GET_FW_VERSION,
|
|
params.response);
|
|
rc.va = fwv;
|
|
|
|
/* Post the f/w command */
|
|
status = be_function_post_mcc_wrb(pfob, wrb, NULL, cb,
|
|
cb_context, NULL, NULL, fwcmd, &rc);
|
|
|
|
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;
|
|
}
|
|
|
|
int
|
|
be_function_queue_mcc_wrb(struct be_function_object *pfob,
|
|
struct be_generic_q_ctxt *q_ctxt)
|
|
{
|
|
int status;
|
|
|
|
ASSERT(q_ctxt);
|
|
|
|
/*
|
|
* issue the WRB to the MPU as appropriate
|
|
*/
|
|
if (pfob->mcc) {
|
|
|
|
/* We're in ring mode. Queue this item. */
|
|
pfob->mcc->backlog_length++;
|
|
list_add_tail(&q_ctxt->context.list, &pfob->mcc->backlog);
|
|
status = BE_PENDING;
|
|
} else {
|
|
status = BE_NOT_OK;
|
|
}
|
|
return status;
|
|
}
|
|
|