/*
* Copyright (C) 2005 - 2009 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 "be.h"
#include "be_cmds.h"
static void be_mcc_notify(struct be_adapter *adapter)
{
struct be_queue_info *mccq = &adapter->mcc_obj.q;
u32 val = 0;
val |= mccq->id & DB_MCCQ_RING_ID_MASK;
val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
iowrite32(val, adapter->db + DB_MCCQ_OFFSET);
}
/* To check if valid bit is set, check the entire word as we don't know
* the endianness of the data (old entry is host endian while a new entry is
* little endian) */
static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
{
if (compl->flags != 0) {
compl->flags = le32_to_cpu(compl->flags);
BUG_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0);
return true;
} else {
return false;
}
}
/* Need to reset the entire word that houses the valid bit */
static inline void be_mcc_compl_use(struct be_mcc_compl *compl)
{
compl->flags = 0;
}
static int be_mcc_compl_process(struct be_adapter *adapter,
struct be_mcc_compl *compl)
{
u16 compl_status, extd_status;
/* Just swap the status to host endian; mcc tag is opaquely copied
* from mcc_wrb */
be_dws_le_to_cpu(compl, 4);
compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
CQE_STATUS_COMPL_MASK;
if (compl_status != MCC_STATUS_SUCCESS) {
extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
CQE_STATUS_EXTD_MASK;
dev_warn(&adapter->pdev->dev,
"Error in cmd completion: status(compl/extd)=%d/%d\n",
compl_status, extd_status);
return -1;
}
return 0;
}
/* Link state evt is a string of bytes; no need for endian swapping */
static void be_async_link_state_process(struct be_adapter *adapter,
struct be_async_event_link_state *evt)
{
be_link_status_update(adapter,
evt->port_link_status == ASYNC_EVENT_LINK_UP);
}
static inline bool is_link_state_evt(u32 trailer)
{
return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
ASYNC_TRAILER_EVENT_CODE_MASK) ==
ASYNC_EVENT_CODE_LINK_STATE);
}
static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter)
{
struct be_queue_info *mcc_cq = &adapter->mcc_obj.cq;
struct be_mcc_compl *compl = queue_tail_node(mcc_cq);
if (be_mcc_compl_is_new(compl)) {
queue_tail_inc(mcc_cq);
return compl;
}
return NULL;
}
void be_process_mcc(struct be_adapter *adapter)
{
struct be_mcc_compl *compl;
int num = 0;
spin_lock_bh(&adapter->mcc_cq_lock);
while ((compl = be_mcc_compl_get(adapter))) {
if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
/* Interpret flags as an async trailer */
BUG_ON(!is_link_state_evt(compl->flags));
/* Interpret compl as a async link evt */
be_async_link_state_process(adapter,
(struct be_async_event_link_state *) compl);
} else {
be_mcc_compl_process(adapter, compl);
atomic_dec(&adapter->mcc_obj.q.used);
}
be_mcc_compl_use(compl);
num++;
}
if (num)
be_cq_notify(adapter, adapter->mcc_obj.cq.id, true, num);
spin_unlock_bh(&adapter->mcc_cq_lock);
}
/* Wait till no more pending mcc requests are present */
static void be_mcc_wait_compl(struct be_adapter *adapter)
{
#define mcc_timeout 50000 /* 5s timeout */
int i;
for (i = 0; i < mcc_timeout; i++) {
be_process_mcc(adapter);
if (atomic_read(&adapter->mcc_obj.q.used) == 0)
break;
udelay(100);
}
if (i == mcc_timeout)
dev_err(&adapter->pdev->dev, "mccq poll timed out\n");
}
/* Notify MCC reques