|
@@ -1,1364 +0,0 @@
|
|
|
-/*
|
|
|
- * 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 <linux/delay.h>
|
|
|
-#include "hwlib.h"
|
|
|
-#include "bestatus.h"
|
|
|
-
|
|
|
-static
|
|
|
-inline void mp_ring_create(struct mp_ring *ring, u32 num, u32 size, void *va)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- memset(ring, 0, sizeof(struct mp_ring));
|
|
|
- ring->num = num;
|
|
|
- ring->pages = DIV_ROUND_UP(num * size, PAGE_SIZE);
|
|
|
- ring->itemSize = size;
|
|
|
- ring->va = va;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * -----------------------------------------------------------------------
|
|
|
- * Interface for 2 index rings. i.e. consumer/producer rings
|
|
|
- * --------------------------------------------------------------------------
|
|
|
- */
|
|
|
-
|
|
|
-/* Returns number items pending on ring. */
|
|
|
-static inline u32 mp_ring_num_pending(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- if (ring->num == 0)
|
|
|
- return 0;
|
|
|
- return be_subc(ring->pidx, ring->cidx, ring->num);
|
|
|
-}
|
|
|
-
|
|
|
-/* Returns number items free on ring. */
|
|
|
-static inline u32 mp_ring_num_empty(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- return ring->num - 1 - mp_ring_num_pending(ring);
|
|
|
-}
|
|
|
-
|
|
|
-/* Consume 1 item */
|
|
|
-static inline void mp_ring_consume(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ASSERT(ring->pidx != ring->cidx);
|
|
|
-
|
|
|
- ring->cidx = be_addc(ring->cidx, 1, ring->num);
|
|
|
-}
|
|
|
-
|
|
|
-/* Produce 1 item */
|
|
|
-static inline void mp_ring_produce(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ring->pidx = be_addc(ring->pidx, 1, ring->num);
|
|
|
-}
|
|
|
-
|
|
|
-/* Consume count items */
|
|
|
-static inline void mp_ring_consume_multiple(struct mp_ring *ring, u32 count)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ASSERT(mp_ring_num_pending(ring) >= count);
|
|
|
- ring->cidx = be_addc(ring->cidx, count, ring->num);
|
|
|
-}
|
|
|
-
|
|
|
-static inline void *mp_ring_item(struct mp_ring *ring, u32 index)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ASSERT(index < ring->num);
|
|
|
- ASSERT(ring->itemSize > 0);
|
|
|
- return (u8 *) ring->va + index * ring->itemSize;
|
|
|
-}
|
|
|
-
|
|
|
-/* Ptr to produce item */
|
|
|
-static inline void *mp_ring_producer_ptr(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- return mp_ring_item(ring, ring->pidx);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Returns a pointer to the current location in the ring.
|
|
|
- * This is used for rings with 1 index.
|
|
|
- */
|
|
|
-static inline void *mp_ring_current(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ASSERT(ring->pidx == 0); /* not used */
|
|
|
-
|
|
|
- return mp_ring_item(ring, ring->cidx);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Increment index for rings with only 1 index.
|
|
|
- * This is used for rings with 1 index.
|
|
|
- */
|
|
|
-static inline void *mp_ring_next(struct mp_ring *ring)
|
|
|
-{
|
|
|
- ASSERT(ring);
|
|
|
- ASSERT(ring->num > 0);
|
|
|
- ASSERT(ring->pidx == 0); /* not used */
|
|
|
-
|
|
|
- ring->cidx = be_addc(ring->cidx, 1, ring->num);
|
|
|
- return mp_ring_current(ring);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- This routine waits for a previously posted mailbox WRB to be completed.
|
|
|
- Specifically it waits for the mailbox to say that it's ready to accept
|
|
|
- more data by setting the LSB of the mailbox pd register to 1.
|
|
|
-
|
|
|
- pcontroller - The function object to post this data to
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL
|
|
|
-*/
|
|
|
-static void be_mcc_mailbox_wait(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct MPU_MAILBOX_DB_AMAP mailbox_db;
|
|
|
- u32 i = 0;
|
|
|
- u32 ready;
|
|
|
-
|
|
|
- if (pfob->emulate) {
|
|
|
- /* No waiting for mailbox in emulated mode. */
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- mailbox_db.dw[0] = PD_READ(pfob, mcc_bootstrap_db);
|
|
|
- ready = AMAP_GET_BITS_PTR(MPU_MAILBOX_DB, ready, &mailbox_db);
|
|
|
-
|
|
|
- while (ready == false) {
|
|
|
- if ((++i & 0x3FFFF) == 0) {
|
|
|
- TRACE(DL_WARN, "Waiting for mailbox ready - %dk polls",
|
|
|
- i / 1000);
|
|
|
- }
|
|
|
- udelay(1);
|
|
|
- mailbox_db.dw[0] = PD_READ(pfob, mcc_bootstrap_db);
|
|
|
- ready = AMAP_GET_BITS_PTR(MPU_MAILBOX_DB, ready, &mailbox_db);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- This routine tells the MCC mailbox that there is data to processed
|
|
|
- in the mailbox. It does this by setting the physical address for the
|
|
|
- mailbox location and clearing the LSB. This routine returns immediately
|
|
|
- and does not wait for the WRB to be processed.
|
|
|
-
|
|
|
- pcontroller - The function object to post this data to
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL
|
|
|
-
|
|
|
-*/
|
|
|
-static void be_mcc_mailbox_notify(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct MPU_MAILBOX_DB_AMAP mailbox_db;
|
|
|
- u32 pa;
|
|
|
-
|
|
|
- ASSERT(pfob->mailbox.pa);
|
|
|
- ASSERT(pfob->mailbox.va);
|
|
|
-
|
|
|
- /* If emulated, do not ring the mailbox */
|
|
|
- if (pfob->emulate) {
|
|
|
- TRACE(DL_WARN, "MPU disabled. Skipping mailbox notify.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* form the higher bits in the address */
|
|
|
- mailbox_db.dw[0] = 0; /* init */
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, hi, &mailbox_db, 1);
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, ready, &mailbox_db, 0);
|
|
|
-
|
|
|
- /* bits 34 to 63 */
|
|
|
- pa = (u32) (pfob->mailbox.pa >> 34);
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, address, &mailbox_db, pa);
|
|
|
-
|
|
|
- /* Wait for the MPU to be ready */
|
|
|
- be_mcc_mailbox_wait(pfob);
|
|
|
-
|
|
|
- /* Ring doorbell 1st time */
|
|
|
- PD_WRITE(pfob, mcc_bootstrap_db, mailbox_db.dw[0]);
|
|
|
-
|
|
|
- /* Wait for 1st write to be acknowledged. */
|
|
|
- be_mcc_mailbox_wait(pfob);
|
|
|
-
|
|
|
- /* lower bits 30 bits from 4th bit (bits 4 to 33)*/
|
|
|
- pa = (u32) (pfob->mailbox.pa >> 4) & 0x3FFFFFFF;
|
|
|
-
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, hi, &mailbox_db, 0);
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, ready, &mailbox_db, 0);
|
|
|
- AMAP_SET_BITS_PTR(MPU_MAILBOX_DB, address, &mailbox_db, pa);
|
|
|
-
|
|
|
- /* Ring doorbell 2nd time */
|
|
|
- PD_WRITE(pfob, mcc_bootstrap_db, mailbox_db.dw[0]);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- This routine tells the MCC mailbox that there is data to processed
|
|
|
- in the mailbox. It does this by setting the physical address for the
|
|
|
- mailbox location and clearing the LSB. This routine spins until the
|
|
|
- MPU writes a 1 into the LSB indicating that the data has been received
|
|
|
- and is ready to be processed.
|
|
|
-
|
|
|
- pcontroller - The function object to post this data to
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL
|
|
|
-*/
|
|
|
-static void
|
|
|
-be_mcc_mailbox_notify_and_wait(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- /*
|
|
|
- * Notify it
|
|
|
- */
|
|
|
- be_mcc_mailbox_notify(pfob);
|
|
|
- /*
|
|
|
- * Now wait for completion of WRB
|
|
|
- */
|
|
|
- be_mcc_mailbox_wait(pfob);
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-be_mcc_process_cqe(struct be_function_object *pfob,
|
|
|
- struct MCC_CQ_ENTRY_AMAP *cqe)
|
|
|
-{
|
|
|
- struct be_mcc_wrb_context *wrb_context = NULL;
|
|
|
- u32 offset, status;
|
|
|
- u8 *p;
|
|
|
-
|
|
|
- ASSERT(cqe);
|
|
|
- /*
|
|
|
- * A command completed. Commands complete out-of-order.
|
|
|
- * Determine which command completed from the TAG.
|
|
|
- */
|
|
|
- offset = offsetof(struct BE_MCC_CQ_ENTRY_AMAP, mcc_tag)/8;
|
|
|
- p = (u8 *) cqe + offset;
|
|
|
- wrb_context = (struct be_mcc_wrb_context *)(void *)(size_t)(*(u64 *)p);
|
|
|
- ASSERT(wrb_context);
|
|
|
-
|
|
|
- /*
|
|
|
- * Perform a response copy if requested.
|
|
|
- * Only copy data if the FWCMD is successful.
|
|
|
- */
|
|
|
- status = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY, completion_status, cqe);
|
|
|
- if (status == MGMT_STATUS_SUCCESS && wrb_context->copy.length > 0) {
|
|
|
- ASSERT(wrb_context->wrb);
|
|
|
- ASSERT(wrb_context->copy.va);
|
|
|
- p = (u8 *)wrb_context->wrb +
|
|
|
- offsetof(struct BE_MCC_WRB_AMAP, payload)/8;
|
|
|
- memcpy(wrb_context->copy.va,
|
|
|
- (u8 *)p + wrb_context->copy.fwcmd_offset,
|
|
|
- wrb_context->copy.length);
|
|
|
- }
|
|
|
-
|
|
|
- if (status)
|
|
|
- status = BE_NOT_OK;
|
|
|
- /* internal callback */
|
|
|
- if (wrb_context->internal_cb) {
|
|
|
- wrb_context->internal_cb(wrb_context->internal_cb_context,
|
|
|
- status, wrb_context->wrb);
|
|
|
- }
|
|
|
-
|
|
|
- /* callback */
|
|
|
- if (wrb_context->cb) {
|
|
|
- wrb_context->cb(wrb_context->cb_context,
|
|
|
- status, wrb_context->wrb);
|
|
|
- }
|
|
|
- /* Free the context structure */
|
|
|
- _be_mcc_free_wrb_context(pfob, wrb_context);
|
|
|
-}
|
|
|
-
|
|
|
-void be_drive_mcc_wrb_queue(struct be_mcc_object *mcc)
|
|
|
-{
|
|
|
- struct be_function_object *pfob = NULL;
|
|
|
- int status = BE_PENDING;
|
|
|
- struct be_generic_q_ctxt *q_ctxt;
|
|
|
- struct MCC_WRB_AMAP *wrb;
|
|
|
- struct MCC_WRB_AMAP *queue_wrb;
|
|
|
- u32 length, payload_length, sge_count, embedded;
|
|
|
- unsigned long irql;
|
|
|
-
|
|
|
- BUILD_BUG_ON((sizeof(struct be_generic_q_ctxt) <
|
|
|
- sizeof(struct be_queue_driver_context) +
|
|
|
- sizeof(struct MCC_WRB_AMAP)));
|
|
|
- pfob = mcc->parent_function;
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
-
|
|
|
- if (mcc->driving_backlog) {
|
|
|
- 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;
|
|
|
- }
|
|
|
- /* Acquire the flag to limit 1 thread to redrive posts. */
|
|
|
- mcc->driving_backlog = 1;
|
|
|
-
|
|
|
- while (!list_empty(&mcc->backlog)) {
|
|
|
- wrb = _be_mpu_peek_ring_wrb(mcc, true); /* Driving the queue */
|
|
|
- if (!wrb)
|
|
|
- break; /* No space in the ring yet. */
|
|
|
- /* Get the next queued entry to process. */
|
|
|
- q_ctxt = list_first_entry(&mcc->backlog,
|
|
|
- struct be_generic_q_ctxt, context.list);
|
|
|
- list_del(&q_ctxt->context.list);
|
|
|
- pfob->mcc->backlog_length--;
|
|
|
- /*
|
|
|
- * Compute the required length of the WRB.
|
|
|
- * Since the queue element may be smaller than
|
|
|
- * the complete WRB, copy only the required number of bytes.
|
|
|
- */
|
|
|
- queue_wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header;
|
|
|
- embedded = AMAP_GET_BITS_PTR(MCC_WRB, embedded, queue_wrb);
|
|
|
- if (embedded) {
|
|
|
- payload_length = AMAP_GET_BITS_PTR(MCC_WRB,
|
|
|
- payload_length, queue_wrb);
|
|
|
- length = sizeof(struct be_mcc_wrb_header) +
|
|
|
- payload_length;
|
|
|
- } else {
|
|
|
- sge_count = AMAP_GET_BITS_PTR(MCC_WRB, sge_count,
|
|
|
- queue_wrb);
|
|
|
- ASSERT(sge_count == 1); /* only 1 frag. */
|
|
|
- length = sizeof(struct be_mcc_wrb_header) +
|
|
|
- sge_count * sizeof(struct MCC_SGE_AMAP);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Truncate the length based on the size of the
|
|
|
- * queue element. Some elements that have output parameters
|
|
|
- * can be smaller than the payload_length field would
|
|
|
- * indicate. We really only need to copy the request
|
|
|
- * parameters, not the response.
|
|
|
- */
|
|
|
- length = min(length, (u32) (q_ctxt->context.bytes -
|
|
|
- offsetof(struct be_generic_q_ctxt, wrb_header)));
|
|
|
-
|
|
|
- /* Copy the queue element WRB into the ring. */
|
|
|
- memcpy(wrb, &q_ctxt->wrb_header, length);
|
|
|
-
|
|
|
- /* Post the wrb. This should not fail assuming we have
|
|
|
- * enough context structs. */
|
|
|
- status = be_function_post_mcc_wrb(pfob, wrb, NULL,
|
|
|
- q_ctxt->context.cb, q_ctxt->context.cb_context,
|
|
|
- q_ctxt->context.internal_cb,
|
|
|
- q_ctxt->context.internal_cb_context,
|
|
|
- q_ctxt->context.optional_fwcmd_va,
|
|
|
- &q_ctxt->context.copy);
|
|
|
-
|
|
|
- if (status == BE_SUCCESS) {
|
|
|
- /*
|
|
|
- * Synchronous completion. Since it was queued,
|
|
|
- * we will invoke the callback.
|
|
|
- * To the user, this is an asynchronous request.
|
|
|
- */
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- ASSERT(q_ctxt->context.cb);
|
|
|
-
|
|
|
- q_ctxt->context.cb(
|
|
|
- q_ctxt->context.cb_context,
|
|
|
- BE_SUCCESS, NULL);
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
-
|
|
|
- } else if (status != BE_PENDING) {
|
|
|
- /*
|
|
|
- * Another resource failed. Should never happen
|
|
|
- * if we have sufficient MCC_WRB_CONTEXT structs.
|
|
|
- * Return to head of the queue.
|
|
|
- */
|
|
|
- TRACE(DL_WARN, "Failed to post a queued WRB. 0x%x",
|
|
|
- status);
|
|
|
- list_add(&q_ctxt->context.list, &mcc->backlog);
|
|
|
- pfob->mcc->backlog_length++;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Free the flag to limit 1 thread to redrive posts. */
|
|
|
- mcc->driving_backlog = 0;
|
|
|
- spin_unlock_irqrestore(&pfob->post_lock, irql);
|
|
|
-}
|
|
|
-
|
|
|
-/* This function asserts that the WRB was consumed in order. */
|
|
|
-#ifdef BE_DEBUG
|
|
|
-u32 be_mcc_wrb_consumed_in_order(struct be_mcc_object *mcc,
|
|
|
- struct MCC_CQ_ENTRY_AMAP *cqe)
|
|
|
-{
|
|
|
- struct be_mcc_wrb_context *wrb_context = NULL;
|
|
|
- u32 wrb_index;
|
|
|
- u32 wrb_consumed_in_order;
|
|
|
- u32 offset;
|
|
|
- u8 *p;
|
|
|
-
|
|
|
- ASSERT(cqe);
|
|
|
- /*
|
|
|
- * A command completed. Commands complete out-of-order.
|
|
|
- * Determine which command completed from the TAG.
|
|
|
- */
|
|
|
- offset = offsetof(struct BE_MCC_CQ_ENTRY_AMAP, mcc_tag)/8;
|
|
|
- p = (u8 *) cqe + offset;
|
|
|
- wrb_context = (struct be_mcc_wrb_context *)(void *)(size_t)(*(u64 *)p);
|
|
|
-
|
|
|
- ASSERT(wrb_context);
|
|
|
-
|
|
|
- wrb_index = (u32) (((u64)(size_t)wrb_context->ring_wrb -
|
|
|
- (u64)(size_t)mcc->sq.ring.va) / sizeof(struct MCC_WRB_AMAP));
|
|
|
-
|
|
|
- ASSERT(wrb_index < mcc->sq.ring.num);
|
|
|
-
|
|
|
- wrb_consumed_in_order = (u32) (wrb_index == mcc->consumed_index);
|
|
|
- mcc->consumed_index = be_addc(mcc->consumed_index, 1, mcc->sq.ring.num);
|
|
|
- return wrb_consumed_in_order;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-int be_mcc_process_cq(struct be_mcc_object *mcc, bool rearm)
|
|
|
-{
|
|
|
- struct be_function_object *pfob = NULL;
|
|
|
- struct MCC_CQ_ENTRY_AMAP *cqe;
|
|
|
- struct CQ_DB_AMAP db;
|
|
|
- struct mp_ring *cq_ring = &mcc->cq.ring;
|
|
|
- struct mp_ring *mp_ring = &mcc->sq.ring;
|
|
|
- u32 num_processed = 0;
|
|
|
- u32 consumed = 0, valid, completed, cqe_consumed, async_event;
|
|
|
-
|
|
|
- pfob = mcc->parent_function;
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->cq_lock, pfob->cq_irq);
|
|
|
-
|
|
|
- /*
|
|
|
- * Verify that only one thread is processing the CQ at once.
|
|
|
- * We cannot hold the lock while processing the CQ due to
|
|
|
- * the callbacks into the OS. Therefore, this flag is used
|
|
|
- * to control it. If any of the threads want to
|
|
|
- * rearm the CQ, we need to honor that.
|
|
|
- */
|
|
|
- if (mcc->processing != 0) {
|
|
|
- mcc->rearm = mcc->rearm || rearm;
|
|
|
- goto Error;
|
|
|
- } else {
|
|
|
- mcc->processing = 1; /* lock processing for this thread. */
|
|
|
- mcc->rearm = rearm; /* set our rearm setting */
|
|
|
- }
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&pfob->cq_lock, pfob->cq_irq);
|
|
|
-
|
|
|
- cqe = mp_ring_current(cq_ring);
|
|
|
- valid = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY, valid, cqe);
|
|
|
- while (valid) {
|
|
|
-
|
|
|
- if (num_processed >= 8) {
|
|
|
- /* coalesce doorbells, but free space in cq
|
|
|
- * ring while processing. */
|
|
|
- db.dw[0] = 0; /* clear */
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, qid, &db, cq_ring->id);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, rearm, &db, false);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, event, &db, false);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, num_popped, &db,
|
|
|
- num_processed);
|
|
|
- num_processed = 0;
|
|
|
-
|
|
|
- PD_WRITE(pfob, cq_db, db.dw[0]);
|
|
|
- }
|
|
|
-
|
|
|
- async_event = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY, async_event, cqe);
|
|
|
- if (async_event) {
|
|
|
- /* This is an asynchronous event. */
|
|
|
- struct ASYNC_EVENT_TRAILER_AMAP *async_trailer =
|
|
|
- (struct ASYNC_EVENT_TRAILER_AMAP *)
|
|
|
- ((u8 *) cqe + sizeof(struct MCC_CQ_ENTRY_AMAP) -
|
|
|
- sizeof(struct ASYNC_EVENT_TRAILER_AMAP));
|
|
|
- u32 event_code;
|
|
|
- async_event = AMAP_GET_BITS_PTR(ASYNC_EVENT_TRAILER,
|
|
|
- async_event, async_trailer);
|
|
|
- ASSERT(async_event == 1);
|
|
|
-
|
|
|
-
|
|
|
- valid = AMAP_GET_BITS_PTR(ASYNC_EVENT_TRAILER,
|
|
|
- valid, async_trailer);
|
|
|
- ASSERT(valid == 1);
|
|
|
-
|
|
|
- /* Call the async event handler if it is installed. */
|
|
|
- if (mcc->async_cb) {
|
|
|
- event_code =
|
|
|
- AMAP_GET_BITS_PTR(ASYNC_EVENT_TRAILER,
|
|
|
- event_code, async_trailer);
|
|
|
- mcc->async_cb(mcc->async_context,
|
|
|
- (u32) event_code, (void *) cqe);
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
- /* This is a completion entry. */
|
|
|
-
|
|
|
- /* No vm forwarding in this driver. */
|
|
|
-
|
|
|
- cqe_consumed = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY,
|
|
|
- consumed, cqe);
|
|
|
- if (cqe_consumed) {
|
|
|
- /*
|
|
|
- * A command on the MCC ring was consumed.
|
|
|
- * Update the consumer index.
|
|
|
- * These occur in order.
|
|
|
- */
|
|
|
- ASSERT(be_mcc_wrb_consumed_in_order(mcc, cqe));
|
|
|
- consumed++;
|
|
|
- }
|
|
|
-
|
|
|
- completed = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY,
|
|
|
- completed, cqe);
|
|
|
- if (completed) {
|
|
|
- /* A command completed. Use tag to
|
|
|
- * determine which command. */
|
|
|
- be_mcc_process_cqe(pfob, cqe);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Reset the CQE */
|
|
|
- AMAP_SET_BITS_PTR(MCC_CQ_ENTRY, valid, cqe, false);
|
|
|
- num_processed++;
|
|
|
-
|
|
|
- /* Update our tracking for the CQ ring. */
|
|
|
- cqe = mp_ring_next(cq_ring);
|
|
|
- valid = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY, valid, cqe);
|
|
|
- }
|
|
|
-
|
|
|
- TRACE(DL_INFO, "num_processed:0x%x, and consumed:0x%x",
|
|
|
- num_processed, consumed);
|
|
|
- /*
|
|
|
- * Grab the CQ lock to synchronize the "rearm" setting for
|
|
|
- * the doorbell, and for clearing the "processing" flag.
|
|
|
- */
|
|
|
- spin_lock_irqsave(&pfob->cq_lock, pfob->cq_irq);
|
|
|
-
|
|
|
- /*
|
|
|
- * Rearm the cq. This is done based on the global mcc->rearm
|
|
|
- * flag which combines the rearm parameter from the current
|
|
|
- * call to process_cq and any other threads
|
|
|
- * that tried to process the CQ while this one was active.
|
|
|
- * This handles the situation where a sync. fwcmd was processing
|
|
|
- * the CQ while the interrupt/dpc tries to process it.
|
|
|
- * The sync process gets to continue -- but it is now
|
|
|
- * responsible for the rearming.
|
|
|
- */
|
|
|
- if (num_processed > 0 || mcc->rearm == true) {
|
|
|
- db.dw[0] = 0; /* clear */
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, qid, &db, cq_ring->id);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, rearm, &db, mcc->rearm);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, event, &db, false);
|
|
|
- AMAP_SET_BITS_PTR(CQ_DB, num_popped, &db, num_processed);
|
|
|
-
|
|
|
- PD_WRITE(pfob, cq_db, db.dw[0]);
|
|
|
- }
|
|
|
- /*
|
|
|
- * Update the consumer index after ringing the CQ doorbell.
|
|
|
- * We don't want another thread to post more WRBs before we
|
|
|
- * have CQ space available.
|
|
|
- */
|
|
|
- mp_ring_consume_multiple(mp_ring, consumed);
|
|
|
-
|
|
|
- /* Clear the processing flag. */
|
|
|
- mcc->processing = 0;
|
|
|
-
|
|
|
-Error:
|
|
|
- spin_unlock_irqrestore(&pfob->cq_lock, pfob->cq_irq);
|
|
|
- /*
|
|
|
- * Use the local variable to detect if the current thread
|
|
|
- * holds the WRB post lock. If rearm is false, this is
|
|
|
- * either a synchronous command, or the upper layer driver is polling
|
|
|
- * from a thread. We do not drive the queue from that
|
|
|
- * context since the driver may hold the
|
|
|
- * wrb post lock already.
|
|
|
- */
|
|
|
- if (rearm)
|
|
|
- be_drive_mcc_wrb_queue(mcc);
|
|
|
- else
|
|
|
- pfob->pend_queue_driving = 1;
|
|
|
-
|
|
|
- return BE_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- *============================================================================
|
|
|
- * P U B L I C R O U T I N E S
|
|
|
- *============================================================================
|
|
|
- */
|
|
|
-
|
|
|
-/*
|
|
|
- This routine creates an MCC object. This object contains an MCC send queue
|
|
|
- and a CQ private to the MCC.
|
|
|
-
|
|
|
- pcontroller - Handle to a function object
|
|
|
-
|
|
|
- EqObject - EQ object that will be used to dispatch this MCC
|
|
|
-
|
|
|
- ppMccObject - Pointer to an internal Mcc Object returned.
|
|
|
-
|
|
|
- Returns BE_SUCCESS if successfull,, otherwise a useful error code
|
|
|
- is returned.
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL
|
|
|
-
|
|
|
-*/
|
|
|
-int
|
|
|
-be_mcc_ring_create(struct be_function_object *pfob,
|
|
|
- struct ring_desc *rd, u32 length,
|
|
|
- struct be_mcc_wrb_context *context_array,
|
|
|
- u32 num_context_entries,
|
|
|
- struct be_cq_object *cq, struct be_mcc_object *mcc)
|
|
|
-{
|
|
|
- int status = 0;
|
|
|
-
|
|
|
- struct FWCMD_COMMON_MCC_CREATE *fwcmd = NULL;
|
|
|
- struct MCC_WRB_AMAP *wrb = NULL;
|
|
|
- u32 num_entries_encoded, n, i;
|
|
|
- void *va = NULL;
|
|
|
- unsigned long irql;
|
|
|
-
|
|
|
- if (length < sizeof(struct MCC_WRB_AMAP) * 2) {
|
|
|
- TRACE(DL_ERR, "Invalid MCC ring length:%d", length);
|
|
|
- return BE_NOT_OK;
|
|
|
- }
|
|
|
- /*
|
|
|
- * Reduce the actual ring size to be less than the number
|
|
|
- * of context entries. This ensures that we run out of
|
|
|
- * ring WRBs first so the queuing works correctly. We never
|
|
|
- * queue based on context structs.
|
|
|
- */
|
|
|
- if (num_context_entries + 1 <
|
|
|
- length / sizeof(struct MCC_WRB_AMAP) - 1) {
|
|
|
-
|
|
|
- u32 max_length =
|
|
|
- (num_context_entries + 2) * sizeof(struct MCC_WRB_AMAP);
|
|
|
-
|
|
|
- if (is_power_of_2(max_length))
|
|
|
- length = __roundup_pow_of_two(max_length+1) / 2;
|
|
|
- else
|
|
|
- length = __roundup_pow_of_two(max_length) / 2;
|
|
|
-
|
|
|
- ASSERT(length <= max_length);
|
|
|
-
|
|
|
- TRACE(DL_WARN,
|
|
|
- "MCC ring length reduced based on context entries."
|
|
|
- " length:%d wrbs:%d context_entries:%d", length,
|
|
|
- (int) (length / sizeof(struct MCC_WRB_AMAP)),
|
|
|
- num_context_entries);
|
|
|
- }
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->post_lock, irql);
|
|
|
-
|
|
|
- num_entries_encoded =
|
|
|
- be_ring_length_to_encoding(length, sizeof(struct MCC_WRB_AMAP));
|
|
|
-
|
|
|
- /* Init MCC object. */
|
|
|
- memset(mcc, 0, sizeof(*mcc));
|
|
|
- mcc->parent_function = pfob;
|
|
|
- mcc->cq_object = cq;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&mcc->backlog);
|
|
|
-
|
|
|
- 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_MCC_CREATE);
|
|
|
-
|
|
|
- fwcmd->params.request.num_pages = DIV_ROUND_UP(length, PAGE_SIZE);
|
|
|
- /*
|
|
|
- * Program MCC ring context
|
|
|
- */
|
|
|
- AMAP_SET_BITS_PTR(MCC_RING_CONTEXT, pdid,
|
|
|
- &fwcmd->params.request.context, 0);
|
|
|
- AMAP_SET_BITS_PTR(MCC_RING_CONTEXT, invalid,
|
|
|
- &fwcmd->params.request.context, false);
|
|
|
- AMAP_SET_BITS_PTR(MCC_RING_CONTEXT, ring_size,
|
|
|
- &fwcmd->params.request.context, num_entries_encoded);
|
|
|
-
|
|
|
- n = cq->cq_id;
|
|
|
- AMAP_SET_BITS_PTR(MCC_RING_CONTEXT,
|
|
|
- cq_id, &fwcmd->params.request.context, n);
|
|
|
- be_rd_to_pa_list(rd, fwcmd->params.request.pages,
|
|
|
- ARRAY_SIZE(fwcmd->params.request.pages));
|
|
|
- /* Post the f/w command */
|
|
|
- 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 CQ failed.");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- /*
|
|
|
- * Create a linked list of context structures
|
|
|
- */
|
|
|
- mcc->wrb_context.base = context_array;
|
|
|
- mcc->wrb_context.num = num_context_entries;
|
|
|
- INIT_LIST_HEAD(&mcc->wrb_context.list_head);
|
|
|
- memset(context_array, 0,
|
|
|
- sizeof(struct be_mcc_wrb_context) * num_context_entries);
|
|
|
- for (i = 0; i < mcc->wrb_context.num; i++) {
|
|
|
- list_add_tail(&context_array[i].next,
|
|
|
- &mcc->wrb_context.list_head);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- *
|
|
|
- * Create an mcc_ring for tracking WRB hw ring
|
|
|
- */
|
|
|
- va = rd->va;
|
|
|
- ASSERT(va);
|
|
|
- mp_ring_create(&mcc->sq.ring, length / sizeof(struct MCC_WRB_AMAP),
|
|
|
- sizeof(struct MCC_WRB_AMAP), va);
|
|
|
- mcc->sq.ring.id = fwcmd->params.response.id;
|
|
|
- /*
|
|
|
- * Init a mcc_ring for tracking the MCC CQ.
|
|
|
- */
|
|
|
- ASSERT(cq->va);
|
|
|
- mp_ring_create(&mcc->cq.ring, cq->num_entries,
|
|
|
- sizeof(struct MCC_CQ_ENTRY_AMAP), cq->va);
|
|
|
- mcc->cq.ring.id = cq->cq_id;
|
|
|
-
|
|
|
- /* Force zeroing of CQ. */
|
|
|
- memset(cq->va, 0, cq->num_entries * sizeof(struct MCC_CQ_ENTRY_AMAP));
|
|
|
-
|
|
|
- /* Initialize debug index. */
|
|
|
- mcc->consumed_index = 0;
|
|
|
-
|
|
|
- atomic_inc(&cq->ref_count);
|
|
|
- pfob->mcc = mcc;
|
|
|
-
|
|
|
- TRACE(DL_INFO, "MCC ring created. id:%d bytes:%d cq_id:%d cq_entries:%d"
|
|
|
- " num_context:%d", mcc->sq.ring.id, length,
|
|
|
- cq->cq_id, cq->num_entries, num_context_entries);
|
|
|
-
|
|
|
-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 routine destroys an MCC send queue
|
|
|
-
|
|
|
- MccObject - Internal Mcc Object to be destroyed.
|
|
|
-
|
|
|
- Returns BE_SUCCESS if successfull, otherwise an error code is returned.
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL
|
|
|
-
|
|
|
- The caller of this routine must ensure that no other WRB may be posted
|
|
|
- until this routine returns.
|
|
|
-
|
|
|
-*/
|
|
|
-int be_mcc_ring_destroy(struct be_mcc_object *mcc)
|
|
|
-{
|
|
|
- int status = 0;
|
|
|
- struct be_function_object *pfob = mcc->parent_function;
|
|
|
-
|
|
|
-
|
|
|
- ASSERT(mcc->processing == 0);
|
|
|
-
|
|
|
- /*
|
|
|
- * Remove the ring from the function object.
|
|
|
- * This transitions back to mailbox mode.
|
|
|
- */
|
|
|
- pfob->mcc = NULL;
|
|
|
-
|
|
|
- /* Send fwcmd to destroy the queue. (Using the mailbox.) */
|
|
|
- status = be_function_ring_destroy(mcc->parent_function, mcc->sq.ring.id,
|
|
|
- FWCMD_RING_TYPE_MCC, NULL, NULL, NULL, NULL);
|
|
|
- ASSERT(status == 0);
|
|
|
-
|
|
|
- /* Release the SQ reference to the CQ */
|
|
|
- atomic_dec(&mcc->cq_object->ref_count);
|
|
|
-
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-mcc_wrb_sync_cb(void *context, int staus, struct MCC_WRB_AMAP *wrb)
|
|
|
-{
|
|
|
- struct be_mcc_wrb_context *wrb_context =
|
|
|
- (struct be_mcc_wrb_context *) context;
|
|
|
- ASSERT(wrb_context);
|
|
|
- *wrb_context->users_final_status = staus;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- This routine posts a command to the MCC send queue
|
|
|
-
|
|
|
- mcc - Internal Mcc Object to be destroyed.
|
|
|
-
|
|
|
- wrb - wrb to post.
|
|
|
-
|
|
|
- Returns BE_SUCCESS if successfull, otherwise an error code is returned.
|
|
|
-
|
|
|
- IRQL < DISPATCH_LEVEL if CompletionCallback is not NULL
|
|
|
- IRQL <=DISPATCH_LEVEL if CompletionCallback is NULL
|
|
|
-
|
|
|
- If this routine is called with CompletionCallback != NULL the
|
|
|
- call is considered to be asynchronous and will return as soon
|
|
|
- as the WRB is posted to the MCC with BE_PENDING.
|
|
|
-
|
|
|
- If CompletionCallback is NULL, then this routine will not return until
|
|
|
- a completion for this MCC command has been processed.
|
|
|
- If called at DISPATCH_LEVEL the CompletionCallback must be NULL.
|
|
|
-
|
|
|
- This routine should only be called if the MPU has been boostraped past
|
|
|
- mailbox mode.
|
|
|
-
|
|
|
-
|
|
|
-*/
|
|
|
-int
|
|
|
-_be_mpu_post_wrb_ring(struct be_mcc_object *mcc, struct MCC_WRB_AMAP *wrb,
|
|
|
- struct be_mcc_wrb_context *wrb_context)
|
|
|
-{
|
|
|
-
|
|
|
- struct MCC_WRB_AMAP *ring_wrb = NULL;
|
|
|
- int status = BE_PENDING;
|
|
|
- int final_status = BE_PENDING;
|
|
|
- mcc_wrb_cqe_callback cb = NULL;
|
|
|
- struct MCC_DB_AMAP mcc_db;
|
|
|
- u32 embedded;
|
|
|
-
|
|
|
- ASSERT(mp_ring_num_empty(&mcc->sq.ring) > 0);
|
|
|
- /*
|
|
|
- * Input wrb is most likely the next wrb in the ring, since the client
|
|
|
- * can peek at the address.
|
|
|
- */
|
|
|
- ring_wrb = mp_ring_producer_ptr(&mcc->sq.ring);
|
|
|
- if (wrb != ring_wrb) {
|
|
|
- /* If not equal, copy it into the ring. */
|
|
|
- memcpy(ring_wrb, wrb, sizeof(struct MCC_WRB_AMAP));
|
|
|
- }
|
|
|
-#ifdef BE_DEBUG
|
|
|
- wrb_context->ring_wrb = ring_wrb;
|
|
|
-#endif
|
|
|
- embedded = AMAP_GET_BITS_PTR(MCC_WRB, embedded, ring_wrb);
|
|
|
- if (embedded) {
|
|
|
- /* embedded commands will have the response within the WRB. */
|
|
|
- wrb_context->wrb = ring_wrb;
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * non-embedded commands will not have the response
|
|
|
- * within the WRB, and they may complete out-of-order.
|
|
|
- * The WRB will not be valid to inspect
|
|
|
- * during the completion.
|
|
|
- */
|
|
|
- wrb_context->wrb = NULL;
|
|
|
- }
|
|
|
- cb = wrb_context->cb;
|
|
|
-
|
|
|
- if (cb == NULL) {
|
|
|
- /* Assign our internal callback if this is a
|
|
|
- * synchronous call. */
|
|
|
- wrb_context->cb = mcc_wrb_sync_cb;
|
|
|
- wrb_context->cb_context = wrb_context;
|
|
|
- wrb_context->users_final_status = &final_status;
|
|
|
- }
|
|
|
- /* Increment producer index */
|
|
|
-
|
|
|
- mcc_db.dw[0] = 0; /* initialize */
|
|
|
- AMAP_SET_BITS_PTR(MCC_DB, rid, &mcc_db, mcc->sq.ring.id);
|
|
|
- AMAP_SET_BITS_PTR(MCC_DB, numPosted, &mcc_db, 1);
|
|
|
-
|
|
|
- mp_ring_produce(&mcc->sq.ring);
|
|
|
- PD_WRITE(mcc->parent_function, mpu_mcc_db, mcc_db.dw[0]);
|
|
|
- TRACE(DL_INFO, "pidx: %x and cidx: %x.", mcc->sq.ring.pidx,
|
|
|
- mcc->sq.ring.cidx);
|
|
|
-
|
|
|
- if (cb == NULL) {
|
|
|
- int polls = 0; /* At >= 1 us per poll */
|
|
|
- /* Wait until this command completes, polling the CQ. */
|
|
|
- do {
|
|
|
- TRACE(DL_INFO, "FWCMD submitted in the poll mode.");
|
|
|
- /* Do not rearm CQ in this context. */
|
|
|
- be_mcc_process_cq(mcc, false);
|
|
|
-
|
|
|
- if (final_status == BE_PENDING) {
|
|
|
- if ((++polls & 0x7FFFF) == 0) {
|
|
|
- TRACE(DL_WARN,
|
|
|
- "Warning : polling MCC CQ for %d"
|
|
|
- "ms.", polls / 1000);
|
|
|
- }
|
|
|
-
|
|
|
- udelay(1);
|
|
|
- }
|
|
|
-
|
|
|
- /* final_status changed when the command completes */
|
|
|
- } while (final_status == BE_PENDING);
|
|
|
-
|
|
|
- status = final_status;
|
|
|
- }
|
|
|
-
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
-struct MCC_WRB_AMAP *
|
|
|
-_be_mpu_peek_ring_wrb(struct be_mcc_object *mcc, bool driving_queue)
|
|
|
-{
|
|
|
- /* If we have queued items, do not allow a post to bypass the queue. */
|
|
|
- if (!driving_queue && !list_empty(&mcc->backlog))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- if (mp_ring_num_empty(&mcc->sq.ring) <= 0)
|
|
|
- return NULL;
|
|
|
- return (struct MCC_WRB_AMAP *) mp_ring_producer_ptr(&mcc->sq.ring);
|
|
|
-}
|
|
|
-
|
|
|
-int
|
|
|
-be_mpu_init_mailbox(struct be_function_object *pfob, struct ring_desc *mailbox)
|
|
|
-{
|
|
|
- ASSERT(mailbox);
|
|
|
- pfob->mailbox.va = mailbox->va;
|
|
|
- pfob->mailbox.pa = cpu_to_le64(mailbox->pa);
|
|
|
- pfob->mailbox.length = mailbox->length;
|
|
|
-
|
|
|
- ASSERT(((u32)(size_t)pfob->mailbox.va & 0xf) == 0);
|
|
|
- ASSERT(((u32)(size_t)pfob->mailbox.pa & 0xf) == 0);
|
|
|
- /*
|
|
|
- * Issue the WRB to set MPU endianness
|
|
|
- */
|
|
|
- {
|
|
|
- u64 *endian_check = (u64 *) (pfob->mailbox.va +
|
|
|
- offsetof(struct BE_MCC_MAILBOX_AMAP, wrb)/8);
|
|
|
- *endian_check = 0xFF1234FFFF5678FFULL;
|
|
|
- }
|
|
|
-
|
|
|
- be_mcc_mailbox_notify_and_wait(pfob);
|
|
|
-
|
|
|
- return BE_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
- This routine posts a command to the MCC mailbox.
|
|
|
-
|
|
|
- FuncObj - Function Object to post the WRB on behalf of.
|
|
|
- wrb - wrb to post.
|
|
|
- CompletionCallback - Address of a callback routine to invoke once the WRB
|
|
|
- is completed.
|
|
|
- CompletionCallbackContext - Opaque context to be passed during the call to
|
|
|
- the CompletionCallback.
|
|
|
- Returns BE_SUCCESS if successfull, otherwise an error code is returned.
|
|
|
-
|
|
|
- IRQL <=DISPATCH_LEVEL if CompletionCallback is NULL
|
|
|
-
|
|
|
- This routine will block until a completion for this MCC command has been
|
|
|
- processed. If called at DISPATCH_LEVEL the CompletionCallback must be NULL.
|
|
|
-
|
|
|
- This routine should only be called if the MPU has not been boostraped past
|
|
|
- mailbox mode.
|
|
|
-*/
|
|
|
-int
|
|
|
-_be_mpu_post_wrb_mailbox(struct be_function_object *pfob,
|
|
|
- struct MCC_WRB_AMAP *wrb, struct be_mcc_wrb_context *wrb_context)
|
|
|
-{
|
|
|
- struct MCC_MAILBOX_AMAP *mailbox = NULL;
|
|
|
- struct MCC_WRB_AMAP *mb_wrb;
|
|
|
- struct MCC_CQ_ENTRY_AMAP *mb_cq;
|
|
|
- u32 offset, status;
|
|
|
-
|
|
|
- ASSERT(pfob->mcc == NULL);
|
|
|
- mailbox = pfob->mailbox.va;
|
|
|
- ASSERT(mailbox);
|
|
|
-
|
|
|
- offset = offsetof(struct BE_MCC_MAILBOX_AMAP, wrb)/8;
|
|
|
- mb_wrb = (struct MCC_WRB_AMAP *) (u8 *)mailbox + offset;
|
|
|
- if (mb_wrb != wrb) {
|
|
|
- memset(mailbox, 0, sizeof(*mailbox));
|
|
|
- memcpy(mb_wrb, wrb, sizeof(struct MCC_WRB_AMAP));
|
|
|
- }
|
|
|
- /* The callback can inspect the final WRB to get output parameters. */
|
|
|
- wrb_context->wrb = mb_wrb;
|
|
|
-
|
|
|
- be_mcc_mailbox_notify_and_wait(pfob);
|
|
|
-
|
|
|
- /* A command completed. Use tag to determine which command. */
|
|
|
- offset = offsetof(struct BE_MCC_MAILBOX_AMAP, cq)/8;
|
|
|
- mb_cq = (struct MCC_CQ_ENTRY_AMAP *) ((u8 *)mailbox + offset);
|
|
|
- be_mcc_process_cqe(pfob, mb_cq);
|
|
|
-
|
|
|
- status = AMAP_GET_BITS_PTR(MCC_CQ_ENTRY, completion_status, mb_cq);
|
|
|
- if (status)
|
|
|
- status = BE_NOT_OK;
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
-struct be_mcc_wrb_context *
|
|
|
-_be_mcc_allocate_wrb_context(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct be_mcc_wrb_context *context = NULL;
|
|
|
- unsigned long irq;
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->mcc_context_lock, irq);
|
|
|
-
|
|
|
- if (!pfob->mailbox.default_context_allocated) {
|
|
|
- /* Use the single default context that we
|
|
|
- * always have allocated. */
|
|
|
- pfob->mailbox.default_context_allocated = true;
|
|
|
- context = &pfob->mailbox.default_context;
|
|
|
- } else if (pfob->mcc) {
|
|
|
- /* Get a context from the free list. If any are available. */
|
|
|
- if (!list_empty(&pfob->mcc->wrb_context.list_head)) {
|
|
|
- context = list_first_entry(
|
|
|
- &pfob->mcc->wrb_context.list_head,
|
|
|
- struct be_mcc_wrb_context, next);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&pfob->mcc_context_lock, irq);
|
|
|
-
|
|
|
- return context;
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-_be_mcc_free_wrb_context(struct be_function_object *pfob,
|
|
|
- struct be_mcc_wrb_context *context)
|
|
|
-{
|
|
|
- unsigned long irq;
|
|
|
-
|
|
|
- ASSERT(context);
|
|
|
- /*
|
|
|
- * Zero during free to try and catch any bugs where the context
|
|
|
- * is accessed after a free.
|
|
|
- */
|
|
|
- memset(context, 0, sizeof(context));
|
|
|
-
|
|
|
- spin_lock_irqsave(&pfob->mcc_context_lock, irq);
|
|
|
-
|
|
|
- if (context == &pfob->mailbox.default_context) {
|
|
|
- /* Free the default context. */
|
|
|
- ASSERT(pfob->mailbox.default_context_allocated);
|
|
|
- pfob->mailbox.default_context_allocated = false;
|
|
|
- } else {
|
|
|
- /* Add to free list. */
|
|
|
- ASSERT(pfob->mcc);
|
|
|
- list_add_tail(&context->next,
|
|
|
- &pfob->mcc->wrb_context.list_head);
|
|
|
- }
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&pfob->mcc_context_lock, irq);
|
|
|
-}
|
|
|
-
|
|
|
-int
|
|
|
-be_mcc_add_async_event_callback(struct be_mcc_object *mcc_object,
|
|
|
- mcc_async_event_callback cb, void *cb_context)
|
|
|
-{
|
|
|
- /* Lock against anyone trying to change the callback/context pointers
|
|
|
- * while being used. */
|
|
|
- spin_lock_irqsave(&mcc_object->parent_function->cq_lock,
|
|
|
- mcc_object->parent_function->cq_irq);
|
|
|
-
|
|
|
- /* Assign the async callback. */
|
|
|
- mcc_object->async_context = cb_context;
|
|
|
- mcc_object->async_cb = cb;
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&mcc_object->parent_function->cq_lock,
|
|
|
- mcc_object->parent_function->cq_irq);
|
|
|
-
|
|
|
- return BE_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-#define MPU_EP_CONTROL 0
|
|
|
-#define MPU_EP_SEMAPHORE 0xac
|
|
|
-
|
|
|
-/*
|
|
|
- *-------------------------------------------------------------------
|
|
|
- * Function: be_wait_for_POST_complete
|
|
|
- * Waits until the BladeEngine POST completes (either in error or success).
|
|
|
- * pfob -
|
|
|
- * return status - BE_SUCCESS (0) on success. Negative error code on failure.
|
|
|
- *-------------------------------------------------------------------
|
|
|
- */
|
|
|
-static int be_wait_for_POST_complete(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct MGMT_HBA_POST_STATUS_STRUCT_AMAP status;
|
|
|
- int s;
|
|
|
- u32 post_error, post_stage;
|
|
|
-
|
|
|
- const u32 us_per_loop = 1000; /* 1000us */
|
|
|
- const u32 print_frequency_loops = 1000000 / us_per_loop;
|
|
|
- const u32 max_loops = 60 * print_frequency_loops;
|
|
|
- u32 loops = 0;
|
|
|
-
|
|
|
- /*
|
|
|
- * Wait for arm fw indicating it is done or a fatal error happened.
|
|
|
- * Note: POST can take some time to complete depending on configuration
|
|
|
- * settings (consider ARM attempts to acquire an IP address
|
|
|
- * over DHCP!!!).
|
|
|
- *
|
|
|
- */
|
|
|
- do {
|
|
|
- status.dw[0] = ioread32(pfob->csr_va + MPU_EP_SEMAPHORE);
|
|
|
- post_error = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT,
|
|
|
- error, &status);
|
|
|
- post_stage = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT,
|
|
|
- stage, &status);
|
|
|
- if (0 == (loops % print_frequency_loops)) {
|
|
|
- /* Print current status */
|
|
|
- TRACE(DL_INFO, "POST status = 0x%x (stage = 0x%x)",
|
|
|
- status.dw[0], post_stage);
|
|
|
- }
|
|
|
- udelay(us_per_loop);
|
|
|
- } while ((post_error != 1) &&
|
|
|
- (post_stage != POST_STAGE_ARMFW_READY) &&
|
|
|
- (++loops < max_loops));
|
|
|
-
|
|
|
- if (post_error == 1) {
|
|
|
- TRACE(DL_ERR, "POST error! Status = 0x%x (stage = 0x%x)",
|
|
|
- status.dw[0], post_stage);
|
|
|
- s = BE_NOT_OK;
|
|
|
- } else if (post_stage != POST_STAGE_ARMFW_READY) {
|
|
|
- TRACE(DL_ERR, "POST time-out! Status = 0x%x (stage = 0x%x)",
|
|
|
- status.dw[0], post_stage);
|
|
|
- s = BE_NOT_OK;
|
|
|
- } else {
|
|
|
- s = BE_SUCCESS;
|
|
|
- }
|
|
|
- return s;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- *-------------------------------------------------------------------
|
|
|
- * Function: be_kickoff_and_wait_for_POST
|
|
|
- * Interacts with the BladeEngine management processor to initiate POST, and
|
|
|
- * subsequently waits until POST completes (either in error or success).
|
|
|
- * The caller must acquire the reset semaphore before initiating POST
|
|
|
- * to prevent multiple drivers interacting with the management processor.
|
|
|
- * Once POST is complete the caller must release the reset semaphore.
|
|
|
- * Callers who only want to wait for POST complete may call
|
|
|
- * be_wait_for_POST_complete.
|
|
|
- * pfob -
|
|
|
- * return status - BE_SUCCESS (0) on success. Negative error code on failure.
|
|
|
- *-------------------------------------------------------------------
|
|
|
- */
|
|
|
-static int
|
|
|
-be_kickoff_and_wait_for_POST(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct MGMT_HBA_POST_STATUS_STRUCT_AMAP status;
|
|
|
- int s;
|
|
|
-
|
|
|
- const u32 us_per_loop = 1000; /* 1000us */
|
|
|
- const u32 print_frequency_loops = 1000000 / us_per_loop;
|
|
|
- const u32 max_loops = 5 * print_frequency_loops;
|
|
|
- u32 loops = 0;
|
|
|
- u32 post_error, post_stage;
|
|
|
-
|
|
|
- /* Wait for arm fw awaiting host ready or a fatal error happened. */
|
|
|
- TRACE(DL_INFO, "Wait for BladeEngine ready to POST");
|
|
|
- do {
|
|
|
- status.dw[0] = ioread32(pfob->csr_va + MPU_EP_SEMAPHORE);
|
|
|
- post_error = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT,
|
|
|
- error, &status);
|
|
|
- post_stage = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT,
|
|
|
- stage, &status);
|
|
|
- if (0 == (loops % print_frequency_loops)) {
|
|
|
- /* Print current status */
|
|
|
- TRACE(DL_INFO, "POST status = 0x%x (stage = 0x%x)",
|
|
|
- status.dw[0], post_stage);
|
|
|
- }
|
|
|
- udelay(us_per_loop);
|
|
|
- } while ((post_error != 1) &&
|
|
|
- (post_stage < POST_STAGE_AWAITING_HOST_RDY) &&
|
|
|
- (++loops < max_loops));
|
|
|
-
|
|
|
- if (post_error == 1) {
|
|
|
- TRACE(DL_ERR, "Pre-POST error! Status = 0x%x (stage = 0x%x)",
|
|
|
- status.dw[0], post_stage);
|
|
|
- s = BE_NOT_OK;
|
|
|
- } else if (post_stage == POST_STAGE_AWAITING_HOST_RDY) {
|
|
|
- iowrite32(POST_STAGE_HOST_RDY, pfob->csr_va + MPU_EP_SEMAPHORE);
|
|
|
-
|
|
|
- /* Wait for POST to complete */
|
|
|
- s = be_wait_for_POST_complete(pfob);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * Either a timeout waiting for host ready signal or POST has
|
|
|
- * moved ahead without requiring a host ready signal.
|
|
|
- * Might as well give POST a chance to complete
|
|
|
- * (or timeout again).
|
|
|
- */
|
|
|
- s = be_wait_for_POST_complete(pfob);
|
|
|
- }
|
|
|
- return s;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- *-------------------------------------------------------------------
|
|
|
- * Function: be_pci_soft_reset
|
|
|
- * This function is called to issue a BladeEngine soft reset.
|
|
|
- * Callers should acquire the soft reset semaphore before calling this
|
|
|
- * function. Additionaly, callers should ensure they cannot be pre-empted
|
|
|
- * while the routine executes. Upon completion of this routine, callers
|
|
|
- * should release the reset semaphore. This routine implicitly waits
|
|
|
- * for BladeEngine POST to complete.
|
|
|
- * pfob -
|
|
|
- * return status - BE_SUCCESS (0) on success. Negative error code on failure.
|
|
|
- *-------------------------------------------------------------------
|
|
|
- */
|
|
|
-int be_pci_soft_reset(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct PCICFG_SOFT_RESET_CSR_AMAP soft_reset;
|
|
|
- struct PCICFG_ONLINE0_CSR_AMAP pciOnline0;
|
|
|
- struct PCICFG_ONLINE1_CSR_AMAP pciOnline1;
|
|
|
- struct EP_CONTROL_CSR_AMAP epControlCsr;
|
|
|
- int status = BE_SUCCESS;
|
|
|
- u32 i, soft_reset_bit;
|
|
|
-
|
|
|
- TRACE(DL_NOTE, "PCI reset...");
|
|
|
-
|
|
|
- /* Issue soft reset #1 to get BladeEngine into a known state. */
|
|
|
- soft_reset.dw[0] = PCICFG0_READ(pfob, soft_reset);
|
|
|
- AMAP_SET_BITS_PTR(PCICFG_SOFT_RESET_CSR, softreset, soft_reset.dw, 1);
|
|
|
- PCICFG0_WRITE(pfob, host_timer_int_ctrl, soft_reset.dw[0]);
|
|
|
- /*
|
|
|
- * wait til soft reset is deasserted - hardware
|
|
|
- * deasserts after some time.
|
|
|
- */
|
|
|
- i = 0;
|
|
|
- do {
|
|
|
- udelay(50);
|
|
|
- soft_reset.dw[0] = PCICFG0_READ(pfob, soft_reset);
|
|
|
- soft_reset_bit = AMAP_GET_BITS_PTR(PCICFG_SOFT_RESET_CSR,
|
|
|
- softreset, soft_reset.dw);
|
|
|
- } while (soft_reset_bit && (i++ < 1024));
|
|
|
- if (soft_reset_bit != 0) {
|
|
|
- TRACE(DL_ERR, "Soft-reset #1 did not deassert as expected.");
|
|
|
- status = BE_NOT_OK;
|
|
|
- goto Error_label;
|
|
|
- }
|
|
|
- /* Mask everything */
|
|
|
- PCICFG0_WRITE(pfob, ue_status_low_mask, 0xFFFFFFFF);
|
|
|
- PCICFG0_WRITE(pfob, ue_status_hi_mask, 0xFFFFFFFF);
|
|
|
- /*
|
|
|
- * Set everything offline except MPU IRAM (it is offline with
|
|
|
- * the soft-reset, but soft-reset does not reset the PCICFG registers!)
|
|
|
- */
|
|
|
- pciOnline0.dw[0] = 0;
|
|
|
- pciOnline1.dw[0] = 0;
|
|
|
- AMAP_SET_BITS_PTR(PCICFG_ONLINE1_CSR, mpu_iram_online,
|
|
|
- pciOnline1.dw, 1);
|
|
|
- PCICFG0_WRITE(pfob, online0, pciOnline0.dw[0]);
|
|
|
- PCICFG0_WRITE(pfob, online1, pciOnline1.dw[0]);
|
|
|
-
|
|
|
- udelay(20000);
|
|
|
-
|
|
|
- /* Issue soft reset #2. */
|
|
|
- AMAP_SET_BITS_PTR(PCICFG_SOFT_RESET_CSR, softreset, soft_reset.dw, 1);
|
|
|
- PCICFG0_WRITE(pfob, host_timer_int_ctrl, soft_reset.dw[0]);
|
|
|
- /*
|
|
|
- * wait til soft reset is deasserted - hardware
|
|
|
- * deasserts after some time.
|
|
|
- */
|
|
|
- i = 0;
|
|
|
- do {
|
|
|
- udelay(50);
|
|
|
- soft_reset.dw[0] = PCICFG0_READ(pfob, soft_reset);
|
|
|
- soft_reset_bit = AMAP_GET_BITS_PTR(PCICFG_SOFT_RESET_CSR,
|
|
|
- softreset, soft_reset.dw);
|
|
|
- } while (soft_reset_bit && (i++ < 1024));
|
|
|
- if (soft_reset_bit != 0) {
|
|
|
- TRACE(DL_ERR, "Soft-reset #1 did not deassert as expected.");
|
|
|
- status = BE_NOT_OK;
|
|
|
- goto Error_label;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- udelay(20000);
|
|
|
-
|
|
|
- /* Take MPU out of reset. */
|
|
|
-
|
|
|
- epControlCsr.dw[0] = ioread32(pfob->csr_va + MPU_EP_CONTROL);
|
|
|
- AMAP_SET_BITS_PTR(EP_CONTROL_CSR, CPU_reset, &epControlCsr, 0);
|
|
|
- iowrite32((u32)epControlCsr.dw[0], pfob->csr_va + MPU_EP_CONTROL);
|
|
|
-
|
|
|
- /* Kickoff BE POST and wait for completion */
|
|
|
- status = be_kickoff_and_wait_for_POST(pfob);
|
|
|
-
|
|
|
-Error_label:
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
- *-------------------------------------------------------------------
|
|
|
- * Function: be_pci_reset_required
|
|
|
- * This private function is called to detect if a host entity is
|
|
|
- * required to issue a PCI soft reset and subsequently drive
|
|
|
- * BladeEngine POST. Scenarios where this is required:
|
|
|
- * 1) BIOS-less configuration
|
|
|
- * 2) Hot-swap/plug/power-on
|
|
|
- * pfob -
|
|
|
- * return true if a reset is required, false otherwise
|
|
|
- *-------------------------------------------------------------------
|
|
|
- */
|
|
|
-static bool be_pci_reset_required(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- struct MGMT_HBA_POST_STATUS_STRUCT_AMAP status;
|
|
|
- bool do_reset = false;
|
|
|
- u32 post_error, post_stage;
|
|
|
-
|
|
|
- /*
|
|
|
- * Read the POST status register
|
|
|
- */
|
|
|
- status.dw[0] = ioread32(pfob->csr_va + MPU_EP_SEMAPHORE);
|
|
|
- post_error = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT, error,
|
|
|
- &status);
|
|
|
- post_stage = AMAP_GET_BITS_PTR(MGMT_HBA_POST_STATUS_STRUCT, stage,
|
|
|
- &status);
|
|
|
- if (post_stage <= POST_STAGE_AWAITING_HOST_RDY) {
|
|
|
- /*
|
|
|
- * If BladeEngine is waiting for host ready indication,
|
|
|
- * we want to do a PCI reset.
|
|
|
- */
|
|
|
- do_reset = true;
|
|
|
- }
|
|
|
-
|
|
|
- return do_reset;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- *-------------------------------------------------------------------
|
|
|
- * Function: be_drive_POST
|
|
|
- * This function is called to drive BladeEngine POST. The
|
|
|
- * caller should ensure they cannot be pre-empted while this routine executes.
|
|
|
- * pfob -
|
|
|
- * return status - BE_SUCCESS (0) on success. Negative error code on failure.
|
|
|
- *-------------------------------------------------------------------
|
|
|
- */
|
|
|
-int be_drive_POST(struct be_function_object *pfob)
|
|
|
-{
|
|
|
- int status;
|
|
|
-
|
|
|
- if (false != be_pci_reset_required(pfob)) {
|
|
|
- /* PCI reset is needed (implicitly starts and waits for POST) */
|
|
|
- status = be_pci_soft_reset(pfob);
|
|
|
- } else {
|
|
|
- /* No PCI reset is needed, start POST */
|
|
|
- status = be_kickoff_and_wait_for_POST(pfob);
|
|
|
- }
|
|
|
-
|
|
|
- return status;
|
|
|
-}
|