| /* |
| * 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; |
| } |