| /* |
| * Copyright (C) 2005 - 2008 ServerEngines |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. The full GNU General |
| * Public License is included in this distribution in the file called COPYING. |
| * |
| * Contact Information: |
| * linux-drivers@serverengines.com |
| * |
| * ServerEngines |
| * 209 N. Fair Oaks Ave |
| * Sunnyvale, CA 94085 |
| */ |
| #include "hwlib.h" |
| #include "bestatus.h" |
| /* |
| This routine creates an event queue based on the client completion |
| queue configuration information. |
| |
| FunctionObject - Handle to a function object |
| EqBaseVa - Base VA for a the EQ ring |
| SizeEncoding - The encoded size for the EQ entries. This value is |
| either CEV_EQ_SIZE_4 or CEV_EQ_SIZE_16 |
| NumEntries - CEV_CQ_CNT_* values. |
| Watermark - Enables watermark based coalescing. This parameter |
| must be of the type CEV_WMARK_* if watermarks |
| are enabled. If watermarks to to be disabled |
| this value should be-1. |
| TimerDelay - If a timer delay is enabled this value should be the |
| time of the delay in 8 microsecond units. If |
| delays are not used this parameter should be |
| set to -1. |
| ppEqObject - Internal EQ Handle returned. |
| |
| Returns BE_SUCCESS if successfull,, otherwise a useful error code |
| is returned. |
| |
| IRQL < DISPATCH_LEVEL |
| */ |
| int |
| be_eq_create(struct be_function_object *pfob, |
| struct ring_desc *rd, u32 eqe_size, u32 num_entries, |
| u32 watermark, /* CEV_WMARK_* or -1 */ |
| u32 timer_delay, /* in 8us units, or -1 */ |
| struct be_eq_object *eq_object) |
| { |
| int status = BE_SUCCESS; |
| u32 num_entries_encoding, eqe_size_encoding, length; |
| struct FWCMD_COMMON_EQ_CREATE *fwcmd = NULL; |
| struct MCC_WRB_AMAP *wrb = NULL; |
| u32 n; |
| unsigned long irql; |
| |
| ASSERT(rd); |
| ASSERT(eq_object); |
| |
| switch (num_entries) { |
| case 256: |
| num_entries_encoding = CEV_EQ_CNT_256; |
| break; |
| case 512: |
| num_entries_encoding = CEV_EQ_CNT_512; |
| break; |
| case 1024: |
| num_entries_encoding = CEV_EQ_CNT_1024; |
| break; |
| case 2048: |
| num_entries_encoding = CEV_EQ_CNT_2048; |
| break; |
| case 4096: |
| num_entries_encoding = CEV_EQ_CNT_4096; |
| break; |
| default: |
| ASSERT(0); |
| return BE_STATUS_INVALID_PARAMETER; |
| } |
| |
| switch (eqe_size) { |
| case 4: |
| eqe_size_encoding = CEV_EQ_SIZE_4; |
| break; |
| case 16: |
| eqe_size_encoding = CEV_EQ_SIZE_16; |
| break; |
| default: |
| ASSERT(0); |
| return BE_STATUS_INVALID_PARAMETER; |
| } |
| |
| if ((eqe_size == 4 && num_entries < 1024) || |
| (eqe_size == 16 && num_entries == 4096)) { |
| TRACE(DL_ERR, "Bad EQ size. eqe_size:%d num_entries:%d", |
| eqe_size, num_entries); |
| ASSERT(0); |
| return BE_STATUS_INVALID_PARAMETER; |
| } |
| |
| memset(eq_object, 0, sizeof(*eq_object)); |
| |
| atomic_set(&eq_object->ref_count, 0); |
| eq_object->parent_function = pfob; |
| eq_object->eq_id = 0xFFFFFFFF; |
| |
| INIT_LIST_HEAD(&eq_object->cq_list_head); |
| |
| length = num_entries * eqe_size; |
| |
| spin_lock_irqsave(&pfob->post_lock, irql); |
| |
| 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_EQ_CREATE); |
| |
| fwcmd->params.request.num_pages = PAGES_SPANNED(OFFSET_IN_PAGE(rd->va), |
| length); |
| n = pfob->pci_function_number; |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Func, &fwcmd->params.request.context, n); |
| |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, valid, &fwcmd->params.request.context, 1); |
| |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Size, |
| &fwcmd->params.request.context, eqe_size_encoding); |
| |
| n = 0; /* Protection Domain is always 0 in Linux driver */ |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, PD, &fwcmd->params.request.context, n); |
| |
| /* Let the caller ARM the EQ with the doorbell. */ |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Armed, &fwcmd->params.request.context, 0); |
| |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Count, &fwcmd->params.request.context, |
| num_entries_encoding); |
| |
| n = pfob->pci_function_number * 32; |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, EventVect, |
| &fwcmd->params.request.context, n); |
| if (watermark != -1) { |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, WME, |
| &fwcmd->params.request.context, 1); |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Watermark, |
| &fwcmd->params.request.context, watermark); |
| ASSERT(watermark <= CEV_WMARK_240); |
| } else |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, WME, |
| &fwcmd->params.request.context, 0); |
| if (timer_delay != -1) { |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR, |
| &fwcmd->params.request.context, 1); |
| |
| ASSERT(timer_delay <= 250); /* max value according to EAS */ |
| timer_delay = min(timer_delay, (u32)250); |
| |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, Delay, |
| &fwcmd->params.request.context, timer_delay); |
| } else { |
| AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR, |
| &fwcmd->params.request.context, 0); |
| } |
| /* Create a page list for the FWCMD. */ |
| be_rd_to_pa_list(rd, fwcmd->params.request.pages, |
| ARRAY_SIZE(fwcmd->params.request.pages)); |
| |
| 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 EQ failed."); |
| goto Error; |
| } |
| /* Get the EQ id. The MPU allocates the IDs. */ |
| eq_object->eq_id = fwcmd->params.response.eq_id; |
| |
| 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; |
| } |
| |
| /* |
| Deferences the given object. Once the object's reference count drops to |
| zero, the object is destroyed and all resources that are held by this |
| object are released. The on-chip context is also destroyed along with |
| the queue ID, and any mappings made into the UT. |
| |
| eq_object - EQ handle returned from eq_object_create. |
| |
| Returns BE_SUCCESS if successfull, otherwise a useful error code |
| is returned. |
| |
| IRQL: IRQL < DISPATCH_LEVEL |
| */ |
| int be_eq_destroy(struct be_eq_object *eq_object) |
| { |
| int status = 0; |
| |
| ASSERT(atomic_read(&eq_object->ref_count) == 0); |
| /* no CQs should reference this EQ now */ |
| ASSERT(list_empty(&eq_object->cq_list_head)); |
| |
| /* Send fwcmd to destroy the EQ. */ |
| status = be_function_ring_destroy(eq_object->parent_function, |
| eq_object->eq_id, FWCMD_RING_TYPE_EQ, |
| NULL, NULL, NULL, NULL); |
| ASSERT(status == 0); |
| |
| return BE_SUCCESS; |
| } |
| /* |
| *--------------------------------------------------------------------------- |
| * Function: be_eq_modify_delay |
| * Changes the EQ delay for a group of EQs. |
| * num_eq - The number of EQs in the eq_array to adjust. |
| * This also is the number of delay values in |
| * the eq_delay_array. |
| * eq_array - Array of struct be_eq_object pointers to adjust. |
| * eq_delay_array - Array of "num_eq" timer delays in units |
| * of microseconds. The be_eq_query_delay_range |
| * fwcmd returns the resolution and range of |
| * legal EQ delays. |
| * cb - |
| * cb_context - |
| * q_ctxt - Optional. Pointer to a previously allocated |
| * struct. If the MCC WRB ring is full, this |
| * structure is used to queue the operation. It |
| * will be posted to the MCC ring when space |
| * becomes available. All queued commands will |
| * be posted to the ring in the order they are |
| * received. It is always valid to pass a pointer to |
| * a generic be_generic_q_cntxt. However, |
| * the specific context structs |
| * are generally smaller than the generic struct. |
| * return pend_status - BE_SUCCESS (0) on success. |
| * BE_PENDING (postive value) if the FWCMD |
| * completion is pending. Negative error code on failure. |
| *------------------------------------------------------------------------- |
| */ |
| int |
| be_eq_modify_delay(struct be_function_object *pfob, |
| u32 num_eq, struct be_eq_object **eq_array, |
| u32 *eq_delay_array, mcc_wrb_cqe_callback cb, |
| void *cb_context, struct be_eq_modify_delay_q_ctxt *q_ctxt) |
| { |
| struct FWCMD_COMMON_MODIFY_EQ_DELAY *fwcmd = NULL; |
| struct MCC_WRB_AMAP *wrb = NULL; |
| int status = 0; |
| struct be_generic_q_ctxt *gen_ctxt = NULL; |
| u32 i; |
| unsigned long irql; |
| |
| spin_lock_irqsave(&pfob->post_lock, irql); |
| |
| wrb = be_function_peek_mcc_wrb(pfob); |
| if (!wrb) { |
| if (q_ctxt && cb) { |
| wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; |
| gen_ctxt = (struct be_generic_q_ctxt *) q_ctxt; |
| gen_ctxt->context.bytes = sizeof(*q_ctxt); |
| } else { |
| status = BE_STATUS_NO_MCC_WRB; |
| goto Error; |
| } |
| } |
| /* Prepares an embedded fwcmd, including request/response sizes. */ |
| fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_MODIFY_EQ_DELAY); |
| |
| ASSERT(num_eq > 0); |
| ASSERT(num_eq <= ARRAY_SIZE(fwcmd->params.request.delay)); |
| fwcmd->params.request.num_eq = num_eq; |
| for (i = 0; i < num_eq; i++) { |
| fwcmd->params.request.delay[i].eq_id = eq_array[i]->eq_id; |
| fwcmd->params.request.delay[i].delay_in_microseconds = |
| eq_delay_array[i]; |
| } |
| |
| /* Post the f/w command */ |
| status = be_function_post_mcc_wrb(pfob, wrb, gen_ctxt, |
| cb, cb_context, NULL, NULL, fwcmd, NULL); |
| |
| 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; |
| } |
| |