Merge from vendor branch GCC:
[dragonfly.git] / sys / dev / raid / ips / ips_commands.c
1 /*-
2  * Written by: David Jeffery
3  * Copyright (c) 2002 Adaptec Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/ips/ips_commands.c,v 1.10 2004/05/30 04:01:29 scottl Exp $
28  * $DragonFly: src/sys/dev/raid/ips/ips_commands.c,v 1.8 2004/12/10 04:09:46 y0netan1 Exp $
29  */
30
31 #include <dev/raid/ips/ips.h>
32
33 /*
34  * This is an interrupt callback.  It is called from
35  * interrupt context when the adapter has completed the
36  * command.  This very generic callback simply stores
37  * the command's return value in command->arg and wake's
38  * up anyone waiting on the command.
39  */
40 static void
41 ips_wakeup_callback(ips_command_t *command)
42 {
43         ips_cmd_status_t *status;
44
45         status = command->arg;
46         status->value = command->status.value;
47         bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
48                         BUS_DMASYNC_POSTWRITE);
49         wakeup(status);
50 }
51
52 /*
53  * Below are a series of functions for sending an IO request
54  * to the adapter.  The flow order is: start, send, callback, finish.
55  * The caller must have already assembled an iorequest struct to hold
56  * the details of the IO request.
57  */
58 static void
59 ips_io_request_finish(ips_command_t *command)
60 {
61         struct bio *iobuf = command->arg;
62
63         if (ips_read_request(iobuf)) {
64                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
65                                 BUS_DMASYNC_POSTREAD);
66         } else {
67                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
68                                 BUS_DMASYNC_POSTWRITE);
69         }
70         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
71         bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
72         if (COMMAND_ERROR(&command->status)) {
73                 iobuf->bio_flags |=BIO_ERROR;
74                 iobuf->bio_error = EIO;
75         }
76         ips_insert_free_cmd(command->sc, command);
77         ipsd_finish(iobuf);
78 }
79
80 static void
81 ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
82                         int error)
83 {
84         ips_softc_t *sc;
85         ips_command_t *command = cmdptr;
86         ips_sg_element_t *sg_list;
87         ips_io_cmd *command_struct;
88         struct bio *iobuf = command->arg;
89         int i, length = 0;
90         u_int8_t cmdtype;
91
92         sc = command->sc;
93         if (error) {
94                 printf("ips: error = %d in ips_sg_request_callback\n", error);
95                 bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
96                 bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
97                 iobuf->bio_flags |= BIO_ERROR;
98                 iobuf->bio_error = ENOMEM;
99                 ips_insert_free_cmd(sc, command);
100                 ipsd_finish(iobuf);
101                 return;
102         }
103         command_struct = (ips_io_cmd *)command->command_buffer;
104         command_struct->id = command->id;
105         command_struct->drivenum = (uintptr_t)iobuf->bio_driver1;
106         if (segnum != 1) {
107                 if (ips_read_request(iobuf))
108                         cmdtype = IPS_SG_READ_CMD;
109                 else
110                         cmdtype = IPS_SG_WRITE_CMD;
111                 command_struct->segnum = segnum;
112                 sg_list = (ips_sg_element_t *)((u_int8_t *)
113                            command->command_buffer + IPS_COMMAND_LEN);
114                 for (i = 0; i < segnum; i++) {
115                         sg_list[i].addr = segments[i].ds_addr;
116                         sg_list[i].len = segments[i].ds_len;
117                         length += segments[i].ds_len;
118                 }
119                 command_struct->buffaddr =
120                     (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
121         } else {
122                 if (ips_read_request(iobuf))
123                         cmdtype = IPS_READ_CMD;
124                 else
125                         cmdtype = IPS_WRITE_CMD;
126                 command_struct->buffaddr = segments[0].ds_addr;
127                 length = segments[0].ds_len;
128         }
129         command_struct->command = cmdtype;
130         command_struct->lba = iobuf->bio_pblkno;
131         length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
132         command_struct->length = length;
133         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
134                         BUS_DMASYNC_PREWRITE);
135         if (ips_read_request(iobuf)) {
136                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
137                                 BUS_DMASYNC_PREREAD);
138         } else {
139                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
140                                 BUS_DMASYNC_PREWRITE);
141         }
142         /*
143          * the cast to long long below is necessary because our b_pblkno
144          * is 32bit wide whereas it's 64bit on FreeBSD-CURRENT.
145          */
146         PRINTF(10, "ips test: command id: %d segments: %d "
147                 "pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
148                 (long long)iobuf->bio_pblkno,
149                 length, segments[0].ds_len);
150
151         sc->ips_issue_cmd(command);
152         return;
153 }
154
155 static int
156 ips_send_io_request(ips_command_t *command)
157 {
158         ips_softc_t *sc = command->sc;
159         struct bio *iobuf = command->arg;
160         command->data_dmatag = sc->sg_dmatag;
161
162         if (bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)) {
163                 device_printf(sc->dev, "dmamap failed\n");
164                 iobuf->bio_flags |= BIO_ERROR;
165                 iobuf->bio_error = ENOMEM;
166                 ips_insert_free_cmd(sc, command);
167                 ipsd_finish(iobuf);
168                 return 0;
169         }
170         command->callback = ips_io_request_finish;
171         PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount);
172         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
173                         iobuf->bio_data, iobuf->bio_bcount,
174                         ips_io_request_callback, command, 0);
175         return 0;
176 }
177
178 void
179 ips_start_io_request(ips_softc_t *sc, struct bio *iobuf)
180 {
181         if (ips_get_free_cmd(sc, ips_send_io_request, iobuf, 0)) {
182                 device_printf(sc->dev, "no mem for command slots!\n");
183                 iobuf->bio_flags |= BIO_ERROR;
184                 iobuf->bio_error = ENOMEM;
185                 ipsd_finish(iobuf);
186                 return;
187         }
188         return;
189 }
190
191 /*
192  * Below are a series of functions for sending an adapter info request
193  * to the adapter.  The flow order is: get, send, callback. It uses
194  * the generic finish callback at the top of this file.
195  * This can be used to get configuration/status info from the card
196  */
197 static void
198 ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum,
199                           int error)
200 {
201         ips_softc_t *sc;
202         ips_command_t *command = cmdptr;
203         ips_adapter_info_cmd *command_struct;
204         sc = command->sc;
205         if (error) {
206                 ips_cmd_status_t * status = command->arg;
207                 status->value = IPS_ERROR_STATUS; /* a lovely error value */
208                 ips_insert_free_cmd(sc, command);
209                 printf("ips: error = %d in ips_get_adapter_info\n", error);
210                 return;
211         }
212         command_struct = (ips_adapter_info_cmd *)command->command_buffer;
213         command_struct->command = IPS_ADAPTER_INFO_CMD;
214         command_struct->id = command->id;
215         command_struct->buffaddr = segments[0].ds_addr;
216
217         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
218                         BUS_DMASYNC_PREWRITE);
219         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
220                         BUS_DMASYNC_PREREAD);
221         sc->ips_issue_cmd(command);
222 }
223
224 static int
225 ips_send_adapter_info_cmd(ips_command_t *command)
226 {
227         ips_softc_t *sc = command->sc;
228         ips_cmd_status_t *status = command->arg;
229         int error = 0;
230
231         if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
232                                 /* alignemnt */ 1,
233                                 /* boundary  */ 0,
234                                 /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
235                                 /* highaddr  */ BUS_SPACE_MAXADDR,
236                                 /* filter    */ NULL,
237                                 /* filterarg */ NULL,
238                                 /* maxsize   */ IPS_ADAPTER_INFO_LEN,
239                                 /* numsegs   */ 1,
240                                 /* maxsegsize*/ IPS_ADAPTER_INFO_LEN,
241                                 /* flags     */ 0,
242                                 &command->data_dmatag) != 0) {
243                 printf("ips: can't alloc dma tag for adapter status\n");
244                 error = ENOMEM;
245                 goto exit;
246         }
247         if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
248            BUS_DMA_NOWAIT, &command->data_dmamap)) {
249                 error = ENOMEM;
250                 goto exit;
251         }
252         command->callback = ips_wakeup_callback;
253         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
254             command->data_buffer, IPS_ADAPTER_INFO_LEN,
255             ips_adapter_info_callback, command, BUS_DMA_NOWAIT);
256         if ((status->value == IPS_ERROR_STATUS) ||
257             tsleep(status, 0, "ips", 30 * hz) == EWOULDBLOCK)
258                 error = ETIMEDOUT;
259         if (error == 0) {
260                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
261                     BUS_DMASYNC_POSTREAD);
262                 memcpy(&(sc->adapter_info), command->data_buffer,
263                         IPS_ADAPTER_INFO_LEN);
264         }
265         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
266 exit:
267         /* I suppose I should clean up my memory allocations */
268         bus_dmamem_free(command->data_dmatag, command->data_buffer,
269             command->data_dmamap);
270         bus_dma_tag_destroy(command->data_dmatag);
271         ips_insert_free_cmd(sc, command);
272         return error;
273 }
274
275 int
276 ips_get_adapter_info(ips_softc_t *sc)
277 {
278         int error = 0;
279         ips_cmd_status_t *status;
280
281         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
282         if (ips_get_free_cmd(sc, ips_send_adapter_info_cmd, status,
283             IPS_NOWAIT_FLAG) > 0) {
284                 device_printf(sc->dev, "unable to get adapter configuration\n");
285                 free(status, M_IPSBUF);
286                 return ENXIO;
287         }
288         if (COMMAND_ERROR(status))
289                 error = ENXIO;
290         free(status, M_IPSBUF);
291         return error;
292 }
293
294 /*
295  * Below are a series of functions for sending a drive info request
296  * to the adapter.  The flow order is: get, send, callback. It uses
297  * the generic finish callback at the top of this file.
298  * This can be used to get drive status info from the card
299  */
300 static void
301 ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
302                         int error)
303 {
304         ips_softc_t *sc;
305         ips_command_t *command = cmdptr;
306         ips_drive_cmd *command_struct;
307
308         sc = command->sc;
309         if (error) {
310                 ips_cmd_status_t *status = command->arg;
311
312                 status->value = IPS_ERROR_STATUS;
313                 ips_insert_free_cmd(sc, command);
314                 printf("ips: error = %d in ips_get_drive_info\n", error);
315                 return;
316         }
317         command_struct = (ips_drive_cmd *)command->command_buffer;
318         command_struct->command = IPS_DRIVE_INFO_CMD;
319         command_struct->id = command->id;
320         command_struct->buffaddr = segments[0].ds_addr;
321         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
322             BUS_DMASYNC_PREWRITE);
323         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
324             BUS_DMASYNC_PREREAD);
325         sc->ips_issue_cmd(command);
326 }
327
328 static int
329 ips_send_drive_info_cmd(ips_command_t *command)
330 {
331         int error = 0;
332         ips_softc_t *sc = command->sc;
333         ips_cmd_status_t *status = command->arg;
334         ips_drive_info_t *driveinfo;
335
336         if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
337                                 /* alignemnt */ 1,
338                                 /* boundary  */ 0,
339                                 /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
340                                 /* highaddr  */ BUS_SPACE_MAXADDR,
341                                 /* filter    */ NULL,
342                                 /* filterarg */ NULL,
343                                 /* maxsize   */ IPS_DRIVE_INFO_LEN,
344                                 /* numsegs   */ 1,
345                                 /* maxsegsize*/ IPS_DRIVE_INFO_LEN,
346                                 /* flags     */ 0,
347                                 &command->data_dmatag) != 0) {
348                 printf("ips: can't alloc dma tag for drive status\n");
349                 error = ENOMEM;
350                 goto exit;
351         }
352         if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
353             BUS_DMA_NOWAIT, &command->data_dmamap)) {
354                 error = ENOMEM;
355                 goto exit;
356         }
357         command->callback = ips_wakeup_callback;
358         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
359             command->data_buffer,IPS_DRIVE_INFO_LEN,
360             ips_drive_info_callback, command, BUS_DMA_NOWAIT);
361         if ((status->value == IPS_ERROR_STATUS) ||
362             tsleep(status, 0, "ips", 10 * hz) == EWOULDBLOCK)
363                 error = ETIMEDOUT;
364
365         if (error == 0) {
366                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
367                     BUS_DMASYNC_POSTREAD);
368                 driveinfo = command->data_buffer;
369                 memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
370                 sc->drivecount = driveinfo->drivecount;
371                 device_printf(sc->dev, "logical drives: %d\n", sc->drivecount);
372         }
373         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
374 exit:
375         /* I suppose I should clean up my memory allocations */
376         bus_dmamem_free(command->data_dmatag, command->data_buffer,
377                         command->data_dmamap);
378         bus_dma_tag_destroy(command->data_dmatag);
379         ips_insert_free_cmd(sc, command);
380         return error;
381
382 }
383 int
384 ips_get_drive_info(ips_softc_t *sc)
385 {
386         int error = 0;
387         ips_cmd_status_t *status;
388
389         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
390         if (ips_get_free_cmd(sc, ips_send_drive_info_cmd, status,
391             IPS_NOWAIT_FLAG) > 0) {
392                 free(status, M_IPSBUF);
393                 device_printf(sc->dev, "unable to get drive configuration\n");
394                 return ENXIO;
395         }
396         if (COMMAND_ERROR(status))
397                 error = ENXIO;
398         free(status, M_IPSBUF);
399         return error;
400 }
401
402 /*
403  * Below is a pair of functions for making sure data is safely
404  * on disk by flushing the adapter's cache.
405  */
406 static int
407 ips_send_flush_cache_cmd(ips_command_t *command)
408 {
409         ips_softc_t *sc = command->sc;
410         ips_cmd_status_t *status = command->arg;
411         ips_generic_cmd *command_struct;
412
413         PRINTF(10,"ips test: got a command, building flush command\n");
414         command->callback = ips_wakeup_callback;
415         command_struct = (ips_generic_cmd *)command->command_buffer;
416         command_struct->command = IPS_CACHE_FLUSH_CMD;
417         command_struct->id = command->id;
418         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
419             BUS_DMASYNC_PREWRITE);
420         sc->ips_issue_cmd(command);
421         if (status->value != IPS_ERROR_STATUS)
422                 tsleep(status, 0, "flush2", 0);
423         ips_insert_free_cmd(sc, command);
424         return 0;
425 }
426
427 int
428 ips_flush_cache(ips_softc_t *sc)
429 {
430         ips_cmd_status_t *status;
431
432         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
433         device_printf(sc->dev, "flushing cache\n");
434         if (ips_get_free_cmd(sc, ips_send_flush_cache_cmd, status,
435             IPS_NOWAIT_FLAG)) {
436                 free(status, M_IPSBUF);
437                 device_printf(sc->dev, "ERROR: unable to get a command! "
438                     "can't flush cache!\n");
439                 return 1;
440         }
441         if (COMMAND_ERROR(status)) {
442                 free(status, M_IPSBUF);
443                 device_printf(sc->dev, "ERROR: cache flush command failed!\n");
444                 return 1;
445         }
446         free(status, M_IPSBUF);
447         return 0;
448 }
449
450 /*
451  * Simplified localtime to provide timevalues for ffdc.
452  * Taken from libc/stdtime/localtime.c
453  */
454 static void
455 ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
456 {
457         long    days, rem, y;
458         int     yleap, *ip, month;
459         int     year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
460         int     mon_lengths[2][IPS_MONSPERYEAR] = {
461                 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
462                 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
463         };
464
465         days = sctime / IPS_SECSPERDAY;
466         rem  = sctime % IPS_SECSPERDAY;
467
468         command->hour = rem / IPS_SECSPERHOUR;
469         rem           = rem % IPS_SECSPERHOUR;
470
471         command->minute = rem / IPS_SECSPERMIN;
472         command->second = rem % IPS_SECSPERMIN;
473
474         y = IPS_EPOCH_YEAR;
475         while (days < 0 || days >= (long)year_lengths[yleap = ips_isleap(y)]) {
476                 long    newy;
477
478                 newy = y + days / IPS_DAYSPERNYEAR;
479                 if (days < 0)
480                         --newy;
481                 days -= (newy - y) * IPS_DAYSPERNYEAR +
482                     IPS_LEAPS_THRU_END_OF(newy - 1) -
483                     IPS_LEAPS_THRU_END_OF(y - 1);
484                 y = newy;
485         }
486         command->yearH = y / 100;
487         command->yearL = y % 100;
488         ip = mon_lengths[yleap];
489         for (month = 0; days >= (long)ip[month]; ++month)
490                 days = days - (long)ip[month];
491         command->month = month + 1;
492         command->day = days + 1;
493 }
494
495 static int
496 ips_send_ffdc_reset_cmd(ips_command_t *command)
497 {
498         ips_softc_t *sc = command->sc;
499         ips_cmd_status_t *status = command->arg;
500         ips_adapter_ffdc_cmd *command_struct;
501
502         PRINTF(10, "ips test: got a command, building ffdc reset command\n");
503         command->callback = ips_wakeup_callback;
504         command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
505         command_struct->command = IPS_FFDC_CMD;
506         command_struct->id = command->id;
507         command_struct->reset_count = sc->ffdc_resetcount;
508         command_struct->reset_type  = 0x0;
509         ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
510         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
511             BUS_DMASYNC_PREWRITE);
512         sc->ips_issue_cmd(command);
513         if (status->value != IPS_ERROR_STATUS)
514                 tsleep(status, 0, "ffdc", 0);
515         ips_insert_free_cmd(sc, command);
516         return 0;
517 }
518
519 int
520 ips_ffdc_reset(ips_softc_t *sc)
521 {
522         ips_cmd_status_t *status;
523
524         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
525         if (ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status,
526             IPS_NOWAIT_FLAG)) {
527                 free(status, M_IPSBUF);
528                 device_printf(sc->dev, "ERROR: unable to get a command! "
529                     "can't send ffdc reset!\n");
530                 return 1;
531         }
532         if (COMMAND_ERROR(status)) {
533                 free(status, M_IPSBUF);
534                 device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
535                 return 1;
536         }
537         free(status, M_IPSBUF);
538         return 0;
539 }
540
541 static void
542 ips_write_nvram(ips_command_t *command)
543 {
544         ips_softc_t *sc = command->sc;
545         ips_rw_nvram_cmd *command_struct;
546         ips_nvram_page5 *nvram;
547
548         /*FIXME check for error */
549         command->callback = ips_wakeup_callback;
550         command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
551         command_struct->command = IPS_RW_NVRAM_CMD;
552         command_struct->id = command->id;
553         command_struct->pagenum = 5;
554         command_struct->rw      = 1;    /* write */
555         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
556             BUS_DMASYNC_POSTREAD);
557         nvram = command->data_buffer;
558         /* retrieve adapter info and save in sc */
559         sc->adapter_type = nvram->adapter_type;
560         strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
561         strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
562         nvram->operating_system = IPS_OS_FREEBSD;
563         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
564             BUS_DMASYNC_PREWRITE);
565         sc->ips_issue_cmd(command);
566 }
567
568 static void
569 ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
570                         int error)
571 {
572         ips_softc_t *sc;
573         ips_command_t *command = cmdptr;
574         ips_rw_nvram_cmd *command_struct;
575
576         sc = command->sc;
577         if (error) {
578                 ips_cmd_status_t *status = command->arg;
579
580                 status->value = IPS_ERROR_STATUS;
581                 ips_insert_free_cmd(sc, command);
582                 printf("ips: error = %d in ips_read_nvram_callback\n", error);
583                 return;
584         }
585         command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
586         command_struct->command = IPS_RW_NVRAM_CMD;
587         command_struct->id = command->id;
588         command_struct->pagenum = 5;
589         command_struct->rw = 0;
590         command_struct->buffaddr = segments[0].ds_addr;
591
592         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
593             BUS_DMASYNC_PREWRITE);
594         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
595             BUS_DMASYNC_PREREAD);
596         sc->ips_issue_cmd(command);
597 }
598
599 static int
600 ips_read_nvram(ips_command_t *command)
601 {
602         int error = 0;
603         ips_softc_t *sc = command->sc;
604         ips_cmd_status_t *status = command->arg;
605
606         if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
607                                 /* alignemnt */ 1,
608                                 /* boundary  */ 0,
609                                 /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
610                                 /* highaddr  */ BUS_SPACE_MAXADDR,
611                                 /* filter    */ NULL,
612                                 /* filterarg */ NULL,
613                                 /* maxsize   */ IPS_NVRAM_PAGE_SIZE,
614                                 /* numsegs   */ 1,
615                                 /* maxsegsize*/ IPS_NVRAM_PAGE_SIZE,
616                                 /* flags     */ 0,
617                                 &command->data_dmatag) != 0) {
618                 printf("ips: can't alloc dma tag for nvram\n");
619                 error = ENOMEM;
620                 goto exit;
621         }
622         if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
623             BUS_DMA_NOWAIT, &command->data_dmamap)) {
624                 error = ENOMEM;
625                 goto exit;
626         }
627         command->callback = ips_write_nvram;
628         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
629             command->data_buffer, IPS_NVRAM_PAGE_SIZE, ips_read_nvram_callback,
630             command, BUS_DMA_NOWAIT);
631         if ((status->value == IPS_ERROR_STATUS) ||
632             tsleep(status, 0, "ips", 0) == EWOULDBLOCK)
633                 error = ETIMEDOUT;
634         if (error == 0) {
635                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
636                                 BUS_DMASYNC_POSTWRITE);
637         }
638         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
639 exit:
640         bus_dmamem_free(command->data_dmatag, command->data_buffer,
641                         command->data_dmamap);
642         bus_dma_tag_destroy(command->data_dmatag);
643         ips_insert_free_cmd(sc, command);
644         return error;
645 }
646
647 int
648 ips_update_nvram(ips_softc_t *sc)
649 {
650         ips_cmd_status_t *status;
651
652         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
653         if (ips_get_free_cmd(sc, ips_read_nvram, status, IPS_NOWAIT_FLAG)) {
654                 free(status, M_IPSBUF);
655                 device_printf(sc->dev, "ERROR: unable to get a command! "
656                     "can't update nvram\n");
657                 return 1;
658         }
659         if (COMMAND_ERROR(status)) {
660                 free(status, M_IPSBUF);
661                 device_printf(sc->dev, "ERROR: nvram update command failed!\n");
662                 return 1;
663         }
664         free(status, M_IPSBUF);
665         return 0;
666 }
667
668 static int
669 ips_send_config_sync_cmd(ips_command_t *command)
670 {
671         ips_softc_t *sc = command->sc;
672         ips_cmd_status_t *status = command->arg;
673         ips_generic_cmd *command_struct;
674
675         PRINTF(10, "ips test: got a command, building flush command\n");
676         command->callback = ips_wakeup_callback;
677         command_struct = (ips_generic_cmd *)command->command_buffer;
678         command_struct->command = IPS_CONFIG_SYNC_CMD;
679         command_struct->id = command->id;
680         command_struct->reserve2 = IPS_POCL;
681         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
682             BUS_DMASYNC_PREWRITE);
683         sc->ips_issue_cmd(command);
684         if (status->value != IPS_ERROR_STATUS)
685                 tsleep(status, 0, "ipssyn", 0);
686         ips_insert_free_cmd(sc, command);
687         return 0;
688 }
689
690 static int
691 ips_send_error_table_cmd(ips_command_t *command)
692 {
693         ips_softc_t *sc = command->sc;
694         ips_cmd_status_t *status = command->arg;
695         ips_generic_cmd *command_struct;
696
697         PRINTF(10, "ips test: got a command, building errortable command\n");
698         command->callback = ips_wakeup_callback;
699         command_struct = (ips_generic_cmd *)command->command_buffer;
700         command_struct->command = IPS_ERROR_TABLE_CMD;
701         command_struct->id = command->id;
702         command_struct->reserve2 = IPS_CSL;
703         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
704             BUS_DMASYNC_PREWRITE);
705         sc->ips_issue_cmd(command);
706         if (status->value != IPS_ERROR_STATUS)
707                 tsleep(status, 0, "ipsetc", 0);
708         ips_insert_free_cmd(sc, command);
709         return 0;
710 }
711
712 int
713 ips_clear_adapter(ips_softc_t *sc)
714 {
715         ips_cmd_status_t *status;
716
717         status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
718         device_printf(sc->dev, "syncing config\n");
719         if (ips_get_free_cmd(sc, ips_send_config_sync_cmd, status,
720             IPS_NOWAIT_FLAG)) {
721                 free(status, M_IPSBUF);
722                 device_printf(sc->dev, "ERROR: unable to get a command! "
723                     "can't sync cache!\n");
724                 return 1;
725         }
726         if (COMMAND_ERROR(status)) {
727                 free(status, M_IPSBUF);
728                 device_printf(sc->dev, "ERROR: cache sync command failed!\n");
729                 return 1;
730         }
731         device_printf(sc->dev, "clearing error table\n");
732         if (ips_get_free_cmd(sc, ips_send_error_table_cmd, status,
733             IPS_NOWAIT_FLAG)) {
734                 free(status, M_IPSBUF);
735                 device_printf(sc->dev, "ERROR: unable to get a command! "
736                     "can't sync cache!\n");
737                 return 1;
738         }
739         if (COMMAND_ERROR(status)) {
740                 device_printf(sc->dev, "ERROR: etable command failed!\n");
741                 free(status, M_IPSBUF);
742                 return 1;
743         }
744         free(status, M_IPSBUF);
745         return 0;
746 }