/*
* Copyright (c) 2012 Intel Corporation. All rights reserved.
* Copyright (c) 2007 - 2012 QLogic Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#include "qib.h"
#include "qib_common.h"
/* default pio off, sdma on */
static ushort sdma_descq_cnt = 256;
module_param_named(sdma_descq_cnt, sdma_descq_cnt, ushort, S_IRUGO);
MODULE_PARM_DESC(sdma_descq_cnt, "Number of SDMA descq entries");
/*
* Bits defined in the send DMA descriptor.
*/
#define SDMA_DESC_LAST (1ULL << 11)
#define SDMA_DESC_FIRST (1ULL << 12)
#define SDMA_DESC_DMA_HEAD (1ULL << 13)
#define SDMA_DESC_USE_LARGE_BUF (1ULL << 14)
#define SDMA_DESC_INTR (1ULL << 15)
#define SDMA_DESC_COUNT_LSB 16
#define SDMA_DESC_GEN_LSB 30
char *qib_sdma_state_names[] = {
[qib_sdma_state_s00_hw_down] = "s00_HwDown",
[qib_sdma_state_s10_hw_start_up_wait] = "s10_HwStartUpWait",
[qib_sdma_state_s20_idle] = "s20_Idle",
[qib_sdma_state_s30_sw_clean_up_wait] = "s30_SwCleanUpWait",
[qib_sdma_state_s40_hw_clean_up_wait] = "s40_HwCleanUpWait",
[qib_sdma_state_s50_hw_halt_wait] = "s50_HwHaltWait",
[qib_sdma_state_s99_running] = "s99_Running",
};
char *qib_sdma_event_names[] = {
[qib_sdma_event_e00_go_hw_down] = "e00_GoHwDown",
[qib_sdma_event_e10_go_hw_start] = "e10_GoHwStart",
[qib_sdma_event_e20_hw_started] = "e20_HwStarted",
[qib_sdma_event_e30_go_running] = "e30_GoRunning",
[qib_sdma_event_e40_sw_cleaned] = "e40_SwCleaned",
[qib_sdma_event_e50_hw_cleaned] = "e50_HwCleaned",
[qib_sdma_event_e60_hw_halted] = "e60_HwHalted",
[qib_sdma_event_e70_go_idle] = "e70_GoIdle",
[qib_sdma_event_e7220_err_halted] = "e7220_ErrHalted",
[qib_sdma_event_e7322_err_halted] = "e7322_ErrHalted",
[qib_sdma_event_e90_timer_tick] = "e90_TimerTick",
};
/* declare all statics here rather than keep sorting */
static int alloc_sdma(struct qib_pportdata *);
static void sdma_complete(struct kref *);
static void sdma_finalput(struct qib_sdma_state *);
static void sdma_get(struct qib_sdma_state *);
static void sdma_put(struct qib_sdma_state *);
static void sdma_set_state(struct qib_pportdata *, enum qib_sdma_states);
static void sdma_start_sw_clean_up(struct qib_pportdata *);
static void sdma_sw_clean_up_task(unsigned long);
static void unmap_desc(struct qib_pportdata *, unsigned);
static void sdma_get(struct qib_sdma_state *ss)
{
kref_get(&ss->kref);
}
static void sdma_complete(struct kref *kref)
{
struct qib_sdma_state *ss =
container_of(kref, struct qib_sdma_state, kref);
complete(&ss->comp);
}
static void sdma_put(struct qib_sdma_state *ss)
{
kref_put(&ss->kref, sdma_complete);
}
static void sdma_finalput(struct qib_sdma_state *ss)
{
sdma_put(ss);
wait_for_completion(&ss->comp);
}
/*
* Complete all the sdma requests on the active list, in the correct
* order, and with appropriate processing. Called when cleaning up
* after sdma shutdown, and when new sdma requests are submitted for
* a link that is down. This matches what is done for requests
* that complete normally, it's just the full list.
*
* Must be called with sdma_lock held
*/
static void clear_sdma_activelist(struct qib_pportdata *ppd)
{
struct qib_sdma_txreq *txp, *txp_next;
list_for_each_entry_safe(txp, txp_next, &ppd->sdma_activelist, list) {
list_del_init(&txp->list);
if (txp->flags & QIB_SDMA_TXREQ_F_FREEDESC) {
unsigned idx;
idx = txp->start_idx;
while (idx != txp->next_descq_idx) {
unmap_desc(ppd, idx);
if (++idx == ppd->sdma_descq_cnt)
idx = 0;
}
}
if (txp->callback)
(*txp->callback)(txp, QIB_SDMA_TXREQ_S_ABORTED);
}
}
static void sdma_sw_clean_up_task(unsigned long opaque)
{
struct qib_pportdata *ppd = (struct qib_pportdata *) opaque;
unsigned long