Merge branch 'for-2.6.29' of git://linux-nfs.org/~bfields/linux
[linux-2.6] / drivers / staging / benet / eq.c
1 /*
2  * Copyright (C) 2005 - 2008 ServerEngines
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation.  The full GNU General
8  * Public License is included in this distribution in the file called COPYING.
9  *
10  * Contact Information:
11  * linux-drivers@serverengines.com
12  *
13  * ServerEngines
14  * 209 N. Fair Oaks Ave
15  * Sunnyvale, CA 94085
16  */
17 #include "hwlib.h"
18 #include "bestatus.h"
19 /*
20     This routine creates an event queue based on the client completion
21     queue configuration information.
22
23     FunctionObject      - Handle to a function object
24     EqBaseVa            - Base VA for a the EQ ring
25     SizeEncoding        - The encoded size for the EQ entries. This value is
26                         either CEV_EQ_SIZE_4 or CEV_EQ_SIZE_16
27     NumEntries          - CEV_CQ_CNT_* values.
28     Watermark           - Enables watermark based coalescing.  This parameter
29                         must be of the type CEV_WMARK_* if watermarks
30                         are enabled.  If watermarks to to be disabled
31                         this value should be-1.
32     TimerDelay          - If a timer delay is enabled this value should be the
33                         time of the delay in 8 microsecond units.  If
34                         delays are not used this parameter should be
35                         set to -1.
36     ppEqObject          - Internal EQ Handle returned.
37
38     Returns BE_SUCCESS if successfull,, otherwise a useful error code
39         is returned.
40
41     IRQL < DISPATCH_LEVEL
42 */
43 int
44 be_eq_create(struct be_function_object *pfob,
45                 struct ring_desc *rd, u32 eqe_size, u32 num_entries,
46                 u32 watermark,  /* CEV_WMARK_* or -1 */
47                 u32 timer_delay,        /* in 8us units, or -1 */
48                 struct be_eq_object *eq_object)
49 {
50         int status = BE_SUCCESS;
51         u32 num_entries_encoding, eqe_size_encoding, length;
52         struct FWCMD_COMMON_EQ_CREATE *fwcmd = NULL;
53         struct MCC_WRB_AMAP *wrb = NULL;
54         u32 n;
55         unsigned long irql;
56
57         ASSERT(rd);
58         ASSERT(eq_object);
59
60         switch (num_entries) {
61         case 256:
62                 num_entries_encoding = CEV_EQ_CNT_256;
63                 break;
64         case 512:
65                 num_entries_encoding = CEV_EQ_CNT_512;
66                 break;
67         case 1024:
68                 num_entries_encoding = CEV_EQ_CNT_1024;
69                 break;
70         case 2048:
71                 num_entries_encoding = CEV_EQ_CNT_2048;
72                 break;
73         case 4096:
74                 num_entries_encoding = CEV_EQ_CNT_4096;
75                 break;
76         default:
77                 ASSERT(0);
78                 return BE_STATUS_INVALID_PARAMETER;
79         }
80
81         switch (eqe_size) {
82         case 4:
83                 eqe_size_encoding = CEV_EQ_SIZE_4;
84                 break;
85         case 16:
86                 eqe_size_encoding = CEV_EQ_SIZE_16;
87                 break;
88         default:
89                 ASSERT(0);
90                 return BE_STATUS_INVALID_PARAMETER;
91         }
92
93         if ((eqe_size == 4 && num_entries < 1024) ||
94             (eqe_size == 16 && num_entries == 4096)) {
95                 TRACE(DL_ERR, "Bad EQ size. eqe_size:%d num_entries:%d",
96                       eqe_size, num_entries);
97                 ASSERT(0);
98                 return BE_STATUS_INVALID_PARAMETER;
99         }
100
101         memset(eq_object, 0, sizeof(*eq_object));
102
103         atomic_set(&eq_object->ref_count, 0);
104         eq_object->parent_function = pfob;
105         eq_object->eq_id = 0xFFFFFFFF;
106
107         INIT_LIST_HEAD(&eq_object->cq_list_head);
108
109         length = num_entries * eqe_size;
110
111         spin_lock_irqsave(&pfob->post_lock, irql);
112
113         wrb = be_function_peek_mcc_wrb(pfob);
114         if (!wrb) {
115                 ASSERT(wrb);
116                 TRACE(DL_ERR, "No free MCC WRBs in create EQ.");
117                 status = BE_STATUS_NO_MCC_WRB;
118                 goto Error;
119         }
120         /* Prepares an embedded fwcmd, including request/response sizes. */
121         fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_EQ_CREATE);
122
123         fwcmd->params.request.num_pages = PAGES_SPANNED(OFFSET_IN_PAGE(rd->va),
124                                                                         length);
125         n = pfob->pci_function_number;
126         AMAP_SET_BITS_PTR(EQ_CONTEXT, Func, &fwcmd->params.request.context, n);
127
128         AMAP_SET_BITS_PTR(EQ_CONTEXT, valid, &fwcmd->params.request.context, 1);
129
130         AMAP_SET_BITS_PTR(EQ_CONTEXT, Size,
131                         &fwcmd->params.request.context, eqe_size_encoding);
132
133         n = 0; /* Protection Domain is always 0 in  Linux  driver */
134         AMAP_SET_BITS_PTR(EQ_CONTEXT, PD, &fwcmd->params.request.context, n);
135
136         /* Let the caller ARM the EQ with the doorbell. */
137         AMAP_SET_BITS_PTR(EQ_CONTEXT, Armed, &fwcmd->params.request.context, 0);
138
139         AMAP_SET_BITS_PTR(EQ_CONTEXT, Count, &fwcmd->params.request.context,
140                                         num_entries_encoding);
141
142         n = pfob->pci_function_number * 32;
143         AMAP_SET_BITS_PTR(EQ_CONTEXT, EventVect,
144                                 &fwcmd->params.request.context, n);
145         if (watermark != -1) {
146                 AMAP_SET_BITS_PTR(EQ_CONTEXT, WME,
147                                 &fwcmd->params.request.context, 1);
148                 AMAP_SET_BITS_PTR(EQ_CONTEXT, Watermark,
149                                 &fwcmd->params.request.context, watermark);
150                 ASSERT(watermark <= CEV_WMARK_240);
151         } else
152                 AMAP_SET_BITS_PTR(EQ_CONTEXT, WME,
153                                         &fwcmd->params.request.context, 0);
154         if (timer_delay != -1) {
155                 AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR,
156                                         &fwcmd->params.request.context, 1);
157
158                 ASSERT(timer_delay <= 250);     /* max value according to EAS */
159                 timer_delay = min(timer_delay, (u32)250);
160
161                 AMAP_SET_BITS_PTR(EQ_CONTEXT, Delay,
162                                 &fwcmd->params.request.context, timer_delay);
163         } else {
164                 AMAP_SET_BITS_PTR(EQ_CONTEXT, TMR,
165                                 &fwcmd->params.request.context, 0);
166         }
167         /* Create a page list for the FWCMD. */
168         be_rd_to_pa_list(rd, fwcmd->params.request.pages,
169                           ARRAY_SIZE(fwcmd->params.request.pages));
170
171         status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL,
172                                         NULL, NULL, fwcmd, NULL);
173         if (status != BE_SUCCESS) {
174                 TRACE(DL_ERR, "MCC to create EQ failed.");
175                 goto Error;
176         }
177         /* Get the EQ id.  The MPU allocates the IDs. */
178         eq_object->eq_id = fwcmd->params.response.eq_id;
179
180 Error:
181         spin_unlock_irqrestore(&pfob->post_lock, irql);
182
183         if (pfob->pend_queue_driving && pfob->mcc) {
184                 pfob->pend_queue_driving = 0;
185                 be_drive_mcc_wrb_queue(pfob->mcc);
186         }
187         return status;
188 }
189
190 /*
191     Deferences the given object. Once the object's reference count drops to
192     zero, the object is destroyed and all resources that are held by this
193     object are released.  The on-chip context is also destroyed along with
194     the queue ID, and any mappings made into the UT.
195
196     eq_object            - EQ handle returned from eq_object_create.
197
198     Returns BE_SUCCESS if successfull, otherwise a useful error code
199         is returned.
200
201     IRQL: IRQL < DISPATCH_LEVEL
202 */
203 int be_eq_destroy(struct be_eq_object *eq_object)
204 {
205         int status = 0;
206
207         ASSERT(atomic_read(&eq_object->ref_count) == 0);
208         /* no CQs should reference this EQ now */
209         ASSERT(list_empty(&eq_object->cq_list_head));
210
211         /* Send fwcmd to destroy the EQ. */
212         status = be_function_ring_destroy(eq_object->parent_function,
213                              eq_object->eq_id, FWCMD_RING_TYPE_EQ,
214                                         NULL, NULL, NULL, NULL);
215         ASSERT(status == 0);
216
217         return BE_SUCCESS;
218 }
219 /*
220  *---------------------------------------------------------------------------
221  * Function: be_eq_modify_delay
222  *   Changes the EQ delay for a group of EQs.
223  * num_eq             - The number of EQs in the eq_array to adjust.
224  *                      This also is the number of delay values in
225  *                      the eq_delay_array.
226  * eq_array           - Array of struct be_eq_object pointers to adjust.
227  * eq_delay_array     - Array of "num_eq" timer delays in units
228  *                      of microseconds. The be_eq_query_delay_range
229  *                      fwcmd returns the resolution and range of
230  *                      legal EQ delays.
231  * cb           -
232  * cb_context   -
233  * q_ctxt             - Optional. Pointer to a previously allocated
234  *                      struct. If the MCC WRB ring is full, this
235  *                      structure is used to queue the operation. It
236  *                      will be posted to the MCC ring when space
237  *                      becomes available. All queued commands will
238  *                      be posted to the ring in the order they are
239  *                      received. It is always valid to pass a pointer to
240  *                      a generic be_generic_q_cntxt. However,
241  *                      the specific context structs
242  *                      are generally smaller than the generic struct.
243  * return pend_status - BE_SUCCESS (0) on success.
244  *                      BE_PENDING (postive value) if the FWCMD
245  *                      completion is pending. Negative error code on failure.
246  *-------------------------------------------------------------------------
247  */
248 int
249 be_eq_modify_delay(struct be_function_object *pfob,
250                    u32 num_eq, struct be_eq_object **eq_array,
251                    u32 *eq_delay_array, mcc_wrb_cqe_callback cb,
252                    void *cb_context, struct be_eq_modify_delay_q_ctxt *q_ctxt)
253 {
254         struct FWCMD_COMMON_MODIFY_EQ_DELAY *fwcmd = NULL;
255         struct MCC_WRB_AMAP *wrb = NULL;
256         int status = 0;
257         struct be_generic_q_ctxt *gen_ctxt = NULL;
258         u32 i;
259         unsigned long irql;
260
261         spin_lock_irqsave(&pfob->post_lock, irql);
262
263         wrb = be_function_peek_mcc_wrb(pfob);
264         if (!wrb) {
265                 if (q_ctxt && cb) {
266                         wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header;
267                         gen_ctxt = (struct be_generic_q_ctxt *) q_ctxt;
268                         gen_ctxt->context.bytes = sizeof(*q_ctxt);
269                 } else {
270                         status = BE_STATUS_NO_MCC_WRB;
271                         goto Error;
272                 }
273         }
274         /* Prepares an embedded fwcmd, including request/response sizes. */
275         fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_MODIFY_EQ_DELAY);
276
277         ASSERT(num_eq > 0);
278         ASSERT(num_eq <= ARRAY_SIZE(fwcmd->params.request.delay));
279         fwcmd->params.request.num_eq = num_eq;
280         for (i = 0; i < num_eq; i++) {
281                 fwcmd->params.request.delay[i].eq_id = eq_array[i]->eq_id;
282                 fwcmd->params.request.delay[i].delay_in_microseconds =
283                     eq_delay_array[i];
284         }
285
286         /* Post the f/w command */
287         status = be_function_post_mcc_wrb(pfob, wrb, gen_ctxt,
288                         cb, cb_context, NULL, NULL, fwcmd, NULL);
289
290 Error:
291         spin_unlock_irqrestore(&pfob->post_lock, irql);
292
293         if (pfob->pend_queue_driving && pfob->mcc) {
294                 pfob->pend_queue_driving = 0;
295                 be_drive_mcc_wrb_queue(pfob->mcc);
296         }
297         return status;
298 }
299